├── .builds ├── alpine-aarch64.yml ├── alpine-riscv64.yml ├── alpine.yml ├── debian-aarch64.yml ├── debian-riscv64.yml ├── debian.yml ├── freebsd.yml ├── musl-riscv64.diff ├── netbsd.diff ├── netbsd.yml ├── nixos.yml ├── openbsd.diff └── openbsd.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── arg.h ├── attr.c ├── cc.h ├── configure ├── cproc.1 ├── decl.c ├── doc ├── c23.md ├── extensions.md └── software.md ├── driver.c ├── eval.c ├── expr.c ├── init.c ├── main.c ├── map.c ├── ops.h ├── pp.c ├── qbe.c ├── runtests ├── scan.c ├── scope.c ├── stmt.c ├── targ.c ├── test ├── abstract-function-declarator.c ├── abstract-function-declarator.qbe ├── add-int-pointer.c ├── add-int-pointer.qbe ├── alignas-0.c ├── alignas-0.qbe ├── alignas-local-strict.c ├── alignas-local-strict.qbe ├── alignas-local.c ├── alignas-local.qbe ├── alignas-member.c ├── alignas-member.qbe ├── alignas-multiple.c ├── alignas-multiple.qbe ├── alignas-type.c ├── alignas-type.qbe ├── alignas-vla-strict.c ├── alignas-vla-strict.qbe ├── alignas.c ├── alignas.qbe ├── array-address.c ├── array-address.qbe ├── asm-label.c ├── asm-label.qbe ├── attribute-syntax.c ├── attribute-syntax.qbe ├── basic.c ├── basic.qbe ├── binary-constant.c ├── binary-constant.qbe ├── bitfield-assignment-sign-extend.c ├── bitfield-assignment-sign-extend.qbe ├── bitfield-compound-assign.c ├── bitfield-compound-assign.qbe ├── bitfield-initializer-overflow.c ├── bitfield-initializer-overflow.qbe ├── bitfield-integer-promotion-long.c ├── bitfield-integer-promotion-long.qbe ├── bitfield-integer-promotion.c ├── bitfield-integer-promotion.qbe ├── bitfield-load-signed.c ├── bitfield-load-signed.qbe ├── bitfield-load-unsigned.c ├── bitfield-load-unsigned.qbe ├── bitfield-non-adjacent.c ├── bitfield-non-adjacent.qbe ├── bitfield-short.c ├── bitfield-short.qbe ├── bitfield-union.c ├── bitfield-union.qbe ├── bitfield-unnamed-size-align.c ├── bitfield-unnamed-size-align.qbe ├── builtin-alloca.c ├── builtin-alloca.qbe ├── builtin-constant-p.c ├── builtin-constant-p.qbe ├── builtin-expect.c ├── builtin-expect.qbe ├── builtin-inff.c ├── builtin-inff.qbe ├── builtin-nanf.c ├── builtin-nanf.qbe ├── builtin-offsetof.c ├── builtin-offsetof.qbe ├── builtin-types-compatible-p.c ├── builtin-types-compatible-p.qbe ├── builtin-va-copy+aarch64.c ├── builtin-va-copy+aarch64.qbe ├── builtin-va-copy+x86_64-sysv.c ├── builtin-va-copy+x86_64-sysv.qbe ├── builtin-vaarg-vm.c ├── builtin-vaarg-vm.qbe ├── cast-bool-char.c ├── cast-bool-char.qbe ├── cast-vm.c ├── cast-vm.qbe ├── char-const-u8.c ├── char-const-u8.qbe ├── char-sign+aarch64.c ├── char-sign+aarch64.qbe ├── char-sign+riscv64.c ├── char-sign+riscv64.qbe ├── char-sign+x86_64-sysv.c ├── char-sign+x86_64-sysv.qbe ├── common-real-int-sign.c ├── common-real-int-sign.qbe ├── common-real-unsigned-char.c ├── common-real-unsigned-char.qbe ├── compare-char.c ├── compare-char.qbe ├── compatible-array-types.c ├── compatible-array-types.qbe ├── compatible-enum-types.c ├── compatible-enum-types.qbe ├── compatible-function-types.c ├── compatible-function-types.qbe ├── compatible-vla-types.c ├── compatible-vla-types.qbe ├── compound-assignment.c ├── compound-assignment.qbe ├── compound-literal-static.c ├── compound-literal-static.qbe ├── conditional-compound-literal.c ├── conditional-compound-literal.qbe ├── conditional-constant.c ├── conditional-constant.qbe ├── conditional.c ├── conditional.qbe ├── const-array.c ├── const-array.qbe ├── const-expr-cast.c ├── const-expr-cast.qbe ├── const-expr-div.c ├── const-expr-div.qbe ├── const-expr-mod.c ├── const-expr-mod.qbe ├── const-expr-neg.c ├── const-expr-neg.qbe ├── const-expr-shr.c ├── const-expr-shr.qbe ├── const-false.c ├── const-false.qbe ├── const-init.c ├── const-init.qbe ├── const-true.c ├── const-true.qbe ├── do-loop.c ├── do-loop.qbe ├── enum-fixed.c ├── enum-fixed.qbe ├── enum-large-value.c ├── enum-large-value.qbe ├── enum.c ├── enum.qbe ├── escaped-newline.c ├── escaped-newline.qbe ├── expr-neg.c ├── expr-neg.qbe ├── extern-initializer.c ├── extern-initializer.qbe ├── float-const-leading-dot.c ├── float-const-leading-dot.qbe ├── float-promote.c ├── float-promote.qbe ├── float-to-uint32.c ├── float-to-uint32.qbe ├── float-to-uint64.c ├── float-to-uint64.qbe ├── for-loop.c ├── for-loop.qbe ├── func-array-param.c ├── func-array-param.qbe ├── func-noreturn.c ├── func-noreturn.qbe ├── func-param-scope.c ├── func-param-scope.qbe ├── func-unnamed-param.c ├── func-unnamed-param.qbe ├── func-vla.c ├── func-vla.qbe ├── generic.c ├── generic.qbe ├── global-align.c ├── global-align.qbe ├── hello.c ├── hello.qbe ├── if-char.c ├── if-char.qbe ├── initializer-address-subtract.c ├── initializer-address-subtract.qbe ├── initializer-cast-float-int.c ├── initializer-cast-float-int.qbe ├── initializer-cast-int-float.c ├── initializer-cast-int-float.qbe ├── initializer-empty-struct.c ├── initializer-empty-struct.qbe ├── initializer-empty.c ├── initializer-empty.qbe ├── initializer-long-string.c ├── initializer-long-string.qbe ├── initializer-member-static.c ├── initializer-member-static.qbe ├── initializer-nested-array-address.c ├── initializer-nested-array-address.qbe ├── initializer-pointer-int-cast.c ├── initializer-pointer-int-cast.qbe ├── initializer-replace-local-string-wide.c ├── initializer-replace-local-string-wide.qbe ├── initializer-replace-local.c ├── initializer-replace-local.qbe ├── initializer-replace-static-string-wide.c ├── initializer-replace-static-string-wide.qbe ├── initializer-replace-static.c ├── initializer-replace-static.qbe ├── initializer-short-string.c ├── initializer-short-string.qbe ├── initializer-string-array.c ├── initializer-string-array.qbe ├── initializer-string-braces.c ├── initializer-string-braces.qbe ├── initializer-string-wide.c ├── initializer-string-wide.qbe ├── initializer-string.c ├── initializer-string.qbe ├── initializer-unsigned-string.c ├── initializer-unsigned-string.qbe ├── inline-definition.c ├── inline-definition.qbe ├── label-typedef.c ├── label-typedef.qbe ├── local-align.c ├── local-align.qbe ├── local-init.c ├── local-init.qbe ├── logical-and.c ├── logical-and.qbe ├── logical-or.c ├── logical-or.qbe ├── lvalue-conversion.c ├── lvalue-conversion.qbe ├── nested-array.c ├── nested-array.qbe ├── nullptr.c ├── nullptr.qbe ├── preprocess-macro-function-no-args.c ├── preprocess-macro-function-no-args.pp ├── preprocess-macro-function-paren.c ├── preprocess-macro-function-paren.pp ├── preprocess-macro-function.c ├── preprocess-macro-function.pp ├── preprocess-macro-hide.c ├── preprocess-macro-hide.pp ├── preprocess-macro-object.c ├── preprocess-macro-object.pp ├── preprocess-macro-stringize-2.c ├── preprocess-macro-stringize-2.pp ├── preprocess-macro-stringize.c ├── preprocess-macro-stringize.pp ├── preprocess-macro-vararg.c ├── preprocess-macro-vararg.pp ├── preprocess-standard-example-1.c ├── preprocess-standard-example-1.pp ├── preprocess-standard-example-2.c ├── preprocess-standard-example-2.pp ├── preprocess-undef.c ├── preprocess-undef.pp ├── sizeof-compound-literal.c ├── sizeof-compound-literal.qbe ├── sizeof-postfix.c ├── sizeof-postfix.qbe ├── sizeof-string-literal.c ├── sizeof-string-literal.qbe ├── sizeof-vla.c ├── sizeof-vla.qbe ├── static-assert-concat.c ├── static-assert-concat.qbe ├── static-assert-struct.c ├── static-assert-struct.qbe ├── string-concat.c ├── string-concat.qbe ├── string-octal.c ├── string-octal.qbe ├── string-u8-type.c ├── string-u8-type.qbe ├── struct-copy.c ├── struct-copy.qbe ├── struct-flexible-array.c ├── struct-flexible-array.qbe ├── struct-packed.c ├── struct-packed.qbe ├── struct-passing-bitfield.c ├── struct-passing-bitfield.qbe ├── struct-passing-call.c ├── struct-passing-call.qbe ├── struct-passing.c ├── struct-passing.qbe ├── struct-return-1.c ├── struct-return-1.qbe ├── struct-return-2.c ├── struct-return-2.qbe ├── subtract-pointer.c ├── subtract-pointer.qbe ├── switch-long-long.c ├── switch-long-long.qbe ├── switch.c ├── switch.qbe ├── tentative.c ├── tentative.qbe ├── thread-local.c ├── thread-local.qbe ├── typedef-name.c ├── typedef-name.qbe ├── typedef-qual.c ├── typedef-qual.qbe ├── typedef.c ├── typedef.qbe ├── typeof-array.c ├── typeof-array.qbe ├── typeof-decay.c ├── typeof-decay.qbe ├── typeof-vm.c ├── typeof-vm.qbe ├── typeof.c ├── typeof.qbe ├── typeof_unqual.c ├── typeof_unqual.qbe ├── uint32-to-float.c ├── uint32-to-float.qbe ├── uint64-to-float.c ├── uint64-to-float.qbe ├── union-passing.c ├── union-passing.qbe ├── union-struct-flexible-array.c ├── union-struct-flexible-array.qbe ├── union.c ├── union.qbe ├── unreachable.c ├── unreachable.qbe ├── unused-return.c ├── unused-return.qbe ├── varargs+aarch64.c ├── varargs+aarch64.qbe ├── varargs+riscv64.c ├── varargs+riscv64.qbe ├── varargs+x86_64-sysv.c ├── varargs+x86_64-sysv.qbe ├── vla-deref.c ├── vla-deref.qbe ├── vla-nested.c ├── vla-nested.qbe ├── vla.c ├── vla.qbe ├── wchar-sign+aarch64.c ├── wchar-sign+aarch64.qbe ├── wchar-sign+riscv64.c ├── wchar-sign+riscv64.qbe ├── wchar-sign+x86_64-sysv.c ├── wchar-sign+x86_64-sysv.qbe ├── while-condition.c └── while-condition.qbe ├── token.c ├── tree.c ├── type.c ├── utf.c ├── utf.h ├── util.c └── util.h /.builds/alpine-aarch64.yml: -------------------------------------------------------------------------------- 1 | image: alpine/latest 2 | packages: 3 | - qemu-aarch64 4 | - qemu-openrc 5 | sources: 6 | - https://git.sr.ht/~mcf/cproc 7 | - git://c9x.me/qbe.git 8 | tasks: 9 | - setup: | 10 | sudo /etc/init.d/qemu-binfmt start 11 | curl -O http://musl.cc/aarch64-linux-musl-cross.tgz 12 | tar --warning=no-unknown-keyword -xzf aarch64-linux-musl-cross.tgz 13 | - build: | 14 | PATH=$HOME/qbe:$HOME/aarch64-linux-musl-cross/bin:$PATH 15 | make -C qbe 16 | cd cproc 17 | ./configure \ 18 | --target=aarch64-linux-musl \ 19 | --with-ldso="$HOME/aarch64-linux-musl-cross/aarch64-linux-musl/lib/libc.so" 20 | make all check bootstrap 21 | file stage2/cproc stage2/cproc-qbe 22 | triggers: 23 | - action: email 24 | condition: failure 25 | to: "<~mcf/cproc-builds@lists.sr.ht>" 26 | -------------------------------------------------------------------------------- /.builds/alpine-riscv64.yml: -------------------------------------------------------------------------------- 1 | image: alpine/latest 2 | packages: 3 | - qemu-riscv64 4 | - qemu-openrc 5 | sources: 6 | - https://git.sr.ht/~mcf/cproc 7 | - git://c9x.me/qbe.git 8 | tasks: 9 | - setup: | 10 | sudo /etc/init.d/qemu-binfmt start 11 | curl -O http://musl.cc/riscv64-linux-musl-cross.tgz 12 | tar --warning=no-unknown-keyword -xzf riscv64-linux-musl-cross.tgz 13 | patch -p0 -d riscv64-linux-musl-cross/riscv64-linux-musl/include < cproc/.builds/musl-riscv64.diff 14 | - build: | 15 | PATH=$HOME/qbe:$HOME/riscv64-linux-musl-cross/bin:$PATH 16 | make -C qbe 17 | cd cproc 18 | ./configure \ 19 | --target=riscv64-linux-musl \ 20 | --with-ldso="$HOME/riscv64-linux-musl-cross/riscv64-linux-musl/lib/libc.so" \ 21 | CFLAGS='-std=c11' 22 | make all check bootstrap 23 | file stage2/cproc stage2/cproc-qbe 24 | triggers: 25 | - action: email 26 | condition: failure 27 | to: "<~mcf/cproc-builds@lists.sr.ht>" 28 | -------------------------------------------------------------------------------- /.builds/alpine.yml: -------------------------------------------------------------------------------- 1 | image: alpine/edge 2 | packages: 3 | - bison 4 | - flex 5 | sources: 6 | - https://git.sr.ht/~mcf/cproc 7 | - git://c9x.me/qbe.git 8 | tasks: 9 | - build: | 10 | PATH=$HOME/qbe:$PATH 11 | make -C qbe 12 | cd cproc 13 | ./configure 14 | make all check bootstrap 15 | triggers: 16 | - action: email 17 | condition: failure 18 | to: "<~mcf/cproc-builds@lists.sr.ht>" 19 | -------------------------------------------------------------------------------- /.builds/debian-aarch64.yml: -------------------------------------------------------------------------------- 1 | image: debian/stable 2 | packages: 3 | - gcc-aarch64-linux-gnu 4 | - libc-dev-arm64-cross 5 | - qemu-user 6 | - qemu-user-binfmt 7 | sources: 8 | - https://git.sr.ht/~mcf/cproc 9 | - git://c9x.me/qbe.git 10 | tasks: 11 | - build: | 12 | PATH=$HOME/qbe:$PATH 13 | make -C qbe 14 | cd cproc 15 | crtbegin=$(aarch64-linux-gnu-gcc -print-file-name=crtbegin.o) 16 | ./configure \ 17 | --target=aarch64-linux-gnu \ 18 | --with-ldso=/usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1 \ 19 | --with-gcc-libdir="${crtbegin%/*}" 20 | make all check 21 | make bootstrap LDFLAGS='-Wl,-rpath,/usr/aarch64-linux-gnu/lib' 22 | aarch64-linux-gnu-readelf -h stage2/cproc stage2/cproc-qbe 23 | triggers: 24 | - action: email 25 | condition: failure 26 | to: "<~mcf/cproc-builds@lists.sr.ht>" 27 | -------------------------------------------------------------------------------- /.builds/debian-riscv64.yml: -------------------------------------------------------------------------------- 1 | image: debian/stable 2 | packages: 3 | - gcc-riscv64-linux-gnu 4 | - libc-dev-riscv64-cross 5 | - qemu-user 6 | - qemu-user-binfmt 7 | sources: 8 | - https://git.sr.ht/~mcf/cproc 9 | - git://c9x.me/qbe.git 10 | tasks: 11 | - build: | 12 | PATH=$HOME/qbe:$PATH 13 | make -C qbe 14 | cd cproc 15 | crtbegin=$(riscv64-linux-gnu-gcc -print-file-name=crtbegin.o) 16 | ./configure \ 17 | --target=riscv64-linux-gnu \ 18 | --with-ldso=/usr/riscv64-linux-gnu/lib/ld-linux-riscv64-lp64d.so.1 \ 19 | --with-gcc-libdir="${crtbegin%/*}" 20 | make all check 21 | make bootstrap LDFLAGS='-Wl,-rpath,/usr/riscv64-linux-gnu/lib' 22 | riscv64-linux-gnu-readelf -h stage2/cproc stage2/cproc-qbe 23 | triggers: 24 | - action: email 25 | condition: failure 26 | to: "<~mcf/cproc-builds@lists.sr.ht>" 27 | -------------------------------------------------------------------------------- /.builds/debian.yml: -------------------------------------------------------------------------------- 1 | image: debian/stable 2 | sources: 3 | - https://git.sr.ht/~mcf/cproc 4 | - git://c9x.me/qbe.git 5 | tasks: 6 | - build: | 7 | PATH=$HOME/qbe:$PATH 8 | make -C qbe 9 | cd cproc 10 | ./configure 11 | make all check bootstrap 12 | triggers: 13 | - action: email 14 | condition: failure 15 | to: "<~mcf/cproc-builds@lists.sr.ht>" 16 | -------------------------------------------------------------------------------- /.builds/freebsd.yml: -------------------------------------------------------------------------------- 1 | image: freebsd/latest 2 | packages: 3 | - binutils 4 | sources: 5 | - https://git.sr.ht/~mcf/cproc 6 | - git://c9x.me/qbe.git 7 | tasks: 8 | - build: | 9 | PATH=$HOME/qbe:$PATH 10 | make -C qbe 11 | cd cproc 12 | ./configure 13 | make all check bootstrap 14 | triggers: 15 | - action: email 16 | condition: failure 17 | to: "<~mcf/cproc-builds@lists.sr.ht>" 18 | -------------------------------------------------------------------------------- /.builds/musl-riscv64.diff: -------------------------------------------------------------------------------- 1 | --- bits/signal.h.orig 2 | +++ bits/signal.h 3 | @@ -19,7 +19,12 @@ 4 | }; 5 | 6 | struct __riscv_mc_q_ext_state { 7 | - unsigned long long __f[64] __attribute__((aligned(16))); 8 | +#if __STDC_VERSION__ >= 201112L 9 | + _Alignas(16) 10 | +#else 11 | + __attribute__((__aligned__(16))) 12 | +#endif 13 | + unsigned long long __f[64]; 14 | unsigned int __fcsr; 15 | unsigned int __reserved[3]; 16 | }; 17 | -------------------------------------------------------------------------------- /.builds/netbsd.diff: -------------------------------------------------------------------------------- 1 | diff --git a/stdlib.h b/stdlib.h 2 | index 51dbb0c70836..f47d0b9467ee 100644 3 | --- a/stdlib.h 4 | +++ b/stdlib.h 5 | @@ -186,12 +186,8 @@ void srandom(unsigned int) __RENAME(__srandom60); 6 | #endif 7 | #ifdef _NETBSD_SOURCE 8 | #define RANDOM_MAX 0x7fffffff /* (((long)1 << 31) - 1) */ 9 | -int mkostemp(char *, int); 10 | -int mkostemps(char *, int, int); 11 | #endif 12 | 13 | -char *mkdtemp(char *); 14 | -int mkstemp(char *); 15 | char *mktemp(char *) 16 | #ifdef __MKTEMP_OK__ 17 | __RENAME(_mktemp) 18 | @@ -206,8 +202,6 @@ int ttyslot(void); 19 | 20 | void *valloc(size_t); /* obsoleted by malloc() */ 21 | 22 | -int getsubopt(char **, char * const *, char **); 23 | - 24 | int grantpt(int); 25 | int unlockpt(int); 26 | char *ptsname(int); 27 | @@ -255,6 +249,24 @@ int posix_openpt(int); 28 | int posix_memalign(void **, size_t, size_t); 29 | #endif 30 | 31 | +/* 32 | + * The Open Group Base Specifications, Issue 7; IEEE Std 1003.1-2008 (POSIX) 33 | + * or 34 | + * X/Open Portability Guide >= Issue 4 Version 2 35 | + */ 36 | +#if (_POSIX_C_SOURCE - 0) >= 200809L || \ 37 | + (defined(_XOPEN_SOURCE) && defined(_XOPEN_SOURCE_EXTENDED)) || \ 38 | + (_XOPEN_SOURCE - 0) >= 500 || defined(_NETBSD_SOURCE) 39 | +char *mkdtemp(char *); 40 | +int mkstemp(char *); 41 | +#ifdef _NETBSD_SOURCE 42 | +int mkostemp(char *, int); 43 | +int mkostemps(char *, int, int); 44 | +#endif 45 | + 46 | +int getsubopt(char **, char * const *, char **); 47 | +#endif 48 | + 49 | /* 50 | * Implementation-defined extensions 51 | */ 52 | diff --git a/sys/cdefs.h b/sys/cdefs.h 53 | index ac8f1e3b9faf..7370015eb513 100644 54 | --- a/sys/cdefs.h 55 | +++ b/sys/cdefs.h 56 | @@ -499,13 +499,11 @@ 57 | #endif 58 | 59 | #if !defined(_STANDALONE) && !defined(_KERNEL) 60 | -#if defined(__GNUC__) || defined(__PCC__) 61 | -#define __RENAME(x) ___RENAME(x) 62 | -#elif defined(__lint__) 63 | +#if defined(__lint__) 64 | #define __RENAME(x) __symbolrename(x) 65 | #else 66 | -#error "No function renaming possible" 67 | -#endif /* __GNUC__ */ 68 | +#define __RENAME(x) ___RENAME(x) 69 | +#endif /* __lint__ */ 70 | #else /* _STANDALONE || _KERNEL */ 71 | #define __RENAME(x) no renaming in kernel/standalone environment 72 | #endif 73 | -------------------------------------------------------------------------------- /.builds/netbsd.yml: -------------------------------------------------------------------------------- 1 | image: netbsd/latest 2 | sources: 3 | - https://git.sr.ht/~mcf/cproc 4 | - git://c9x.me/qbe.git 5 | tasks: 6 | - setup: | 7 | sudo patch -p1 -d /usr/include < cproc/.builds/netbsd.diff 8 | - build: | 9 | PATH=$HOME/qbe:$PATH 10 | make -C qbe 11 | cd cproc 12 | ./configure 13 | make all check bootstrap 14 | -------------------------------------------------------------------------------- /.builds/nixos.yml: -------------------------------------------------------------------------------- 1 | image: nixos/latest 2 | sources: 3 | - https://git.sr.ht/~mcf/cproc 4 | - git://c9x.me/qbe.git 5 | repositories: 6 | nixpkgs: https://nixos.org/channels/nixpkgs-unstable 7 | packages: 8 | - nixpkgs.gcc 9 | - nixpkgs.gnumake 10 | tasks: 11 | - build: | 12 | PATH=$HOME/qbe:$PATH 13 | make -C qbe CC=cc 14 | cd cproc 15 | ./configure --with-ldso= 16 | make all check bootstrap 17 | triggers: 18 | - action: email 19 | condition: failure 20 | to: "<~mcf/cproc-builds@lists.sr.ht>" 21 | -------------------------------------------------------------------------------- /.builds/openbsd.diff: -------------------------------------------------------------------------------- 1 | --- machine/endian.h.orig Sat Sep 5 19:21:50 2020 2 | +++ machine/endian.h Sat Sep 5 19:22:51 2020 3 | @@ -31,6 +31,8 @@ 4 | #include 5 | #endif 6 | 7 | +#ifdef __GNUC__ 8 | + 9 | static __inline __uint16_t 10 | __swap16md(__uint16_t _x) 11 | { 12 | @@ -54,6 +56,8 @@ 13 | 14 | /* Tell sys/endian.h we have MD variants of the swap macros. */ 15 | #define __HAVE_MD_SWAP 16 | + 17 | +#endif 18 | 19 | #define _BYTE_ORDER _LITTLE_ENDIAN 20 | 21 | --- machine/_types.h.orig Sat Mar 23 19:04:19 2024 22 | +++ machine/_types.h Sat Mar 23 19:04:36 2024 23 | @@ -122,11 +122,7 @@ 24 | typedef long __ptrdiff_t; 25 | typedef unsigned long __size_t; 26 | typedef long __ssize_t; 27 | -#if defined(__GNUC__) && __GNUC__ >= 3 28 | typedef __builtin_va_list __va_list; 29 | -#else 30 | -typedef char * __va_list; 31 | -#endif 32 | 33 | /* Wide character support types */ 34 | #ifndef __cplusplus 35 | -------------------------------------------------------------------------------- /.builds/openbsd.yml: -------------------------------------------------------------------------------- 1 | image: openbsd/latest 2 | sources: 3 | - https://git.sr.ht/~mcf/cproc 4 | - git://c9x.me/qbe.git 5 | tasks: 6 | - setup: | 7 | # avoid __asm__ on non __GNUC__ compilers 8 | doas patch -d /usr/include < cproc/.builds/openbsd.diff 9 | - build: | 10 | PATH=$HOME/qbe:$PATH 11 | make -C qbe 12 | cd cproc 13 | ./configure 14 | make all check bootstrap 15 | triggers: 16 | - action: email 17 | condition: failure 18 | to: "<~mcf/cproc-builds@lists.sr.ht>" 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | /cproc 3 | /cproc-qbe 4 | /config.h 5 | /config.mk 6 | /stage2 7 | /stage3 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2019-2024 Michael Forney 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- 15 | tree.c is based on musl's src/search/tsearch.c by Szabolcs Nagy 16 | 17 | Copyright © 2005-2014 Rich Felker, et al. 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining 20 | a copy of this software and associated documentation files (the 21 | "Software"), to deal in the Software without restriction, including 22 | without limitation the rights to use, copy, modify, merge, publish, 23 | distribute, sublicense, and/or sell copies of the Software, and to 24 | permit persons to whom the Software is furnished to do so, subject to 25 | the following conditions: 26 | 27 | The above copyright notice and this permission notice shall be 28 | included in all copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 31 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 32 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 33 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 34 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 35 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 36 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37 | -------------------------------------------------------------------------------- 38 | test/* is in public domain through Unlicense: 39 | 40 | This is free and unencumbered software released into the public domain. 41 | 42 | Anyone is free to copy, modify, publish, use, compile, sell, or 43 | distribute this software, either in source code form or as a compiled 44 | binary, for any purpose, commercial or non-commercial, and by any 45 | means. 46 | 47 | In jurisdictions that recognize copyright laws, the author or authors 48 | of this software dedicate any and all copyright interest in the 49 | software to the public domain. We make this dedication for the benefit 50 | of the public at large and to the detriment of our heirs and 51 | successors. We intend this dedication to be an overt act of 52 | relinquishment in perpetuity of all present and future rights to this 53 | software under copyright law. 54 | 55 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 56 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 57 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 58 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 59 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 60 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 61 | OTHER DEALINGS IN THE SOFTWARE. 62 | 63 | For more information, please refer to 64 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | PREFIX=/usr/local 4 | BINDIR=$(PREFIX)/bin 5 | MANDIR=$(PREFIX)/share/man 6 | BACKEND=qbe 7 | 8 | objdir=. 9 | -include config.mk 10 | 11 | .PHONY: all 12 | all: $(objdir)/cproc $(objdir)/cproc-qbe 13 | 14 | DRIVER_SRC=\ 15 | driver.c\ 16 | util.c 17 | DRIVER_OBJ=$(DRIVER_SRC:%.c=$(objdir)/%.o) 18 | 19 | config.h: 20 | ./configure 21 | 22 | $(objdir)/cproc: $(DRIVER_OBJ) 23 | $(CC) $(LDFLAGS) -o $@ $(DRIVER_OBJ) 24 | 25 | SRC=\ 26 | attr.c\ 27 | decl.c\ 28 | eval.c\ 29 | expr.c\ 30 | init.c\ 31 | main.c\ 32 | map.c\ 33 | pp.c\ 34 | scan.c\ 35 | scope.c\ 36 | stmt.c\ 37 | targ.c\ 38 | token.c\ 39 | tree.c\ 40 | type.c\ 41 | utf.c\ 42 | util.c\ 43 | $(BACKEND).c 44 | OBJ=$(SRC:%.c=$(objdir)/%.o) 45 | 46 | $(objdir)/cproc-qbe: $(OBJ) 47 | $(CC) $(LDFLAGS) -o $@ $(OBJ) 48 | 49 | $(objdir)/attr.o : attr.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ attr.c 50 | $(objdir)/decl.o : decl.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ decl.c 51 | $(objdir)/driver.o : driver.c util.h config.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ driver.c 52 | $(objdir)/eval.o : eval.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ eval.c 53 | $(objdir)/expr.o : expr.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ expr.c 54 | $(objdir)/init.o : init.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ init.c 55 | $(objdir)/main.o : main.c util.h cc.h arg.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ main.c 56 | $(objdir)/map.o : map.c util.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ map.c 57 | $(objdir)/pp.o : pp.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ pp.c 58 | $(objdir)/qbe.o : qbe.c util.h cc.h ops.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ qbe.c 59 | $(objdir)/scan.o : scan.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ scan.c 60 | $(objdir)/scope.o : scope.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ scope.c 61 | $(objdir)/stmt.o : stmt.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ stmt.c 62 | $(objdir)/targ.o : targ.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ targ.c 63 | $(objdir)/token.o : token.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ token.c 64 | $(objdir)/tree.o : tree.c util.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ tree.c 65 | $(objdir)/type.o : type.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ type.c 66 | $(objdir)/utf.o : utf.c utf.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ utf.c 67 | $(objdir)/util.o : util.c util.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ util.c 68 | 69 | # Make sure stage2 and stage3 binaries are stripped by adding -s to 70 | # LDFLAGS. Otherwise they will contain paths to object files, which 71 | # differ between stages. 72 | 73 | .PHONY: stage2 74 | stage2: all 75 | @mkdir -p $@ 76 | $(MAKE) objdir=$@ stagedeps='cproc cproc-qbe' CC=$(objdir)/cproc LDFLAGS='$(LDFLAGS) -s' 77 | 78 | .PHONY: stage3 79 | stage3: stage2 80 | @mkdir -p $@ 81 | $(MAKE) objdir=$@ stagedeps='stage2/cproc stage2/cproc-qbe' CC=$(objdir)/stage2/cproc LDFLAGS='$(LDFLAGS) -s' 82 | 83 | .PHONY: bootstrap 84 | bootstrap: stage2 stage3 85 | cmp stage2/cproc stage3/cproc 86 | cmp stage2/cproc-qbe stage3/cproc-qbe 87 | 88 | .PHONY: check 89 | check: all 90 | @CCQBE=./cproc-qbe ./runtests 91 | 92 | .PHONY: install 93 | install: all 94 | mkdir -p $(DESTDIR)$(BINDIR) 95 | cp $(objdir)/cproc $(objdir)/cproc-qbe $(DESTDIR)$(BINDIR) 96 | mkdir -p $(DESTDIR)$(MANDIR)/man1 97 | cp cproc.1 $(DESTDIR)$(MANDIR)/man1 98 | 99 | .PHONY: clean 100 | clean: 101 | rm -rf cproc $(DRIVER_OBJ) cproc-qbe $(OBJ) stage2 stage3 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | (mirrored on [GitHub][GitHub mirror]) 2 | 3 | [![builds.sr.ht status](https://builds.sr.ht/~mcf/cproc/commits/master.svg)](https://builds.sr.ht/~mcf/cproc/commits/master) 4 | 5 | `cproc` is a [C11] compiler using [QBE] as a backend. It is released 6 | under the [ISC] license. 7 | 8 | Some [C23 features] and [GNU C extensions] are also implemented. 9 | 10 | There is still much to do, but it currently implements most of the 11 | language and is capable of [building software] including itself, mcpp, 12 | gcc 4.7, binutils, and more. 13 | 14 | It was inspired by several other small C compilers including [8cc], 15 | [c], [lacc], and [scc]. 16 | 17 | ## Requirements 18 | 19 | The compiler itself is written in standard C99 and can be built with 20 | any conforming C99 compiler. 21 | 22 | The POSIX driver depends on POSIX.1-2008 interfaces, and the `Makefile` 23 | requires a POSIX-compatible make(1). 24 | 25 | At runtime, you will need QBE, an assembler, and a linker for the 26 | target system. Since the preprocessor is not yet implemented, an 27 | external one is currently required as well. 28 | 29 | ## Supported targets 30 | 31 | All architectures supported by QBE should work (currently x86\_64, 32 | aarch64, and riscv64). 33 | 34 | The following targets are tested by the continuous build and known to 35 | bootstrap and pass all tests: 36 | 37 | - `x86_64-linux-musl` 38 | - `x86_64-linux-gnu` 39 | - `x86_64-freebsd` 40 | - `aarch64-linux-musl` 41 | - `aarch64-linux-gnu` 42 | - `riscv64-linux-gnu` 43 | 44 | ## Building 45 | 46 | Run `./configure` to create a `config.h` and `config.mk` appropriate for 47 | your system. If your system is not supported by the configure script, 48 | you can create these files manually. `config.h` should define several 49 | string arrays (`static char *[]`): 50 | 51 | - **`startfiles`**: Objects to pass to the linker at the beginning of 52 | the link command. 53 | - **`endfiles`**: Objects to pass to the linker at the end of the link 54 | command (including libc). 55 | - **`preprocesscmd`**: The preprocessor command, and any necessary flags 56 | for the target system. 57 | - **`codegencmd`**: The QBE command, and possibly explicit target flags. 58 | - **`assemblecmd`**: The assembler command. 59 | - **`linkcmd`**: The linker command. 60 | 61 | You may also want to customize your environment or `config.mk` with the 62 | appropriate `CC`, `CFLAGS` and `LDFLAGS`. 63 | 64 | Once this is done, you can build with 65 | 66 | make 67 | 68 | ### Bootstrap 69 | 70 | The `Makefile` includes several other targets that can be used for 71 | bootstrapping. These targets require the ability to run the tools 72 | specified in `config.h`. 73 | 74 | - **`stage2`**: Build the compiler with the initial (`stage1`) output. 75 | - **`stage3`**: Build the compiler with the `stage2` output. 76 | - **`bootstrap`**: Build the `stage2` and `stage3` compilers, and verify 77 | that they are byte-wise identical. 78 | 79 | ## What's missing 80 | 81 | - Digraph sequences ([6.4.6p3], will not be implemented). 82 | - Variable-length arrays ([#1]). 83 | - `volatile`-qualified types ([#7]). 84 | - `long double` type ([#3]). 85 | - Inline assembly ([#5]). 86 | - Preprocessor ([#6]). 87 | - Generation of position independent code (i.e. shared libraries, 88 | modules, PIEs). 89 | 90 | ## Mailing list 91 | 92 | There is a mailing list at [~mcf/cproc@lists.sr.ht]. Feel free to 93 | use it for general discussion, questions, patches, or bug reports 94 | (if you don't have an sr.ht account). 95 | 96 | ## Issue tracker 97 | 98 | Please report any issues to [~mcf/cproc@todo.sr.ht]. 99 | 100 | ## Contributing 101 | 102 | Patches are greatly appreciated. Send them to the mailing list 103 | (preferred), or as pull-requests on the [GitHub mirror]. 104 | 105 | [QBE]: https://c9x.me/compile/ 106 | [C11]: http://port70.net/~nsz/c/c11/n1570.html 107 | [ISC]: https://git.sr.ht/~mcf/cproc/blob/master/LICENSE 108 | [C23 features]: https://man.sr.ht/~mcf/cproc/doc/c23.md 109 | [GNU C extensions]: https://man.sr.ht/~mcf/cproc/doc/extensions.md 110 | [building software]: https://man.sr.ht/~mcf/cproc/doc/software.md 111 | [8cc]: https://github.com/rui314/8cc 112 | [c]: https://github.com/andrewchambers/c 113 | [lacc]: https://github.com/larmel/lacc 114 | [scc]: http://www.simple-cc.org/ 115 | [5.2.1.1]: http://port70.net/~nsz/c/c11/n1570.html#5.2.1.1 116 | [6.4.6p3]: http://port70.net/~nsz/c/c11/n1570.html#6.4.6p3 117 | [#1]: https://todo.sr.ht/~mcf/cproc/1 118 | [#3]: https://todo.sr.ht/~mcf/cproc/3 119 | [#5]: https://todo.sr.ht/~mcf/cproc/5 120 | [#6]: https://todo.sr.ht/~mcf/cproc/6 121 | [#7]: https://todo.sr.ht/~mcf/cproc/7 122 | [#35]: https://todo.sr.ht/~mcf/cproc/35 123 | [#44]: https://todo.sr.ht/~mcf/cproc/44 124 | [~mcf/cproc@lists.sr.ht]: https://lists.sr.ht/~mcf/cproc 125 | [~mcf/cproc@todo.sr.ht]: https://todo.sr.ht/~mcf/cproc 126 | [GitHub mirror]: https://github.com/michaelforney/cproc 127 | -------------------------------------------------------------------------------- /arg.h: -------------------------------------------------------------------------------- 1 | #define ARGBEGIN \ 2 | for (;;) { \ 3 | if (argc > 0) \ 4 | ++argv, --argc; \ 5 | if (argc == 0 || (*argv)[0] != '-') \ 6 | break; \ 7 | if ((*argv)[1] == '-' && !(*argv)[2]) { \ 8 | ++argv, --argc; \ 9 | break; \ 10 | } \ 11 | for (char *opt_ = &(*argv)[1], done_ = 0; !done_ && *opt_; ++opt_) { \ 12 | switch (*opt_) 13 | 14 | #define ARGEND \ 15 | } \ 16 | } 17 | 18 | #define EARGF(x) \ 19 | (done_ = 1, opt_[1] ? ++opt_ : argv[1] ? --argc, *++argv : ((x), abort(), (char *)0)) 20 | -------------------------------------------------------------------------------- /attr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "util.h" 5 | #include "cc.h" 6 | 7 | enum attrprefix { 8 | PREFIXNONE = 1, /* standard attribute */ 9 | PREFIXGNU, 10 | }; 11 | 12 | static char * 13 | strip(char *name) 14 | { 15 | size_t len; 16 | 17 | len = strlen(name); 18 | if (len >= 4 && name[0] == '_' && name[1] == '_' && name[len - 2] == '_' && name[len - 1] == '_') { 19 | name[len - 2] = '\0'; 20 | name += 2; 21 | } 22 | return name; 23 | } 24 | 25 | static bool 26 | parseattr(struct attr *a, enum attrkind allowed, enum attrprefix prefix) 27 | { 28 | char *name, *prefixname = ""; 29 | enum attrkind kind; 30 | int paren; 31 | 32 | if (tok.kind != TIDENT) 33 | return false; 34 | name = strip(tok.lit); 35 | next(); 36 | if (!prefix) { 37 | if (consume(TCOLONCOLON)) { 38 | if (strcmp(name, "gnu") == 0) 39 | prefix = PREFIXGNU; 40 | else 41 | prefix = 0; 42 | name = strip(expect(TIDENT, "after attribute prefix")); 43 | } else { 44 | prefix = PREFIXNONE; 45 | } 46 | } 47 | kind = 0; 48 | switch (prefix) { 49 | case PREFIXGNU: 50 | prefixname = "GNU "; 51 | if (strcmp(name, "aligned") == 0) { 52 | kind = ATTRALIGNED; 53 | if (consume(TLPAREN)) { 54 | unsigned long long i; 55 | 56 | i = intconstexpr(&filescope, false); 57 | if (!i || i & i - 1 || i > INT_MAX) 58 | error(&tok.loc, "invalid alignment %llu", i); 59 | if (a) 60 | a->align = i; 61 | expect(TRPAREN, "after alignment"); 62 | } else { 63 | if (a) 64 | a->align = 16; 65 | } 66 | } else if (strcmp(name, "constructor") == 0) { 67 | kind = ATTRCONSTRUCTOR; 68 | } else if (strcmp(name, "destructor") == 0) { 69 | kind = ATTRDESTRUCTOR; 70 | } else if (strcmp(name, "packed") == 0) { 71 | kind = ATTRPACKED; 72 | } 73 | break; 74 | } 75 | if (kind) { 76 | if (!(kind & allowed)) 77 | error(&tok.loc, "%sattribute '%s' is not supported here", prefixname, name); 78 | if (a) 79 | a->kind |= kind; 80 | } else if (consume(TLPAREN)) { 81 | /* skip arguments */ 82 | for (paren = 1; paren > 0; next()) { 83 | switch (tok.kind) { 84 | case TLPAREN: ++paren; break; 85 | case TRPAREN: --paren; break; 86 | } 87 | } 88 | } 89 | return true; 90 | } 91 | 92 | static bool 93 | attrspec(struct attr *a, enum attrkind allowed) 94 | { 95 | if (tok.kind != TLBRACK || !peek(TLBRACK)) 96 | return false; 97 | while (parseattr(a, allowed, 0) || consume(TCOMMA)) 98 | ; 99 | expect(TRBRACK, "to end attribute specifier"); 100 | expect(TRBRACK, "to end attribute specifier"); 101 | return true; 102 | } 103 | 104 | bool 105 | attr(struct attr *a, enum attrkind allowed) 106 | { 107 | if (!attrspec(a, allowed)) 108 | return false; 109 | while (attrspec(a, allowed)) 110 | ; 111 | return true; 112 | } 113 | 114 | static bool 115 | gnuattrspec(struct attr *a, enum attrkind allowed) 116 | { 117 | if (!consume(T__ATTRIBUTE__)) 118 | return false; 119 | while (parseattr(a, allowed, PREFIXGNU) || consume(TCOMMA)) 120 | ; 121 | expect(TLPAREN, "after '__attribute__' to begin attribute specifier"); 122 | expect(TLPAREN, "after '__attribute__' to begin attribute specifier"); 123 | while (parseattr(a, allowed, PREFIXGNU) || consume(TCOMMA)) 124 | ; 125 | expect(TRPAREN, "to end attribute specifier"); 126 | expect(TRPAREN, "to end attribute specifier"); 127 | return true; 128 | } 129 | 130 | bool 131 | gnuattr(struct attr *a, enum attrkind allowed) 132 | { 133 | if (!gnuattrspec(a, allowed)) 134 | return false; 135 | while (gnuattrspec(a, allowed)) 136 | ; 137 | return true; 138 | } 139 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | fail() { 4 | echo "$0: $*" >&2 5 | exit 1 6 | } 7 | 8 | prefix=/usr/local 9 | bindir='$(PREFIX)/bin' 10 | host= 11 | target= 12 | gcclibdir= 13 | 14 | for arg ; do 15 | case "$arg" in 16 | --prefix=*) prefix=${arg#*=} ;; 17 | --bindir=*) bindir=${arg#*=} ;; 18 | --host=*) host=${arg#*=} ;; 19 | --target=*) target=${arg#*=} ;; 20 | --with-cpp=*) DEFAULT_PREPROCESSOR=${arg#*=} ;; 21 | --with-qbe=*) DEFAULT_QBE=${arg#*=} ;; 22 | --with-as=*) DEFAULT_ASSEMBLER=${arg#*=} ;; 23 | --with-ld=*) DEFAULT_LINKER=${arg#*=} ;; 24 | --with-ldso=*) DEFAULT_DYNAMIC_LINKER=${arg#*=} ;; 25 | --with-gcc-libdir=*) gcclibdir=${arg#*=} ;; 26 | CC=*) CC=${arg#*=} ;; 27 | CFLAGS=*) CFLAGS=${arg#*=} ;; 28 | LDFLAGS=*) LDFLAGS=${arg#*=} ;; 29 | *) fail "unknown option '$arg'" 30 | esac 31 | done 32 | 33 | : ${CC:=cc} 34 | 35 | printf 'checking host system type... ' 36 | test -n "$host" || host=$($CC -dumpmachine 2>/dev/null) || fail "could not determine host" 37 | printf '%s\n' "$host" 38 | 39 | printf 'checking target system type... ' 40 | test -n "$target" || target=$host 41 | printf '%s\n' "$target" 42 | 43 | toolprefix= 44 | if [ "$host" != "$target" ] ; then 45 | toolprefix=$target- 46 | fi 47 | 48 | startfiles=0 49 | endfiles=0 50 | defines= 51 | linkflags= 52 | 53 | case "$target" in 54 | *-linux-*musl*) 55 | test "${DEFAULT_DYNAMIC_LINKER+set}" || case "$target" in 56 | x86_64*) DEFAULT_DYNAMIC_LINKER=/lib/ld-musl-x86_64.so.1 ;; 57 | aarch64*) DEFAULT_DYNAMIC_LINKER=/lib/ld-musl-aarch64.so.1 ;; 58 | riscv64*) DEFAULT_DYNAMIC_LINKER=/lib/ld-musl-riscv64.so.1 ;; 59 | *) fail "unsuported target '$target'" 60 | esac 61 | startfiles='"-l", ":crt1.o", "-l", ":crti.o"' 62 | endfiles='"-l", "c", "-l", ":crtn.o"' 63 | ;; 64 | *-linux-*gnu*) 65 | test "${DEFAULT_DYNAMIC_LINKER+set}" || case "$target" in 66 | x86_64*) DEFAULT_DYNAMIC_LINKER=/lib64/ld-linux-x86-64.so.2 ;; 67 | aarch64*) DEFAULT_DYNAMIC_LINKER=/lib/ld-linux-aarch64.so.1 ;; 68 | riscv64*) DEFAULT_DYNAMIC_LINKER=/lib/ld-linux-riscv64-lp64d.so.1 ;; 69 | *) fail "unsuported target '$target'" 70 | esac 71 | startfiles='"-l", ":crt1.o", "-l", ":crti.o", "-l", ":crtbegin.o"' 72 | endfiles='"-l", "c", "-l", ":crtend.o", "-l", ":crtn.o"' 73 | if [ -z "$gcclibdir" ] ; then 74 | test "$host" = "$target" || fail "gcc libdir must be specified when building a cross-compiler" 75 | crtbegin=$($CC -print-file-name=crtbegin.o 2>/dev/null) 76 | gcclibdir=${crtbegin%/*} 77 | fi 78 | linkflags='"-L", "'$gcclibdir'",' 79 | ;; 80 | *-*freebsd*) 81 | : ${DEFAULT_DYNAMIC_LINKER:=/libexec/ld-elf.so.1} 82 | startfiles='"-l", ":crt1.o", "-l", ":crti.o"' 83 | endfiles='"-l", "c", "-l", ":crtn.o"' 84 | linkflags='"-L", "/usr/lib",' 85 | defines=' 86 | "-D", "_Pragma(x)=", 87 | "-D", "_Nullable=", 88 | "-D", "_Nonnull=", 89 | 90 | "-D", "__GNUCLIKE_BUILTIN_STDARG", 91 | "-D", "__GNUCLIKE_BUILTIN_VARARGS", 92 | 93 | /* required to define _RuneLocale, needed by xlocale/_ctype.h */ 94 | "-D", "_USE_CTYPE_INLINE_", 95 | /* workaround for #42 */ 96 | "-D", "_XLOCALE_INLINE=static inline", 97 | /* used like attribute after declarator, so _Alignas will not work here */ 98 | "-D", "__aligned(x)=", 99 | /* TLS is not yet supported (#8) */ 100 | "-D", "__NO_TLS", 101 | 102 | /* disable warnings for redefining _Pragma */ 103 | "-Wno-builtin-macro-redefined", 104 | ' 105 | ;; 106 | *-*openbsd*) 107 | : ${DEFAULT_DYNAMIC_LINKER:=/usr/libexec/ld.so} 108 | test "$host" = "$target" && : ${DEFAULT_PREPROCESSOR:=/usr/libexec/cpp} 109 | startfiles='"-l", ":crt0.o", "-l", ":crtbegin.o"' 110 | endfiles='"-l", "c", "-l", ":crtend.o"' 111 | linkflags='"-L", "/usr/lib", "-nopie",' 112 | defines=' 113 | /* required to prevent libc headers from declaring functions with conflicting linkage */ 114 | "-D", "_ANSI_LIBRARY", 115 | 116 | /* used like attribute after declarator, so _Alignas will not work here */ 117 | "-D", "__aligned(x)=", 118 | ' 119 | ;; 120 | *-*netbsd*) 121 | : ${DEFAULT_DYNAMIC_LINKER:=/usr/libexec/ld.elf_so} 122 | startfiles='"-l", ":crt0.o", "-l", ":crti.o"' 123 | endfiles='"-l", "c", "-l", ":crtn.o"' 124 | defines='"-D", "__builtin_stdarg_start(ap, last)=__builtin_va_start(ap, last)"' 125 | ;; 126 | *) 127 | fail "unknown target '$target', please create config.h manually" 128 | esac 129 | 130 | : ${DEFAULT_PREPROCESSOR:=${toolprefix}cpp} 131 | : ${DEFAULT_QBE:=qbe} 132 | : ${DEFAULT_ASSEMBLER:=${toolprefix}as} 133 | : ${DEFAULT_LINKER:=${toolprefix}ld} 134 | 135 | test "$DEFAULT_DYNAMIC_LINKER" && linkflags=$linkflags' "--dynamic-linker", "'$DEFAULT_DYNAMIC_LINKER'"' 136 | 137 | printf "creating config.h... " 138 | cat >config.h <config.mk < 119 | -#include 120 | #include 121 | #include 122 | #include 123 | @@ -15,6 +14,8 @@ 124 | #include 125 | #include 126 | 127 | +float ceilf(float); 128 | + 129 | static char *argv0; 130 | #include "arg.h" 131 | #include "st.h" 132 | ``` 133 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "util.h" 5 | #include "arg.h" 6 | #include "cc.h" 7 | 8 | static void 9 | usage(void) 10 | { 11 | fprintf(stderr, "usage: %s [input]\n", argv0); 12 | exit(2); 13 | } 14 | 15 | int 16 | main(int argc, char *argv[]) 17 | { 18 | bool pponly = false; 19 | char *output = NULL, *target = NULL; 20 | 21 | argv0 = progname(argv[0], "cproc-qbe"); 22 | ARGBEGIN { 23 | case 'E': 24 | pponly = true; 25 | break; 26 | case 't': 27 | target = EARGF(usage()); 28 | break; 29 | case 'o': 30 | output = EARGF(usage()); 31 | break; 32 | default: 33 | usage(); 34 | } ARGEND 35 | 36 | targinit(target); 37 | 38 | if (output && !freopen(output, "w", stdout)) 39 | fatal("open %s:", output); 40 | 41 | if (argc) { 42 | while (argc--) 43 | scanfrom(argv[argc], NULL); 44 | scanopen(); 45 | } else { 46 | scanfrom("", stdin); 47 | } 48 | 49 | ppinit(); 50 | if (pponly) { 51 | ppflags |= PPNEWLINE; 52 | while (tok.kind != TEOF) { 53 | tokenprint(&tok); 54 | next(); 55 | } 56 | } else { 57 | scopeinit(); 58 | while (tok.kind != TEOF) { 59 | if (!decl(&filescope, NULL)) { 60 | if (tok.kind == TSEMICOLON) 61 | error(&tok.loc, "unexpected ';' at top-level"); 62 | error(&tok.loc, "expected declaration or function definition"); 63 | } 64 | } 65 | emittentativedefns(); 66 | } 67 | 68 | fflush(stdout); 69 | if (ferror(stdout)) 70 | fatal("write failed"); 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "util.h" 6 | 7 | static unsigned long 8 | hash(const void *ptr, size_t len) 9 | { 10 | unsigned long h; 11 | const unsigned char *pos, *end; 12 | 13 | /* FNV-1a */ 14 | h = 0x811c9dc5; 15 | for (pos = ptr, end = pos + len; pos != end; ++pos) 16 | h = (h ^ *pos) * 0x1000193; 17 | return h; 18 | } 19 | 20 | void 21 | mapkey(struct mapkey *k, const void *s, size_t n) 22 | { 23 | k->str = s; 24 | k->len = n; 25 | k->hash = hash(s, n); 26 | } 27 | 28 | void 29 | mapinit(struct map *h, size_t cap) 30 | { 31 | size_t i; 32 | 33 | assert(!(cap & cap - 1)); 34 | h->len = 0; 35 | h->cap = cap; 36 | h->keys = xreallocarray(NULL, cap, sizeof(h->keys[0])); 37 | h->vals = xreallocarray(NULL, cap, sizeof(h->vals[0])); 38 | for (i = 0; i < cap; ++i) 39 | h->keys[i].str = NULL; 40 | } 41 | 42 | void 43 | mapfree(struct map *h, void del(void *)) 44 | { 45 | size_t i; 46 | 47 | if (del) { 48 | for (i = 0; i < h->cap; ++i) { 49 | if (h->keys[i].str) 50 | del(h->vals[i]); 51 | } 52 | } 53 | free(h->keys); 54 | free(h->vals); 55 | } 56 | 57 | static bool 58 | keyequal(struct mapkey *k1, struct mapkey *k2) 59 | { 60 | if (k1->hash != k2->hash || k1->len != k2->len) 61 | return false; 62 | return memcmp(k1->str, k2->str, k1->len) == 0; 63 | } 64 | 65 | static size_t 66 | keyindex(struct map *h, struct mapkey *k) 67 | { 68 | size_t i; 69 | 70 | i = k->hash & h->cap - 1; 71 | while (h->keys[i].str && !keyequal(&h->keys[i], k)) 72 | i = i + 1 & h->cap - 1; 73 | return i; 74 | } 75 | 76 | void ** 77 | mapput(struct map *h, struct mapkey *k) 78 | { 79 | struct mapkey *oldkeys; 80 | void **oldvals; 81 | size_t i, j, oldcap; 82 | 83 | if (h->cap / 2 < h->len) { 84 | oldkeys = h->keys; 85 | oldvals = h->vals; 86 | oldcap = h->cap; 87 | h->cap *= 2; 88 | h->keys = xreallocarray(NULL, h->cap, sizeof(h->keys[0])); 89 | h->vals = xreallocarray(NULL, h->cap, sizeof(h->vals[0])); 90 | for (i = 0; i < h->cap; ++i) 91 | h->keys[i].str = NULL; 92 | for (i = 0; i < oldcap; ++i) { 93 | if (oldkeys[i].str) { 94 | j = keyindex(h, &oldkeys[i]); 95 | h->keys[j] = oldkeys[i]; 96 | h->vals[j] = oldvals[i]; 97 | } 98 | } 99 | free(oldkeys); 100 | free(oldvals); 101 | } 102 | i = keyindex(h, k); 103 | if (!h->keys[i].str) { 104 | h->keys[i] = *k; 105 | h->vals[i] = NULL; 106 | ++h->len; 107 | } 108 | 109 | return &h->vals[i]; 110 | } 111 | 112 | void * 113 | mapget(struct map *h, struct mapkey *k) 114 | { 115 | size_t i; 116 | 117 | i = keyindex(h, k); 118 | return h->keys[i].str ? h->vals[i] : NULL; 119 | } 120 | -------------------------------------------------------------------------------- /ops.h: -------------------------------------------------------------------------------- 1 | /* arithmetic and bits */ 2 | OP(IADD, "add") 3 | OP(ISUB, "sub") 4 | OP(INEG, "neg") 5 | OP(IDIV, "div") 6 | OP(IMUL, "mul") 7 | OP(IUDIV, "udiv") 8 | OP(IREM, "rem") 9 | OP(IUREM, "urem") 10 | OP(IOR, "or") 11 | OP(IXOR, "xor") 12 | OP(IAND, "and") 13 | OP(ISAR, "sar") 14 | OP(ISHR, "shr") 15 | OP(ISHL, "shl") 16 | 17 | /* memory */ 18 | OP(ISTORED, "stored") 19 | OP(ISTORES, "stores") 20 | OP(ISTOREL, "storel") 21 | OP(ISTOREW, "storew") 22 | OP(ISTOREH, "storeh") 23 | OP(ISTOREB, "storeb") 24 | OP(ILOADD, "loadd") 25 | OP(ILOADS, "loads") 26 | OP(ILOADL, "loadl") 27 | OP(ILOADW, "loadw") 28 | OP(ILOADSH, "loadsh") 29 | OP(ILOADUH, "loaduh") 30 | OP(ILOADSB, "loadsb") 31 | OP(ILOADUB, "loadub") 32 | OP(IALLOC4, "alloc4") 33 | OP(IALLOC8, "alloc8") 34 | OP(IALLOC16, "alloc16") 35 | 36 | /* comparisons */ 37 | OP(ICEQW, "ceqw") 38 | OP(ICNEW, "cnew") 39 | OP(ICSLEW, "cslew") 40 | OP(ICSLTW, "csltw") 41 | OP(ICSGEW, "csgew") 42 | OP(ICSGTW, "csgtw") 43 | OP(ICULEW, "culew") 44 | OP(ICULTW, "cultw") 45 | OP(ICUGEW, "cugew") 46 | OP(ICUGTW, "cugtw") 47 | 48 | OP(ICEQL, "ceql") 49 | OP(ICNEL, "cnel") 50 | OP(ICSLEL, "cslel") 51 | OP(ICSLTL, "csltl") 52 | OP(ICSGEL, "csgel") 53 | OP(ICSGTL, "csgtl") 54 | OP(ICULEL, "culel") 55 | OP(ICULTL, "cultl") 56 | OP(ICUGEL, "cugel") 57 | OP(ICUGTL, "cugtl") 58 | 59 | OP(ICEQS, "ceqs") 60 | OP(ICNES, "cnes") 61 | OP(ICLES, "cles") 62 | OP(ICLTS, "clts") 63 | OP(ICGES, "cges") 64 | OP(ICGTS, "cgts") 65 | OP(ICOS, "cos") 66 | OP(ICUOS, "cuos") 67 | 68 | OP(ICEQD, "ceqd") 69 | OP(ICNED, "cned") 70 | OP(ICLED, "cled") 71 | OP(ICLTD, "cltd") 72 | OP(ICGED, "cged") 73 | OP(ICGTD, "cgtd") 74 | OP(ICOD, "cod") 75 | OP(ICUOD, "cuod") 76 | 77 | /* conversions */ 78 | OP(IEXTSW, "extsw") 79 | OP(IEXTUW, "extuw") 80 | OP(IEXTSH, "extsh") 81 | OP(IEXTUH, "extuh") 82 | OP(IEXTSB, "extsb") 83 | OP(IEXTUB, "extub") 84 | OP(IEXTS, "exts") 85 | OP(ITRUNCD, "truncd") 86 | OP(ISTOSI, "stosi") 87 | OP(ISTOUI, "stoui") 88 | OP(IDTOSI, "dtosi") 89 | OP(IDTOUI, "dtoui") 90 | OP(ISWTOF, "swtof") 91 | OP(IUWTOF, "uwtof") 92 | OP(ISLTOF, "sltof") 93 | OP(IULTOF, "ultof") 94 | 95 | /* cast and copy */ 96 | OP(ICAST, "cast") 97 | OP(ICOPY, "copy") 98 | 99 | /* call */ 100 | OP(ICALL, "call") 101 | 102 | /* variadic */ 103 | OP(IVASTART, "vastart") 104 | OP(IVAARG, "vaarg") 105 | -------------------------------------------------------------------------------- /runtests: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | : ${CCQBE:=./cproc-qbe} 4 | 5 | if [ $# = 0 ] ; then 6 | set -- test/*.c 7 | fi 8 | 9 | numtest=0 10 | numpass=0 11 | numfail=0 12 | fail= 13 | got=$(mktemp) 14 | trap 'rm "$got"' EXIT 15 | 16 | for test ; do 17 | name=${test%.c} 18 | case $name in 19 | *+*) arch=${name##*+} ;; 20 | *) arch=x86_64-sysv ;; 21 | esac 22 | if [ -f "$name.qbe" ] ; then 23 | want=$name.qbe 24 | set -- $CCQBE -t $arch -o "$got" "$test" 25 | elif [ -f "$name.pp" ] ; then 26 | want=$name.pp 27 | set -- $CCQBE -t $arch -E -o "$got" "$test" 28 | else 29 | echo "invalid test '$test'" >&2 30 | continue 31 | fi 32 | numtest=$((numtest + 1)) 33 | if "$@" && diff -Nu "$want" "$got" ; then 34 | result="PASS" 35 | numpass=$((numpass + 1)) 36 | else 37 | result="FAIL" 38 | numfail=$((numfail + 1)) 39 | fail="$fail $test" 40 | fi 41 | echo "[$result] $test" >&2 42 | done 43 | 44 | printf "\n%d/%d tests passed\n" "$numpass" "$numtest" 45 | if [ "$numfail" -gt 0 ] ; then 46 | printf "%d test(s) failed (%s)\n" "$numfail" "${fail# }" 47 | exit 1 48 | fi 49 | -------------------------------------------------------------------------------- /scope.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "util.h" 5 | #include "cc.h" 6 | 7 | struct scope filescope; 8 | 9 | void 10 | scopeinit(void) 11 | { 12 | static struct decl builtins[] = { 13 | {.name = "__builtin_alloca", .kind = DECLBUILTIN, .u.builtin = BUILTINALLOCA}, 14 | {.name = "__builtin_constant_p", .kind = DECLBUILTIN, .u.builtin = BUILTINCONSTANTP}, 15 | {.name = "__builtin_expect", .kind = DECLBUILTIN, .u.builtin = BUILTINEXPECT}, 16 | {.name = "__builtin_inff", .kind = DECLBUILTIN, .u.builtin = BUILTININFF}, 17 | {.name = "__builtin_nanf", .kind = DECLBUILTIN, .u.builtin = BUILTINNANF}, 18 | {.name = "__builtin_offsetof", .kind = DECLBUILTIN, .u.builtin = BUILTINOFFSETOF}, 19 | {.name = "__builtin_types_compatible_p", .kind = DECLBUILTIN, .u.builtin = BUILTINTYPESCOMPATIBLEP}, 20 | {.name = "__builtin_unreachable", .kind = DECLBUILTIN, .u.builtin = BUILTINUNREACHABLE}, 21 | {.name = "__builtin_va_arg", .kind = DECLBUILTIN, .u.builtin = BUILTINVAARG}, 22 | {.name = "__builtin_va_copy", .kind = DECLBUILTIN, .u.builtin = BUILTINVACOPY}, 23 | {.name = "__builtin_va_end", .kind = DECLBUILTIN, .u.builtin = BUILTINVAEND}, 24 | {.name = "__builtin_va_start", .kind = DECLBUILTIN, .u.builtin = BUILTINVASTART}, 25 | }; 26 | static struct decl valist; 27 | struct decl *d; 28 | 29 | for (d = builtins; d < builtins + LEN(builtins); ++d) 30 | scopeputdecl(&filescope, d); 31 | valist.name = "__builtin_va_list"; 32 | valist.kind = DECLTYPE; 33 | valist.type = targ->typevalist; 34 | scopeputdecl(&filescope, &valist); 35 | } 36 | 37 | struct scope * 38 | mkscope(struct scope *parent) 39 | { 40 | struct scope *s; 41 | 42 | s = xmalloc(sizeof(*s)); 43 | s->decls.len = 0; 44 | s->tags.len = 0; 45 | s->breaklabel = parent->breaklabel; 46 | s->continuelabel = parent->continuelabel; 47 | s->switchcases = parent->switchcases; 48 | s->parent = parent; 49 | 50 | return s; 51 | } 52 | 53 | struct scope * 54 | delscope(struct scope *s) 55 | { 56 | struct scope *parent = s->parent; 57 | 58 | if (s->decls.len) 59 | mapfree(&s->decls, NULL); 60 | if (s->tags.len) 61 | mapfree(&s->tags, NULL); 62 | free(s); 63 | 64 | return parent; 65 | } 66 | 67 | struct decl * 68 | scopegetdecl(struct scope *s, const char *name, bool recurse) 69 | { 70 | struct decl *d; 71 | struct mapkey k; 72 | 73 | mapkey(&k, name, strlen(name)); 74 | do { 75 | d = s->decls.len ? mapget(&s->decls, &k) : NULL; 76 | s = s->parent; 77 | } while (!d && s && recurse); 78 | 79 | return d; 80 | } 81 | 82 | struct type * 83 | scopegettag(struct scope *s, const char *name, bool recurse) 84 | { 85 | struct type *t; 86 | struct mapkey k; 87 | 88 | mapkey(&k, name, strlen(name)); 89 | do { 90 | t = s->tags.len ? mapget(&s->tags, &k) : NULL; 91 | s = s->parent; 92 | } while (!t && s && recurse); 93 | 94 | return t; 95 | } 96 | 97 | void 98 | scopeputdecl(struct scope *s, struct decl *d) 99 | { 100 | struct mapkey k; 101 | 102 | if (!s->decls.len) 103 | mapinit(&s->decls, 32); 104 | mapkey(&k, d->name, strlen(d->name)); 105 | *mapput(&s->decls, &k) = d; 106 | } 107 | 108 | void 109 | scopeputtag(struct scope *s, const char *name, struct type *t) 110 | { 111 | struct mapkey k; 112 | 113 | if (!s->tags.len) 114 | mapinit(&s->tags, 32); 115 | mapkey(&k, name, strlen(name)); 116 | *mapput(&s->tags, &k) = t; 117 | } 118 | -------------------------------------------------------------------------------- /stmt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "util.h" 7 | #include "cc.h" 8 | 9 | /* 6.8.1 Labeled statements */ 10 | static bool 11 | label(struct func *f, struct scope *s) 12 | { 13 | char *name; 14 | struct gotolabel *g; 15 | struct block *b; 16 | unsigned long long i; 17 | 18 | attr(NULL, 0); 19 | switch (tok.kind) { 20 | case TCASE: 21 | next(); 22 | if (!s->switchcases) 23 | error(&tok.loc, "'case' label must be in switch"); 24 | b = mkblock("switch_case"); 25 | funclabel(f, b); 26 | i = intconstexpr(s, true); 27 | switchcase(s->switchcases, i, b); 28 | expect(TCOLON, "after case expression"); 29 | break; 30 | case TDEFAULT: 31 | next(); 32 | if (!s->switchcases) 33 | error(&tok.loc, "'default' label must be in switch"); 34 | if (s->switchcases->defaultlabel) 35 | error(&tok.loc, "multiple 'default' labels"); 36 | expect(TCOLON, "after 'default'"); 37 | s->switchcases->defaultlabel = mkblock("switch_default"); 38 | funclabel(f, s->switchcases->defaultlabel); 39 | break; 40 | case TIDENT: 41 | name = tok.lit; 42 | if (!peek(TCOLON)) 43 | return false; 44 | g = funcgoto(f, name); 45 | g->defined = true; 46 | funclabel(f, g->label); 47 | break; 48 | default: 49 | return false; 50 | } 51 | return true; 52 | } 53 | 54 | static void 55 | labelstmt(struct func *f, struct scope *s) 56 | { 57 | while (label(f, s)) 58 | ; 59 | stmt(f, s); 60 | } 61 | 62 | /* 6.8 Statements and blocks */ 63 | void 64 | stmt(struct func *f, struct scope *s) 65 | { 66 | char *name; 67 | struct expr *e; 68 | struct type *t; 69 | struct value *v; 70 | struct block *b[4]; 71 | struct switchcases swtch; 72 | 73 | attr(NULL, 0); 74 | switch (tok.kind) { 75 | /* 6.8.2 Compound statement */ 76 | case TLBRACE: 77 | next(); 78 | s = mkscope(s); 79 | while (tok.kind != TRBRACE) { 80 | if (!label(f, s) && !decl(s, f)) 81 | stmt(f, s); 82 | } 83 | s = delscope(s); 84 | next(); 85 | break; 86 | 87 | /* 6.8.3 Expression statement */ 88 | case TSEMICOLON: 89 | next(); 90 | break; 91 | default: 92 | e = expr(s); 93 | v = funcexpr(f, e); 94 | delexpr(e); 95 | expect(TSEMICOLON, "after expression statement"); 96 | break; 97 | 98 | /* 6.8.4 Selection statement */ 99 | case TIF: 100 | next(); 101 | s = mkscope(s); 102 | expect(TLPAREN, "after 'if'"); 103 | e = expr(s); 104 | t = e->type; 105 | if (!(t->prop & PROPSCALAR)) 106 | error(&tok.loc, "controlling expression of if statement must have scalar type"); 107 | v = funcexpr(f, e); 108 | delexpr(e); 109 | expect(TRPAREN, "after expression"); 110 | 111 | b[0] = mkblock("if_true"); 112 | b[1] = mkblock("if_false"); 113 | funcjnz(f, v, t, b[0], b[1]); 114 | 115 | funclabel(f, b[0]); 116 | s = mkscope(s); 117 | labelstmt(f, s); 118 | s = delscope(s); 119 | 120 | if (consume(TELSE)) { 121 | b[2] = mkblock("if_join"); 122 | funcjmp(f, b[2]); 123 | funclabel(f, b[1]); 124 | s = mkscope(s); 125 | labelstmt(f, s); 126 | s = delscope(s); 127 | funclabel(f, b[2]); 128 | } else { 129 | funclabel(f, b[1]); 130 | } 131 | s = delscope(s); 132 | break; 133 | case TSWITCH: 134 | next(); 135 | 136 | s = mkscope(s); 137 | expect(TLPAREN, "after 'switch'"); 138 | e = expr(s); 139 | expect(TRPAREN, "after expression"); 140 | 141 | if (!(e->type->prop & PROPINT)) 142 | error(&tok.loc, "controlling expression of switch statement must have integer type"); 143 | e = exprpromote(e); 144 | 145 | swtch.root = NULL; 146 | swtch.type = e->type; 147 | swtch.defaultlabel = NULL; 148 | 149 | b[0] = mkblock("switch_cond"); 150 | b[1] = mkblock("switch_join"); 151 | 152 | v = funcexpr(f, e); 153 | funcjmp(f, b[0]); 154 | s = mkscope(s); 155 | s->breaklabel = b[1]; 156 | s->switchcases = &swtch; 157 | labelstmt(f, s); 158 | funcjmp(f, b[1]); 159 | 160 | funclabel(f, b[0]); 161 | funcswitch(f, v, &swtch, swtch.defaultlabel ? swtch.defaultlabel : b[1]); 162 | s = delscope(s); 163 | 164 | funclabel(f, b[1]); 165 | s = delscope(s); 166 | break; 167 | 168 | /* 6.8.5 Iteration statements */ 169 | case TWHILE: 170 | next(); 171 | s = mkscope(s); 172 | expect(TLPAREN, "after 'while'"); 173 | e = expr(s); 174 | t = e->type; 175 | if (!(t->prop & PROPSCALAR)) 176 | error(&tok.loc, "controlling expression of loop must have scalar type"); 177 | expect(TRPAREN, "after expression"); 178 | 179 | b[0] = mkblock("while_cond"); 180 | b[1] = mkblock("while_body"); 181 | b[2] = mkblock("while_join"); 182 | 183 | funclabel(f, b[0]); 184 | v = funcexpr(f, e); 185 | funcjnz(f, v, t, b[1], b[2]); 186 | funclabel(f, b[1]); 187 | s = mkscope(s); 188 | s->continuelabel = b[0]; 189 | s->breaklabel = b[2]; 190 | labelstmt(f, s); 191 | s = delscope(s); 192 | funcjmp(f, b[0]); 193 | funclabel(f, b[2]); 194 | s = delscope(s); 195 | break; 196 | case TDO: 197 | next(); 198 | 199 | b[0] = mkblock("do_body"); 200 | b[1] = mkblock("do_cond"); 201 | b[2] = mkblock("do_join"); 202 | 203 | s = mkscope(s); 204 | s = mkscope(s); 205 | s->continuelabel = b[1]; 206 | s->breaklabel = b[2]; 207 | funclabel(f, b[0]); 208 | labelstmt(f, s); 209 | s = delscope(s); 210 | 211 | expect(TWHILE, "after 'do' statement"); 212 | expect(TLPAREN, "after 'while'"); 213 | funclabel(f, b[1]); 214 | e = expr(s); 215 | t = e->type; 216 | if (!(t->prop & PROPSCALAR)) 217 | error(&tok.loc, "controlling expression of loop must have scalar type"); 218 | expect(TRPAREN, "after expression"); 219 | 220 | v = funcexpr(f, e); 221 | funcjnz(f, v, t, b[0], b[2]); 222 | funclabel(f, b[2]); 223 | s = delscope(s); 224 | expect(TSEMICOLON, "after 'do' statement"); 225 | break; 226 | case TFOR: 227 | next(); 228 | expect(TLPAREN, "after while"); 229 | s = mkscope(s); 230 | if (!decl(s, f)) { 231 | if (tok.kind != TSEMICOLON) { 232 | e = expr(s); 233 | funcexpr(f, e); 234 | delexpr(e); 235 | } 236 | expect(TSEMICOLON, NULL); 237 | } 238 | 239 | b[0] = mkblock("for_cond"); 240 | b[1] = mkblock("for_body"); 241 | b[2] = mkblock("for_cont"); 242 | b[3] = mkblock("for_join"); 243 | 244 | funclabel(f, b[0]); 245 | if (tok.kind != TSEMICOLON) { 246 | e = expr(s); 247 | t = e->type; 248 | if (!(t->prop & PROPSCALAR)) 249 | error(&tok.loc, "controlling expression of loop must have scalar type"); 250 | v = funcexpr(f, e); 251 | funcjnz(f, v, t, b[1], b[3]); 252 | delexpr(e); 253 | } 254 | expect(TSEMICOLON, NULL); 255 | e = tok.kind == TRPAREN ? NULL : expr(s); 256 | expect(TRPAREN, NULL); 257 | 258 | funclabel(f, b[1]); 259 | s = mkscope(s); 260 | s->breaklabel = b[3]; 261 | s->continuelabel = b[2]; 262 | labelstmt(f, s); 263 | s = delscope(s); 264 | 265 | funclabel(f, b[2]); 266 | if (e) { 267 | funcexpr(f, e); 268 | delexpr(e); 269 | } 270 | funcjmp(f, b[0]); 271 | funclabel(f, b[3]); 272 | s = delscope(s); 273 | break; 274 | 275 | /* 6.8.6 Jump statements */ 276 | case TGOTO: 277 | next(); 278 | name = expect(TIDENT, "after 'goto'"); 279 | funcjmp(f, funcgoto(f, name)->label); 280 | expect(TSEMICOLON, "after 'goto' statement"); 281 | break; 282 | case TCONTINUE: 283 | next(); 284 | if (!s->continuelabel) 285 | error(&tok.loc, "'continue' statement must be in loop"); 286 | funcjmp(f, s->continuelabel); 287 | expect(TSEMICOLON, "after 'continue' statement"); 288 | break; 289 | case TBREAK: 290 | next(); 291 | if (!s->breaklabel) 292 | error(&tok.loc, "'break' statement must be in loop or switch"); 293 | funcjmp(f, s->breaklabel); 294 | expect(TSEMICOLON, "after 'break' statement"); 295 | break; 296 | case TRETURN: 297 | next(); 298 | t = functype(f); 299 | if (t->base != &typevoid) { 300 | e = exprassign(expr(s), t->base); 301 | v = funcexpr(f, e); 302 | delexpr(e); 303 | } else { 304 | v = NULL; 305 | } 306 | funcret(f, v); 307 | expect(TSEMICOLON, "after 'return' statement"); 308 | break; 309 | 310 | case T__ASM__: 311 | error(&tok.loc, "inline assembly is not yet supported"); 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /targ.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "util.h" 4 | #include "cc.h" 5 | 6 | const struct target *targ; 7 | 8 | static const struct target alltargs[] = { 9 | { 10 | .name = "x86_64-sysv", 11 | .typewchar = &typeint, 12 | .typevalist = &(struct type){ 13 | .kind = TYPEARRAY, 14 | .align = 8, .size = 24, 15 | .base = &(struct type){ 16 | .kind = TYPESTRUCT, 17 | .align = 8, .size = 24, 18 | }, 19 | }, 20 | .signedchar = 1, 21 | }, 22 | { 23 | .name = "aarch64", 24 | .typevalist = &(struct type){ 25 | .kind = TYPESTRUCT, 26 | .align = 8, .size = 32, 27 | .u.structunion.tag = "va_list", 28 | }, 29 | .typewchar = &typeuint, 30 | }, 31 | { 32 | .name = "riscv64", 33 | .typevalist = &(struct type){ 34 | .kind = TYPEPOINTER, .prop = PROPSCALAR, 35 | .align = 8, .size = 8, 36 | .base = &typevoid, 37 | }, 38 | .typewchar = &typeint, 39 | }, 40 | }; 41 | 42 | void 43 | targinit(const char *name) 44 | { 45 | size_t i; 46 | enum typequal qual; 47 | 48 | if (!name) { 49 | /* TODO: provide a way to set this default */ 50 | targ = &alltargs[0]; 51 | } 52 | for (i = 0; i < LEN(alltargs) && !targ; ++i) { 53 | if (strcmp(alltargs[i].name, name) == 0) 54 | targ = &alltargs[i]; 55 | } 56 | if (!targ) 57 | fatal("unknown target '%s'", name); 58 | typechar.u.basic.issigned = targ->signedchar; 59 | qual = QUALNONE; 60 | typeadjvalist = typeadjust(targ->typevalist, &qual); 61 | } 62 | -------------------------------------------------------------------------------- /test/abstract-function-declarator.c: -------------------------------------------------------------------------------- 1 | typedef int x; 2 | 3 | void f1(int(x)); 4 | void f1(int (*)(int)); 5 | 6 | void f2(int(y)); 7 | void f2(int); 8 | 9 | void f3(int((*))); 10 | void f3(int *); 11 | 12 | void f4(int((*x))); 13 | void f4(int *); 14 | 15 | void f5(int((x))); 16 | void f5(int (*)(int)); 17 | -------------------------------------------------------------------------------- /test/abstract-function-declarator.qbe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelforney/cproc/a2ddee1be68e76a1ac7f83b55732be8e54fe08c4/test/abstract-function-declarator.qbe -------------------------------------------------------------------------------- /test/add-int-pointer.c: -------------------------------------------------------------------------------- 1 | int x[2]; 2 | void f(void) { 3 | 1 + x; 4 | } 5 | -------------------------------------------------------------------------------- /test/add-int-pointer.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | @body.2 5 | %.1 =l extsw 1 6 | %.2 =l mul %.1, 4 7 | %.3 =l add $x, %.2 8 | ret 9 | } 10 | export data $x = align 4 { z 8 } 11 | -------------------------------------------------------------------------------- /test/alignas-0.c: -------------------------------------------------------------------------------- 1 | alignas(0) int x; 2 | alignas(8) alignas(0) int y; 3 | alignas(0) alignas(16) int z; 4 | -------------------------------------------------------------------------------- /test/alignas-0.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { z 4 } 2 | export data $y = align 8 { z 4 } 3 | export data $z = align 16 { z 4 } 4 | -------------------------------------------------------------------------------- /test/alignas-local-strict.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | alignas(32) char x; 3 | return (unsigned long)&x % 32; 4 | } 5 | -------------------------------------------------------------------------------- /test/alignas-local-strict.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | %.1 =l add 1, 16 5 | %.2 =l alloc16 %.1 6 | %.3 =l add %.2, 16 7 | %.4 =l and %.3, 18446744073709551584 8 | @body.2 9 | %.5 =l extsw 32 10 | %.6 =l urem %.4, %.5 11 | ret %.6 12 | } 13 | -------------------------------------------------------------------------------- /test/alignas-local.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | alignas(16) char x; 3 | return (unsigned long)&x % 16; 4 | } 5 | -------------------------------------------------------------------------------- /test/alignas-local.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | %.1 =l alloc16 1 5 | @body.2 6 | %.2 =l extsw 16 7 | %.3 =l urem %.1, %.2 8 | ret %.3 9 | } 10 | -------------------------------------------------------------------------------- /test/alignas-member.c: -------------------------------------------------------------------------------- 1 | struct { 2 | int x; 3 | alignas(32) char s[32]; 4 | } s = {123, "abc"}; 5 | -------------------------------------------------------------------------------- /test/alignas-member.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 32 { w 123, z 28, b "abc\000", z 28, } 2 | -------------------------------------------------------------------------------- /test/alignas-multiple.c: -------------------------------------------------------------------------------- 1 | alignas(8) alignas(4) char x; 2 | -------------------------------------------------------------------------------- /test/alignas-multiple.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 8 { z 1 } 2 | -------------------------------------------------------------------------------- /test/alignas-type.c: -------------------------------------------------------------------------------- 1 | alignas(int) char x[4]; 2 | -------------------------------------------------------------------------------- /test/alignas-type.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { z 4 } 2 | -------------------------------------------------------------------------------- /test/alignas-vla-strict.c: -------------------------------------------------------------------------------- 1 | int n = 43; 2 | int main(void) { 3 | char alignas(64) a[n]; 4 | return (unsigned long)a % 64; 5 | } 6 | -------------------------------------------------------------------------------- /test/alignas-vla-strict.qbe: -------------------------------------------------------------------------------- 1 | export data $n = align 4 { w 43, } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =w loadw $n 7 | %.2 =l extsw %.1 8 | %.3 =l mul %.2, 1 9 | %.4 =l add %.3, 48 10 | %.5 =l alloc16 %.4 11 | %.6 =l add %.5, 48 12 | %.7 =l and %.6, 18446744073709551552 13 | %.8 =l extsw 64 14 | %.9 =l urem %.7, %.8 15 | ret %.9 16 | } 17 | -------------------------------------------------------------------------------- /test/alignas.c: -------------------------------------------------------------------------------- 1 | alignas(32) long x[4] = {1, 2, 3, 4}; 2 | -------------------------------------------------------------------------------- /test/alignas.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 32 { l 1, l 2, l 3, l 4, } 2 | -------------------------------------------------------------------------------- /test/array-address.c: -------------------------------------------------------------------------------- 1 | int x[2], y = sizeof(*&x); 2 | -------------------------------------------------------------------------------- /test/array-address.qbe: -------------------------------------------------------------------------------- 1 | export data $y = align 4 { w 8, } 2 | export data $x = align 4 { z 8 } 3 | -------------------------------------------------------------------------------- /test/asm-label.c: -------------------------------------------------------------------------------- 1 | int x __asm__("y"); 2 | int x = 2; 3 | 4 | void f(void) __asm__("g"); 5 | void f(void) {} 6 | -------------------------------------------------------------------------------- /test/asm-label.qbe: -------------------------------------------------------------------------------- 1 | export data $"y" = align 4 { w 2, } 2 | export 3 | function $"g"() { 4 | @start.1 5 | @body.2 6 | ret 7 | } 8 | -------------------------------------------------------------------------------- /test/attribute-syntax.c: -------------------------------------------------------------------------------- 1 | /* applies to a and b */ 2 | [[dummy]] int a, b; 3 | 4 | /* applies only to d */ 5 | int c, d [[dummy]]; 6 | 7 | /* applies to type int in this instance */ 8 | int [[dummy]] x; 9 | 10 | /* applies to array type */ 11 | int array[8] [[dummy]]; 12 | 13 | /* applies to function type */ 14 | int function(void) [[dummy]]; 15 | 16 | /* applies to pointer type */ 17 | int *[[dummy]]const pointer; 18 | 19 | /* standalone attribute is implementation-defined */ 20 | [[dummy]]; 21 | 22 | /* applies to type `struct s`, here and elsewhere */ 23 | struct [[dummy]] s { 24 | /* applies to i */ 25 | [[dummy]] int i; 26 | 27 | /* applies to type float in this instance */ 28 | float [[dummy]] f; 29 | }; 30 | 31 | /* applies to type `union u`, here and elsewhere */ 32 | union [[dummy]] u { 33 | /* applies to i */ 34 | [[dummy]] int i; 35 | 36 | /* applies to type float in this instance */ 37 | float [[dummy]] f; 38 | }; 39 | 40 | /* applies to type `enum e`, here and elsewhere */ 41 | enum [[dummy]] e { 42 | /* applies to E1 */ 43 | E1 [[dummy]], 44 | 45 | /* applies to E2 */ 46 | E2 [[dummy]] = 123, 47 | }; 48 | 49 | /* applies to function */ 50 | [[dummy]] void f([[dummy]] int p1 /* applies to p1 */, int [[dummy]] p2 /* applies to type int */) { 51 | /* applies to type int */ 52 | sizeof(int [[dummy]]); 53 | 54 | /* applies to array type */ 55 | sizeof(int[4] [[dummy]]); 56 | 57 | /* applies to function type */ 58 | sizeof(int (*)(float) [[dummy]]); 59 | 60 | /* applies to pointer type */ 61 | sizeof(int *[[dummy]]); 62 | 63 | /* applies to statement */ 64 | [[dummy]] 65 | if (0) 66 | ; 67 | 68 | /* applies to label */ 69 | [[dummy]] 70 | L1: 0; 71 | 72 | /* applies to statement */ 73 | L2: [[dummy]]0; 74 | } 75 | 76 | [[dummy(attribute with parameters [123 456 ({+ -} if else)])]]; 77 | [[dummy1,dummy2,,dummy3]]; 78 | [[]]; 79 | [[dummy1]] [[dummy2]]; 80 | -------------------------------------------------------------------------------- /test/attribute-syntax.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f(w %.1, w %.3) { 3 | @start.1 4 | %.2 =l alloc4 4 5 | storew %.1, %.2 6 | %.4 =l alloc4 4 7 | storew %.3, %.4 8 | @body.2 9 | jnz 0, @if_true.3, @if_false.4 10 | @if_true.3 11 | @if_false.4 12 | @L1.5 13 | @L2.6 14 | ret 15 | } 16 | export data $a = align 4 { z 4 } 17 | export data $b = align 4 { z 4 } 18 | export data $c = align 4 { z 4 } 19 | export data $d = align 4 { z 4 } 20 | export data $x = align 4 { z 4 } 21 | export data $array = align 4 { z 32 } 22 | export data $pointer = align 8 { z 8 } 23 | -------------------------------------------------------------------------------- /test/basic.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /test/basic.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | ret 0 6 | } 7 | -------------------------------------------------------------------------------- /test/binary-constant.c: -------------------------------------------------------------------------------- 1 | int x = 0b11100100; 2 | -------------------------------------------------------------------------------- /test/binary-constant.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 228, } 2 | -------------------------------------------------------------------------------- /test/bitfield-assignment-sign-extend.c: -------------------------------------------------------------------------------- 1 | struct { 2 | signed x : 4; 3 | } s; 4 | int main(void) { 5 | return (s.x = 15) != -1; 6 | } 7 | -------------------------------------------------------------------------------- /test/bitfield-assignment-sign-extend.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =l add $s, 0 6 | %.2 =w shl 15, 0 7 | %.3 =w shl %.2, 28 8 | %.4 =w sar %.3, 28 9 | %.5 =w and %.2, 15 10 | %.6 =w loadw %.1 11 | %.7 =w and %.6, 18446744073709551600 12 | %.8 =w or %.5, %.7 13 | storew %.8, %.1 14 | %.9 =w neg 1 15 | %.10 =w cnew %.4, %.9 16 | ret %.10 17 | } 18 | export data $s = align 4 { z 4 } 19 | -------------------------------------------------------------------------------- /test/bitfield-compound-assign.c: -------------------------------------------------------------------------------- 1 | struct { 2 | int : 4, x : 9, : 3; 3 | } s; 4 | 5 | void f(void) { 6 | s.x += 3; 7 | } 8 | -------------------------------------------------------------------------------- /test/bitfield-compound-assign.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | @body.2 5 | %.1 =l add $s, 0 6 | %.2 =w loadw %.1 7 | %.3 =w shl %.2, 19 8 | %.4 =w sar %.3, 23 9 | %.5 =w add %.4, 3 10 | %.6 =w shl %.5, 4 11 | %.7 =w shl %.6, 19 12 | %.8 =w sar %.7, 23 13 | %.9 =w and %.6, 8176 14 | %.10 =w loadw %.1 15 | %.11 =w and %.10, 18446744073709543439 16 | %.12 =w or %.9, %.11 17 | storew %.12, %.1 18 | ret 19 | } 20 | export data $s = align 4 { z 4 } 21 | -------------------------------------------------------------------------------- /test/bitfield-initializer-overflow.c: -------------------------------------------------------------------------------- 1 | struct { 2 | unsigned : 30, a : 2, b; 3 | } s = {5}; 4 | -------------------------------------------------------------------------------- /test/bitfield-initializer-overflow.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 4 { z 3, b 64, z 4 } 2 | -------------------------------------------------------------------------------- /test/bitfield-integer-promotion-long.c: -------------------------------------------------------------------------------- 1 | struct {unsigned long x:31;} s1; 2 | struct {unsigned long x:32;} s2; 3 | struct {unsigned long x:33;} s3; 4 | int c1 = __builtin_types_compatible_p(typeof(+s1.x), int); 5 | int c2 = __builtin_types_compatible_p(typeof(+s2.x), unsigned); 6 | int c3 = __builtin_types_compatible_p(typeof(+s3.x), unsigned long); 7 | -------------------------------------------------------------------------------- /test/bitfield-integer-promotion-long.qbe: -------------------------------------------------------------------------------- 1 | export data $c1 = align 4 { w 1, } 2 | export data $c2 = align 4 { w 1, } 3 | export data $c3 = align 4 { w 1, } 4 | export data $s1 = align 8 { z 8 } 5 | export data $s2 = align 8 { z 8 } 6 | export data $s3 = align 8 { z 8 } 7 | -------------------------------------------------------------------------------- /test/bitfield-integer-promotion.c: -------------------------------------------------------------------------------- 1 | struct { 2 | unsigned a : 2; 3 | } s; 4 | 5 | int main(void) { 6 | return -1 > s.a; 7 | } 8 | -------------------------------------------------------------------------------- /test/bitfield-integer-promotion.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w neg 1 6 | %.2 =l add $s, 0 7 | %.3 =w loadw %.2 8 | %.4 =w shl %.3, 30 9 | %.5 =w shr %.4, 30 10 | %.6 =w csgtw %.1, %.5 11 | ret %.6 12 | } 13 | export data $s = align 4 { z 4 } 14 | -------------------------------------------------------------------------------- /test/bitfield-load-signed.c: -------------------------------------------------------------------------------- 1 | struct { 2 | signed : 4, x : 15, : 13; 3 | } s; 4 | 5 | void f(void) { 6 | s.x; 7 | } 8 | -------------------------------------------------------------------------------- /test/bitfield-load-signed.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | @body.2 5 | %.1 =l add $s, 0 6 | %.2 =w loadw %.1 7 | %.3 =w shl %.2, 13 8 | %.4 =w sar %.3, 17 9 | ret 10 | } 11 | export data $s = align 4 { z 4 } 12 | -------------------------------------------------------------------------------- /test/bitfield-load-unsigned.c: -------------------------------------------------------------------------------- 1 | struct { 2 | unsigned : 4, x : 15, : 13; 3 | } s; 4 | 5 | void f(void) { 6 | s.x; 7 | } 8 | -------------------------------------------------------------------------------- /test/bitfield-load-unsigned.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | @body.2 5 | %.1 =l add $s, 0 6 | %.2 =w loadw %.1 7 | %.3 =w shl %.2, 13 8 | %.4 =w shr %.3, 17 9 | ret 10 | } 11 | export data $s = align 4 { z 4 } 12 | -------------------------------------------------------------------------------- /test/bitfield-non-adjacent.c: -------------------------------------------------------------------------------- 1 | struct { 2 | int x : 1, y, z : 1; 3 | } s = {.z = 1}; 4 | -------------------------------------------------------------------------------- /test/bitfield-non-adjacent.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 4 { z 8, b 1, z 3 } 2 | -------------------------------------------------------------------------------- /test/bitfield-short.c: -------------------------------------------------------------------------------- 1 | struct { 2 | short x : 7; 3 | } s = {.x = -64}; 4 | 5 | int main(void) { 6 | return s.x > 0; 7 | } 8 | -------------------------------------------------------------------------------- /test/bitfield-short.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 2 { b 64, z 1 } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =l add $s, 0 7 | %.2 =w loadsh %.1 8 | %.3 =w shl %.2, 25 9 | %.4 =w sar %.3, 25 10 | %.5 =w extsh %.4 11 | %.6 =w csgtw %.5, 0 12 | ret %.6 13 | } 14 | -------------------------------------------------------------------------------- /test/bitfield-union.c: -------------------------------------------------------------------------------- 1 | union { 2 | int x : 5; 3 | int y : 10; 4 | } u = {.y = 123}; 5 | -------------------------------------------------------------------------------- /test/bitfield-union.qbe: -------------------------------------------------------------------------------- 1 | export data $u = align 4 { b 123, z 3 } 2 | -------------------------------------------------------------------------------- /test/bitfield-unnamed-size-align.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | int : 8; 3 | char c; 4 | }; 5 | union u { 6 | int : 8; 7 | char c; 8 | }; 9 | int s1 = sizeof(struct s); 10 | int s2 = alignof(struct s); 11 | int u1 = sizeof(union u); 12 | int u2 = alignof(union u); 13 | -------------------------------------------------------------------------------- /test/bitfield-unnamed-size-align.qbe: -------------------------------------------------------------------------------- 1 | export data $s1 = align 4 { w 2, } 2 | export data $s2 = align 4 { w 1, } 3 | export data $u1 = align 4 { w 1, } 4 | export data $u2 = align 4 { w 1, } 5 | -------------------------------------------------------------------------------- /test/builtin-alloca.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | int *x = __builtin_alloca(32); 3 | } 4 | -------------------------------------------------------------------------------- /test/builtin-alloca.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc8 8 5 | @body.2 6 | %.2 =l extsw 32 7 | %.3 =l alloc16 %.2 8 | storel %.3, %.1 9 | ret 10 | } 11 | -------------------------------------------------------------------------------- /test/builtin-constant-p.c: -------------------------------------------------------------------------------- 1 | int f(void); 2 | int x = __builtin_constant_p(1+2*3); 3 | int y = __builtin_constant_p(f()); 4 | -------------------------------------------------------------------------------- /test/builtin-constant-p.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 1, } 2 | export data $y = align 4 { w 0, } 3 | -------------------------------------------------------------------------------- /test/builtin-expect.c: -------------------------------------------------------------------------------- 1 | int x; 2 | int main(void) { 3 | return __builtin_expect(x, 0); 4 | } 5 | -------------------------------------------------------------------------------- /test/builtin-expect.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w loadw $x 6 | ret %.1 7 | } 8 | export data $x = align 4 { z 4 } 9 | -------------------------------------------------------------------------------- /test/builtin-inff.c: -------------------------------------------------------------------------------- 1 | float x = __builtin_inff(); 2 | -------------------------------------------------------------------------------- /test/builtin-inff.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { s s_inf, } 2 | -------------------------------------------------------------------------------- /test/builtin-nanf.c: -------------------------------------------------------------------------------- 1 | float x = __builtin_nanf(""); 2 | -------------------------------------------------------------------------------- /test/builtin-nanf.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { s s_nan, } 2 | -------------------------------------------------------------------------------- /test/builtin-offsetof.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | struct { 3 | union { 4 | float z; 5 | char *c; 6 | } b; 7 | } a[5]; 8 | }; 9 | int x = __builtin_offsetof(struct s, a[2].b.c); 10 | -------------------------------------------------------------------------------- /test/builtin-offsetof.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 16, } 2 | -------------------------------------------------------------------------------- /test/builtin-types-compatible-p.c: -------------------------------------------------------------------------------- 1 | int x = __builtin_types_compatible_p(unsigned, enum {A}); 2 | int y = __builtin_types_compatible_p(const int, int); /* qualifiers are ignored */ 3 | int z = __builtin_types_compatible_p(int *, unsigned *); 4 | -------------------------------------------------------------------------------- /test/builtin-types-compatible-p.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 1, } 2 | export data $y = align 4 { w 1, } 3 | export data $z = align 4 { w 0, } 4 | -------------------------------------------------------------------------------- /test/builtin-va-copy+aarch64.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | static __builtin_va_list a, b; 3 | __builtin_va_copy(a, b); 4 | } 5 | -------------------------------------------------------------------------------- /test/builtin-va-copy+aarch64.qbe: -------------------------------------------------------------------------------- 1 | data $.La.2 = align 8 { z 32 } 2 | data $.Lb.3 = align 8 { z 32 } 3 | export 4 | function $f() { 5 | @start.1 6 | @body.2 7 | %.1 =l loadl $.Lb.3 8 | storel %.1, $.La.2 9 | %.2 =l add $.Lb.3, 8 10 | %.3 =l add $.La.2, 8 11 | %.4 =l loadl %.2 12 | storel %.4, %.3 13 | %.5 =l add %.2, 8 14 | %.6 =l add %.3, 8 15 | %.7 =l loadl %.5 16 | storel %.7, %.6 17 | %.8 =l add %.5, 8 18 | %.9 =l add %.6, 8 19 | %.10 =l loadl %.8 20 | storel %.10, %.9 21 | ret 22 | } 23 | -------------------------------------------------------------------------------- /test/builtin-va-copy+x86_64-sysv.c: -------------------------------------------------------------------------------- 1 | void f1(void) { 2 | static __builtin_va_list a, b; 3 | __builtin_va_copy(a, b); 4 | } 5 | void f2(__builtin_va_list b) { 6 | static __builtin_va_list a; 7 | __builtin_va_copy(a, b); 8 | } 9 | void f3(__builtin_va_list a, __builtin_va_list b) { 10 | __builtin_va_copy(a, b); 11 | } 12 | -------------------------------------------------------------------------------- /test/builtin-va-copy+x86_64-sysv.qbe: -------------------------------------------------------------------------------- 1 | data $.La.2 = align 8 { z 24 } 2 | data $.Lb.3 = align 8 { z 24 } 3 | export 4 | function $f1() { 5 | @start.1 6 | @body.2 7 | %.1 =l loadl $.Lb.3 8 | storel %.1, $.La.2 9 | %.2 =l add $.Lb.3, 8 10 | %.3 =l add $.La.2, 8 11 | %.4 =l loadl %.2 12 | storel %.4, %.3 13 | %.5 =l add %.2, 8 14 | %.6 =l add %.3, 8 15 | %.7 =l loadl %.5 16 | storel %.7, %.6 17 | ret 18 | } 19 | data $.La.5 = align 8 { z 24 } 20 | export 21 | function $f2(l %.1) { 22 | @start.3 23 | %.2 =l alloc8 8 24 | storel %.1, %.2 25 | @body.4 26 | %.3 =l loadl %.2 27 | %.4 =l loadl %.3 28 | storel %.4, $.La.5 29 | %.5 =l add %.3, 8 30 | %.6 =l add $.La.5, 8 31 | %.7 =l loadl %.5 32 | storel %.7, %.6 33 | %.8 =l add %.5, 8 34 | %.9 =l add %.6, 8 35 | %.10 =l loadl %.8 36 | storel %.10, %.9 37 | ret 38 | } 39 | export 40 | function $f3(l %.1, l %.3) { 41 | @start.5 42 | %.2 =l alloc8 8 43 | storel %.1, %.2 44 | %.4 =l alloc8 8 45 | storel %.3, %.4 46 | @body.6 47 | %.5 =l loadl %.4 48 | %.6 =l loadl %.2 49 | %.7 =l loadl %.5 50 | storel %.7, %.6 51 | %.8 =l add %.5, 8 52 | %.9 =l add %.6, 8 53 | %.10 =l loadl %.8 54 | storel %.10, %.9 55 | %.11 =l add %.8, 8 56 | %.12 =l add %.9, 8 57 | %.13 =l loadl %.11 58 | storel %.13, %.12 59 | ret 60 | } 61 | -------------------------------------------------------------------------------- /test/builtin-vaarg-vm.c: -------------------------------------------------------------------------------- 1 | int f(int i, ...) { 2 | int r, c = 0; 3 | __builtin_va_list ap; 4 | 5 | __builtin_va_start(ap, i); 6 | r = **__builtin_va_arg(ap, int (*)[++i]); 7 | __builtin_va_end(ap); 8 | return r + i; 9 | } 10 | 11 | int main(void) { 12 | int a[3]; 13 | 14 | a[0] = 123; 15 | return f(3, &a) != 127; 16 | } 17 | -------------------------------------------------------------------------------- /test/builtin-vaarg-vm.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $f(w %.1, ...) { 3 | @start.1 4 | %.2 =l alloc4 4 5 | storew %.1, %.2 6 | %.3 =l alloc4 4 7 | %.4 =l alloc4 4 8 | %.5 =l alloc8 24 9 | @body.2 10 | storew 0, %.4 11 | vastart %.5 12 | %.6 =w loadw %.2 13 | %.7 =w add %.6, 1 14 | storew %.7, %.2 15 | %.8 =l extsw %.7 16 | %.9 =l mul %.8, 4 17 | %.10 =l vaarg %.5 18 | %.11 =w loadw %.10 19 | storew %.11, %.3 20 | %.12 =w loadw %.3 21 | %.13 =w loadw %.2 22 | %.14 =w add %.12, %.13 23 | ret %.14 24 | } 25 | export 26 | function w $main() { 27 | @start.3 28 | %.1 =l alloc4 12 29 | @body.4 30 | %.2 =l extsw 0 31 | %.3 =l mul %.2, 4 32 | %.4 =l add %.1, %.3 33 | storew 123, %.4 34 | %.5 =w call $f(w 3, ..., l %.1) 35 | %.6 =w cnew %.5, 127 36 | ret %.6 37 | } 38 | -------------------------------------------------------------------------------- /test/cast-bool-char.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | return (bool)(unsigned char)256; 3 | } 4 | -------------------------------------------------------------------------------- /test/cast-bool-char.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w extub 256 6 | %.2 =w cnew %.1, 0 7 | %.3 =w extub %.2 8 | ret %.3 9 | } 10 | -------------------------------------------------------------------------------- /test/cast-vm.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | int l = 0; 3 | (int (*)[++l])0; 4 | (int (*(*)(void))[++l])0; 5 | return l != 2; 6 | } 7 | -------------------------------------------------------------------------------- /test/cast-vm.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | %.1 =l alloc4 4 5 | @body.2 6 | storew 0, %.1 7 | %.2 =w loadw %.1 8 | %.3 =w add %.2, 1 9 | storew %.3, %.1 10 | %.4 =l extsw %.3 11 | %.5 =l mul %.4, 4 12 | %.6 =l extsw 0 13 | %.7 =w loadw %.1 14 | %.8 =w add %.7, 1 15 | storew %.8, %.1 16 | %.9 =l extsw %.8 17 | %.10 =l mul %.9, 4 18 | %.11 =l extsw 0 19 | %.12 =w loadw %.1 20 | %.13 =w cnew %.12, 2 21 | ret %.13 22 | } 23 | -------------------------------------------------------------------------------- /test/char-const-u8.c: -------------------------------------------------------------------------------- 1 | unsigned char u8 = u8'a'; 2 | static_assert(__builtin_types_compatible_p(typeof(u8'b'), unsigned char), 3 | "UTF-8 character constant has incorrect type"); 4 | -------------------------------------------------------------------------------- /test/char-const-u8.qbe: -------------------------------------------------------------------------------- 1 | export data $u8 = align 1 { b 97, } 2 | -------------------------------------------------------------------------------- /test/char-sign+aarch64.c: -------------------------------------------------------------------------------- 1 | int x = (char)-1 < 0; 2 | int main(void) { 3 | return (char)-1 < 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/char-sign+aarch64.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 0, } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =w neg 1 7 | %.2 =w extub %.1 8 | %.3 =w csltw %.2, 0 9 | ret %.3 10 | } 11 | -------------------------------------------------------------------------------- /test/char-sign+riscv64.c: -------------------------------------------------------------------------------- 1 | int x = (char)-1 < 0; 2 | int main(void) { 3 | return (char)-1 < 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/char-sign+riscv64.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 0, } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =w neg 1 7 | %.2 =w extub %.1 8 | %.3 =w csltw %.2, 0 9 | ret %.3 10 | } 11 | -------------------------------------------------------------------------------- /test/char-sign+x86_64-sysv.c: -------------------------------------------------------------------------------- 1 | int x = (char)-1 > 0; 2 | int main(void) { 3 | return (char)-1 > 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/char-sign+x86_64-sysv.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 0, } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =w neg 1 7 | %.2 =w extsb %.1 8 | %.3 =w csgtw %.2, 0 9 | ret %.3 10 | } 11 | -------------------------------------------------------------------------------- /test/common-real-int-sign.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | return 0 > -1u; 3 | } 4 | -------------------------------------------------------------------------------- /test/common-real-int-sign.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w neg 1 6 | %.2 =w cugtw 0, %.1 7 | ret %.2 8 | } 9 | -------------------------------------------------------------------------------- /test/common-real-unsigned-char.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | return ((unsigned char)1 - (unsigned char)2) > 0; 3 | } 4 | -------------------------------------------------------------------------------- /test/common-real-unsigned-char.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w extub 1 6 | %.2 =w extub 2 7 | %.3 =w sub %.1, %.2 8 | %.4 =w csgtw %.3, 0 9 | ret %.4 10 | } 11 | -------------------------------------------------------------------------------- /test/compare-char.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | return (unsigned char)0 < (unsigned char)256; 3 | } 4 | -------------------------------------------------------------------------------- /test/compare-char.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w extub 0 6 | %.2 =w extub 256 7 | %.3 =w csltw %.1, %.2 8 | ret %.3 9 | } 10 | -------------------------------------------------------------------------------- /test/compatible-array-types.c: -------------------------------------------------------------------------------- 1 | static_assert(__builtin_types_compatible_p(int[2], int[1 + 1])); 2 | static_assert(!__builtin_types_compatible_p(int[2], int[1])); 3 | static_assert(!__builtin_types_compatible_p(int[2], unsigned[2])); 4 | static_assert(!__builtin_types_compatible_p(const int (*)[2], int (*)[2])); 5 | typedef int T[2]; 6 | /* FIXME 7 | static_assert(__builtin_types_compatible_p(const T *, const int (*)[2])); 8 | */ 9 | static_assert(__builtin_types_compatible_p(float[], float[3])); 10 | -------------------------------------------------------------------------------- /test/compatible-array-types.qbe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelforney/cproc/a2ddee1be68e76a1ac7f83b55732be8e54fe08c4/test/compatible-array-types.qbe -------------------------------------------------------------------------------- /test/compatible-enum-types.c: -------------------------------------------------------------------------------- 1 | enum {A = 1} x; 2 | unsigned x; 3 | enum {B = -1} y; 4 | int y; 5 | -------------------------------------------------------------------------------- /test/compatible-enum-types.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { z 4 } 2 | export data $y = align 4 { z 4 } 3 | -------------------------------------------------------------------------------- /test/compatible-function-types.c: -------------------------------------------------------------------------------- 1 | void f1(); 2 | void f1(void); 3 | 4 | void f2(int, char *); 5 | void f2(int, char *); 6 | 7 | void f3(int(const double)); 8 | void f3(int (*)(double)); 9 | 10 | void f4(const char *, ...); 11 | void f5(const char[], ...); 12 | 13 | void f6(const int); 14 | void f6(int); 15 | 16 | static_assert(!__builtin_types_compatible_p(void(), void(int))); 17 | static_assert(!__builtin_types_compatible_p(void(int), void(char))); 18 | static_assert(!__builtin_types_compatible_p(int(float), char(float))); 19 | static_assert(!__builtin_types_compatible_p(void(int, ...), void(int))); 20 | static_assert(!__builtin_types_compatible_p(void(const char *), void(char *))); 21 | static_assert(!__builtin_types_compatible_p(void(char *), void(unsigned char *))); 22 | static_assert(!__builtin_types_compatible_p(void(int, float), void(int, double))); 23 | -------------------------------------------------------------------------------- /test/compatible-function-types.qbe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelforney/cproc/a2ddee1be68e76a1ac7f83b55732be8e54fe08c4/test/compatible-function-types.qbe -------------------------------------------------------------------------------- /test/compatible-vla-types.c: -------------------------------------------------------------------------------- 1 | void f1(int n, int (*a)[n], int (*b)[*], int (*c)[3], 2 | struct { 3 | int x; 4 | static_assert(__builtin_types_compatible_p(typeof(a), typeof(b))); 5 | static_assert(__builtin_types_compatible_p(typeof(a), int (*)[3])); 6 | static_assert(__builtin_types_compatible_p(typeof(b), int (*)[3])); 7 | static_assert(__builtin_types_compatible_p(typeof(a), int (*)[])); 8 | static_assert(__builtin_types_compatible_p(typeof(b), int (*)[])); 9 | } s); 10 | void f2(void) { 11 | int n = 12, m = 6 * 2; 12 | static_assert(__builtin_types_compatible_p(int [n], int [12])); 13 | static_assert(__builtin_types_compatible_p(int [], int [n])); 14 | static_assert(__builtin_types_compatible_p(int [n], int [m])); 15 | static_assert(__builtin_types_compatible_p(int [2][n], int [1 + 1][n])); 16 | static_assert(!__builtin_types_compatible_p(int [4][n], int [5][n])); 17 | } 18 | -------------------------------------------------------------------------------- /test/compatible-vla-types.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f2() { 3 | @start.1 4 | %.1 =l alloc4 4 5 | %.2 =l alloc4 4 6 | @body.2 7 | storew 12, %.1 8 | %.3 =w mul 6, 2 9 | storew %.3, %.2 10 | ret 11 | } 12 | -------------------------------------------------------------------------------- /test/compound-assignment.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | int x[1] = {0}, *p = x; 3 | *p++ += 1; 4 | } 5 | -------------------------------------------------------------------------------- /test/compound-assignment.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc4 4 5 | %.2 =l alloc8 8 6 | @body.2 7 | storew 0, %.1 8 | storel %.1, %.2 9 | %.3 =l loadl %.2 10 | %.4 =l add %.3, 4 11 | storel %.4, %.2 12 | %.5 =w loadw %.3 13 | %.6 =w add %.5, 1 14 | storew %.6, %.3 15 | ret 16 | } 17 | -------------------------------------------------------------------------------- /test/compound-literal-static.c: -------------------------------------------------------------------------------- 1 | int *x = &(int){2}; 2 | -------------------------------------------------------------------------------- /test/compound-literal-static.qbe: -------------------------------------------------------------------------------- 1 | data $.L.1 = align 4 { w 2, } 2 | export data $x = align 8 { l $.L.1, } 3 | -------------------------------------------------------------------------------- /test/conditional-compound-literal.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | int x = 0, *p = x ? 0 : &(int){x}; 3 | return *p; 4 | } 5 | -------------------------------------------------------------------------------- /test/conditional-compound-literal.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | %.1 =l alloc4 4 5 | %.2 =l alloc8 8 6 | %.4 =l alloc4 4 7 | @body.2 8 | storew 0, %.1 9 | %.3 =w loadw %.1 10 | jnz %.3, @cond_true.3, @cond_false.4 11 | @cond_true.3 12 | jmp @cond_join.5 13 | @cond_false.4 14 | %.5 =w loadw %.1 15 | storew %.5, %.4 16 | @cond_join.5 17 | %.6 =l phi @cond_true.3 0, @cond_false.4 %.4 18 | storel %.6, %.2 19 | %.7 =l loadl %.2 20 | %.8 =w loadw %.7 21 | ret %.8 22 | } 23 | -------------------------------------------------------------------------------- /test/conditional-constant.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | if ((0 ? 0 : 123) != 123) 3 | return 1; 4 | if ((1 ? 456 : 0) != 456) 5 | return 1; 6 | } 7 | -------------------------------------------------------------------------------- /test/conditional-constant.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w cnew 123, 123 6 | jnz %.1, @if_true.3, @if_false.4 7 | @if_true.3 8 | ret 1 9 | @if_false.4 10 | %.2 =w cnew 456, 456 11 | jnz %.2, @if_true.5, @if_false.6 12 | @if_true.5 13 | ret 1 14 | @if_false.6 15 | ret 0 16 | } 17 | -------------------------------------------------------------------------------- /test/conditional.c: -------------------------------------------------------------------------------- 1 | int i; 2 | float f; 3 | void *p; 4 | int main(void) { 5 | if (i ? 1 : 0) 6 | return 1; 7 | if (f ? 1 : 0) 8 | return 1; 9 | if (p ? 1 : 0) 10 | return 1; 11 | } 12 | -------------------------------------------------------------------------------- /test/conditional.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w loadw $i 6 | jnz %.1, @cond_true.3, @cond_false.4 7 | @cond_true.3 8 | jmp @cond_join.5 9 | @cond_false.4 10 | @cond_join.5 11 | %.2 =w phi @cond_true.3 1, @cond_false.4 0 12 | jnz %.2, @if_true.6, @if_false.7 13 | @if_true.6 14 | ret 1 15 | @if_false.7 16 | %.3 =s loads $f 17 | %.4 =w cnes %.3, s_0 18 | jnz %.4, @cond_true.8, @cond_false.9 19 | @cond_true.8 20 | jmp @cond_join.10 21 | @cond_false.9 22 | @cond_join.10 23 | %.5 =w phi @cond_true.8 1, @cond_false.9 0 24 | jnz %.5, @if_true.11, @if_false.12 25 | @if_true.11 26 | ret 1 27 | @if_false.12 28 | %.6 =l loadl $p 29 | %.7 =w cnel %.6, 0 30 | jnz %.7, @cond_true.13, @cond_false.14 31 | @cond_true.13 32 | jmp @cond_join.15 33 | @cond_false.14 34 | @cond_join.15 35 | %.8 =w phi @cond_true.13 1, @cond_false.14 0 36 | jnz %.8, @if_true.16, @if_false.17 37 | @if_true.16 38 | ret 1 39 | @if_false.17 40 | ret 0 41 | } 42 | export data $i = align 4 { z 4 } 43 | export data $f = align 4 { z 4 } 44 | export data $p = align 8 { z 8 } 45 | -------------------------------------------------------------------------------- /test/const-array.c: -------------------------------------------------------------------------------- 1 | /* C11 6.7.3p9 - type qualifiers on array type qualify the element type */ 2 | typedef int T[2]; 3 | void f(const T x) { 4 | x = 0; 5 | } 6 | -------------------------------------------------------------------------------- /test/const-array.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f(l %.1) { 3 | @start.1 4 | %.2 =l alloc8 8 5 | storel %.1, %.2 6 | @body.2 7 | %.3 =l extsw 0 8 | storel %.3, %.2 9 | ret 10 | } 11 | -------------------------------------------------------------------------------- /test/const-expr-cast.c: -------------------------------------------------------------------------------- 1 | enum { 2 | A = (unsigned char)0x321, 3 | B = (short)-2147438112, 4 | C = 0x80000003 * 2, 5 | }; 6 | 7 | int a = A, b = B, c = C; 8 | -------------------------------------------------------------------------------- /test/const-expr-cast.qbe: -------------------------------------------------------------------------------- 1 | export data $a = align 4 { w 33, } 2 | export data $b = align 4 { w 18446744073709531616, } 3 | export data $c = align 4 { w 6, } 4 | -------------------------------------------------------------------------------- /test/const-expr-div.c: -------------------------------------------------------------------------------- 1 | int x = -2 / -1; 2 | -------------------------------------------------------------------------------- /test/const-expr-div.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 2, } 2 | -------------------------------------------------------------------------------- /test/const-expr-mod.c: -------------------------------------------------------------------------------- 1 | int x = -2 % -1; 2 | -------------------------------------------------------------------------------- /test/const-expr-mod.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 0, } 2 | -------------------------------------------------------------------------------- /test/const-expr-neg.c: -------------------------------------------------------------------------------- 1 | double x = -0.0; 2 | -------------------------------------------------------------------------------- /test/const-expr-neg.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 8 { d d_-0, } 2 | -------------------------------------------------------------------------------- /test/const-expr-shr.c: -------------------------------------------------------------------------------- 1 | int x = -1 >> 1; 2 | -------------------------------------------------------------------------------- /test/const-expr-shr.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 18446744073709551615, } 2 | -------------------------------------------------------------------------------- /test/const-false.c: -------------------------------------------------------------------------------- 1 | bool x = false; 2 | typeof(false) x; 3 | -------------------------------------------------------------------------------- /test/const-false.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 1 { b 0, } 2 | -------------------------------------------------------------------------------- /test/const-init.c: -------------------------------------------------------------------------------- 1 | const struct {struct {int x, y;} t;} s = {{{1}, 2}}; 2 | -------------------------------------------------------------------------------- /test/const-init.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 4 { w 1, w 2, } 2 | -------------------------------------------------------------------------------- /test/const-true.c: -------------------------------------------------------------------------------- 1 | bool x = true; 2 | typeof(true) x; 3 | -------------------------------------------------------------------------------- /test/const-true.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 1 { b 1, } 2 | -------------------------------------------------------------------------------- /test/do-loop.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | int x = 2, y = 0; 3 | do { 4 | if (x == 1) 5 | continue; 6 | ++y; 7 | } while (x--); 8 | return y != 2; 9 | } 10 | -------------------------------------------------------------------------------- /test/do-loop.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | %.1 =l alloc4 4 5 | %.2 =l alloc4 4 6 | @body.2 7 | storew 2, %.1 8 | storew 0, %.2 9 | @do_body.3 10 | %.3 =w loadw %.1 11 | %.4 =w ceqw %.3, 1 12 | jnz %.4, @if_true.6, @if_false.7 13 | @if_true.6 14 | jmp @do_cond.4 15 | @if_false.7 16 | %.5 =w loadw %.2 17 | %.6 =w add %.5, 1 18 | storew %.6, %.2 19 | @do_cond.4 20 | %.7 =w loadw %.1 21 | %.8 =w sub %.7, 1 22 | storew %.8, %.1 23 | jnz %.7, @do_body.3, @do_join.5 24 | @do_join.5 25 | %.9 =w loadw %.2 26 | %.10 =w cnew %.9, 2 27 | ret %.10 28 | } 29 | -------------------------------------------------------------------------------- /test/enum-fixed.c: -------------------------------------------------------------------------------- 1 | enum E1 : short; 2 | static_assert(__builtin_types_compatible_p(enum E1, short)); 3 | 4 | enum E2 : unsigned short { 5 | A2 = 0x7fff, 6 | B2, 7 | A2type = __builtin_types_compatible_p(typeof(A2), unsigned short), 8 | B2type = __builtin_types_compatible_p(typeof(B2), unsigned short), 9 | }; 10 | static_assert(__builtin_types_compatible_p(typeof(A2), unsigned short)); 11 | static_assert(A2type == 1); 12 | static_assert(__builtin_types_compatible_p(typeof(B2), unsigned short)); 13 | static_assert(B2type == 1); 14 | static_assert(__builtin_types_compatible_p(enum E2, unsigned short)); 15 | 16 | enum E3 : long long { 17 | A3, 18 | B3, 19 | A3type = __builtin_types_compatible_p(typeof(A3), long long), 20 | B3type = __builtin_types_compatible_p(typeof(B3), long long), 21 | }; 22 | static_assert(__builtin_types_compatible_p(typeof(A3), long long)); 23 | static_assert(A3type == 1); 24 | static_assert(__builtin_types_compatible_p(typeof(B3), long long)); 25 | static_assert(B3type == 1); 26 | static_assert(__builtin_types_compatible_p(enum E3, long long)); 27 | 28 | enum E4 : long long { 29 | A4 = sizeof(enum E4), 30 | A4type1 = __builtin_types_compatible_p(typeof(A4), enum E4), 31 | A4type2 = !__builtin_types_compatible_p(typeof(A4), enum E3), 32 | }; 33 | static_assert(__builtin_types_compatible_p(typeof(A4), enum E4)); 34 | static_assert(A4type1 == 1); 35 | static_assert(!__builtin_types_compatible_p(typeof(A4), enum E3)); 36 | static_assert(A4type2 == 1); 37 | -------------------------------------------------------------------------------- /test/enum-fixed.qbe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelforney/cproc/a2ddee1be68e76a1ac7f83b55732be8e54fe08c4/test/enum-fixed.qbe -------------------------------------------------------------------------------- /test/enum-large-value.c: -------------------------------------------------------------------------------- 1 | enum E1 { 2 | A1 = 0x80000000, 3 | B1 = 0x80000000ll, 4 | A1type = __builtin_types_compatible_p(typeof(A1), unsigned), 5 | B1type = __builtin_types_compatible_p(typeof(B1), long long), 6 | }; 7 | static_assert(__builtin_types_compatible_p(typeof(A1), unsigned)); 8 | static_assert(A1type == 1); 9 | static_assert(__builtin_types_compatible_p(typeof(B1), unsigned)); 10 | static_assert(B1type == 1); 11 | static_assert(__builtin_types_compatible_p(enum E1, unsigned)); 12 | static_assert(!__builtin_types_compatible_p(enum E1, enum { A1_ = A1, B1_ = B1 })); 13 | 14 | enum E2 { 15 | A2 = 0x80000000, 16 | B2 = -1ll, 17 | A2type = __builtin_types_compatible_p(typeof(A2), unsigned), 18 | B2type = __builtin_types_compatible_p(typeof(B2), int), 19 | }; 20 | static_assert(__builtin_types_compatible_p(typeof(A2), long)); 21 | static_assert(A2type == 1); 22 | static_assert(__builtin_types_compatible_p(typeof(B2), long)); 23 | static_assert(B2type == 1); 24 | static_assert(__builtin_types_compatible_p(enum E2, long)); 25 | static_assert(!__builtin_types_compatible_p(enum E2, enum { A2_ = A2, B2_ = B2 })); 26 | 27 | enum E3 { 28 | A3 = 0x7fffffff, 29 | B3, 30 | A3type = __builtin_types_compatible_p(typeof(A3), int), 31 | B3type = __builtin_types_compatible_p(typeof(B3), long), 32 | }; 33 | static_assert(__builtin_types_compatible_p(typeof(A3), unsigned)); 34 | static_assert(A3type == 1); 35 | static_assert(__builtin_types_compatible_p(typeof(B3), unsigned)); 36 | static_assert(B3type == 1); 37 | static_assert(__builtin_types_compatible_p(enum E3, unsigned)); 38 | static_assert(!__builtin_types_compatible_p(enum E3, enum { A3_ = A3, B3_ })); 39 | 40 | enum E4 { 41 | A4 = -0x80000001l, 42 | B4, 43 | C4 = B4, 44 | A4type = __builtin_types_compatible_p(typeof(A4), long), 45 | B4type = __builtin_types_compatible_p(typeof(B4), long), 46 | C4type = __builtin_types_compatible_p(typeof(C4), int), 47 | }; 48 | static_assert(__builtin_types_compatible_p(typeof(A4), long)); 49 | static_assert(A4type == 1); 50 | static_assert(__builtin_types_compatible_p(typeof(B4), long)); 51 | static_assert(B4type == 1); 52 | static_assert(__builtin_types_compatible_p(enum E4, long)); 53 | static_assert(!__builtin_types_compatible_p(enum E4, enum { A4_ = A4, B4_, C4 = C4 })); 54 | 55 | enum E5 { 56 | A5 = 0x100000000, 57 | B5 = -1ull, 58 | A5type = __builtin_types_compatible_p(typeof(A5), long), 59 | B5type = __builtin_types_compatible_p(typeof(B5), unsigned long long), 60 | }; 61 | static_assert(__builtin_types_compatible_p(typeof(A5), unsigned long)); 62 | static_assert(A5type == 1); 63 | static_assert(__builtin_types_compatible_p(typeof(B5), unsigned long)); 64 | static_assert(B5type == 1); 65 | static_assert(__builtin_types_compatible_p(enum E5, unsigned long)); 66 | static_assert(!__builtin_types_compatible_p(enum E5, enum { A5_ = A5, B5_ = B5 })); 67 | -------------------------------------------------------------------------------- /test/enum-large-value.qbe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelforney/cproc/a2ddee1be68e76a1ac7f83b55732be8e54fe08c4/test/enum-large-value.qbe -------------------------------------------------------------------------------- /test/enum.c: -------------------------------------------------------------------------------- 1 | enum E1 { 2 | A1 = 1u, 3 | B1 = -1, 4 | A1type = __builtin_types_compatible_p(typeof(A1), int), 5 | B1type = __builtin_types_compatible_p(typeof(B1), int), 6 | }; 7 | static_assert(__builtin_types_compatible_p(typeof(A1), int)); 8 | static_assert(A1type == 1); 9 | static_assert(__builtin_types_compatible_p(typeof(B1), int)); 10 | static_assert(B1type == 1); 11 | static_assert(__builtin_types_compatible_p(enum E1, int)); 12 | 13 | enum E2 { 14 | A2 = 1, 15 | B2 = 2, 16 | A2type = __builtin_types_compatible_p(typeof(A2), int), 17 | B2type = __builtin_types_compatible_p(typeof(B2), int), 18 | }; 19 | static_assert(__builtin_types_compatible_p(typeof(A2), int)); 20 | static_assert(A2type == 1); 21 | static_assert(__builtin_types_compatible_p(typeof(B2), int)); 22 | static_assert(B2type == 1); 23 | static_assert(__builtin_types_compatible_p(enum E2, unsigned)); 24 | -------------------------------------------------------------------------------- /test/enum.qbe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelforney/cproc/a2ddee1be68e76a1ac7f83b55732be8e54fe08c4/test/enum.qbe -------------------------------------------------------------------------------- /test/escaped-newline.c: -------------------------------------------------------------------------------- 1 | int split\ 2 | ident = 3; 3 | -------------------------------------------------------------------------------- /test/escaped-newline.qbe: -------------------------------------------------------------------------------- 1 | export data $splitident = align 4 { w 3, } 2 | -------------------------------------------------------------------------------- /test/expr-neg.c: -------------------------------------------------------------------------------- 1 | double f(double x) { 2 | return -x; 3 | } 4 | -------------------------------------------------------------------------------- /test/expr-neg.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function d $f(d %.1) { 3 | @start.1 4 | %.2 =l alloc8 8 5 | stored %.1, %.2 6 | @body.2 7 | %.3 =d loadd %.2 8 | %.4 =d neg %.3 9 | ret %.4 10 | } 11 | -------------------------------------------------------------------------------- /test/extern-initializer.c: -------------------------------------------------------------------------------- 1 | extern int x = 1; 2 | -------------------------------------------------------------------------------- /test/extern-initializer.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 1, } 2 | -------------------------------------------------------------------------------- /test/float-const-leading-dot.c: -------------------------------------------------------------------------------- 1 | float x = .5; 2 | -------------------------------------------------------------------------------- /test/float-const-leading-dot.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { s s_0.5, } 2 | -------------------------------------------------------------------------------- /test/float-promote.c: -------------------------------------------------------------------------------- 1 | void g1(int, ...); 2 | void g2(float); 3 | void f(void) { 4 | g1(0, 1.0f); 5 | g2(1.0f); 6 | } 7 | -------------------------------------------------------------------------------- /test/float-promote.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | @body.2 5 | %.1 =d exts s_1 6 | call $g1(w 0, ..., d %.1) 7 | call $g2(s s_1) 8 | ret 9 | } 10 | -------------------------------------------------------------------------------- /test/float-to-uint32.c: -------------------------------------------------------------------------------- 1 | float g(void); 2 | unsigned f(void) { 3 | return g(); 4 | } 5 | -------------------------------------------------------------------------------- /test/float-to-uint32.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $f() { 3 | @start.1 4 | @body.2 5 | %.1 =s call $g() 6 | %.2 =w stoui %.1 7 | ret %.2 8 | } 9 | -------------------------------------------------------------------------------- /test/float-to-uint64.c: -------------------------------------------------------------------------------- 1 | float g(void); 2 | unsigned long long f(void) { 3 | return g(); 4 | } 5 | -------------------------------------------------------------------------------- /test/float-to-uint64.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function l $f() { 3 | @start.1 4 | @body.2 5 | %.1 =s call $g() 6 | %.2 =l stoui %.1 7 | ret %.2 8 | } 9 | -------------------------------------------------------------------------------- /test/for-loop.c: -------------------------------------------------------------------------------- 1 | void g(int); 2 | void f(void) { 3 | int i; 4 | for (i = 0; i < 10; ++i) 5 | g(i); 6 | } 7 | -------------------------------------------------------------------------------- /test/for-loop.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc4 4 5 | @body.2 6 | storew 0, %.1 7 | @for_cond.3 8 | %.2 =w loadw %.1 9 | %.3 =w csltw %.2, 10 10 | jnz %.3, @for_body.4, @for_join.6 11 | @for_body.4 12 | %.4 =w loadw %.1 13 | call $g(w %.4) 14 | @for_cont.5 15 | %.5 =w loadw %.1 16 | %.6 =w add %.5, 1 17 | storew %.6, %.1 18 | jmp @for_cond.3 19 | @for_join.6 20 | ret 21 | } 22 | -------------------------------------------------------------------------------- /test/func-array-param.c: -------------------------------------------------------------------------------- 1 | typedef int T1[]; 2 | typedef const int T2[]; 3 | void f(int a[const], const int b[], const T1 c, T2 d) { 4 | /* check type of address, since __builtin_types_compatible_p ignores top-level qualifiers */ 5 | static_assert(__builtin_types_compatible_p(typeof(&a), int *const*)); 6 | static_assert(__builtin_types_compatible_p(typeof(&b), const int **)); 7 | static_assert(__builtin_types_compatible_p(typeof(&c), const int **)); 8 | static_assert(__builtin_types_compatible_p(typeof(&d), const int **)); 9 | } 10 | -------------------------------------------------------------------------------- /test/func-array-param.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f(l %.1, l %.3, l %.5, l %.7) { 3 | @start.1 4 | %.2 =l alloc8 8 5 | storel %.1, %.2 6 | %.4 =l alloc8 8 7 | storel %.3, %.4 8 | %.6 =l alloc8 8 9 | storel %.5, %.6 10 | %.8 =l alloc8 8 11 | storel %.7, %.8 12 | @body.2 13 | ret 14 | } 15 | -------------------------------------------------------------------------------- /test/func-noreturn.c: -------------------------------------------------------------------------------- 1 | _Noreturn void exit(int); 2 | int puts(const char *); 3 | void _Noreturn f(void) { 4 | static int c; 5 | while (c) 6 | puts("loop"); 7 | } 8 | int main(void) { 9 | exit(0); 10 | (*f)(); 11 | (***f)(); 12 | return 1; 13 | } 14 | -------------------------------------------------------------------------------- /test/func-noreturn.qbe: -------------------------------------------------------------------------------- 1 | data $.Lc.2 = align 4 { z 4 } 2 | data $.Lstring.3 = align 1 { b "loop\000", } 3 | export 4 | function $f() { 5 | @start.1 6 | @body.2 7 | @while_cond.3 8 | %.1 =w loadw $.Lc.2 9 | jnz %.1, @while_body.4, @while_join.5 10 | @while_body.4 11 | %.2 =w call $puts(l $.Lstring.3) 12 | jmp @while_cond.3 13 | @while_join.5 14 | hlt 15 | } 16 | export 17 | function w $main() { 18 | @start.6 19 | @body.7 20 | call $exit(w 0) 21 | hlt 22 | @dead.8 23 | call $f() 24 | hlt 25 | @dead.9 26 | call $f() 27 | hlt 28 | } 29 | -------------------------------------------------------------------------------- /test/func-param-scope.c: -------------------------------------------------------------------------------- 1 | enum {A = 1}; 2 | char (*f(enum {A = 2} *p, int (*a)[A], double (*b)[sizeof **a]))[A] { 3 | static_assert(A == 2); 4 | static_assert(sizeof *b == sizeof(int) * sizeof(double)); 5 | static_assert(sizeof *a == 2 * sizeof(int)); 6 | return 0; 7 | } 8 | static_assert(A == 1); 9 | static_assert(sizeof *f(0, 0, 0) == 1); 10 | -------------------------------------------------------------------------------- /test/func-param-scope.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function l $f(l %.1, l %.3, l %.5) { 3 | @start.1 4 | %.2 =l alloc8 8 5 | storel %.1, %.2 6 | %.4 =l alloc8 8 7 | storel %.3, %.4 8 | %.6 =l alloc8 8 9 | storel %.5, %.6 10 | @body.2 11 | %.7 =l extsw 0 12 | ret %.7 13 | } 14 | -------------------------------------------------------------------------------- /test/func-unnamed-param.c: -------------------------------------------------------------------------------- 1 | void f(int) {} 2 | -------------------------------------------------------------------------------- /test/func-unnamed-param.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f(w %.1) { 3 | @start.1 4 | @body.2 5 | ret 6 | } 7 | -------------------------------------------------------------------------------- /test/func-vla.c: -------------------------------------------------------------------------------- 1 | int f(int n, long long (*a)[n]) { 2 | return sizeof *a; 3 | } 4 | 5 | int main(void) { 6 | long long a[5]; 7 | return f(5, &a) != sizeof a; 8 | } 9 | -------------------------------------------------------------------------------- /test/func-vla.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $f(w %.1, l %.3) { 3 | @start.1 4 | %.2 =l alloc4 4 5 | storew %.1, %.2 6 | %.4 =w loadw %.2 7 | %.5 =l extsw %.4 8 | %.6 =l mul %.5, 8 9 | %.7 =l alloc8 8 10 | storel %.3, %.7 11 | @body.2 12 | %.8 =l loadl %.7 13 | ret %.6 14 | } 15 | export 16 | function w $main() { 17 | @start.3 18 | %.1 =l alloc8 40 19 | @body.4 20 | %.2 =w call $f(w 5, l %.1) 21 | %.3 =l extsw %.2 22 | %.4 =w cnel %.3, 40 23 | ret %.4 24 | } 25 | -------------------------------------------------------------------------------- /test/generic.c: -------------------------------------------------------------------------------- 1 | int x = _Generic(123, 2 | const int: 1, 3 | unsigned: 2, 4 | int: 3, 5 | int *: 4 6 | ); 7 | -------------------------------------------------------------------------------- /test/generic.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 3, } 2 | -------------------------------------------------------------------------------- /test/global-align.c: -------------------------------------------------------------------------------- 1 | alignas(8) char c; 2 | -------------------------------------------------------------------------------- /test/global-align.qbe: -------------------------------------------------------------------------------- 1 | export data $c = align 8 { z 1 } 2 | -------------------------------------------------------------------------------- /test/hello.c: -------------------------------------------------------------------------------- 1 | int puts(const char *); 2 | int main(void) { 3 | puts("hello"); 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /test/hello.qbe: -------------------------------------------------------------------------------- 1 | data $.Lstring.2 = align 1 { b "hello\000", } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =w call $puts(l $.Lstring.2) 7 | ret 0 8 | } 9 | -------------------------------------------------------------------------------- /test/if-char.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | if ((unsigned char)0x100) 3 | return 1; 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /test/if-char.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w extub 256 6 | jnz %.1, @if_true.3, @if_false.4 7 | @if_true.3 8 | ret 1 9 | @if_false.4 10 | ret 0 11 | } 12 | -------------------------------------------------------------------------------- /test/initializer-address-subtract.c: -------------------------------------------------------------------------------- 1 | int a[4]; 2 | int *p = a + 2 - 1; 3 | -------------------------------------------------------------------------------- /test/initializer-address-subtract.qbe: -------------------------------------------------------------------------------- 1 | export data $p = align 8 { l $a + 4, } 2 | export data $a = align 4 { z 16 } 3 | -------------------------------------------------------------------------------- /test/initializer-cast-float-int.c: -------------------------------------------------------------------------------- 1 | int x = -1.0; 2 | -------------------------------------------------------------------------------- /test/initializer-cast-float-int.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 18446744073709551615, } 2 | -------------------------------------------------------------------------------- /test/initializer-cast-int-float.c: -------------------------------------------------------------------------------- 1 | double x = -1; 2 | -------------------------------------------------------------------------------- /test/initializer-cast-int-float.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 8 { d d_-1, } 2 | -------------------------------------------------------------------------------- /test/initializer-empty-struct.c: -------------------------------------------------------------------------------- 1 | struct s {float x; long y;}; 2 | struct s s = {}; 3 | void f(void) { 4 | struct s t = {}; 5 | } 6 | -------------------------------------------------------------------------------- /test/initializer-empty-struct.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 8 { z 16 } 2 | export 3 | function $f() { 4 | @start.1 5 | %.1 =l alloc8 16 6 | @body.2 7 | storel 0, %.1 8 | %.2 =l add %.1, 8 9 | storel 0, %.2 10 | ret 11 | } 12 | -------------------------------------------------------------------------------- /test/initializer-empty.c: -------------------------------------------------------------------------------- 1 | int x = {}; 2 | void f(void) { 3 | int y = {}; 4 | } 5 | -------------------------------------------------------------------------------- /test/initializer-empty.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { z 4 } 2 | export 3 | function $f() { 4 | @start.1 5 | %.1 =l alloc4 4 6 | @body.2 7 | storew 0, %.1 8 | ret 9 | } 10 | -------------------------------------------------------------------------------- /test/initializer-long-string.c: -------------------------------------------------------------------------------- 1 | char s[4] = "hello"; 2 | -------------------------------------------------------------------------------- /test/initializer-long-string.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 1 { b "hell", } 2 | -------------------------------------------------------------------------------- /test/initializer-member-static.c: -------------------------------------------------------------------------------- 1 | extern struct { int x; } s; 2 | int *p = &s.x; 3 | -------------------------------------------------------------------------------- /test/initializer-member-static.qbe: -------------------------------------------------------------------------------- 1 | export data $p = align 8 { l $s + 0, } 2 | -------------------------------------------------------------------------------- /test/initializer-nested-array-address.c: -------------------------------------------------------------------------------- 1 | int x[2][3]; 2 | int *y = &x[1][2]; 3 | -------------------------------------------------------------------------------- /test/initializer-nested-array-address.qbe: -------------------------------------------------------------------------------- 1 | export data $y = align 8 { l $x + 20, } 2 | export data $x = align 4 { z 24 } 3 | -------------------------------------------------------------------------------- /test/initializer-pointer-int-cast.c: -------------------------------------------------------------------------------- 1 | int x; 2 | long p = (long)&x; 3 | -------------------------------------------------------------------------------- /test/initializer-pointer-int-cast.qbe: -------------------------------------------------------------------------------- 1 | export data $p = align 8 { l $x, } 2 | export data $x = align 4 { z 4 } 3 | -------------------------------------------------------------------------------- /test/initializer-replace-local-string-wide.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | struct { 3 | unsigned short u[6]; 4 | unsigned U[6]; 5 | typeof(L' ') L[6]; 6 | } x = { 7 | .u[0] = u'x', 8 | .u[4] = u'y', 9 | .u = u"hello", 10 | .u[1] = u'a', 11 | 12 | .U[0] = U'x', 13 | .U[4] = U'y', 14 | .U = U"hello", 15 | .U[1] = U'a', 16 | 17 | .L[0] = L'x', 18 | .L[4] = L'y', 19 | .L = L"hello", 20 | .L[1] = L'a', 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /test/initializer-replace-local-string-wide.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc4 60 5 | @body.2 6 | %.2 =l add %.1, 0 7 | storeh 104, %.2 8 | %.3 =l add %.1, 2 9 | storeh 101, %.3 10 | %.4 =l add %.1, 4 11 | storeh 108, %.4 12 | %.5 =l add %.1, 6 13 | storeh 108, %.5 14 | %.6 =l add %.1, 8 15 | storeh 111, %.6 16 | %.7 =l add %.1, 10 17 | storeh 0, %.7 18 | %.8 =l add %.1, 2 19 | storeh 97, %.8 20 | %.9 =l add %.1, 4 21 | storew 0, %.9 22 | %.10 =l add %.1, 8 23 | storew 0, %.10 24 | %.11 =l add %.1, 12 25 | storew 104, %.11 26 | %.12 =l add %.1, 16 27 | storew 101, %.12 28 | %.13 =l add %.1, 20 29 | storew 108, %.13 30 | %.14 =l add %.1, 24 31 | storew 108, %.14 32 | %.15 =l add %.1, 28 33 | storew 111, %.15 34 | %.16 =l add %.1, 32 35 | storew 0, %.16 36 | %.17 =l add %.1, 16 37 | storew 97, %.17 38 | %.18 =l add %.1, 20 39 | storew 0, %.18 40 | %.19 =l add %.1, 24 41 | storew 0, %.19 42 | %.20 =l add %.1, 28 43 | storew 0, %.20 44 | %.21 =l add %.1, 32 45 | storew 0, %.21 46 | %.22 =l add %.1, 36 47 | storew 104, %.22 48 | %.23 =l add %.1, 40 49 | storew 101, %.23 50 | %.24 =l add %.1, 44 51 | storew 108, %.24 52 | %.25 =l add %.1, 48 53 | storew 108, %.25 54 | %.26 =l add %.1, 52 55 | storew 111, %.26 56 | %.27 =l add %.1, 56 57 | storew 0, %.27 58 | %.28 =l add %.1, 40 59 | storew 97, %.28 60 | ret 61 | } 62 | -------------------------------------------------------------------------------- /test/initializer-replace-local.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | struct { 3 | char s[6]; 4 | } x = { 5 | .s[0] = 'x', 6 | .s[4] = 'y', 7 | .s = "hello", 8 | .s[1] = 'a', 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /test/initializer-replace-local.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc4 6 5 | @body.2 6 | %.2 =l add %.1, 0 7 | storeb 104, %.2 8 | %.3 =l add %.1, 1 9 | storeb 101, %.3 10 | %.4 =l add %.1, 2 11 | storeb 108, %.4 12 | %.5 =l add %.1, 3 13 | storeb 108, %.5 14 | %.6 =l add %.1, 4 15 | storeb 111, %.6 16 | %.7 =l add %.1, 5 17 | storeb 0, %.7 18 | %.8 =l add %.1, 1 19 | storeb 97, %.8 20 | ret 21 | } 22 | -------------------------------------------------------------------------------- /test/initializer-replace-static-string-wide.c: -------------------------------------------------------------------------------- 1 | struct { 2 | unsigned short s[6]; 3 | } u = { 4 | .s = u"aα€😐", 5 | .s[2] = u'£', 6 | }; 7 | 8 | struct { 9 | unsigned s[5]; 10 | } U = { 11 | .s = U"aα€😐", 12 | .s[3] = U'😃', 13 | }; 14 | 15 | struct { 16 | typeof(L' ') s[5]; 17 | } L = { 18 | .s = L"aα€😐", 19 | .s[3] = L'😃', 20 | }; 21 | -------------------------------------------------------------------------------- /test/initializer-replace-static-string-wide.qbe: -------------------------------------------------------------------------------- 1 | export data $u = align 2 { h 97 945 163 55357 56848 0 , } 2 | export data $U = align 4 { w 97 945 8364 128515 0 , } 3 | export data $L = align 4 { w 97 945 8364 128515 0 , } 4 | -------------------------------------------------------------------------------- /test/initializer-replace-static.c: -------------------------------------------------------------------------------- 1 | struct { 2 | char s[6]; 3 | } x = { 4 | .s = "hello", 5 | .s[1] = 'a', 6 | }; 7 | -------------------------------------------------------------------------------- /test/initializer-replace-static.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 1 { b "hallo\000", } 2 | -------------------------------------------------------------------------------- /test/initializer-short-string.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | char s[10] = "abc"; 3 | } 4 | -------------------------------------------------------------------------------- /test/initializer-short-string.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc4 10 5 | @body.2 6 | %.2 =l add %.1, 0 7 | storeb 97, %.2 8 | %.3 =l add %.1, 1 9 | storeb 98, %.3 10 | %.4 =l add %.1, 2 11 | storeb 99, %.4 12 | %.5 =l add %.1, 3 13 | storeb 0, %.5 14 | %.6 =l add %.1, 4 15 | storeb 0, %.6 16 | %.7 =l add %.1, 5 17 | storeb 0, %.7 18 | %.8 =l add %.1, 6 19 | storeb 0, %.8 20 | %.9 =l add %.1, 7 21 | storeb 0, %.9 22 | %.10 =l add %.1, 8 23 | storeb 0, %.10 24 | %.11 =l add %.1, 9 25 | storeb 0, %.11 26 | ret 27 | } 28 | -------------------------------------------------------------------------------- /test/initializer-string-array.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | char x[][4] = {"abc", "xyz"}; 3 | } 4 | -------------------------------------------------------------------------------- /test/initializer-string-array.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc4 8 5 | @body.2 6 | %.2 =l add %.1, 0 7 | storeb 97, %.2 8 | %.3 =l add %.1, 1 9 | storeb 98, %.3 10 | %.4 =l add %.1, 2 11 | storeb 99, %.4 12 | %.5 =l add %.1, 3 13 | storeb 0, %.5 14 | %.6 =l add %.1, 4 15 | storeb 120, %.6 16 | %.7 =l add %.1, 5 17 | storeb 121, %.7 18 | %.8 =l add %.1, 6 19 | storeb 122, %.8 20 | %.9 =l add %.1, 7 21 | storeb 0, %.9 22 | ret 23 | } 24 | -------------------------------------------------------------------------------- /test/initializer-string-braces.c: -------------------------------------------------------------------------------- 1 | char s[] = {"abc"}; 2 | -------------------------------------------------------------------------------- /test/initializer-string-braces.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 1 { b "abc\000", } 2 | -------------------------------------------------------------------------------- /test/initializer-string-wide.c: -------------------------------------------------------------------------------- 1 | char s[] = "aα€😀\xAA\xBBBB\xCCCCCCCC"; 2 | char u8[] = u8"aα€😀\xAA\xBBBB\xCCCCCCCC"; 3 | unsigned short u[] = u"aα€😀\xAA\xBBBB\xCCCCCCCC"; 4 | unsigned U[] = U"aα€😀\xAA\xBBBB\xCCCCCCCC"; 5 | typeof(L' ') L[] = L"aα€😀\xAA\xBBBB\xCCCCCCCC"; 6 | 7 | void f(void) { 8 | char s[] = "aα€😀\xAA\xBBBB\xCCCCCCCC"; 9 | char u8[] = u8"aα€😀\xAA\xBBBB\xCCCCCCCC"; 10 | unsigned short u[] = u"aα€😀\xAA\xBBBB\xCCCCCCCC"; 11 | unsigned U[] = U"aα€😀\xAA\xBBBB\xCCCCCCCC"; 12 | typeof(L' ') L[] = L"aα€😀\xAA\xBBBB\xCCCCCCCC"; 13 | } 14 | -------------------------------------------------------------------------------- /test/initializer-string-wide.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 1 { b "a\316\261\342\202\254\360\237\230\200\252\273\314\000", } 2 | export data $u8 = align 1 { b "a\316\261\342\202\254\360\237\230\200\252\273\314\000", } 3 | export data $u = align 2 { h 97 945 8364 55357 56832 170 48059 52428 0 , } 4 | export data $U = align 4 { w 97 945 8364 128512 170 48059 3435973836 0 , } 5 | export data $L = align 4 { w 97 945 8364 128512 170 48059 3435973836 0 , } 6 | export 7 | function $f() { 8 | @start.1 9 | %.1 =l alloc4 14 10 | %.16 =l alloc4 14 11 | %.31 =l alloc4 18 12 | %.41 =l alloc4 32 13 | %.50 =l alloc4 32 14 | @body.2 15 | %.2 =l add %.1, 0 16 | storeb 97, %.2 17 | %.3 =l add %.1, 1 18 | storeb 206, %.3 19 | %.4 =l add %.1, 2 20 | storeb 177, %.4 21 | %.5 =l add %.1, 3 22 | storeb 226, %.5 23 | %.6 =l add %.1, 4 24 | storeb 130, %.6 25 | %.7 =l add %.1, 5 26 | storeb 172, %.7 27 | %.8 =l add %.1, 6 28 | storeb 240, %.8 29 | %.9 =l add %.1, 7 30 | storeb 159, %.9 31 | %.10 =l add %.1, 8 32 | storeb 152, %.10 33 | %.11 =l add %.1, 9 34 | storeb 128, %.11 35 | %.12 =l add %.1, 10 36 | storeb 170, %.12 37 | %.13 =l add %.1, 11 38 | storeb 187, %.13 39 | %.14 =l add %.1, 12 40 | storeb 204, %.14 41 | %.15 =l add %.1, 13 42 | storeb 0, %.15 43 | %.17 =l add %.16, 0 44 | storeb 97, %.17 45 | %.18 =l add %.16, 1 46 | storeb 206, %.18 47 | %.19 =l add %.16, 2 48 | storeb 177, %.19 49 | %.20 =l add %.16, 3 50 | storeb 226, %.20 51 | %.21 =l add %.16, 4 52 | storeb 130, %.21 53 | %.22 =l add %.16, 5 54 | storeb 172, %.22 55 | %.23 =l add %.16, 6 56 | storeb 240, %.23 57 | %.24 =l add %.16, 7 58 | storeb 159, %.24 59 | %.25 =l add %.16, 8 60 | storeb 152, %.25 61 | %.26 =l add %.16, 9 62 | storeb 128, %.26 63 | %.27 =l add %.16, 10 64 | storeb 170, %.27 65 | %.28 =l add %.16, 11 66 | storeb 187, %.28 67 | %.29 =l add %.16, 12 68 | storeb 204, %.29 69 | %.30 =l add %.16, 13 70 | storeb 0, %.30 71 | %.32 =l add %.31, 0 72 | storeh 97, %.32 73 | %.33 =l add %.31, 2 74 | storeh 945, %.33 75 | %.34 =l add %.31, 4 76 | storeh 8364, %.34 77 | %.35 =l add %.31, 6 78 | storeh 55357, %.35 79 | %.36 =l add %.31, 8 80 | storeh 56832, %.36 81 | %.37 =l add %.31, 10 82 | storeh 170, %.37 83 | %.38 =l add %.31, 12 84 | storeh 48059, %.38 85 | %.39 =l add %.31, 14 86 | storeh 52428, %.39 87 | %.40 =l add %.31, 16 88 | storeh 0, %.40 89 | %.42 =l add %.41, 0 90 | storew 97, %.42 91 | %.43 =l add %.41, 4 92 | storew 945, %.43 93 | %.44 =l add %.41, 8 94 | storew 8364, %.44 95 | %.45 =l add %.41, 12 96 | storew 128512, %.45 97 | %.46 =l add %.41, 16 98 | storew 170, %.46 99 | %.47 =l add %.41, 20 100 | storew 48059, %.47 101 | %.48 =l add %.41, 24 102 | storew 3435973836, %.48 103 | %.49 =l add %.41, 28 104 | storew 0, %.49 105 | %.51 =l add %.50, 0 106 | storew 97, %.51 107 | %.52 =l add %.50, 4 108 | storew 945, %.52 109 | %.53 =l add %.50, 8 110 | storew 8364, %.53 111 | %.54 =l add %.50, 12 112 | storew 128512, %.54 113 | %.55 =l add %.50, 16 114 | storew 170, %.55 115 | %.56 =l add %.50, 20 116 | storew 48059, %.56 117 | %.57 =l add %.50, 24 118 | storew 3435973836, %.57 119 | %.58 =l add %.50, 28 120 | storew 0, %.58 121 | ret 122 | } 123 | -------------------------------------------------------------------------------- /test/initializer-string.c: -------------------------------------------------------------------------------- 1 | char x[] = "hello"; 2 | void f(void) { 3 | char y[] = "hello"; 4 | } 5 | -------------------------------------------------------------------------------- /test/initializer-string.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 1 { b "hello\000", } 2 | export 3 | function $f() { 4 | @start.1 5 | %.1 =l alloc4 6 6 | @body.2 7 | %.2 =l add %.1, 0 8 | storeb 104, %.2 9 | %.3 =l add %.1, 1 10 | storeb 101, %.3 11 | %.4 =l add %.1, 2 12 | storeb 108, %.4 13 | %.5 =l add %.1, 3 14 | storeb 108, %.5 15 | %.6 =l add %.1, 4 16 | storeb 111, %.6 17 | %.7 =l add %.1, 5 18 | storeb 0, %.7 19 | ret 20 | } 21 | -------------------------------------------------------------------------------- /test/initializer-unsigned-string.c: -------------------------------------------------------------------------------- 1 | unsigned char s[] = "abc"; 2 | -------------------------------------------------------------------------------- /test/initializer-unsigned-string.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 1 { b "abc\000", } 2 | -------------------------------------------------------------------------------- /test/inline-definition.c: -------------------------------------------------------------------------------- 1 | extern void f(void); 2 | 3 | /* 4 | f is not an inline definition, due to the preceeding declaration, 5 | so we *should* emit an external definition for it. 6 | */ 7 | inline void f(void) {} 8 | -------------------------------------------------------------------------------- /test/inline-definition.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | @body.2 5 | ret 6 | } 7 | -------------------------------------------------------------------------------- /test/label-typedef.c: -------------------------------------------------------------------------------- 1 | typedef int x; 2 | void f(void) { 3 | x:; 4 | } 5 | -------------------------------------------------------------------------------- /test/label-typedef.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | @body.2 5 | @x.3 6 | ret 7 | } 8 | -------------------------------------------------------------------------------- /test/local-align.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | alignas(16) char x[4]; 3 | } 4 | -------------------------------------------------------------------------------- /test/local-align.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc16 4 5 | @body.2 6 | ret 7 | } 8 | -------------------------------------------------------------------------------- /test/local-init.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | struct { 3 | char c[8]; 4 | long long i[3]; 5 | } s = {'a'}; 6 | } 7 | -------------------------------------------------------------------------------- /test/local-init.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc8 32 5 | @body.2 6 | storeb 97, %.1 7 | %.2 =l add %.1, 1 8 | storeb 0, %.2 9 | %.3 =l add %.1, 2 10 | storeh 0, %.3 11 | %.4 =l add %.1, 4 12 | storew 0, %.4 13 | %.5 =l add %.1, 8 14 | storel 0, %.5 15 | %.6 =l add %.1, 16 16 | storel 0, %.6 17 | %.7 =l add %.1, 24 18 | storel 0, %.7 19 | ret 20 | } 21 | -------------------------------------------------------------------------------- /test/logical-and.c: -------------------------------------------------------------------------------- 1 | int i; 2 | float f; 3 | void *p; 4 | int main(void) { 5 | if (i && 1) 6 | return 1; 7 | if (f && 1) 8 | return 1; 9 | if (p && 1) 10 | return 1; 11 | } 12 | -------------------------------------------------------------------------------- /test/logical-and.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w loadw $i 6 | jnz %.1, @logic_right.3, @logic_join.4 7 | @logic_right.3 8 | %.2 =w cnew 1, 0 9 | @logic_join.4 10 | %.3 =w phi @body.2 0, @logic_right.3 %.2 11 | jnz %.3, @if_true.5, @if_false.6 12 | @if_true.5 13 | ret 1 14 | @if_false.6 15 | %.4 =s loads $f 16 | %.5 =w cnes %.4, s_0 17 | jnz %.5, @logic_right.7, @logic_join.8 18 | @logic_right.7 19 | %.6 =w cnew 1, 0 20 | @logic_join.8 21 | %.7 =w phi @if_false.6 0, @logic_right.7 %.6 22 | jnz %.7, @if_true.9, @if_false.10 23 | @if_true.9 24 | ret 1 25 | @if_false.10 26 | %.8 =l loadl $p 27 | %.9 =w cnel %.8, 0 28 | jnz %.9, @logic_right.11, @logic_join.12 29 | @logic_right.11 30 | %.10 =w cnew 1, 0 31 | @logic_join.12 32 | %.11 =w phi @if_false.10 0, @logic_right.11 %.10 33 | jnz %.11, @if_true.13, @if_false.14 34 | @if_true.13 35 | ret 1 36 | @if_false.14 37 | ret 0 38 | } 39 | export data $i = align 4 { z 4 } 40 | export data $f = align 4 { z 4 } 41 | export data $p = align 8 { z 8 } 42 | -------------------------------------------------------------------------------- /test/logical-or.c: -------------------------------------------------------------------------------- 1 | int i; 2 | float f; 3 | void *p; 4 | int main(void) { 5 | if (i || 0) 6 | return 1; 7 | if (f || 0) 8 | return 1; 9 | if (p || 0) 10 | return 1; 11 | } 12 | -------------------------------------------------------------------------------- /test/logical-or.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | %.1 =w loadw $i 6 | jnz %.1, @logic_join.4, @logic_right.3 7 | @logic_right.3 8 | %.2 =w cnew 0, 0 9 | @logic_join.4 10 | %.3 =w phi @body.2 1, @logic_right.3 %.2 11 | jnz %.3, @if_true.5, @if_false.6 12 | @if_true.5 13 | ret 1 14 | @if_false.6 15 | %.4 =s loads $f 16 | %.5 =w cnes %.4, s_0 17 | jnz %.5, @logic_join.8, @logic_right.7 18 | @logic_right.7 19 | %.6 =w cnew 0, 0 20 | @logic_join.8 21 | %.7 =w phi @if_false.6 1, @logic_right.7 %.6 22 | jnz %.7, @if_true.9, @if_false.10 23 | @if_true.9 24 | ret 1 25 | @if_false.10 26 | %.8 =l loadl $p 27 | %.9 =w cnel %.8, 0 28 | jnz %.9, @logic_join.12, @logic_right.11 29 | @logic_right.11 30 | %.10 =w cnew 0, 0 31 | @logic_join.12 32 | %.11 =w phi @if_false.10 1, @logic_right.11 %.10 33 | jnz %.11, @if_true.13, @if_false.14 34 | @if_true.13 35 | ret 1 36 | @if_false.14 37 | ret 0 38 | } 39 | export data $i = align 4 { z 4 } 40 | export data $f = align 4 { z 4 } 41 | export data $p = align 8 { z 8 } 42 | -------------------------------------------------------------------------------- /test/lvalue-conversion.c: -------------------------------------------------------------------------------- 1 | void g(int); 2 | void f(void) { 3 | static const unsigned char c = 0; 4 | g(c); 5 | g(~c); 6 | } 7 | -------------------------------------------------------------------------------- /test/lvalue-conversion.qbe: -------------------------------------------------------------------------------- 1 | data $.Lc.2 = align 1 { b 0, } 2 | export 3 | function $f() { 4 | @start.1 5 | @body.2 6 | %.1 =w loadub $.Lc.2 7 | %.2 =w extub %.1 8 | call $g(w %.2) 9 | %.3 =w loadub $.Lc.2 10 | %.4 =w extub %.3 11 | %.5 =w xor %.4, 18446744073709551615 12 | call $g(w %.5) 13 | ret 14 | } 15 | -------------------------------------------------------------------------------- /test/nested-array.c: -------------------------------------------------------------------------------- 1 | int x[2][3]; 2 | -------------------------------------------------------------------------------- /test/nested-array.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { z 24 } 2 | -------------------------------------------------------------------------------- /test/nullptr.c: -------------------------------------------------------------------------------- 1 | typeof(nullptr) x = 0; 2 | static_assert(sizeof x == sizeof(char *)); 3 | int *y = nullptr; 4 | bool z = nullptr; 5 | -------------------------------------------------------------------------------- /test/nullptr.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 8 { l 0, } 2 | export data $y = align 8 { l 0, } 3 | export data $z = align 1 { b 0, } 4 | -------------------------------------------------------------------------------- /test/preprocess-macro-function-no-args.c: -------------------------------------------------------------------------------- 1 | #define f(x) x - 2 2 | f() 3 | -------------------------------------------------------------------------------- /test/preprocess-macro-function-no-args.pp: -------------------------------------------------------------------------------- 1 | - 2 2 | -------------------------------------------------------------------------------- /test/preprocess-macro-function-paren.c: -------------------------------------------------------------------------------- 1 | #define g(x) [x] 2 | #define f(a) g a (def) 3 | f((abc)) 4 | f() 5 | -------------------------------------------------------------------------------- /test/preprocess-macro-function-paren.pp: -------------------------------------------------------------------------------- 1 | [abc] (def) 2 | [def] 3 | -------------------------------------------------------------------------------- /test/preprocess-macro-function.c: -------------------------------------------------------------------------------- 1 | #define f(a, b) a, abc, b 2 | f(foo, bar) 3 | -------------------------------------------------------------------------------- /test/preprocess-macro-function.pp: -------------------------------------------------------------------------------- 1 | foo, abc, bar 2 | -------------------------------------------------------------------------------- /test/preprocess-macro-hide.c: -------------------------------------------------------------------------------- 1 | #define foo foo bar 2 | foo 3 | -------------------------------------------------------------------------------- /test/preprocess-macro-hide.pp: -------------------------------------------------------------------------------- 1 | foo bar 2 | -------------------------------------------------------------------------------- /test/preprocess-macro-object.c: -------------------------------------------------------------------------------- 1 | #define foo bar 2 | foo 3 | -------------------------------------------------------------------------------- /test/preprocess-macro-object.pp: -------------------------------------------------------------------------------- 1 | bar 2 | -------------------------------------------------------------------------------- /test/preprocess-macro-stringize-2.c: -------------------------------------------------------------------------------- 1 | #define str(x) # x 2 | str(@) 3 | str(12 x- 3 4 | abc) 5 | str('"' "\\") 6 | str( abc) 7 | -------------------------------------------------------------------------------- /test/preprocess-macro-stringize-2.pp: -------------------------------------------------------------------------------- 1 | "@" 2 | "12 x- 3 abc" 3 | "'\"' \"\\\\\"" 4 | "abc" 5 | -------------------------------------------------------------------------------- /test/preprocess-macro-stringize.c: -------------------------------------------------------------------------------- 1 | #define stringize(a) #a 2 | stringize(hello) 3 | -------------------------------------------------------------------------------- /test/preprocess-macro-stringize.pp: -------------------------------------------------------------------------------- 1 | "hello" 2 | -------------------------------------------------------------------------------- /test/preprocess-macro-vararg.c: -------------------------------------------------------------------------------- 1 | #define f(a, ...) __VA_ARGS__ + a, # __VA_ARGS__ 2 | f(abc, 1, (2, 3), 4) 3 | -------------------------------------------------------------------------------- /test/preprocess-macro-vararg.pp: -------------------------------------------------------------------------------- 1 | 1, (2, 3), 4 + abc, "1, (2, 3), 4" 2 | -------------------------------------------------------------------------------- /test/preprocess-standard-example-1.c: -------------------------------------------------------------------------------- 1 | /* C11 6.10.3.4p4 */ 2 | #define f(a) a*g 3 | #define g(a) f(a) 4 | f(2)(9) 5 | -------------------------------------------------------------------------------- /test/preprocess-standard-example-1.pp: -------------------------------------------------------------------------------- 1 | 2*9*g 2 | -------------------------------------------------------------------------------- /test/preprocess-standard-example-2.c: -------------------------------------------------------------------------------- 1 | /* C11 6.10.3.5p5 with token concatenation disabled for now */ 2 | #define x 3 3 | #define f(a) f(x * (a)) 4 | #undef x 5 | #define x 2 6 | #define g f 7 | #define z z[0] 8 | #define h g(~ 9 | #define m(a) a(w) 10 | #define w 0,1 11 | #define t(a) a 12 | #define p() int 13 | #define q(x) x 14 | //#define r(x,y) x ## y 15 | #define str(x) # x 16 | f(y+1) + f(f(z)) % t(t(g)(0) + t)(1); 17 | g(x+(3,4)-w) | h 5) & m 18 | (f)^m(m); 19 | p() i[q()] = { q(1), /*r(2,3), r(4,), r(,5), r(,)*/ }; 20 | char c[2][6] = { str(hello), str() }; 21 | -------------------------------------------------------------------------------- /test/preprocess-standard-example-2.pp: -------------------------------------------------------------------------------- 1 | f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1); 2 | f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1); 3 | int i[] = { 1, }; 4 | char c[2][6] = { "hello", "" }; 5 | -------------------------------------------------------------------------------- /test/preprocess-undef.c: -------------------------------------------------------------------------------- 1 | #define foo bar 2 | #undef foo 3 | foo 4 | -------------------------------------------------------------------------------- /test/preprocess-undef.pp: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /test/sizeof-compound-literal.c: -------------------------------------------------------------------------------- 1 | int x = sizeof (int){1}; 2 | -------------------------------------------------------------------------------- /test/sizeof-compound-literal.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 4, } 2 | -------------------------------------------------------------------------------- /test/sizeof-postfix.c: -------------------------------------------------------------------------------- 1 | int x; 2 | int f(void) { 3 | return sizeof (x)++; 4 | } 5 | -------------------------------------------------------------------------------- /test/sizeof-postfix.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $f() { 3 | @start.1 4 | @body.2 5 | ret 4 6 | } 7 | export data $x = align 4 { z 4 } 8 | -------------------------------------------------------------------------------- /test/sizeof-string-literal.c: -------------------------------------------------------------------------------- 1 | int x = sizeof("abc"); 2 | -------------------------------------------------------------------------------- /test/sizeof-string-literal.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 4, } 2 | -------------------------------------------------------------------------------- /test/sizeof-vla.c: -------------------------------------------------------------------------------- 1 | int c = 0; 2 | int main(void) { 3 | int r = 0; 4 | int l = 2; 5 | int (*p)[l] = 0; 6 | r += sizeof(c++, p) != sizeof(int (*)[]); /* VM, but not VLA */ 7 | r += c != 0; 8 | r += sizeof(*(c++, p)) != 2 * sizeof(int); /* VLA */ 9 | r += c != 1; 10 | r += sizeof(c++, *p) != sizeof(int *); /* VLA decayed to pointer */ 11 | r += c != 1; 12 | r += sizeof(int[++l]) != 3 * sizeof(int); /* VLA */ 13 | r += l != 3; 14 | r += sizeof(int[++l][1]) != sizeof(int[4][1]); /* VLA */ 15 | r += l != 4; 16 | r += sizeof(int[(c++, 5)]) != 5 * sizeof(int); /* VLA */ 17 | r += c != 2; 18 | return r; 19 | } 20 | -------------------------------------------------------------------------------- /test/sizeof-vla.qbe: -------------------------------------------------------------------------------- 1 | export data $c = align 4 { w 0, } 2 | export 3 | function w $main() { 4 | @start.1 5 | %.1 =l alloc4 4 6 | %.2 =l alloc4 4 7 | %.6 =l alloc8 8 8 | @body.2 9 | storew 0, %.1 10 | storew 2, %.2 11 | %.3 =w loadw %.2 12 | %.4 =l extsw %.3 13 | %.5 =l mul %.4, 4 14 | %.7 =l extsw 0 15 | storel %.7, %.6 16 | %.8 =w loadw %.1 17 | %.9 =w cnel 8, 8 18 | %.10 =w add %.8, %.9 19 | storew %.10, %.1 20 | %.11 =w loadw %.1 21 | %.12 =w loadw $c 22 | %.13 =w cnew %.12, 0 23 | %.14 =w add %.11, %.13 24 | storew %.14, %.1 25 | %.15 =w loadw %.1 26 | %.16 =w loadw $c 27 | %.17 =w add %.16, 1 28 | storew %.17, $c 29 | %.18 =l loadl %.6 30 | %.19 =l extsw 2 31 | %.20 =l mul %.19, 4 32 | %.21 =w cnel %.5, %.20 33 | %.22 =w add %.15, %.21 34 | storew %.22, %.1 35 | %.23 =w loadw %.1 36 | %.24 =w loadw $c 37 | %.25 =w cnew %.24, 1 38 | %.26 =w add %.23, %.25 39 | storew %.26, %.1 40 | %.27 =w loadw %.1 41 | %.28 =w cnel 8, 8 42 | %.29 =w add %.27, %.28 43 | storew %.29, %.1 44 | %.30 =w loadw %.1 45 | %.31 =w loadw $c 46 | %.32 =w cnew %.31, 1 47 | %.33 =w add %.30, %.32 48 | storew %.33, %.1 49 | %.34 =w loadw %.1 50 | %.35 =w loadw %.2 51 | %.36 =w add %.35, 1 52 | storew %.36, %.2 53 | %.37 =l extsw %.36 54 | %.38 =l mul %.37, 4 55 | %.39 =l extsw 3 56 | %.40 =l mul %.39, 4 57 | %.41 =w cnel %.38, %.40 58 | %.42 =w add %.34, %.41 59 | storew %.42, %.1 60 | %.43 =w loadw %.1 61 | %.44 =w loadw %.2 62 | %.45 =w cnew %.44, 3 63 | %.46 =w add %.43, %.45 64 | storew %.46, %.1 65 | %.47 =w loadw %.1 66 | %.48 =w loadw %.2 67 | %.49 =w add %.48, 1 68 | storew %.49, %.2 69 | %.50 =l extsw %.49 70 | %.51 =l mul %.50, 4 71 | %.52 =w cnel %.51, 16 72 | %.53 =w add %.47, %.52 73 | storew %.53, %.1 74 | %.54 =w loadw %.1 75 | %.55 =w loadw %.2 76 | %.56 =w cnew %.55, 4 77 | %.57 =w add %.54, %.56 78 | storew %.57, %.1 79 | %.58 =w loadw %.1 80 | %.59 =w loadw $c 81 | %.60 =w add %.59, 1 82 | storew %.60, $c 83 | %.61 =l extsw 5 84 | %.62 =l mul %.61, 4 85 | %.63 =l extsw 5 86 | %.64 =l mul %.63, 4 87 | %.65 =w cnel %.62, %.64 88 | %.66 =w add %.58, %.65 89 | storew %.66, %.1 90 | %.67 =w loadw %.1 91 | %.68 =w loadw $c 92 | %.69 =w cnew %.68, 2 93 | %.70 =w add %.67, %.69 94 | storew %.70, %.1 95 | %.71 =w loadw %.1 96 | ret %.71 97 | } 98 | -------------------------------------------------------------------------------- /test/static-assert-concat.c: -------------------------------------------------------------------------------- 1 | static_assert(1, "abc" "def"); 2 | -------------------------------------------------------------------------------- /test/static-assert-concat.qbe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelforney/cproc/a2ddee1be68e76a1ac7f83b55732be8e54fe08c4/test/static-assert-concat.qbe -------------------------------------------------------------------------------- /test/static-assert-struct.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | int x; 3 | static_assert(1, ""); 4 | }; 5 | -------------------------------------------------------------------------------- /test/static-assert-struct.qbe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelforney/cproc/a2ddee1be68e76a1ac7f83b55732be8e54fe08c4/test/static-assert-struct.qbe -------------------------------------------------------------------------------- /test/string-concat.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | return "\0" "1"[0]; 3 | } 4 | -------------------------------------------------------------------------------- /test/string-concat.qbe: -------------------------------------------------------------------------------- 1 | data $.Lstring.2 = align 1 { b "\0001\000", } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =l extsw 0 7 | %.2 =l mul %.1, 1 8 | %.3 =l add $.Lstring.2, %.2 9 | %.4 =w loadsb %.3 10 | %.5 =w extsb %.4 11 | ret %.5 12 | } 13 | -------------------------------------------------------------------------------- /test/string-octal.c: -------------------------------------------------------------------------------- 1 | char *s = "\1\12\123\1234"; 2 | -------------------------------------------------------------------------------- /test/string-octal.qbe: -------------------------------------------------------------------------------- 1 | data $.Lstring.1 = align 1 { b "\001\012SS4\000", } 2 | export data $s = align 8 { l $.Lstring.1, } 3 | -------------------------------------------------------------------------------- /test/string-u8-type.c: -------------------------------------------------------------------------------- 1 | static_assert(__builtin_types_compatible_p(__typeof__(u8"abc"), unsigned char[])); 2 | -------------------------------------------------------------------------------- /test/string-u8-type.qbe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelforney/cproc/a2ddee1be68e76a1ac7f83b55732be8e54fe08c4/test/string-u8-type.qbe -------------------------------------------------------------------------------- /test/struct-copy.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | char s[5]; 3 | float f; 4 | } x; 5 | 6 | void f(void) { 7 | struct s y = x; 8 | } 9 | -------------------------------------------------------------------------------- /test/struct-copy.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc4 12 5 | @body.2 6 | %.2 =w loadw $x 7 | storew %.2, %.1 8 | %.3 =l add $x, 4 9 | %.4 =l add %.1, 4 10 | %.5 =w loadw %.3 11 | storew %.5, %.4 12 | %.6 =l add %.3, 4 13 | %.7 =l add %.4, 4 14 | %.8 =w loadw %.6 15 | storew %.8, %.7 16 | ret 17 | } 18 | export data $x = align 4 { z 12 } 19 | -------------------------------------------------------------------------------- /test/struct-flexible-array.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | int a; 3 | short b[]; 4 | }; 5 | 6 | int x = sizeof(struct s); 7 | 8 | int f(struct s *s) { 9 | return s->b[2]; 10 | } 11 | -------------------------------------------------------------------------------- /test/struct-flexible-array.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 4, } 2 | export 3 | function w $f(l %.1) { 4 | @start.1 5 | %.2 =l alloc8 8 6 | storel %.1, %.2 7 | @body.2 8 | %.3 =l loadl %.2 9 | %.4 =l add %.3, 4 10 | %.5 =l extsw 2 11 | %.6 =l mul %.5, 2 12 | %.7 =l add %.4, %.6 13 | %.8 =w loadsh %.7 14 | %.9 =w extsh %.8 15 | ret %.9 16 | } 17 | -------------------------------------------------------------------------------- /test/struct-packed.c: -------------------------------------------------------------------------------- 1 | struct [[gnu::packed]] { 2 | char a; 3 | long long b; 4 | short c; 5 | } s = {1, 2, 3}; 6 | 7 | int main(void) { 8 | return s.a + s.b - s.c; 9 | } 10 | -------------------------------------------------------------------------------- /test/struct-packed.qbe: -------------------------------------------------------------------------------- 1 | export data $s = align 1 { b 1, l 2, h 3, } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =l add $s, 0 7 | %.2 =w loadsb %.1 8 | %.3 =l extsb %.2 9 | %.4 =l add $s, 1 10 | %.5 =l loadl %.4 11 | %.6 =l add %.3, %.5 12 | %.7 =l add $s, 9 13 | %.8 =w loadsh %.7 14 | %.9 =l extsh %.8 15 | %.10 =l sub %.6, %.9 16 | ret %.10 17 | } 18 | -------------------------------------------------------------------------------- /test/struct-passing-bitfield.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | char x, y; 3 | long long z : 48; 4 | }; 5 | 6 | void f(struct s s) { 7 | } 8 | -------------------------------------------------------------------------------- /test/struct-passing-bitfield.qbe: -------------------------------------------------------------------------------- 1 | type :s.1 = { l, } 2 | export 3 | function $f(:s.1 %.1) { 4 | @start.1 5 | @body.2 6 | ret 7 | } 8 | -------------------------------------------------------------------------------- /test/struct-passing-call.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | int x; 3 | } s; 4 | 5 | void f(struct s); 6 | void g(void) { 7 | f(s); 8 | } 9 | -------------------------------------------------------------------------------- /test/struct-passing-call.qbe: -------------------------------------------------------------------------------- 1 | type :s.1 = { w, } 2 | export 3 | function $g() { 4 | @start.1 5 | @body.2 6 | call $f(:s.1 $s) 7 | ret 8 | } 9 | export data $s = align 4 { z 4 } 10 | -------------------------------------------------------------------------------- /test/struct-passing.c: -------------------------------------------------------------------------------- 1 | struct s { 2 | int x; 3 | struct { 4 | char y[3]; 5 | short z; 6 | } s[2]; 7 | double w; 8 | }; 9 | 10 | void f(struct s s) { 11 | } 12 | -------------------------------------------------------------------------------- /test/struct-passing.qbe: -------------------------------------------------------------------------------- 1 | type :.2 = { b 3, h, } 2 | type :s.1 = { w, :.2 2, d, } 3 | export 4 | function $f(:s.1 %.1) { 5 | @start.1 6 | @body.2 7 | ret 8 | } 9 | -------------------------------------------------------------------------------- /test/struct-return-1.c: -------------------------------------------------------------------------------- 1 | struct s {int x;} f(void) { 2 | return (struct s){2}; 3 | } 4 | -------------------------------------------------------------------------------- /test/struct-return-1.qbe: -------------------------------------------------------------------------------- 1 | type :s.1 = { w, } 2 | export 3 | function :s.1 $f() { 4 | @start.1 5 | %.1 =l alloc4 4 6 | @body.2 7 | storew 2, %.1 8 | ret %.1 9 | } 10 | -------------------------------------------------------------------------------- /test/struct-return-2.c: -------------------------------------------------------------------------------- 1 | struct {int x, y;} g(void); 2 | int f(void) { 3 | return g().y; 4 | } 5 | -------------------------------------------------------------------------------- /test/struct-return-2.qbe: -------------------------------------------------------------------------------- 1 | type :.1 = { w, w, } 2 | export 3 | function w $f() { 4 | @start.1 5 | @body.2 6 | %.1 =:.1 call $g() 7 | %.2 =l add %.1, 4 8 | %.3 =w loadw %.2 9 | ret %.3 10 | } 11 | -------------------------------------------------------------------------------- /test/subtract-pointer.c: -------------------------------------------------------------------------------- 1 | void f(int *x, int *y) { 2 | x - y; 3 | } 4 | -------------------------------------------------------------------------------- /test/subtract-pointer.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f(l %.1, l %.3) { 3 | @start.1 4 | %.2 =l alloc8 8 5 | storel %.1, %.2 6 | %.4 =l alloc8 8 7 | storel %.3, %.4 8 | @body.2 9 | %.5 =l loadl %.2 10 | %.6 =l loadl %.4 11 | %.7 =l sub %.5, %.6 12 | %.8 =l div %.7, 4 13 | ret 14 | } 15 | -------------------------------------------------------------------------------- /test/switch-long-long.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | switch (0x12300000000) { 3 | case 0: return 1; 4 | case 0x12300000000: return 0; 5 | } 6 | return 2; 7 | } 8 | -------------------------------------------------------------------------------- /test/switch-long-long.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | @body.2 5 | jmp @switch_cond.3 6 | @switch_case.5 7 | ret 1 8 | @switch_case.6 9 | ret 0 10 | @switch_cond.3 11 | %.1 =w ceql 1249835483136, 0 12 | jnz %.1, @switch_case.5, @switch_ne.7 13 | @switch_ne.7 14 | %.2 =w cultl 1249835483136, 0 15 | jnz %.2, @switch_lt.8, @switch_gt.9 16 | @switch_lt.8 17 | jmp @switch_join.4 18 | @switch_gt.9 19 | %.3 =w ceql 1249835483136, 1249835483136 20 | jnz %.3, @switch_case.6, @switch_ne.10 21 | @switch_ne.10 22 | %.4 =w cultl 1249835483136, 1249835483136 23 | jnz %.4, @switch_lt.11, @switch_gt.12 24 | @switch_lt.11 25 | jmp @switch_join.4 26 | @switch_gt.12 27 | jmp @switch_join.4 28 | @switch_join.4 29 | ret 2 30 | } 31 | -------------------------------------------------------------------------------- /test/switch.c: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | switch (0) { 3 | case 3: break; 4 | case 52: break; 5 | case -3: break; 6 | default: break; 7 | case 0: break; 8 | case 101: break; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/switch.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | @body.2 5 | jmp @switch_cond.3 6 | @switch_case.5 7 | jmp @switch_join.4 8 | @switch_case.6 9 | jmp @switch_join.4 10 | @switch_case.7 11 | jmp @switch_join.4 12 | @switch_default.8 13 | jmp @switch_join.4 14 | @switch_case.9 15 | jmp @switch_join.4 16 | @switch_case.10 17 | jmp @switch_join.4 18 | @switch_cond.3 19 | %.1 =w ceqw 0, 52 20 | jnz %.1, @switch_case.6, @switch_ne.11 21 | @switch_ne.11 22 | %.2 =w cultw 0, 52 23 | jnz %.2, @switch_lt.12, @switch_gt.13 24 | @switch_lt.12 25 | %.3 =w ceqw 0, 3 26 | jnz %.3, @switch_case.5, @switch_ne.14 27 | @switch_ne.14 28 | %.4 =w cultw 0, 3 29 | jnz %.4, @switch_lt.15, @switch_gt.16 30 | @switch_lt.15 31 | %.5 =w ceqw 0, 0 32 | jnz %.5, @switch_case.9, @switch_ne.17 33 | @switch_ne.17 34 | %.6 =w cultw 0, 0 35 | jnz %.6, @switch_lt.18, @switch_gt.19 36 | @switch_lt.18 37 | jmp @switch_default.8 38 | @switch_gt.19 39 | jmp @switch_default.8 40 | @switch_gt.16 41 | jmp @switch_default.8 42 | @switch_gt.13 43 | %.7 =w ceqw 0, 18446744073709551613 44 | jnz %.7, @switch_case.7, @switch_ne.20 45 | @switch_ne.20 46 | %.8 =w cultw 0, 18446744073709551613 47 | jnz %.8, @switch_lt.21, @switch_gt.22 48 | @switch_lt.21 49 | %.9 =w ceqw 0, 101 50 | jnz %.9, @switch_case.10, @switch_ne.23 51 | @switch_ne.23 52 | %.10 =w cultw 0, 101 53 | jnz %.10, @switch_lt.24, @switch_gt.25 54 | @switch_lt.24 55 | jmp @switch_default.8 56 | @switch_gt.25 57 | jmp @switch_default.8 58 | @switch_gt.22 59 | jmp @switch_default.8 60 | @switch_join.4 61 | ret 62 | } 63 | -------------------------------------------------------------------------------- /test/tentative.c: -------------------------------------------------------------------------------- 1 | int x; 2 | int x = 5; 3 | -------------------------------------------------------------------------------- /test/tentative.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 5, } 2 | -------------------------------------------------------------------------------- /test/thread-local.c: -------------------------------------------------------------------------------- 1 | thread_local int a = 1; 2 | static thread_local int b = 2; 3 | extern thread_local int c = 3; 4 | thread_local int d; 5 | static thread_local int e; 6 | extern thread_local int f; 7 | int main(void) { 8 | static thread_local int x = 6; 9 | return a + b + c + d + e - x; 10 | } 11 | -------------------------------------------------------------------------------- /test/thread-local.qbe: -------------------------------------------------------------------------------- 1 | thread export data $a = align 4 { w 1, } 2 | thread data $b = align 4 { w 2, } 3 | thread export data $c = align 4 { w 3, } 4 | thread export data $d = align 4 { z 4 } 5 | thread data $e = align 4 { z 4 } 6 | thread data $.Lx.2 = align 4 { w 6, } 7 | export 8 | function w $main() { 9 | @start.1 10 | @body.2 11 | %.1 =w loadw thread $a 12 | %.2 =w loadw thread $b 13 | %.3 =w add %.1, %.2 14 | %.4 =w loadw thread $c 15 | %.5 =w add %.3, %.4 16 | %.6 =w loadw thread $d 17 | %.7 =w add %.5, %.6 18 | %.8 =w loadw thread $e 19 | %.9 =w add %.7, %.8 20 | %.10 =w loadw thread $.Lx.2 21 | %.11 =w sub %.9, %.10 22 | ret %.11 23 | } 24 | -------------------------------------------------------------------------------- /test/typedef-name.c: -------------------------------------------------------------------------------- 1 | typedef int x; 2 | void f(void) { 3 | long x; 4 | } 5 | -------------------------------------------------------------------------------- /test/typedef-name.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | %.1 =l alloc8 8 5 | @body.2 6 | ret 7 | } 8 | -------------------------------------------------------------------------------- /test/typedef-qual.c: -------------------------------------------------------------------------------- 1 | typedef const int T; 2 | const int x; 3 | T x; 4 | -------------------------------------------------------------------------------- /test/typedef-qual.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { z 4 } 2 | -------------------------------------------------------------------------------- /test/typedef.c: -------------------------------------------------------------------------------- 1 | typedef int T; 2 | T x; 3 | -------------------------------------------------------------------------------- /test/typedef.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { z 4 } 2 | -------------------------------------------------------------------------------- /test/typeof-array.c: -------------------------------------------------------------------------------- 1 | int x[5]; 2 | typeof(x) y; 3 | -------------------------------------------------------------------------------- /test/typeof-array.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { z 20 } 2 | export data $y = align 4 { z 20 } 3 | -------------------------------------------------------------------------------- /test/typeof-decay.c: -------------------------------------------------------------------------------- 1 | char a[4]; 2 | typeof(a) a; 3 | -------------------------------------------------------------------------------- /test/typeof-decay.qbe: -------------------------------------------------------------------------------- 1 | export data $a = align 1 { z 4 } 2 | -------------------------------------------------------------------------------- /test/typeof-vm.c: -------------------------------------------------------------------------------- 1 | int a[3] = {12, 34, 56}; 2 | int b[3] = {'a', 'b', 'c'}; 3 | int c = 0; 4 | int f(void) { 5 | ++c; 6 | return 3; 7 | } 8 | int g(int n, ...) { 9 | __builtin_va_list ap; 10 | char (*p)[n] = 0; 11 | int out = 1; 12 | 13 | __builtin_va_start(ap, n); 14 | __builtin_va_arg(ap, typeof(out--, p)); 15 | __builtin_va_end(ap); 16 | return out; 17 | } 18 | int main(void) { 19 | int r = 0; 20 | int (*p)[f()] = 0; 21 | 22 | r += c != 1; 23 | typeof(c++, p) t1; /* VM; evaluated */ 24 | r += c != 2; 25 | typeof(p, c++) t2; /* non-VM; not evaluated */ 26 | r += c != 2; 27 | typeof(c++, **(p = &a)) t3; /* non-VM; not evaluated */ 28 | r += c != 2; 29 | r += p != 0; 30 | typeof(*(p = (c++, &a))) t4; /* VM, evaluated */ 31 | r += c != 3; 32 | r += p != &a; 33 | /* 34 | while *(p = &b) has VM type, it is not the immediate operand 35 | of typeof, so is converted from VLA to int pointer due to 36 | the comma operator, so the typeof expression is not evaluated 37 | */ 38 | typeof(c++, *(p = &b)) t5; 39 | r += c != 3; 40 | r += p != &a; 41 | (typeof(c++, p))0; 42 | r += c != 4; 43 | (typeof(c++, p)){0}; 44 | r += c != 5; 45 | r += g(3, p); 46 | typeof(typeof(c++, p)) t6; 47 | r += c != 6; 48 | return r; 49 | } 50 | -------------------------------------------------------------------------------- /test/typeof-vm.qbe: -------------------------------------------------------------------------------- 1 | export data $a = align 4 { w 12, w 34, w 56, } 2 | export data $b = align 4 { w 97, w 98, w 99, } 3 | export data $c = align 4 { w 0, } 4 | export 5 | function w $f() { 6 | @start.1 7 | @body.2 8 | %.1 =w loadw $c 9 | %.2 =w add %.1, 1 10 | storew %.2, $c 11 | ret 3 12 | } 13 | export 14 | function w $g(w %.1, ...) { 15 | @start.3 16 | %.2 =l alloc4 4 17 | storew %.1, %.2 18 | %.3 =l alloc8 24 19 | %.7 =l alloc8 8 20 | %.9 =l alloc4 4 21 | @body.4 22 | %.4 =w loadw %.2 23 | %.5 =l extsw %.4 24 | %.6 =l mul %.5, 1 25 | %.8 =l extsw 0 26 | storel %.8, %.7 27 | storew 1, %.9 28 | vastart %.3 29 | %.10 =w loadw %.9 30 | %.11 =w sub %.10, 1 31 | storew %.11, %.9 32 | %.12 =l loadl %.7 33 | %.13 =l vaarg %.3 34 | %.14 =w loadw %.9 35 | ret %.14 36 | } 37 | export 38 | function w $main() { 39 | @start.5 40 | %.1 =l alloc4 4 41 | %.5 =l alloc8 8 42 | %.14 =l alloc8 8 43 | %.19 =l alloc4 4 44 | %.24 =l alloc4 4 45 | %.45 =l alloc8 8 46 | %.65 =l alloc8 8 47 | %.79 =l alloc8 8 48 | @body.6 49 | storew 0, %.1 50 | %.2 =w call $f() 51 | %.3 =l extsw %.2 52 | %.4 =l mul %.3, 4 53 | %.6 =l extsw 0 54 | storel %.6, %.5 55 | %.7 =w loadw %.1 56 | %.8 =w loadw $c 57 | %.9 =w cnew %.8, 1 58 | %.10 =w add %.7, %.9 59 | storew %.10, %.1 60 | %.11 =w loadw $c 61 | %.12 =w add %.11, 1 62 | storew %.12, $c 63 | %.13 =l loadl %.5 64 | %.15 =w loadw %.1 65 | %.16 =w loadw $c 66 | %.17 =w cnew %.16, 2 67 | %.18 =w add %.15, %.17 68 | storew %.18, %.1 69 | %.20 =w loadw %.1 70 | %.21 =w loadw $c 71 | %.22 =w cnew %.21, 2 72 | %.23 =w add %.20, %.22 73 | storew %.23, %.1 74 | %.25 =w loadw %.1 75 | %.26 =w loadw $c 76 | %.27 =w cnew %.26, 2 77 | %.28 =w add %.25, %.27 78 | storew %.28, %.1 79 | %.29 =w loadw %.1 80 | %.30 =l loadl %.5 81 | %.31 =l extsw 0 82 | %.32 =w cnel %.30, %.31 83 | %.33 =w add %.29, %.32 84 | storew %.33, %.1 85 | %.34 =w loadw $c 86 | %.35 =w add %.34, 1 87 | storew %.35, $c 88 | storel $a, %.5 89 | %.36 =l alloc4 %.4 90 | %.37 =w loadw %.1 91 | %.38 =w loadw $c 92 | %.39 =w cnew %.38, 3 93 | %.40 =w add %.37, %.39 94 | storew %.40, %.1 95 | %.41 =w loadw %.1 96 | %.42 =l loadl %.5 97 | %.43 =w cnel %.42, $a 98 | %.44 =w add %.41, %.43 99 | storew %.44, %.1 100 | %.46 =w loadw %.1 101 | %.47 =w loadw $c 102 | %.48 =w cnew %.47, 3 103 | %.49 =w add %.46, %.48 104 | storew %.49, %.1 105 | %.50 =w loadw %.1 106 | %.51 =l loadl %.5 107 | %.52 =w cnel %.51, $a 108 | %.53 =w add %.50, %.52 109 | storew %.53, %.1 110 | %.54 =w loadw $c 111 | %.55 =w add %.54, 1 112 | storew %.55, $c 113 | %.56 =l loadl %.5 114 | %.57 =l extsw 0 115 | %.58 =w loadw %.1 116 | %.59 =w loadw $c 117 | %.60 =w cnew %.59, 4 118 | %.61 =w add %.58, %.60 119 | storew %.61, %.1 120 | %.62 =w loadw $c 121 | %.63 =w add %.62, 1 122 | storew %.63, $c 123 | %.64 =l loadl %.5 124 | %.66 =l extsw 0 125 | storel %.66, %.65 126 | %.67 =l loadl %.65 127 | %.68 =w loadw %.1 128 | %.69 =w loadw $c 129 | %.70 =w cnew %.69, 5 130 | %.71 =w add %.68, %.70 131 | storew %.71, %.1 132 | %.72 =w loadw %.1 133 | %.73 =l loadl %.5 134 | %.74 =w call $g(w 3, ..., l %.73) 135 | %.75 =w add %.72, %.74 136 | storew %.75, %.1 137 | %.76 =w loadw $c 138 | %.77 =w add %.76, 1 139 | storew %.77, $c 140 | %.78 =l loadl %.5 141 | %.80 =w loadw %.1 142 | %.81 =w loadw $c 143 | %.82 =w cnew %.81, 6 144 | %.83 =w add %.80, %.82 145 | storew %.83, %.1 146 | %.84 =w loadw %.1 147 | ret %.84 148 | } 149 | -------------------------------------------------------------------------------- /test/typeof.c: -------------------------------------------------------------------------------- 1 | int f(char *); 2 | typeof(f) f, g; 3 | 4 | typeof(g(0)) x; 5 | int x; 6 | 7 | typedef int *t; 8 | const typeof(t) y; 9 | int *const y; 10 | -------------------------------------------------------------------------------- /test/typeof.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { z 4 } 2 | export data $y = align 8 { z 8 } 3 | -------------------------------------------------------------------------------- /test/typeof_unqual.c: -------------------------------------------------------------------------------- 1 | const int a; 2 | int b; 3 | typeof_unqual(a) b; 4 | -------------------------------------------------------------------------------- /test/typeof_unqual.qbe: -------------------------------------------------------------------------------- 1 | export data $a = align 4 { z 4 } 2 | export data $b = align 4 { z 4 } 3 | -------------------------------------------------------------------------------- /test/uint32-to-float.c: -------------------------------------------------------------------------------- 1 | unsigned g(void); 2 | float f(void) { 3 | return g(); 4 | } 5 | -------------------------------------------------------------------------------- /test/uint32-to-float.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function s $f() { 3 | @start.1 4 | @body.2 5 | %.1 =w call $g() 6 | %.2 =s uwtof %.1 7 | ret %.2 8 | } 9 | -------------------------------------------------------------------------------- /test/uint64-to-float.c: -------------------------------------------------------------------------------- 1 | unsigned long long g(void); 2 | float f(void) { 3 | return g(); 4 | } 5 | -------------------------------------------------------------------------------- /test/uint64-to-float.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function s $f() { 3 | @start.1 4 | @body.2 5 | %.1 =l call $g() 6 | %.2 =s ultof %.1 7 | ret %.2 8 | } 9 | -------------------------------------------------------------------------------- /test/union-passing.c: -------------------------------------------------------------------------------- 1 | void f(union {int x; float y;} u) { 2 | } 3 | -------------------------------------------------------------------------------- /test/union-passing.qbe: -------------------------------------------------------------------------------- 1 | type :.1 = { { w } { s } } 2 | export 3 | function $f(:.1 %.1) { 4 | @start.1 5 | @body.2 6 | ret 7 | } 8 | -------------------------------------------------------------------------------- /test/union-struct-flexible-array.c: -------------------------------------------------------------------------------- 1 | union u { 2 | struct { 3 | int a; 4 | short b[]; 5 | } s; 6 | unsigned char storage[32]; 7 | }; 8 | 9 | int x = sizeof(union u); 10 | 11 | int f(union u *u) { 12 | return u->s.b[2] + u->storage[0]; 13 | } 14 | -------------------------------------------------------------------------------- /test/union-struct-flexible-array.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 32, } 2 | export 3 | function w $f(l %.1) { 4 | @start.1 5 | %.2 =l alloc8 8 6 | storel %.1, %.2 7 | @body.2 8 | %.3 =l loadl %.2 9 | %.4 =l add %.3, 0 10 | %.5 =l add %.4, 4 11 | %.6 =l extsw 2 12 | %.7 =l mul %.6, 2 13 | %.8 =l add %.5, %.7 14 | %.9 =w loadsh %.8 15 | %.10 =w extsh %.9 16 | %.11 =l loadl %.2 17 | %.12 =l add %.11, 0 18 | %.13 =l extsw 0 19 | %.14 =l mul %.13, 1 20 | %.15 =l add %.12, %.14 21 | %.16 =w loadub %.15 22 | %.17 =w extub %.16 23 | %.18 =w add %.10, %.17 24 | ret %.18 25 | } 26 | -------------------------------------------------------------------------------- /test/union.c: -------------------------------------------------------------------------------- 1 | union { 2 | int x; 3 | double y; 4 | } a = {.x = 5}, b = {.y = 7.5}; 5 | -------------------------------------------------------------------------------- /test/union.qbe: -------------------------------------------------------------------------------- 1 | export data $a = align 8 { w 5, z 4 } 2 | export data $b = align 8 { d d_7.5, } 3 | -------------------------------------------------------------------------------- /test/unreachable.c: -------------------------------------------------------------------------------- 1 | void f(int x) { 2 | return; 3 | switch (x) { 4 | case 0: 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/unreachable.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f(w %.1) { 3 | @start.1 4 | %.2 =l alloc4 4 5 | storew %.1, %.2 6 | @body.2 7 | ret 8 | @dead.5 9 | %.3 =w loadw %.2 10 | jmp @switch_cond.3 11 | @switch_case.6 12 | jmp @switch_join.4 13 | @switch_cond.3 14 | %.4 =w ceqw %.3, 0 15 | jnz %.4, @switch_case.6, @switch_ne.7 16 | @switch_ne.7 17 | %.5 =w cultw %.3, 0 18 | jnz %.5, @switch_lt.8, @switch_gt.9 19 | @switch_lt.8 20 | jmp @switch_join.4 21 | @switch_gt.9 22 | jmp @switch_join.4 23 | @switch_join.4 24 | ret 25 | } 26 | -------------------------------------------------------------------------------- /test/unused-return.c: -------------------------------------------------------------------------------- 1 | int g(void); 2 | void f(void) { 3 | g(); 4 | } 5 | -------------------------------------------------------------------------------- /test/unused-return.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function $f() { 3 | @start.1 4 | @body.2 5 | %.1 =w call $g() 6 | ret 7 | } 8 | -------------------------------------------------------------------------------- /test/varargs+aarch64.c: -------------------------------------------------------------------------------- 1 | varargs+x86_64-sysv.c -------------------------------------------------------------------------------- /test/varargs+aarch64.qbe: -------------------------------------------------------------------------------- 1 | type :va_list.1 = align 8 { 32 } 2 | export 3 | function w $f1(w %.1, :va_list.1 %.3) { 4 | @start.1 5 | %.2 =l alloc4 4 6 | storew %.1, %.2 7 | @body.2 8 | %.4 =w vaarg %.3 9 | ret %.4 10 | } 11 | export 12 | function w $f2(w %.1, ...) { 13 | @start.3 14 | %.2 =l alloc4 4 15 | storew %.1, %.2 16 | %.3 =l alloc4 4 17 | %.4 =l alloc8 32 18 | @body.4 19 | vastart %.4 20 | %.5 =w loadw %.2 21 | %.6 =w call $f1(w %.5, :va_list.1 %.4) 22 | storew %.6, %.3 23 | %.7 =w loadw %.3 24 | ret %.7 25 | } 26 | export 27 | function $f3(w %.1, ...) { 28 | @start.5 29 | %.2 =l alloc4 4 30 | storew %.1, %.2 31 | %.3 =l alloc8 32 32 | @body.6 33 | vastart %.3 34 | @while_cond.7 35 | %.4 =w loadw %.2 36 | jnz %.4, @while_body.8, @while_join.9 37 | @while_body.8 38 | %.5 =w vaarg %.3 39 | %.6 =s vaarg %.3 40 | %.7 =l vaarg %.3 41 | %.8 =w loadw %.2 42 | %.9 =w sub %.8, 1 43 | storew %.9, %.2 44 | jmp @while_cond.7 45 | @while_join.9 46 | ret 47 | } 48 | export 49 | function $f4(...) { 50 | @start.10 51 | %.1 =l alloc8 32 52 | @body.11 53 | vastart %.1 54 | %.2 =d vaarg %.1 55 | %.3 =w vaarg %.1 56 | ret 57 | } 58 | -------------------------------------------------------------------------------- /test/varargs+riscv64.c: -------------------------------------------------------------------------------- 1 | varargs+x86_64-sysv.c -------------------------------------------------------------------------------- /test/varargs+riscv64.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $f1(w %.1, l %.3) { 3 | @start.1 4 | %.2 =l alloc4 4 5 | storew %.1, %.2 6 | %.4 =l alloc8 8 7 | storel %.3, %.4 8 | @body.2 9 | %.5 =w vaarg %.4 10 | ret %.5 11 | } 12 | export 13 | function w $f2(w %.1, ...) { 14 | @start.3 15 | %.2 =l alloc4 4 16 | storew %.1, %.2 17 | %.3 =l alloc4 4 18 | %.4 =l alloc8 8 19 | @body.4 20 | vastart %.4 21 | %.5 =w loadw %.2 22 | %.6 =l loadl %.4 23 | %.7 =w call $f1(w %.5, l %.6) 24 | storew %.7, %.3 25 | %.8 =l loadl %.4 26 | %.9 =w loadw %.3 27 | ret %.9 28 | } 29 | export 30 | function $f3(w %.1, ...) { 31 | @start.5 32 | %.2 =l alloc4 4 33 | storew %.1, %.2 34 | %.3 =l alloc8 8 35 | @body.6 36 | vastart %.3 37 | @while_cond.7 38 | %.4 =w loadw %.2 39 | jnz %.4, @while_body.8, @while_join.9 40 | @while_body.8 41 | %.5 =w vaarg %.3 42 | %.6 =s vaarg %.3 43 | %.7 =l vaarg %.3 44 | %.8 =w loadw %.2 45 | %.9 =w sub %.8, 1 46 | storew %.9, %.2 47 | jmp @while_cond.7 48 | @while_join.9 49 | %.10 =l loadl %.3 50 | ret 51 | } 52 | export 53 | function $f4(...) { 54 | @start.10 55 | %.1 =l alloc8 8 56 | @body.11 57 | vastart %.1 58 | %.2 =d vaarg %.1 59 | %.3 =w vaarg %.1 60 | %.4 =l loadl %.1 61 | ret 62 | } 63 | -------------------------------------------------------------------------------- /test/varargs+x86_64-sysv.c: -------------------------------------------------------------------------------- 1 | int f1(int n, __builtin_va_list ap) { 2 | return __builtin_va_arg(ap, int); 3 | } 4 | 5 | int f2(int n, ...) { 6 | int r; 7 | __builtin_va_list ap; 8 | 9 | __builtin_va_start(ap, n); 10 | r = f1(n, ap); 11 | __builtin_va_end(ap); 12 | return r; 13 | } 14 | 15 | void f3(int n, ...) { 16 | __builtin_va_list ap; 17 | 18 | __builtin_va_start(ap, n); 19 | while (n) { 20 | __builtin_va_arg(ap, int); 21 | __builtin_va_arg(ap, float); 22 | __builtin_va_arg(ap, char *); 23 | --n; 24 | } 25 | __builtin_va_end(ap); 26 | } 27 | 28 | void f4(...) { 29 | __builtin_va_list ap; 30 | __builtin_va_start(ap); 31 | __builtin_va_arg(ap, double); 32 | __builtin_va_arg(ap, int); 33 | __builtin_va_end(ap); 34 | } 35 | -------------------------------------------------------------------------------- /test/varargs+x86_64-sysv.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $f1(w %.1, l %.3) { 3 | @start.1 4 | %.2 =l alloc4 4 5 | storew %.1, %.2 6 | %.4 =l alloc8 8 7 | storel %.3, %.4 8 | @body.2 9 | %.5 =l loadl %.4 10 | %.6 =w vaarg %.5 11 | ret %.6 12 | } 13 | export 14 | function w $f2(w %.1, ...) { 15 | @start.3 16 | %.2 =l alloc4 4 17 | storew %.1, %.2 18 | %.3 =l alloc4 4 19 | %.4 =l alloc8 24 20 | @body.4 21 | vastart %.4 22 | %.5 =w loadw %.2 23 | %.6 =w call $f1(w %.5, l %.4) 24 | storew %.6, %.3 25 | %.7 =w loadw %.3 26 | ret %.7 27 | } 28 | export 29 | function $f3(w %.1, ...) { 30 | @start.5 31 | %.2 =l alloc4 4 32 | storew %.1, %.2 33 | %.3 =l alloc8 24 34 | @body.6 35 | vastart %.3 36 | @while_cond.7 37 | %.4 =w loadw %.2 38 | jnz %.4, @while_body.8, @while_join.9 39 | @while_body.8 40 | %.5 =w vaarg %.3 41 | %.6 =s vaarg %.3 42 | %.7 =l vaarg %.3 43 | %.8 =w loadw %.2 44 | %.9 =w sub %.8, 1 45 | storew %.9, %.2 46 | jmp @while_cond.7 47 | @while_join.9 48 | ret 49 | } 50 | export 51 | function $f4(...) { 52 | @start.10 53 | %.1 =l alloc8 24 54 | @body.11 55 | vastart %.1 56 | %.2 =d vaarg %.1 57 | %.3 =w vaarg %.1 58 | ret 59 | } 60 | -------------------------------------------------------------------------------- /test/vla-deref.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | int l = 3; 3 | char a[*&l]; 4 | } 5 | -------------------------------------------------------------------------------- /test/vla-deref.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | %.1 =l alloc4 4 5 | @body.2 6 | storew 3, %.1 7 | %.2 =w loadw %.1 8 | %.3 =l extsw %.2 9 | %.4 =l mul %.3, 1 10 | %.5 =l alloc4 %.4 11 | ret 0 12 | } 13 | -------------------------------------------------------------------------------- /test/vla-nested.c: -------------------------------------------------------------------------------- 1 | int l; 2 | int f(int x) { 3 | l += x; 4 | return x; 5 | } 6 | int main(void) { 7 | int r = 0; 8 | int (*p[f(2)])[f(3)]; 9 | r += l != 5; 10 | r += sizeof p != sizeof(int (*[2])[3]); 11 | r += sizeof **p != sizeof(int[3]); 12 | return r; 13 | } 14 | -------------------------------------------------------------------------------- /test/vla-nested.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $f(w %.1) { 3 | @start.1 4 | %.2 =l alloc4 4 5 | storew %.1, %.2 6 | @body.2 7 | %.3 =w loadw $l 8 | %.4 =w loadw %.2 9 | %.5 =w add %.3, %.4 10 | storew %.5, $l 11 | %.6 =w loadw %.2 12 | ret %.6 13 | } 14 | export 15 | function w $main() { 16 | @start.3 17 | %.1 =l alloc4 4 18 | @body.4 19 | storew 0, %.1 20 | %.2 =w call $f(w 3) 21 | %.3 =l extsw %.2 22 | %.4 =l mul %.3, 4 23 | %.5 =w call $f(w 2) 24 | %.6 =l extsw %.5 25 | %.7 =l mul %.6, 8 26 | %.8 =l alloc8 %.7 27 | %.9 =w loadw %.1 28 | %.10 =w loadw $l 29 | %.11 =w cnew %.10, 5 30 | %.12 =w add %.9, %.11 31 | storew %.12, %.1 32 | %.13 =w loadw %.1 33 | %.14 =w cnel %.7, 16 34 | %.15 =w add %.13, %.14 35 | storew %.15, %.1 36 | %.16 =w loadw %.1 37 | %.17 =l loadl %.8 38 | %.18 =w cnel %.4, 12 39 | %.19 =w add %.16, %.18 40 | storew %.19, %.1 41 | %.20 =w loadw %.1 42 | ret %.20 43 | } 44 | export data $l = align 4 { z 4 } 45 | -------------------------------------------------------------------------------- /test/vla.c: -------------------------------------------------------------------------------- 1 | short g() { return 1; } 2 | long f(void) { 3 | double a[10 + g()]; 4 | return sizeof(a); 5 | } 6 | -------------------------------------------------------------------------------- /test/vla.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $g() { 3 | @start.1 4 | @body.2 5 | ret 1 6 | } 7 | export 8 | function l $f() { 9 | @start.3 10 | @body.4 11 | %.1 =w call $g() 12 | %.2 =w extsh %.1 13 | %.3 =w add 10, %.2 14 | %.4 =l extsw %.3 15 | %.5 =l mul %.4, 8 16 | %.6 =l alloc8 %.5 17 | ret %.5 18 | } 19 | -------------------------------------------------------------------------------- /test/wchar-sign+aarch64.c: -------------------------------------------------------------------------------- 1 | int x = -L'\001' < 0; 2 | int main(void) { 3 | return -L'\001' < 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/wchar-sign+aarch64.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 0, } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =w neg 1 7 | %.2 =w cultw %.1, 0 8 | ret %.2 9 | } 10 | -------------------------------------------------------------------------------- /test/wchar-sign+riscv64.c: -------------------------------------------------------------------------------- 1 | int x = -L'\001' > 0; 2 | int main(void) { 3 | return -L'\001' > 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/wchar-sign+riscv64.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 0, } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =w neg 1 7 | %.2 =w csgtw %.1, 0 8 | ret %.2 9 | } 10 | -------------------------------------------------------------------------------- /test/wchar-sign+x86_64-sysv.c: -------------------------------------------------------------------------------- 1 | int x = -L'\001' > 0; 2 | int main(void) { 3 | return -L'\001' > 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/wchar-sign+x86_64-sysv.qbe: -------------------------------------------------------------------------------- 1 | export data $x = align 4 { w 0, } 2 | export 3 | function w $main() { 4 | @start.1 5 | @body.2 6 | %.1 =w neg 1 7 | %.2 =w csgtw %.1, 0 8 | ret %.2 9 | } 10 | -------------------------------------------------------------------------------- /test/while-condition.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | double x = 1; 3 | 4 | while (x) 5 | x /= 2; 6 | return x; 7 | } 8 | -------------------------------------------------------------------------------- /test/while-condition.qbe: -------------------------------------------------------------------------------- 1 | export 2 | function w $main() { 3 | @start.1 4 | %.1 =l alloc8 8 5 | @body.2 6 | %.2 =d swtof 1 7 | stored %.2, %.1 8 | @while_cond.3 9 | %.3 =d loadd %.1 10 | %.4 =w cned %.3, d_0 11 | jnz %.4, @while_body.4, @while_join.5 12 | @while_body.4 13 | %.5 =d loadd %.1 14 | %.6 =d swtof 2 15 | %.7 =d div %.5, %.6 16 | stored %.7, %.1 17 | jmp @while_cond.3 18 | @while_join.5 19 | %.8 =d loadd %.1 20 | %.9 =w dtosi %.8 21 | ret %.9 22 | } 23 | -------------------------------------------------------------------------------- /token.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "util.h" 8 | #include "cc.h" 9 | 10 | struct token tok; 11 | 12 | const char *tokstr[] = { 13 | /* keyword */ 14 | [TALIGNAS] = "alignas", 15 | [TALIGNOF] = "alignof", 16 | [TAUTO] = "auto", 17 | [TBOOL] = "bool", 18 | [TBREAK] = "break", 19 | [TCASE] = "case", 20 | [TCHAR] = "char", 21 | [TCONST] = "const", 22 | [TCONSTEXPR] = "constexpr", 23 | [TCONTINUE] = "continue", 24 | [TDEFAULT] = "default", 25 | [TDO] = "do", 26 | [TDOUBLE] = "double", 27 | [TELSE] = "else", 28 | [TENUM] = "enum", 29 | [TEXTERN] = "extern", 30 | [TFALSE] = "false", 31 | [TFLOAT] = "float", 32 | [TFOR] = "for", 33 | [TGOTO] = "goto", 34 | [TIF] = "if", 35 | [TINLINE] = "inline", 36 | [TINT] = "int", 37 | [TLONG] = "long", 38 | [TNULLPTR] = "nullptr", 39 | [TREGISTER] = "register", 40 | [TRESTRICT] = "restrict", 41 | [TRETURN] = "return", 42 | [TSHORT] = "short", 43 | [TSIGNED] = "signed", 44 | [TSIZEOF] = "sizeof", 45 | [TSTATIC] = "static", 46 | [TSTATIC_ASSERT] = "static_assert", 47 | [TSTRUCT] = "struct", 48 | [TSWITCH] = "switch", 49 | [TTHREAD_LOCAL] = "thread_local", 50 | [TTRUE] = "true", 51 | [TTYPEDEF] = "typedef", 52 | [TTYPEOF] = "typeof", 53 | [TTYPEOF_UNQUAL] = "typeof_unqual", 54 | [TUNION] = "union", 55 | [TUNSIGNED] = "unsigned", 56 | [TVOID] = "void", 57 | [TVOLATILE] = "volatile", 58 | [TWHILE] = "while", 59 | [T_ATOMIC] = "_Atomic", 60 | [T_BITINT] = "_BitInt", 61 | [T_COMPLEX] = "_Complex", 62 | [T_DECIMAL128] = "_Decimal128", 63 | [T_DECIMAL32] = "_Decimal32", 64 | [T_DECIMAL64] = "_Decimal64", 65 | [T_GENERIC] = "_Generic", 66 | [T_IMAGINARY] = "_Imaginary", 67 | [T_NORETURN] = "_Noreturn", 68 | [T__ASM__] = "__asm__", 69 | [T__ATTRIBUTE__] = "__attribute__", 70 | 71 | /* punctuator */ 72 | [TLBRACK] = "[", 73 | [TRBRACK] = "]", 74 | [TLPAREN] = "(", 75 | [TRPAREN] = ")", 76 | [TLBRACE] = "{", 77 | [TRBRACE] = "}", 78 | [TPERIOD] = ".", 79 | [TARROW] = "->", 80 | [TINC] = "++", 81 | [TDEC] = "--", 82 | [TBAND] = "&", 83 | [TMUL] = "*", 84 | [TADD] = "+", 85 | [TSUB] = "-", 86 | [TBNOT] = "~", 87 | [TLNOT] = "!", 88 | [TDIV] = "/", 89 | [TMOD] = "%", 90 | [TSHL] = "<<", 91 | [TSHR] = ">>", 92 | [TLESS] = "<", 93 | [TGREATER] = ">", 94 | [TLEQ] = "<=", 95 | [TGEQ] = ">=", 96 | [TEQL] = "==", 97 | [TNEQ] = "!=", 98 | [TXOR] = "^", 99 | [TBOR] = "|", 100 | [TLAND] = "&&", 101 | [TLOR] = "||", 102 | [TQUESTION] = "?", 103 | [TCOLON] = ":", 104 | [TCOLONCOLON] = "::", 105 | [TSEMICOLON] = ";", 106 | [TELLIPSIS] = "...", 107 | [TASSIGN] = "=", 108 | [TMULASSIGN] = "*=", 109 | [TDIVASSIGN] = "/=", 110 | [TMODASSIGN] = "%=", 111 | [TADDASSIGN] = "+=", 112 | [TSUBASSIGN] = "-=", 113 | [TSHLASSIGN] = "<<=", 114 | [TSHRASSIGN] = ">>=", 115 | [TBANDASSIGN] = "&=", 116 | [TXORASSIGN] = "^=", 117 | [TBORASSIGN] = "|=", 118 | [TCOMMA] = ",", 119 | [THASH] = "#", 120 | [THASHHASH] = "##", 121 | }; 122 | 123 | void 124 | tokenprint(const struct token *t) 125 | { 126 | const char *str; 127 | 128 | if (t->space) 129 | fputc(' ', stdout); 130 | switch (t->kind) { 131 | case TIDENT: 132 | case TNUMBER: 133 | case TCHARCONST: 134 | case TSTRINGLIT: 135 | str = t->lit; 136 | break; 137 | case TNEWLINE: 138 | str = "\n"; 139 | break; 140 | case TEOF: 141 | return; 142 | default: 143 | str = tokstr[t->kind]; 144 | } 145 | if (!str) 146 | fatal("cannot print token %d", t->kind); 147 | fputs(str, stdout); 148 | } 149 | 150 | static void 151 | tokendesc(char *buf, size_t len, enum tokenkind kind, const char *lit) 152 | { 153 | const char *class; 154 | bool quote = true; 155 | 156 | switch (kind) { 157 | case TEOF: class = "EOF"; break; 158 | case TIDENT: class = "identifier"; quote = true; break; 159 | case TNUMBER: class = "number"; quote = true; break; 160 | case TCHARCONST: class = "character"; quote = false; break; 161 | case TSTRINGLIT: class = "string"; quote = false; break; 162 | case TNEWLINE: class = "newline"; break; 163 | case TOTHER: class = NULL; break; 164 | default: 165 | class = NULL; 166 | lit = kind < LEN(tokstr) ? tokstr[kind] : NULL; 167 | } 168 | if (class && lit) 169 | snprintf(buf, len, quote ? "%s '%s'" : "%s %s", class, lit); 170 | else if (class) 171 | snprintf(buf, len, "%s", class); 172 | else if (kind == TOTHER && !isprint(*(unsigned char *)lit)) 173 | snprintf(buf, len, "", *(unsigned char *)lit); 174 | else if (lit) 175 | snprintf(buf, len, "'%s'", lit); 176 | else 177 | snprintf(buf, len, ""); 178 | } 179 | 180 | char * 181 | tokencheck(const struct token *t, enum tokenkind kind, const char *msg) 182 | { 183 | char want[64], got[64]; 184 | 185 | if (t->kind != kind) { 186 | tokendesc(want, sizeof(want), kind, NULL); 187 | tokendesc(got, sizeof(got), t->kind, t->lit); 188 | error(&t->loc, "expected %s %s, saw %s", want, msg, got); 189 | } 190 | return t->lit; 191 | } 192 | 193 | void error(const struct location *loc, const char *fmt, ...) 194 | { 195 | va_list ap; 196 | 197 | fprintf(stderr, "%s:%zu:%zu: error: ", loc->file, loc->line, loc->col); 198 | va_start(ap, fmt); 199 | vfprintf(stderr, fmt, ap); 200 | va_end(ap); 201 | putc('\n', stderr); 202 | exit(1); 203 | } 204 | -------------------------------------------------------------------------------- /tree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "util.h" 5 | 6 | #define MAXH (sizeof(void *) * 8 * 3 / 2) 7 | 8 | static inline int 9 | height(struct treenode *n) { 10 | return n ? n->height : 0; 11 | } 12 | 13 | static int 14 | rot(void **p, struct treenode *x, int dir /* deeper side */) 15 | { 16 | struct treenode *y = x->child[dir]; 17 | struct treenode *z = y->child[!dir]; 18 | int hx = x->height; 19 | int hz = height(z); 20 | if (hz > height(y->child[dir])) { 21 | /* 22 | * x 23 | * / \ dir z 24 | * A y / \ 25 | * / \ --> x y 26 | * z D /| |\ 27 | * / \ A B C D 28 | * B C 29 | */ 30 | x->child[dir] = z->child[!dir]; 31 | y->child[!dir] = z->child[dir]; 32 | z->child[!dir] = x; 33 | z->child[dir] = y; 34 | x->height = hz; 35 | y->height = hz; 36 | z->height = hz + 1; 37 | } else { 38 | /* 39 | * x y 40 | * / \ / \ 41 | * A y --> x D 42 | * / \ / \ 43 | * z D A z 44 | */ 45 | x->child[dir] = z; 46 | y->child[!dir] = x; 47 | x->height = hz + 1; 48 | y->height = hz + 2; 49 | z = y; 50 | } 51 | *p = z; 52 | return z->height - hx; 53 | } 54 | 55 | /* balance *p, return 0 if height is unchanged. */ 56 | static int balance(void **p) 57 | { 58 | struct treenode *n = *p; 59 | int h0 = height(n->child[0]); 60 | int h1 = height(n->child[1]); 61 | if (h0 - h1 + 1u < 3u) { 62 | int old = n->height; 63 | n->height = h0 < h1 ? h1 + 1 : h0 + 1; 64 | return n->height - old; 65 | } 66 | return rot(p, n, h0key) { 79 | n->new = false; 80 | return n; 81 | } 82 | a[i++] = &n->child[key > n->key]; 83 | n = n->child[key > n->key]; 84 | } 85 | assert(sz > sizeof(*n)); 86 | n = xmalloc(sz); 87 | n->key = key; 88 | n->child[0] = n->child[1] = 0; 89 | n->height = 1; 90 | /* insert new node, rebalance ancestors. */ 91 | *a[--i] = n; 92 | while (i && balance(a[--i])) 93 | ; 94 | n->new = true; 95 | return n; 96 | } 97 | -------------------------------------------------------------------------------- /type.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "util.h" 6 | #include "cc.h" 7 | 8 | #define INTTYPE(k, n, s, p) { \ 9 | .kind = k, .size = n, .align = n, .u.basic.issigned = s, \ 10 | .prop = PROPSCALAR|PROPARITH|PROPREAL|PROPINT|p, \ 11 | } 12 | #define FLTTYPE(k, n) { \ 13 | .kind = k, .size = n, .align = n, \ 14 | .prop = PROPSCALAR|PROPARITH|PROPREAL|PROPFLOAT, \ 15 | } 16 | 17 | struct type typevoid = {.kind = TYPEVOID, .incomplete = true}; 18 | 19 | struct type typebool = INTTYPE(TYPEBOOL, 1, false, 0); 20 | 21 | struct type typechar = INTTYPE(TYPECHAR, 1, true, PROPCHAR); 22 | struct type typeschar = INTTYPE(TYPECHAR, 1, true, PROPCHAR); 23 | struct type typeuchar = INTTYPE(TYPECHAR, 1, false, PROPCHAR); 24 | 25 | struct type typeshort = INTTYPE(TYPESHORT, 2, true, 0); 26 | struct type typeushort = INTTYPE(TYPESHORT, 2, false, 0); 27 | 28 | struct type typeint = INTTYPE(TYPEINT, 4, true, 0); 29 | struct type typeuint = INTTYPE(TYPEINT, 4, false, 0); 30 | 31 | struct type typelong = INTTYPE(TYPELONG, 8, true, 0); 32 | struct type typeulong = INTTYPE(TYPELONG, 8, false, 0); 33 | 34 | struct type typellong = INTTYPE(TYPELLONG, 8, true, 0); 35 | struct type typeullong = INTTYPE(TYPELLONG, 8, false, 0); 36 | 37 | struct type typefloat = FLTTYPE(TYPEFLOAT, 4); 38 | struct type typedouble = FLTTYPE(TYPEDOUBLE, 8); 39 | struct type typeldouble = FLTTYPE(TYPELDOUBLE, 16); 40 | 41 | struct type typenullptr = {.kind = TYPENULLPTR, .size = 8, .align = 8, .prop = PROPSCALAR}; 42 | 43 | struct type *typeadjvalist; 44 | 45 | struct type * 46 | mktype(enum typekind kind, enum typeprop prop) 47 | { 48 | struct type *t; 49 | 50 | t = xmalloc(sizeof(*t)); 51 | t->kind = kind; 52 | t->prop = prop; 53 | t->value = NULL; 54 | t->incomplete = false; 55 | t->flexible = false; 56 | 57 | return t; 58 | } 59 | 60 | struct type * 61 | mkpointertype(struct type *base, enum typequal qual) 62 | { 63 | struct type *t; 64 | 65 | t = mktype(TYPEPOINTER, PROPSCALAR); 66 | t->base = base; 67 | t->qual = qual; 68 | t->size = 8; 69 | t->align = 8; 70 | if (base) 71 | t->prop |= base->prop & PROPVM; 72 | 73 | return t; 74 | } 75 | 76 | struct type * 77 | mkarraytype(struct type *base, enum typequal qual, unsigned long long len) 78 | { 79 | struct type *t; 80 | 81 | t = mktype(TYPEARRAY, 0); 82 | t->base = base; 83 | t->qual = qual; 84 | t->u.array.length = NULL; 85 | t->u.array.ptrqual = QUALNONE; 86 | t->incomplete = !len; 87 | if (t->base) { 88 | t->align = t->base->align; 89 | t->size = t->base->size * len; /* XXX: overflow? */ 90 | } 91 | 92 | return t; 93 | } 94 | 95 | static int 96 | typerank(struct type *t) 97 | { 98 | if (t->kind == TYPEENUM) 99 | t = t->base; 100 | assert(t->prop & PROPINT); 101 | switch (t->kind) { 102 | case TYPEBOOL: return 1; 103 | case TYPECHAR: return 2; 104 | case TYPESHORT: return 3; 105 | case TYPEINT: return 4; 106 | case TYPELONG: return 5; 107 | case TYPELLONG: return 6; 108 | } 109 | fatal("internal error; unhandled integer type"); 110 | return 0; 111 | } 112 | 113 | bool 114 | typecompatible(struct type *t1, struct type *t2) 115 | { 116 | struct decl *p1, *p2; 117 | struct expr *e1, *e2; 118 | 119 | if (t1 == t2) 120 | return true; 121 | if (t1->kind != t2->kind) { 122 | /* 123 | enum types are compatible with their underlying 124 | type, but not with each other (unless they are the 125 | same type) 126 | */ 127 | return t1->kind == TYPEENUM && t2 == t1->base || 128 | t2->kind == TYPEENUM && t1 == t2->base; 129 | } 130 | switch (t1->kind) { 131 | case TYPEPOINTER: 132 | goto derived; 133 | case TYPEARRAY: 134 | if (t1->incomplete || t2->incomplete) 135 | goto derived; 136 | e1 = t1->u.array.length; 137 | e2 = t2->u.array.length; 138 | if (e1 && e2 && e1->kind == EXPRCONST && e2->kind == EXPRCONST && e1->u.constant.u != e2->u.constant.u) 139 | return false; 140 | goto derived; 141 | case TYPEFUNC: 142 | if (t1->u.func.isvararg != t2->u.func.isvararg) 143 | return false; 144 | for (p1 = t1->u.func.params, p2 = t2->u.func.params; p1 && p2; p1 = p1->next, p2 = p2->next) { 145 | if (!typecompatible(p1->type, p2->type)) 146 | return false; 147 | } 148 | if (p1 || p2) 149 | return false; 150 | goto derived; 151 | derived: 152 | return t1->qual == t2->qual && typecompatible(t1->base, t2->base); 153 | } 154 | return false; 155 | } 156 | 157 | bool 158 | typesame(struct type *t1, struct type *t2) 159 | { 160 | /* XXX: implement */ 161 | return typecompatible(t1, t2); 162 | } 163 | 164 | struct type * 165 | typecomposite(struct type *t1, struct type *t2) 166 | { 167 | /* XXX: implement 6.2.7 */ 168 | /* XXX: merge with typecompatible? */ 169 | return t1; 170 | } 171 | 172 | struct type * 173 | typepromote(struct type *t, unsigned width) 174 | { 175 | if (t == &typefloat) 176 | return &typedouble; 177 | if (t->prop & PROPINT && (typerank(t) <= typerank(&typeint) || width <= typeint.size * 8)) { 178 | if (width == -1) 179 | width = t->size * 8; 180 | return width - t->u.basic.issigned < typeint.size * 8 ? &typeint : &typeuint; 181 | } 182 | return t; 183 | } 184 | 185 | struct type * 186 | typecommonreal(struct type *t1, unsigned w1, struct type *t2, unsigned w2) 187 | { 188 | struct type *tmp; 189 | 190 | assert(t1->prop & PROPREAL && t2->prop & PROPREAL); 191 | if (t1 == &typeldouble || t2 == &typeldouble) 192 | return &typeldouble; 193 | if (t1 == &typedouble || t2 == &typedouble) 194 | return &typedouble; 195 | if (t1 == &typefloat || t2 == &typefloat) 196 | return &typefloat; 197 | t1 = typepromote(t1, w1); 198 | t2 = typepromote(t2, w2); 199 | if (t1 == t2) 200 | return t1; 201 | if (t1->u.basic.issigned == t2->u.basic.issigned) 202 | return typerank(t1) > typerank(t2) ? t1 : t2; 203 | if (t1->u.basic.issigned) { 204 | tmp = t1; 205 | t1 = t2; 206 | t2 = tmp; 207 | } 208 | if (typerank(t1) >= typerank(t2)) 209 | return t1; 210 | if (t1->size < t2->size) 211 | return t2; 212 | if (t2 == &typelong) 213 | return &typeulong; 214 | if (t2 == &typellong) 215 | return &typeullong; 216 | fatal("internal error; could not find common real type"); 217 | return NULL; 218 | } 219 | 220 | /* function parameter type adjustment (C11 6.7.6.3p7) */ 221 | struct type * 222 | typeadjust(struct type *t, enum typequal *tq) 223 | { 224 | enum typequal ptrqual; 225 | 226 | switch (t->kind) { 227 | case TYPEARRAY: 228 | ptrqual = t->u.array.ptrqual; 229 | t = mkpointertype(t->base, *tq | t->qual); 230 | *tq = ptrqual; 231 | break; 232 | case TYPEFUNC: 233 | assert(*tq == QUALNONE); 234 | t = mkpointertype(t, QUALNONE); 235 | break; 236 | } 237 | 238 | return t; 239 | } 240 | 241 | struct member * 242 | typemember(struct type *t, const char *name, unsigned long long *offset) 243 | { 244 | struct member *m, *sub; 245 | 246 | assert(t->kind == TYPESTRUCT || t->kind == TYPEUNION); 247 | for (m = t->u.structunion.members; m; m = m->next) { 248 | if (m->name) { 249 | if (strcmp(m->name, name) == 0) { 250 | *offset += m->offset; 251 | return m; 252 | } 253 | } else { 254 | sub = typemember(m->type, name, offset); 255 | if (sub) { 256 | *offset += m->offset; 257 | return sub; 258 | } 259 | } 260 | } 261 | return NULL; 262 | } 263 | 264 | bool 265 | typehasint(struct type *t, unsigned long long i, bool sign) 266 | { 267 | assert(t->prop & PROPINT); 268 | if (sign && i >= -1ull << 63) 269 | return t->u.basic.issigned && i >= -1ull << (t->size << 3) - 1; 270 | return i <= 0xffffffffffffffffull >> (8 - t->size << 3) + t->u.basic.issigned; 271 | } 272 | -------------------------------------------------------------------------------- /utf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utf.h" 5 | 6 | size_t 7 | utf8enc(unsigned char *s, uint_least32_t c) 8 | { 9 | if (c < 0x80) { 10 | s[0] = c; 11 | return 1; 12 | } 13 | if (c < 0x800) { 14 | s[0] = 0xc0 | c >> 6; 15 | s[1] = 0x80 | c & 0x3f; 16 | return 2; 17 | } 18 | if (c < 0xd800 || c - 0xe000 < 0x2000) { 19 | s[0] = 0xe0 | c >> 12; 20 | s[1] = 0x80 | c >> 6 & 0x3f; 21 | s[2] = 0x80 | c & 0x3f; 22 | return 3; 23 | } 24 | if (c - 0x10000 < 0x100000) { 25 | s[0] = 0xf0 | c >> 18; 26 | s[1] = 0x80 | c >> 12 & 0x3f; 27 | s[2] = 0x80 | c >> 6 & 0x3f; 28 | s[3] = 0x80 | c & 0x3f; 29 | return 4; 30 | } 31 | assert(0); 32 | return 0; 33 | } 34 | 35 | size_t 36 | utf8dec(uint_least32_t *c, const unsigned char *s, size_t n) 37 | { 38 | size_t i, l; 39 | unsigned char b; 40 | uint_least32_t x; 41 | 42 | b = s[0]; 43 | if (b < 0x80) { 44 | *c = b; 45 | return 1; 46 | } 47 | if ((b & 0xe0) == 0xc0) { 48 | x = b & 0x1f; 49 | l = 2; 50 | } else if ((b & 0xf0) == 0xe0) { 51 | x = b & 0x0f; 52 | l = 3; 53 | } else if ((b & 0xf8) == 0xf0) { 54 | x = b & 0x07; 55 | l = 4; 56 | } else { 57 | return -1; 58 | } 59 | if (n < l) 60 | return -1; 61 | for (i = 1; i < l; ++i) { 62 | b = *++s; 63 | if ((b & 0xc0) != 0x80) 64 | return -1; 65 | x = x << 6 | b & 0x3f; 66 | } 67 | if (x >= 0x110000 || x - 0xd800 < 0x0200) 68 | return -1; 69 | *c = x; 70 | return l; 71 | } 72 | 73 | size_t 74 | utf16enc(uint_least16_t *s, uint_least32_t c) 75 | { 76 | if (c < 0xd800 || c - 0xe000 < 0x2000) { 77 | s[0] = c; 78 | return 1; 79 | } 80 | c -= 0x10000; 81 | if (c < 0x100000) { 82 | s[0] = 0xd800 | c >> 10 & 0x3ff; 83 | s[1] = 0xdc00 | c & 0x3ff; 84 | return 2; 85 | } 86 | assert(0); 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /utf.h: -------------------------------------------------------------------------------- 1 | size_t utf8enc(unsigned char *, uint_least32_t); 2 | size_t utf8dec(uint_least32_t *, const unsigned char *, size_t); 3 | size_t utf16enc(uint_least16_t *, uint_least32_t); 4 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "util.h" 10 | 11 | char *argv0; 12 | 13 | static void 14 | vwarn(const char *fmt, va_list ap) 15 | { 16 | fprintf(stderr, "%s: ", argv0); 17 | vfprintf(stderr, fmt, ap); 18 | if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { 19 | fputc(' ', stderr); 20 | perror(NULL); 21 | } else { 22 | fputc('\n', stderr); 23 | } 24 | } 25 | 26 | void 27 | warn(const char *fmt, ...) 28 | { 29 | va_list ap; 30 | 31 | va_start(ap, fmt); 32 | vwarn(fmt, ap); 33 | va_end(ap); 34 | } 35 | 36 | void 37 | fatal(const char *fmt, ...) 38 | { 39 | va_list ap; 40 | 41 | va_start(ap, fmt); 42 | vwarn(fmt, ap); 43 | va_end(ap); 44 | exit(1); 45 | } 46 | 47 | void * 48 | reallocarray(void *buf, size_t n, size_t m) 49 | { 50 | if (n > 0 && SIZE_MAX / n < m) { 51 | errno = ENOMEM; 52 | return NULL; 53 | } 54 | return realloc(buf, n * m); 55 | } 56 | 57 | void * 58 | xreallocarray(void *buf, size_t n, size_t m) 59 | { 60 | buf = reallocarray(buf, n, m); 61 | if (!buf && n && m) 62 | fatal("reallocarray:"); 63 | 64 | return buf; 65 | } 66 | 67 | void * 68 | xmalloc(size_t len) 69 | { 70 | void *buf; 71 | 72 | buf = malloc(len); 73 | if (!buf && len) 74 | fatal("malloc:"); 75 | 76 | return buf; 77 | } 78 | 79 | char * 80 | progname(char *name, char *fallback) 81 | { 82 | char *slash; 83 | 84 | if (!name) 85 | return fallback; 86 | slash = strrchr(name, '/'); 87 | return slash ? slash + 1 : name; 88 | } 89 | 90 | void * 91 | arrayadd(struct array *a, size_t n) 92 | { 93 | void *v; 94 | 95 | if (a->cap - a->len < n) { 96 | do a->cap = a->cap ? a->cap * 2 : 256; 97 | while (a->cap - a->len < n); 98 | a->val = realloc(a->val, a->cap); 99 | if (!a->val) 100 | fatal("realloc"); 101 | } 102 | v = (char *)a->val + a->len; 103 | a->len += n; 104 | 105 | return v; 106 | } 107 | 108 | void 109 | arrayaddptr(struct array *a, void *v) 110 | { 111 | *(void **)arrayadd(a, sizeof(v)) = v; 112 | } 113 | 114 | void 115 | arrayaddbuf(struct array *a, const void *src, size_t n) 116 | { 117 | memcpy(arrayadd(a, n), src, n); 118 | } 119 | 120 | void * 121 | arraylast(struct array *a, size_t n) 122 | { 123 | if (a->len == 0) 124 | return NULL; 125 | assert(n <= a->len); 126 | return (char *)a->val + a->len - n; 127 | } 128 | 129 | void 130 | listinsert(struct list *list, struct list *new) 131 | { 132 | new->next = list->next; 133 | new->prev = list; 134 | list->next->prev = new; 135 | list->next = new; 136 | } 137 | 138 | void 139 | listremove(struct list *list) 140 | { 141 | list->next->prev = list->prev; 142 | list->prev->next = list->next; 143 | list->next = list->prev = NULL; 144 | } 145 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | struct list { 2 | struct list *prev, *next; 3 | }; 4 | 5 | struct array { 6 | void *val; 7 | size_t len, cap; 8 | }; 9 | 10 | struct treenode { 11 | unsigned long long key; 12 | void *child[2]; 13 | int height; 14 | bool new; /* set by treeinsert if this node was newly allocated */ 15 | }; 16 | 17 | extern char *argv0; 18 | 19 | #define LEN(a) (sizeof(a) / sizeof((a)[0])) 20 | #define ALIGNDOWN(x, n) ((x) & -(n)) 21 | #define ALIGNUP(x, n) ALIGNDOWN((x) + (n) - 1, n) 22 | 23 | void warn(const char *, ...); 24 | void fatal(const char *fmt, ...); 25 | 26 | void *reallocarray(void *, size_t, size_t); 27 | void *xreallocarray(void *, size_t, size_t); 28 | void *xmalloc(size_t); 29 | 30 | char *progname(char *, char *); 31 | 32 | void listinsert(struct list *, struct list *); 33 | void listremove(struct list *); 34 | #define listelement(list, type, member) (type *)((char *)list - offsetof(type, member)) 35 | 36 | void *arrayadd(struct array *, size_t); 37 | void arrayaddptr(struct array *, void *); 38 | void arrayaddbuf(struct array *, const void *, size_t); 39 | void *arraylast(struct array *, size_t); 40 | #define arrayforeach(a, m) for (m = (a)->val; m != (void *)((char *)(a)->val + (a)->len); ++m) 41 | 42 | /* map */ 43 | 44 | struct map { 45 | size_t len, cap; 46 | struct mapkey *keys; 47 | void **vals; 48 | }; 49 | 50 | struct mapkey { 51 | unsigned long hash; 52 | const void *str; 53 | size_t len; 54 | }; 55 | 56 | void mapkey(struct mapkey *, const void *, size_t); 57 | void mapinit(struct map *, size_t); 58 | void mapfree(struct map *, void(void *)); 59 | void **mapput(struct map *, struct mapkey *); 60 | void *mapget(struct map *, struct mapkey *); 61 | 62 | /* tree */ 63 | 64 | void *treeinsert(void **, unsigned long long, size_t); 65 | --------------------------------------------------------------------------------