├── .clang-format ├── .github └── workflows │ └── tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── include ├── all │ ├── _map.h │ ├── _vec.h │ ├── reflect.h │ ├── stdalign.h │ ├── stdatomic.h │ └── stdnoreturn.h ├── linux │ ├── float.h │ ├── stdarg.h │ ├── stdbool.h │ ├── stddef.h │ └── stdnoreturn.h └── win │ └── stddef.h ├── m ├── m.bat ├── src ├── alloc.c ├── build_amalg.py ├── build_compiler_includes_header.py ├── codegen.in.c ├── dbp_example │ ├── VisualC │ │ ├── dbp_example.sln │ │ └── dbp_example.vcxproj │ ├── dyn_basic_pdb_example.c │ ├── entry.py │ └── helper.py ├── dyibicc.h ├── dyn_basic_pdb.h ├── dynasm │ ├── COPYRIGHT │ ├── README.modifications │ ├── dasm_arm.h │ ├── dasm_arm.lua │ ├── dasm_arm64.h │ ├── dasm_arm64.lua │ ├── dasm_mips.h │ ├── dasm_mips.lua │ ├── dasm_mips64.lua │ ├── dasm_ppc.h │ ├── dasm_ppc.lua │ ├── dasm_proto.h │ ├── dasm_x64.lua │ ├── dasm_x86.h │ ├── dasm_x86.lua │ ├── dynasm.lua │ └── minilua.c ├── entry.c ├── fuzz_entry.c ├── gen.py ├── hashmap.c ├── libdyibicc.h ├── link.c ├── main.c ├── parse.c ├── preprocess.c ├── regen_container_headers.py ├── testrun.py ├── tokenize.c ├── type.c ├── unicode.c └── util.c ├── test ├── alignof.c ├── alloca.c ├── arith.c ├── asm.c ├── atomic.c ├── attribute.c ├── bitfield.c ├── builtin.c ├── cast.c ├── common.c ├── commonsym.c ├── compat.c ├── complit.c ├── const.c ├── constexpr.c ├── container.c ├── container2.c ├── container3.c ├── control.c ├── decl.c ├── enum.c ├── err_add_non_pointer.c ├── err_addvoid.c ├── err_arrayelem.c ├── err_assign_incompatible_struct.c ├── err_assign_to_struct.c ├── err_identinclude.c ├── err_incomplete_array_missing_initializer.c ├── err_incomplete_array_trailing.c ├── err_incomplete_array_type.c ├── err_large_array_designator.c ├── err_lshvoid.c ├── err_methodcall_func_not_found.c ├── err_negative_array_bounds.c ├── err_nocode.c ├── err_non_methodcall.c ├── err_redefstruct.c ├── err_sub_non_pointer.c ├── err_subvoid.c ├── err_undefvar.c ├── extensions.c ├── extern.c ├── float.c ├── funcstack.c ├── function.c ├── fuzzcases │ ├── array-too-large.c │ ├── degenerate-function-decl.c │ ├── double-amp-initializer.c │ ├── empty-child-array-unspecified-init.c │ ├── incomplete-array-element-type.c │ ├── incomplete-designator.c │ ├── init-expr-div-by-zero.c │ ├── init-expr-div-by-zero2.c │ ├── invalid-token-in-pp-if.c │ ├── label-initializer.c │ ├── mm-during-preprocessor-expression.c │ ├── more-zero-size-array-init.c │ ├── non-constant-array-reference.c │ ├── postfix-inc-dec-preprocessor.c │ ├── unclosed-char-literal-timeout.c │ ├── unterminated-comment.c │ ├── unterminated-ifdef.c │ ├── unterminated-ifndef.c │ └── unterminated-pp-line-directive.c ├── generic.c ├── include1.h ├── include2.h ├── include3.h ├── include4.h ├── initializer.c ├── line.c ├── line_directive_bug.c ├── literal.c ├── macro.c ├── offsetof.c ├── pointer.c ├── pragma-once.c ├── reflect.c ├── sizeof.c ├── stdhdr.c ├── string.c ├── struct.c ├── struct_bug17.c ├── struct_copy.c ├── struct_string.c ├── substructs.c ├── test.h ├── test_helpers_for_update.py ├── tls.c ├── typedef.c ├── typeof.c ├── unicode.c ├── union.c ├── update_basic.py ├── update_morestruct.py ├── update_structabi.py ├── update_structabi2.py ├── update_twofiles.py ├── usualconv.c ├── varargs.c ├── variable.c └── vla.c └── third_party └── ninja ├── ninja-linux └── ninja-win.exe /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | ColumnLimit: 100 3 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Test Runner 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | tests: 11 | strategy: 12 | matrix: 13 | style: [d, r, a] 14 | os: [ubuntu-latest, windows-latest] 15 | runs-on: ${{ matrix.os }} 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: actions/setup-python@v4 20 | with: 21 | python-version: '3.12' 22 | - uses: ilammy/msvc-dev-cmd@v1 23 | with: 24 | arch: amd64 25 | vsversion: 2022 26 | - run: ./m ${{matrix.style}} test 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | out/ 3 | scratch/ 4 | tags 5 | test/__pycache__ 6 | test/x.c 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Rui Ueyama 4 | Copyright (c) 2023 Scott Graham 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dyibicc: A Small DynASM JIT C Compiler 2 | 3 | This is a fork of [chibicc](https://github.com/rui314/chibicc) which directly 4 | generates machine code via [DynASM](https://luajit.org/dynasm.html). 5 | 6 | I renamed it from chibicc to dyibicc to avoid confusion between the two. But the 7 | code still overwhelmingly follows Rui's model and style. 8 | 9 | Currently only supports Linux and Windows, and x64 only. 10 | 11 | Build and test on Linux with: 12 | 13 | ``` 14 | $ ./m r 15 | $ ./m r test 16 | ``` 17 | 18 | or on Windows (from a VS x64 cmd): 19 | 20 | ``` 21 | > m r 22 | > m r test 23 | ``` 24 | 25 | `r` means Release, and can also be `d` for Debug or `a` for ASAN. 26 | 27 | For example, using `m a test -j1` would run the tests under an ASAN build, one 28 | at a time (the extra args `-j1` are passed to the sub-ninja invocation). 29 | 30 | On Linux, there is also a fuzzer target, which can be built by `m f`. This makes 31 | the main binary a [libFuzzer](https://llvm.org/docs/LibFuzzer.html) runner. As 32 | the fuzzer doesn't yet understand C structure, it's only mostly only able to 33 | fuzz the tokenizer and preprocessor. 34 | -------------------------------------------------------------------------------- /include/all/reflect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct _ReflectType _ReflectType; 7 | typedef struct _ReflectTypeMember _ReflectTypeMember; 8 | typedef struct _ReflectTypeEnumerant _ReflectTypeEnumerant; 9 | 10 | #define _REFLECT_KIND_VOID 0 11 | #define _REFLECT_KIND_BOOL 1 12 | #define _REFLECT_KIND_CHAR 2 13 | #define _REFLECT_KIND_SHORT 3 14 | #define _REFLECT_KIND_INT 4 15 | #define _REFLECT_KIND_LONG 5 16 | #define _REFLECT_KIND_FLOAT 6 17 | #define _REFLECT_KIND_DOUBLE 7 18 | // TODO: LDOUBLE 19 | #define _REFLECT_KIND_ENUM 9 20 | #define _REFLECT_KIND_PTR 10 21 | #define _REFLECT_KIND_FUNC 11 22 | #define _REFLECT_KIND_ARRAY 12 23 | #define _REFLECT_KIND_VLA 13 24 | #define _REFLECT_KIND_STRUCT 14 25 | #define _REFLECT_KIND_UNION 15 26 | 27 | #define _REFLECT_TYPEFLAG_UNSIGNED 0x0001 // Integer types only 28 | #define _REFLECT_TYPEFLAG_ATOMIC 0x0002 // Integer types only 29 | #define _REFLECT_TYPEFLAG_FLEXIBLE 0x0004 // Arrays at end of structs only 30 | #define _REFLECT_TYPEFLAG_PACKED 0x0008 // Structs and unions only 31 | #define _REFLECT_TYPEFLAG_VARIADIC 0x0010 // Functions only 32 | 33 | #ifdef _MSC_VER 34 | #pragma warning(push) 35 | #pragma warning(disable : 4200) // Zero-sized array. 36 | #pragma warning(disable : 4201) // Unnamed union. 37 | #endif 38 | 39 | struct _ReflectTypeMember { 40 | _ReflectType* type; 41 | char* name; 42 | int32_t align; 43 | int32_t offset; 44 | int32_t bit_width; // -1 if not bitfield 45 | int32_t bit_offset; // -1 if not bitfield 46 | }; 47 | 48 | struct _ReflectTypeEnumerant { 49 | _ReflectTypeEnumerant* next; 50 | char* name; 51 | int32_t value; 52 | }; 53 | 54 | struct _ReflectType { 55 | char* name; // Either the built-in typename, or the user declared one. 56 | int32_t size; 57 | int32_t align; 58 | int32_t kind; // One of _REFLECT_KIND_*. 59 | int32_t flags; // Combination of _REFLECT_TYPEFLAG_*. 60 | 61 | union { 62 | struct { 63 | _ReflectType* base; 64 | int32_t len; 65 | } arr; 66 | struct { 67 | _ReflectType* base; 68 | } ptr; 69 | struct { 70 | size_t num_members; 71 | _ReflectTypeMember members[]; 72 | } su; 73 | struct { 74 | _ReflectType* return_ty; 75 | size_t num_params; 76 | _ReflectType* params[]; 77 | } func; 78 | struct { 79 | _ReflectTypeEnumerant* enums; 80 | } enumer; 81 | struct { 82 | _ReflectType* vla_size; 83 | // len? 84 | } vla; 85 | }; 86 | }; 87 | #ifdef _MSC_VER 88 | #pragma warning(pop) 89 | #endif 90 | 91 | #if __dyibicc__ 92 | extern _ReflectType* _ReflectTypeOf(...); 93 | #endif 94 | -------------------------------------------------------------------------------- /include/all/stdalign.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDALIGN_H 2 | #define __STDALIGN_H 3 | 4 | #define alignas _Alignas 5 | #define alignof _Alignof 6 | #define __alignas_is_defined 1 7 | #define __alignof_is_defined 1 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/all/stdatomic.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDATOMIC_H 2 | #define __STDATOMIC_H 3 | 4 | #define ATOMIC_BOOL_LOCK_FREE 1 5 | #define ATOMIC_CHAR_LOCK_FREE 1 6 | #define ATOMIC_CHAR16_T_LOCK_FREE 1 7 | #define ATOMIC_CHAR32_T_LOCK_FREE 1 8 | #define ATOMIC_WCHAR_T_LOCK_FREE 1 9 | #define ATOMIC_SHORT_LOCK_FREE 1 10 | #define ATOMIC_INT_LOCK_FREE 1 11 | #define ATOMIC_LONG_LOCK_FREE 1 12 | #define ATOMIC_LLONG_LOCK_FREE 1 13 | #define ATOMIC_POINTER_LOCK_FREE 1 14 | 15 | typedef enum { 16 | memory_order_relaxed, 17 | memory_order_consume, 18 | memory_order_acquire, 19 | memory_order_release, 20 | memory_order_acq_rel, 21 | memory_order_seq_cst, 22 | } memory_order; 23 | 24 | #define ATOMIC_FLAG_INIT(x) (x) 25 | #define atomic_init(addr, val) (*(addr) = (val)) 26 | #define kill_dependency(x) (x) 27 | #define atomic_thread_fence(order) 28 | #define atomic_signal_fence(order) 29 | #define atomic_is_lock_free(x) 1 30 | 31 | #define atomic_load(addr) (*(addr)) 32 | #define atomic_store(addr, val) (*(addr) = (val)) 33 | 34 | #define atomic_load_explicit(addr, order) (*(addr)) 35 | #define atomic_store_explicit(addr, val, order) (*(addr) = (val)) 36 | 37 | #define atomic_fetch_add(obj, val) (*(obj) += (val)) 38 | #define atomic_fetch_sub(obj, val) (*(obj) -= (val)) 39 | #define atomic_fetch_or(obj, val) (*(obj) |= (val)) 40 | #define atomic_fetch_xor(obj, val) (*(obj) ^= (val)) 41 | #define atomic_fetch_and(obj, val) (*(obj) &= (val)) 42 | 43 | #define atomic_fetch_add_explicit(obj, val, order) (*(obj) += (val)) 44 | #define atomic_fetch_sub_explicit(obj, val, order) (*(obj) -= (val)) 45 | #define atomic_fetch_or_explicit(obj, val, order) (*(obj) |= (val)) 46 | #define atomic_fetch_xor_explicit(obj, val, order) (*(obj) ^= (val)) 47 | #define atomic_fetch_and_explicit(obj, val, order) (*(obj) &= (val)) 48 | 49 | #define atomic_compare_exchange_weak(p, old, new) __builtin_compare_and_swap((p), (old), (new)) 50 | 51 | #define atomic_compare_exchange_strong(p, old, new) __builtin_compare_and_swap((p), (old), (new)) 52 | 53 | #define atomic_exchange(obj, val) __builtin_atomic_exchange((obj), (val)) 54 | #define atomic_exchange_explicit(obj, val, order) __builtin_atomic_exchange((obj), (val)) 55 | 56 | #define atomic_flag_test_and_set(obj) atomic_exchange((obj), 1) 57 | #define atomic_flag_test_and_set_explicit(obj, order) atomic_exchange((obj), 1) 58 | #define atomic_flag_clear(obj) (*(obj) = 0) 59 | #define atomic_flag_clear_explicit(obj, order) (*(obj) = 0) 60 | 61 | typedef _Atomic _Bool atomic_flag; 62 | typedef _Atomic _Bool atomic_bool; 63 | typedef _Atomic char atomic_char; 64 | typedef _Atomic signed char atomic_schar; 65 | typedef _Atomic unsigned char atomic_uchar; 66 | typedef _Atomic short atomic_short; 67 | typedef _Atomic unsigned short atomic_ushort; 68 | typedef _Atomic int atomic_int; 69 | typedef _Atomic unsigned int atomic_uint; 70 | typedef _Atomic long atomic_long; 71 | typedef _Atomic unsigned long atomic_ulong; 72 | typedef _Atomic long long atomic_llong; 73 | typedef _Atomic unsigned long long atomic_ullong; 74 | typedef _Atomic unsigned short atomic_char16_t; 75 | typedef _Atomic unsigned atomic_char32_t; 76 | typedef _Atomic unsigned atomic_wchar_t; 77 | typedef _Atomic signed char atomic_int_least8_t; 78 | typedef _Atomic unsigned char atomic_uint_least8_t; 79 | typedef _Atomic short atomic_int_least16_t; 80 | typedef _Atomic unsigned short atomic_uint_least16_t; 81 | typedef _Atomic int atomic_int_least32_t; 82 | typedef _Atomic unsigned int atomic_uint_least32_t; 83 | typedef _Atomic long atomic_int_least64_t; 84 | typedef _Atomic unsigned long atomic_uint_least64_t; 85 | typedef _Atomic signed char atomic_int_fast8_t; 86 | typedef _Atomic unsigned char atomic_uint_fast8_t; 87 | typedef _Atomic short atomic_int_fast16_t; 88 | typedef _Atomic unsigned short atomic_uint_fast16_t; 89 | typedef _Atomic int atomic_int_fast32_t; 90 | typedef _Atomic unsigned int atomic_uint_fast32_t; 91 | typedef _Atomic long atomic_int_fast64_t; 92 | typedef _Atomic unsigned long atomic_uint_fast64_t; 93 | typedef _Atomic long atomic_intptr_t; 94 | typedef _Atomic unsigned long atomic_uintptr_t; 95 | typedef _Atomic unsigned long atomic_size_t; 96 | typedef _Atomic long atomic_ptrdiff_t; 97 | typedef _Atomic long atomic_intmax_t; 98 | typedef _Atomic unsigned long atomic_uintmax_t; 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /include/all/stdnoreturn.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDNORETURN_H 2 | #define __STDNORETURN_H 3 | 4 | #define noreturn _Noreturn 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/linux/float.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDFLOAT_H 2 | #define __STDFLOAT_H 3 | 4 | #define DECIMAL_DIG 21 5 | #define FLT_EVAL_METHOD 0 // C11 5.2.4.2.2p9 6 | #define FLT_RADIX 2 7 | #define FLT_ROUNDS 1 // C11 5.2.4.2.2p8: to nearest 8 | 9 | #define FLT_DIG 6 10 | #define FLT_EPSILON 0x1p-23 11 | #define FLT_MANT_DIG 24 12 | #define FLT_MAX 0x1.fffffep+127 13 | #define FLT_MAX_10_EXP 38 14 | #define FLT_MAX_EXP 128 15 | #define FLT_MIN 0x1p-126 16 | #define FLT_MIN_10_EXP -37 17 | #define FLT_MIN_EXP -125 18 | #define FLT_TRUE_MIN 0x1p-149 19 | 20 | #define DBL_DIG 15 21 | #define DBL_EPSILON 0x1p-52 22 | #define DBL_MANT_DIG 53 23 | #define DBL_MAX 0x1.fffffffffffffp+1023 24 | #define DBL_MAX_10_EXP 308 25 | #define DBL_MAX_EXP 1024 26 | #define DBL_MIN 0x1p-1022 27 | #define DBL_MIN_10_EXP -307 28 | #define DBL_MIN_EXP -1021 29 | #define DBL_TRUE_MIN 0x0.0000000000001p-1022 30 | 31 | #define LDBL_DIG 15 32 | #define LDBL_EPSILON 0x1p-52 33 | #define LDBL_MANT_DIG 53 34 | #define LDBL_MAX 0x1.fffffffffffffp+1023 35 | #define LDBL_MAX_10_EXP 308 36 | #define LDBL_MAX_EXP 1024 37 | #define LDBL_MIN 0x1p-1022 38 | #define LDBL_MIN_10_EXP -307 39 | #define LDBL_MIN_EXP -1021 40 | #define LDBL_TRUE_MIN 0x0.0000000000001p-1022 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/linux/stdarg.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDARG_H 2 | #define __STDARG_H 3 | 4 | typedef struct { 5 | unsigned int gp_offset; 6 | unsigned int fp_offset; 7 | void* overflow_arg_area; 8 | void* reg_save_area; 9 | } __va_elem; 10 | 11 | typedef __va_elem va_list[1]; 12 | 13 | #define va_start(ap, last) \ 14 | do { \ 15 | *(ap) = *(__va_elem*)__va_area__; \ 16 | } while (0) 17 | 18 | #define va_end(ap) 19 | 20 | static void* __va_arg_mem(__va_elem* ap, int sz, int align) { 21 | void* p = ap->overflow_arg_area; 22 | if (align > 8) 23 | p = (p + 15) / 16 * 16; 24 | ap->overflow_arg_area = ((unsigned long)p + sz + 7) / 8 * 8; 25 | return p; 26 | } 27 | 28 | static void* __va_arg_gp(__va_elem* ap, int sz, int align) { 29 | if (ap->gp_offset >= 48) 30 | return __va_arg_mem(ap, sz, align); 31 | 32 | void* r = ap->reg_save_area + ap->gp_offset; 33 | ap->gp_offset += 8; 34 | return r; 35 | } 36 | 37 | static void* __va_arg_fp(__va_elem* ap, int sz, int align) { 38 | if (ap->fp_offset >= 112) 39 | return __va_arg_mem(ap, sz, align); 40 | 41 | void* r = ap->reg_save_area + ap->fp_offset; 42 | ap->fp_offset += 8; 43 | return r; 44 | } 45 | 46 | #define va_arg(ap, ty) \ 47 | ({ \ 48 | int klass = __builtin_reg_class(ty); \ 49 | *(ty*)(klass == 0 ? __va_arg_gp(ap, sizeof(ty), _Alignof(ty)) \ 50 | : klass == 1 ? __va_arg_fp(ap, sizeof(ty), _Alignof(ty)) \ 51 | : __va_arg_mem(ap, sizeof(ty), _Alignof(ty))); \ 52 | }) 53 | 54 | #define va_copy(dest, src) ((dest)[0] = (src)[0]) 55 | 56 | #define __GNUC_VA_LIST 1 57 | typedef va_list __gnuc_va_list; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/linux/stdbool.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDBOOL_H 2 | #define __STDBOOL_H 3 | 4 | #define bool _Bool 5 | #define true 1 6 | #define false 0 7 | #define __bool_true_false_are_defined 1 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/linux/stddef.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDDEF_H 2 | #define __STDDEF_H 3 | 4 | #define NULL ((void*)0) 5 | 6 | typedef unsigned long size_t; 7 | typedef long ptrdiff_t; 8 | typedef unsigned int wchar_t; 9 | typedef long max_align_t; 10 | 11 | #define offsetof(type, member) ((size_t) & (((type*)0)->member)) 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/linux/stdnoreturn.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDNORETURN_H 2 | #define __STDNORETURN_H 3 | 4 | #define noreturn _Noreturn 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/win/stddef.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDDEF_H 2 | #define __STDDEF_H 3 | 4 | #include 5 | 6 | #define offsetof(type, member) ((size_t) & (((type*)0)->member)) 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /m: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ $# -eq 0 ] 3 | then 4 | echo "usage: m d|r|a [target]" 5 | exit 1 6 | fi 7 | python3 src/gen.py 8 | third_party/ninja/ninja-linux -C out/l$1 $2 $3 $4 $5 9 | -------------------------------------------------------------------------------- /m.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | if '%1==' goto :err 3 | call python src/gen.py || exit /b 4 | call third_party\ninja\ninja-win.exe -C out\w%1 %2 %3 %4 %5 || exit /b 5 | goto :EOF 6 | :err 7 | echo usage: m d^|r^|a [target] 8 | -------------------------------------------------------------------------------- /src/alloc.c: -------------------------------------------------------------------------------- 1 | #include "dyibicc.h" 2 | 3 | #if X64WIN 4 | #include 5 | #include "dyn_basic_pdb.h" 6 | #else 7 | #include 8 | #endif 9 | 10 | // MSVC chokes during preprocess on __has_feature(). 11 | #if defined(__has_feature) 12 | #if __has_feature(address_sanitizer) 13 | #define __SANITIZE_ADDRESS__ 1 14 | #endif 15 | #endif 16 | 17 | #if defined(__SANITIZE_ADDRESS__) 18 | void __asan_poison_memory_region(void const volatile* addr, size_t size); 19 | void __asan_unpoison_memory_region(void const volatile* addr, size_t size); 20 | #define ASAN_POISON_MEMORY_REGION(addr, size) __asan_poison_memory_region((addr), (size)) 21 | #define ASAN_UNPOISON_MEMORY_REGION(addr, size) __asan_unpoison_memory_region((addr), (size)) 22 | #else 23 | #define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) 24 | #define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) 25 | #endif 26 | 27 | IMPLSTATIC UserContext* user_context; 28 | IMPLSTATIC jmp_buf toplevel_update_jmpbuf; 29 | IMPLSTATIC CompilerState compiler_state; 30 | IMPLSTATIC LinkerState linker_state; 31 | 32 | typedef struct HeapData { 33 | char* base; 34 | char* alloc_pointer; 35 | size_t size; 36 | } HeapData; 37 | 38 | static HeapData heap[NUM_BUMP_HEAPS] = { 39 | {NULL, NULL, 1024 << 20}, // AL_Compile 40 | {NULL, NULL, 128 << 20}, // AL_Temp 41 | {NULL, NULL, 128 << 20}, // AL_Link 42 | {NULL, NULL, 64 << 20}, // AL_UserContext 43 | }; 44 | 45 | IMPLSTATIC void alloc_init(AllocLifetime lifetime) { 46 | assert(lifetime < NUM_BUMP_HEAPS); 47 | HeapData* hd = &heap[lifetime]; 48 | 49 | hd->alloc_pointer = hd->base = allocate_writable_memory(hd->size); 50 | ASAN_POISON_MEMORY_REGION(hd->base, hd->size); 51 | if (lifetime == AL_Compile) { 52 | memset(&compiler_state, 0, sizeof(compiler_state)); 53 | } else if (lifetime == AL_Link) { 54 | memset(&linker_state, 0, sizeof(linker_state)); 55 | } 56 | } 57 | 58 | IMPLSTATIC void alloc_reset(AllocLifetime lifetime) { 59 | assert(lifetime < NUM_BUMP_HEAPS); 60 | HeapData* hd = &heap[lifetime]; 61 | // We allow double resets because we may longjmp out during error handling, 62 | // and don't know which heaps are initialized at that point. 63 | if (hd->base) { 64 | ASAN_POISON_MEMORY_REGION(hd->base, hd->size); 65 | free_executable_memory(hd->base, hd->size); 66 | hd->alloc_pointer = NULL; 67 | hd->base = NULL; 68 | } 69 | } 70 | 71 | IMPLSTATIC void* bumpcalloc(size_t num, size_t size, AllocLifetime lifetime) { 72 | if (lifetime == AL_Manual) { 73 | return calloc(num, size); 74 | } 75 | 76 | size_t toalloc = align_to_u(num * size, 8); 77 | HeapData* hd = &heap[lifetime]; 78 | char* ret = hd->alloc_pointer; 79 | hd->alloc_pointer += toalloc; 80 | if (hd->alloc_pointer > hd->base + hd->size) { 81 | error("heap exhausted"); 82 | } 83 | ASAN_UNPOISON_MEMORY_REGION(ret, toalloc); 84 | memset(ret, 0, toalloc); 85 | return ret; 86 | } 87 | 88 | IMPLSTATIC void alloc_free(void* p, AllocLifetime lifetime) { 89 | (void)lifetime; 90 | assert(lifetime == AL_Manual); 91 | free(p); 92 | } 93 | 94 | IMPLSTATIC void* bumplamerealloc(void* old, 95 | size_t old_size, 96 | size_t new_size, 97 | AllocLifetime lifetime) { 98 | void* newptr = bumpcalloc(1, new_size, lifetime); 99 | memcpy(newptr, old, MIN(old_size, new_size)); 100 | ASAN_POISON_MEMORY_REGION(old, old_size); 101 | return newptr; 102 | } 103 | 104 | IMPLSTATIC void* aligned_allocate(size_t size, size_t alignment) { 105 | size = align_to_u(size, alignment); 106 | #if X64WIN 107 | return _aligned_malloc(size, alignment); 108 | #else 109 | return aligned_alloc(alignment, size); 110 | #endif 111 | } 112 | 113 | IMPLSTATIC void aligned_free(void* p) { 114 | #if X64WIN 115 | _aligned_free(p); 116 | #else 117 | free(p); 118 | #endif 119 | } 120 | 121 | // Allocates RW memory of given size and returns a pointer to it. On failure, 122 | // prints out the error and returns NULL. Unlike malloc, the memory is allocated 123 | // on a page boundary so it's suitable for calling mprotect. 124 | IMPLSTATIC void* allocate_writable_memory(size_t size) { 125 | #if X64WIN 126 | void* p = VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 127 | if (!p) { 128 | error("VirtualAlloc of %zu failed: 0x%x\n", size, GetLastError()); 129 | } 130 | ASAN_UNPOISON_MEMORY_REGION(p, size); 131 | return p; 132 | #else 133 | void* ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 134 | if (ptr == (void*)-1) { 135 | perror("mmap"); 136 | return NULL; 137 | } 138 | ASAN_UNPOISON_MEMORY_REGION(ptr, size); 139 | return ptr; 140 | #endif 141 | } 142 | 143 | // Sets a RX permission on the given memory, which must be page-aligned. Returns 144 | // 0 on success. On failure, prints out the error and returns -1. 145 | IMPLSTATIC bool make_memory_readwrite(void* m, size_t size) { 146 | #if X64WIN 147 | DWORD old_protect; 148 | if (!VirtualProtect(m, size, PAGE_READWRITE, &old_protect)) { 149 | error("VirtualProtect %p %zu failed: 0x%x\n", m, size, GetLastError()); 150 | } 151 | return true; 152 | #else 153 | if (mprotect(m, size, PROT_READ | PROT_WRITE) == -1) { 154 | perror("mprotect"); 155 | return false; 156 | } 157 | return true; 158 | #endif 159 | } 160 | 161 | // Sets a RX permission on the given memory, which must be page-aligned. Returns 162 | // 0 on success. On failure, prints out the error and returns -1. 163 | IMPLSTATIC bool make_memory_executable(void* m, size_t size) { 164 | #if X64WIN 165 | DWORD old_protect; 166 | if (!VirtualProtect(m, size, PAGE_EXECUTE_READ, &old_protect)) { 167 | error("VirtualProtect %p %zu failed: 0x%x\n", m, size, GetLastError()); 168 | } 169 | return true; 170 | #else 171 | if (mprotect(m, size, PROT_READ | PROT_EXEC) == -1) { 172 | perror("mprotect"); 173 | return false; 174 | } 175 | return true; 176 | #endif 177 | } 178 | 179 | IMPLSTATIC void free_executable_memory(void* p, size_t size) { 180 | #if X64WIN 181 | (void)size; // If |size| is passed, free will fail. 182 | if (!VirtualFree(p, 0, MEM_RELEASE)) { 183 | error("VirtualFree %p %zu failed: 0x%x\n", p, size, GetLastError()); 184 | } 185 | ASAN_POISON_MEMORY_REGION(p, size); 186 | #else 187 | munmap(p, size); 188 | ASAN_POISON_MEMORY_REGION(p, size); 189 | #endif 190 | } 191 | -------------------------------------------------------------------------------- /src/build_amalg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import shutil 5 | import subprocess 6 | import sys 7 | 8 | 9 | HEADER = '''\ 10 | // 11 | // Amalgamated (single file) build of https://github.com/sgraham/dyibicc. 12 | // Revision: %s 13 | // 14 | // This file should not be edited or modified, patches should be to the 15 | // non-amalgamated files in src/. The user-facing API is in libdyibicc.h 16 | // which should be located in the same directory as this .c file. 17 | // 18 | 19 | ''' 20 | 21 | 22 | def push_disable_dynasm_warnings(f): 23 | f.write('#ifdef _MSC_VER\n') 24 | f.write('#pragma warning(push)\n') 25 | f.write('#pragma warning(disable: 4127)\n') 26 | f.write('#pragma warning(disable: 4242)\n') 27 | f.write('#pragma warning(disable: 4244)\n') 28 | f.write('#endif\n') 29 | 30 | 31 | def pop_dynasm_warnings(f): 32 | f.write('#ifdef _MSC_VER\n') 33 | f.write('#pragma warning(pop)\n') 34 | f.write('#endif\n') 35 | 36 | 37 | def include_file(f, src): 38 | with open(src, 'r') as i: 39 | f.write('#undef C\n') 40 | f.write('#undef L\n') 41 | f.write('#undef VOID\n') 42 | if 'dynasm/' in src: 43 | push_disable_dynasm_warnings(f) 44 | f.write('//\n// START OF %s\n//\n' % src) 45 | for line in i.readlines(): 46 | if line.startswith('#include "dyibicc.h"'): 47 | continue 48 | if line.startswith('#include "dynasm/dasm_proto.h"'): 49 | continue 50 | if line.startswith('#include "dynasm/dasm_x86.h"'): 51 | continue 52 | if line.startswith('#include "dyn_basic_pdb.h"'): 53 | continue 54 | if line.startswith('#include "compincl.h"'): 55 | continue 56 | if line.startswith('#include "../include/all/reflect.h"'): 57 | continue 58 | if line.startswith('#pragma once'): 59 | continue 60 | if line.startswith('IMPLEXTERN '): 61 | continue 62 | f.write(line.replace('IMPLSTATIC ', 'static ').replace('DASM_FDEF ', 'static ')) 63 | f.write('//\n// END OF %s\n//\n' % src) 64 | if 'dynasm/' in src: 65 | pop_dynasm_warnings(f) 66 | 67 | 68 | def main(): 69 | out_dir = sys.argv[1] 70 | root_src = sys.argv[2] 71 | if not os.path.isdir(out_dir): 72 | os.makedirs(out_dir) 73 | with open(os.path.join(out_dir, 'libdyibicc.c'), 'w', newline='\n') as f: 74 | rev_parse = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True) 75 | cur_rev = rev_parse.stdout.decode('utf-8').strip() 76 | f.write(HEADER % cur_rev) 77 | for src in sys.argv[3:]: 78 | include_file(f, src) 79 | 80 | f.write('#if X64WIN\n') 81 | include_file(f, 'codegen.w.c') 82 | f.write('#else // ^^^ X64WIN / !X64WIN vvv\n') 83 | include_file(f, 'codegen.l.c') 84 | f.write('#endif // !X64WIN\n') 85 | shutil.copyfile(os.path.join(root_src, 'libdyibicc.h'), os.path.join(out_dir, 'libdyibicc.h')) 86 | shutil.copyfile(os.path.join(root_src, '..', 'LICENSE'), os.path.join(out_dir, 'LICENSE')) 87 | 88 | # Smoke test that there's no warnings, etc. Currently only on host platform. 89 | os.chdir(out_dir) 90 | if sys.platform == 'win32': 91 | subprocess.check_call(['cl', '/nologo', '/W4', '/Wall', '/WX', '/c', 'libdyibicc.c']) 92 | os.remove('libdyibicc.obj') 93 | elif sys.platform == 'linux': 94 | subprocess.check_call(['gcc', '-Wall', '-Wextra', '-Werror', '-c', 'libdyibicc.c']) 95 | subprocess.check_call(['clang', '-Wall', '-Wextra', '-Werror', '-c', 'libdyibicc.c']) 96 | symsp = subprocess.run(['readelf', '-s', 'libdyibicc.o'], capture_output=True) 97 | os.remove('libdyibicc.o') 98 | syms = str(symsp.stdout, encoding='utf-8').splitlines() 99 | syms = [l for l in syms if 'GLOBAL ' in l] 100 | syms = [l for l in syms if 'UND ' not in l] 101 | print('These are the global exported symbols from libdyibicc.o,') 102 | print('check that they match libdyibicc.h (only).') 103 | print('-'*80) 104 | for s in syms: 105 | print(s) 106 | print('-'*80) 107 | 108 | return 0 109 | 110 | 111 | if __name__ == '__main__': 112 | sys.exit(main()) 113 | -------------------------------------------------------------------------------- /src/build_compiler_includes_header.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import textwrap 4 | 5 | ''' 6 | static const unsigned char compiler_include_blob[N] = { 7 | ... 8 | }; 9 | 10 | CompilerInclude compiler_includes_all[] = { 11 | { "stddef.h", }, 12 | ... 13 | }; 14 | ''' 15 | 16 | def escape(s, encoding='ascii'): 17 | result = '' 18 | for c in s: 19 | if not (32 <= ord(c) < 127) or c in ('\\', '"'): 20 | result += '\\%03o' % ord(c) 21 | else: 22 | result += c 23 | return '"' + result + '"' 24 | 25 | def main(): 26 | out, inputs = sys.argv[1], sys.argv[2:] 27 | with open(out, 'w', newline='\n', encoding='utf-8') as f: 28 | files = [] 29 | for i in inputs: 30 | PREFIX = '../../include/' 31 | if not i.startswith(PREFIX): 32 | print('Expecting %s as prefix' % PREFIX) 33 | return 1 34 | path = i[len(PREFIX):] 35 | category, name = os.path.split(path) 36 | with open(i, 'r', encoding='utf-8') as g: 37 | files.append((category, name, g.read())) 38 | 39 | f.write('typedef struct CompilerInclude {\n' 40 | ' char* path;\n' 41 | ' int offset;\n' 42 | '} CompilerInclude;\n\n') 43 | 44 | blob = '' 45 | f.write('static CompilerInclude compiler_includes[%d] = {\n' % len(files)) 46 | for cat, name, contents in files: 47 | f.write(' { "__include__/%s/%s", %d },\n' % (cat, name, len(blob))) 48 | blob += contents + '\0' 49 | f.write('};\n') 50 | f.write('static unsigned char compiler_include_blob[%d] = {\n' % len(blob)) 51 | f.write('\n'.join(textwrap.wrap(', '.join(str(ord(x)) for x in blob)))) 52 | f.write('};\n') 53 | 54 | if __name__ == '__main__': 55 | sys.exit(main()) 56 | 57 | -------------------------------------------------------------------------------- /src/dbp_example/VisualC/dbp_example.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33213.308 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dbp_example", "dbp_example.vcxproj", "{3A975551-7F50-4156-814E-1C1AB255466B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {3A975551-7F50-4156-814E-1C1AB255466B}.Debug|x64.ActiveCfg = Debug|x64 17 | {3A975551-7F50-4156-814E-1C1AB255466B}.Debug|x64.Build.0 = Debug|x64 18 | {3A975551-7F50-4156-814E-1C1AB255466B}.Debug|x86.ActiveCfg = Debug|Win32 19 | {3A975551-7F50-4156-814E-1C1AB255466B}.Debug|x86.Build.0 = Debug|Win32 20 | {3A975551-7F50-4156-814E-1C1AB255466B}.Release|x64.ActiveCfg = Release|x64 21 | {3A975551-7F50-4156-814E-1C1AB255466B}.Release|x64.Build.0 = Release|x64 22 | {3A975551-7F50-4156-814E-1C1AB255466B}.Release|x86.ActiveCfg = Release|Win32 23 | {3A975551-7F50-4156-814E-1C1AB255466B}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {92C7EB31-0B39-47E8-8990-8FC906C1D60B} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/dbp_example/VisualC/dbp_example.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {3a975551-7f50-4156-814e-1c1ab255466b} 25 | dbpexample 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | NotSet 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | NotSet 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | true 77 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 78 | true 79 | 80 | 81 | Console 82 | true 83 | 84 | 85 | 86 | 87 | Level3 88 | true 89 | true 90 | true 91 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | true 98 | true 99 | 100 | 101 | 102 | 103 | Level4 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | $(SolutionDir)..\.. 108 | ProgramDatabase 109 | true 110 | 111 | 112 | Console 113 | true 114 | 115 | 116 | 117 | 118 | Level4 119 | true 120 | true 121 | true 122 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 123 | true 124 | $(SolutionDir)..\.. 125 | true 126 | 127 | 128 | Console 129 | true 130 | true 131 | true 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /src/dbp_example/dyn_basic_pdb_example.c: -------------------------------------------------------------------------------- 1 | // This is an example of dyn_basic_pdb.h, which handles generating a .pdb for 2 | // JIT-generated code, and then entices Visual Studio into loading the pdb and 3 | // associating it with the JIT'd code. 4 | // 5 | // Stepping through main() in Visual Studio should give a general idea of how 6 | // the library is supposed to be used. 7 | 8 | // clang-format off 9 | // This is the pretend output of the JIT compiler. Two simple functions that 10 | // correspond to helper.py:Func and entry.py:Entry (also in this directory). 11 | static unsigned char test_data[] = { 12 | /* --- Func */ 13 | /* 0x0000: */ 0x48, 0x83, 0xec, 0x18, // sub rsp,18h 14 | /* 0x0004: */ 0xc7, 0x04, 0x24, 0x04, 0x00, 0x00, 0x00, // mov dword ptr [rsp],4 15 | /* 0x000b: */ 0x8b, 0x04, 0x24, // mov eax,dword ptr [rsp] 16 | /* 0x000e: */ 0x83, 0xc0, 0x64, // add eax,64h 17 | /* 0x0011: */ 0x48, 0x83, 0xc4, 0x18, // add rsp,18h 18 | /* 0x0015: */ 0xc3, // ret 19 | /* --- Entry */ 20 | /* 0x0016: */ 0x48, 0x83, 0xec, 0x38, // sub rsp,38h 21 | /* 0x001a: */ 0xe8, 0xe1, 0xff, 0xff, 0xff, // call Func 22 | /* 0x001f: */ 0x05, 0xe8, 0x03, 0x00, 0x00, // add eax,3E8h 23 | /* 0x0024: */ 0x89, 0x44, 0x24, 0x20, // mov dword ptr [rsp+20h],eax 24 | /* 0x0028: */ 0x8b, 0x44, 0x24, 0x20, // mov eax,dword ptr [rsp+20h] 25 | /* 0x002c: */ 0x48, 0x83, 0xc4, 0x38, // add rsp,38h 26 | /* 0x0030: */ 0xc3, // ret 27 | 28 | /* --- padding */ 29 | /* 0x0031 */ 0xcc, // int 3 30 | /* 0x0032 */ 0xcc, // int 3 31 | /* 0x0033 */ 0xcc, // int 3 32 | 33 | // --- UnwindData for RtlAddFunctionTable(), see exception_tables. 34 | /* 0x0034 */ 0x01, 0x04, 0x01, 0x00, 0x04, 0x22, 0x00, 0x00, 35 | /* 0x003c */ 0x01, 0x04, 0x01, 0x00, 0x04, 0x62, 0x00, 0x00, 36 | }; 37 | // clang-format on 38 | 39 | #ifdef _DEBUG 40 | #ifdef __clang__ 41 | #pragma clang diagnostic ignored "-Wreserved-macro-identifier" 42 | #endif 43 | #define _CRTDBG_MAP_ALLOC 44 | #include 45 | #endif 46 | 47 | #define DYN_BASIC_PDB_IMPLEMENTATION 48 | #include "dyn_basic_pdb.h" 49 | 50 | #include 51 | #include 52 | 53 | #define CHECK(x) \ 54 | do { \ 55 | if (!(x)) { \ 56 | fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, #x); \ 57 | abort(); \ 58 | } \ 59 | } while (0) 60 | 61 | // Just a goofy example function to get the fully qualified correct path 62 | // to the pretend interpreter sources so that VS doesn't ask to browse to 63 | // find them locally if the cwd has changed. 64 | static void get_file_path(const char* fn, char* buf_out, DWORD buf_out_size) { 65 | char buf[MAX_PATH]; 66 | sprintf_s(buf, sizeof(buf), "%s\\..\\%s", __FILE__, fn); 67 | CHECK(GetFullPathName(buf, buf_out_size, buf_out, NULL)); 68 | } 69 | 70 | static DbpRUNTIME_FUNCTION function_table[] = { 71 | {.begin_address = 0, .end_address = 0x16, .unwind_data = 0}, 72 | {.begin_address = 0x16, .end_address = 0x31, .unwind_data = 8}, 73 | }; 74 | 75 | static DbpExceptionTables exception_tables = { 76 | .pdata = function_table, 77 | .num_pdata_entries = 2, 78 | .unwind_info = &test_data[0x34], 79 | .unwind_info_byte_length = 16, 80 | }; 81 | 82 | int main(int argc, char** argv) { 83 | // Create a context. |image_size| is VirtualAlloc()d so must be a multiple of 84 | // PAGE_SIZE (== 4096). 85 | size_t image_size = 4096; 86 | DbpContext* ctx = dbp_create(image_size, argc < 2 ? "dbp.pdb" : argv[1]); 87 | 88 | // "JIT" some code. The source code is found in entry.py and helper.py, and 89 | // the compiled binary code is above in |test_data|. 90 | char* image_addr = dbp_get_image_base(ctx); 91 | CHECK(image_addr); 92 | memcpy(image_addr, test_data, sizeof(test_data)); 93 | 94 | // Silly example code to get full path to sources. If dynamically allocated, 95 | // the file name pointers passed in can be freed immediately after the call to 96 | // dbp_add_function_symbol(). 97 | char helper_fn[MAX_PATH], entry_fn[MAX_PATH]; 98 | get_file_path("helper.py", helper_fn, MAX_PATH); 99 | get_file_path("entry.py", entry_fn, MAX_PATH); 100 | 101 | // Add the name and byte range of our two functions. 102 | DbpFunctionSymbol* fs_func = dbp_add_function_symbol(ctx, "Func", helper_fn, 0x00, 0x16); 103 | DbpFunctionSymbol* fs_zippy = dbp_add_function_symbol(ctx, "Entry", entry_fn, 0x16, 0x31); 104 | 105 | // Fill out offset/line information. The offset corresponds to the byte offset 106 | // in |test_data| above, and the line number is a one-based line number in the 107 | // source file that contains the symbol. TODO: Probably need to expand this 108 | // interface for inlining, etc. 109 | dbp_add_line_mapping(ctx, fs_func, 0x00, 1); 110 | dbp_add_line_mapping(ctx, fs_func, 0x04, 2); 111 | dbp_add_line_mapping(ctx, fs_func, 0x0b, 3); 112 | 113 | dbp_add_line_mapping(ctx, fs_zippy, 0x16, 1); 114 | dbp_add_line_mapping(ctx, fs_zippy, 0x1a, 2); 115 | dbp_add_line_mapping(ctx, fs_zippy, 0x28, 3); 116 | 117 | // dyn_basic_pdb does not support any type information, only function symbols, 118 | // and source line mappings, so that's all the information we have to add. 119 | 120 | // Completes the generation of the pdb, and tricks VS into loading it. The 121 | // code is also VirtualProtect()d to PAGE_EXECUTE_READ. (You should see a DLL 122 | // in the "Modules" window with "Symbols loaded" after stepping over this 123 | // call.) 124 | CHECK(dbp_ready_to_execute(ctx, &exception_tables)); 125 | 126 | // Try: Set a breakpoint here in VS, and step into the following calls 127 | // First, we step into a simple helper. 128 | int result = ((int (*)(void))(image_addr))(); 129 | printf("Func() returned: %d\n", result); 130 | 131 | // Then step into another function that also calls `Func()` itself. 132 | // Step In, Step Out, Set Next Statement, View Disassembly, etc. should all 133 | // work as expected. 134 | int result2 = ((int (*)(void))(image_addr + 0x0016))(); 135 | printf("Zippy() returned: %d\n", result2); 136 | 137 | // Frees the DbpContext and associated resources, including the memory holding 138 | // the JIT'd code. 139 | dbp_free(ctx); 140 | 141 | #ifdef _DEBUG 142 | assert(!_CrtDumpMemoryLeaks()); 143 | #endif 144 | } 145 | -------------------------------------------------------------------------------- /src/dbp_example/entry.py: -------------------------------------------------------------------------------- 1 | def int Entry(): 2 | ret = 1000 + Func() 3 | return ret 4 | -------------------------------------------------------------------------------- /src/dbp_example/helper.py: -------------------------------------------------------------------------------- 1 | def int Func(): 2 | x = 4 3 | return x + 100 4 | -------------------------------------------------------------------------------- /src/dynasm/COPYRIGHT: -------------------------------------------------------------------------------- 1 | =============================================================================== 2 | LuaJIT -- a Just-In-Time Compiler for Lua. https://luajit.org/ 3 | 4 | Copyright (C) 2005-2022 Mike Pall. All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | [ MIT license: https://www.opensource.org/licenses/mit-license.php ] 25 | 26 | =============================================================================== 27 | [ LuaJIT includes code from Lua 5.1/5.2, which has this license statement: ] 28 | 29 | Copyright (C) 1994-2012 Lua.org, PUC-Rio. 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy 32 | of this software and associated documentation files (the "Software"), to deal 33 | in the Software without restriction, including without limitation the rights 34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | copies of the Software, and to permit persons to whom the Software is 36 | furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in 39 | all copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 47 | THE SOFTWARE. 48 | 49 | =============================================================================== 50 | [ LuaJIT includes code from dlmalloc, which has this license statement: ] 51 | 52 | This is a version (aka dlmalloc) of malloc/free/realloc written by 53 | Doug Lea and released to the public domain, as explained at 54 | https://creativecommons.org/licenses/publicdomain 55 | 56 | =============================================================================== 57 | -------------------------------------------------------------------------------- /src/dynasm/README.modifications: -------------------------------------------------------------------------------- 1 | From luajit 2.1 at 505e2c03de35e2718eef0d2d3660712e06dadf1f with a small patch: 2 | 3 | --- a/dynasm.lua 4 | +++ b/dynasm.lua 5 | @@ -829,7 +829,7 @@ local function doline(line) 6 | if g_opt.flushline then wflush() end 7 | 8 | -- Assembler line? 9 | - local indent, aline = match(line, "^(%s*)%|(.*)$") 10 | + local indent, aline = match(line, "^(%s*)%///|(.*)$") 11 | if not aline then 12 | -- No, plain C code line, need to flush first. 13 | wflush() 14 | 15 | 16 | to make the dynasm prefix ///| rather than |. 17 | 18 | This is so that clang-format doesn't destroy things when dynasm lines get 19 | formatted. LuaJIT's jit is written primarily in asm assembled by DynASM and 20 | there's only an occasional sprinkle of C, so having only | as a prefix makes 21 | sense. Here though, the code is mostly C with a sprinkle of DynASM (handling the 22 | messy encoding details), so it makes more sense to integrate the asm in special 23 | C-comments. 24 | 25 | Additionally something like this is nice in .vimrc to highlight dynasm lines: 26 | 27 | autocmd BufRead,BufEnter *.in.c syn region cDynasm start="^\s*\(///|\)" skip="\\$" end="$" keepend 28 | autocmd BufRead,BufEnter *.in.c hi def link cDynasm SpecialChar 29 | -------------------------------------------------------------------------------- /src/dynasm/dasm_mips64.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------ 2 | -- DynASM MIPS64 module. 3 | -- 4 | -- Copyright (C) 2005-2022 Mike Pall. All rights reserved. 5 | -- See dynasm.lua for full copyright notice. 6 | ------------------------------------------------------------------------------ 7 | -- This module just sets 64 bit mode for the combined MIPS/MIPS64 module. 8 | -- All the interesting stuff is there. 9 | ------------------------------------------------------------------------------ 10 | 11 | mips64 = true -- Using a global is an ugly, but effective solution. 12 | return require("dasm_mips") 13 | -------------------------------------------------------------------------------- /src/dynasm/dasm_proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** DynASM encoding engine prototypes. 3 | ** Copyright (C) 2005-2022 Mike Pall. All rights reserved. 4 | ** Released under the MIT license. See dynasm.lua for full copyright notice. 5 | */ 6 | 7 | #ifndef _DASM_PROTO_H 8 | #define _DASM_PROTO_H 9 | 10 | #include 11 | #include 12 | 13 | #define DASM_IDENT "DynASM 1.5.0" 14 | #define DASM_VERSION 10500 /* 1.5.0 */ 15 | 16 | #ifndef Dst_DECL 17 | #define Dst_DECL dasm_State **Dst 18 | #endif 19 | 20 | #ifndef Dst_REF 21 | #define Dst_REF (*Dst) 22 | #endif 23 | 24 | #ifndef DASM_FDEF 25 | #define DASM_FDEF extern 26 | #endif 27 | 28 | #ifndef DASM_M_GROW 29 | #define DASM_M_GROW(ctx, t, p, sz, need) \ 30 | do { \ 31 | size_t _sz = (sz), _need = (need); \ 32 | if (_sz < _need) { \ 33 | if (_sz < 16) _sz = 16; \ 34 | while (_sz < _need) _sz += _sz; \ 35 | (p) = (t *)realloc((p), _sz); \ 36 | if ((p) == NULL) exit(1); \ 37 | (sz) = _sz; \ 38 | } \ 39 | } while(0) 40 | #endif 41 | 42 | #ifndef DASM_M_FREE 43 | #define DASM_M_FREE(ctx, p, sz) free(p) 44 | #endif 45 | 46 | /* Internal DynASM encoder state. */ 47 | typedef struct dasm_State dasm_State; 48 | 49 | 50 | /* Initialize and free DynASM state. */ 51 | DASM_FDEF void dasm_init(Dst_DECL, int maxsection); 52 | DASM_FDEF void dasm_free(Dst_DECL); 53 | 54 | /* Setup global array. Must be called before dasm_setup(). */ 55 | DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl); 56 | 57 | /* Grow PC label array. Can be called after dasm_setup(), too. */ 58 | DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc); 59 | 60 | /* Setup encoder. */ 61 | DASM_FDEF void dasm_setup(Dst_DECL, const void *actionlist); 62 | 63 | /* Feed encoder with actions. Calls are generated by pre-processor. */ 64 | DASM_FDEF void dasm_put(Dst_DECL, int start, ...); 65 | 66 | /* Link sections and return the resulting size. */ 67 | DASM_FDEF int dasm_link(Dst_DECL, size_t *szp); 68 | 69 | /* Encode sections into buffer. */ 70 | DASM_FDEF int dasm_encode(Dst_DECL, void *buffer); 71 | 72 | /* Get PC label offset. */ 73 | DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc); 74 | 75 | #ifdef DASM_CHECKS 76 | /* Optional sanity checker to call between isolated encoding steps. */ 77 | DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch); 78 | #else 79 | #define dasm_checkstep(a, b) 0 80 | #endif 81 | 82 | 83 | #endif /* _DASM_PROTO_H */ 84 | -------------------------------------------------------------------------------- /src/dynasm/dasm_x64.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------ 2 | -- DynASM x64 module. 3 | -- 4 | -- Copyright (C) 2005-2022 Mike Pall. All rights reserved. 5 | -- See dynasm.lua for full copyright notice. 6 | ------------------------------------------------------------------------------ 7 | -- This module just sets 64 bit mode for the combined x86/x64 module. 8 | -- All the interesting stuff is there. 9 | ------------------------------------------------------------------------------ 10 | 11 | x64 = true -- Using a global is an ugly, but effective solution. 12 | return require("dasm_x86") 13 | -------------------------------------------------------------------------------- /src/entry.c: -------------------------------------------------------------------------------- 1 | #include "dyibicc.h" 2 | 3 | static void usage(int status) { 4 | printf("dyibicc [-e symbolname] [-I ] [-c] [-g] [...]\n"); 5 | exit(status); 6 | } 7 | 8 | static bool take_arg(char* arg) { 9 | char* x[] = { 10 | "-I", 11 | "-e", 12 | }; 13 | 14 | for (size_t i = 0; i < sizeof(x) / sizeof(*x); i++) 15 | if (!strcmp(arg, x[i])) 16 | return true; 17 | return false; 18 | } 19 | 20 | // Returns the contents of a given file. Doesn't support '-' for reading from 21 | // stdin. 22 | static bool read_file(const char* path, char** contents, size_t* size) { 23 | FILE* fp = fopen(path, "rb"); 24 | if (!fp) { 25 | return false; 26 | } 27 | 28 | fseek(fp, 0, SEEK_END); 29 | *size = ftell(fp); 30 | rewind(fp); 31 | *contents = malloc(*size); 32 | fread(*contents, 1, *size, fp); 33 | fclose(fp); 34 | return true; 35 | } 36 | 37 | static void parse_args(int argc, 38 | char** argv, 39 | char** entry_point_override, 40 | bool* compile_only, 41 | bool* debug_symbols, 42 | StringArray* include_paths, 43 | StringArray* input_paths) { 44 | for (int i = 1; i < argc; i++) 45 | if (take_arg(argv[i])) 46 | if (!argv[++i]) 47 | usage(1); 48 | 49 | for (int i = 1; i < argc; i++) { 50 | if (!strncmp(argv[i], "-I", 2)) { 51 | strarray_push(include_paths, argv[i] + 2, AL_Link); 52 | continue; 53 | } 54 | 55 | if (!strncmp(argv[i], "-e", 2)) { 56 | *entry_point_override = argv[i] + 2; 57 | continue; 58 | } 59 | 60 | if (!strncmp(argv[i], "-c", 2)) { 61 | *compile_only = true; 62 | continue; 63 | } 64 | 65 | if (!strncmp(argv[i], "-g", 2)) { 66 | *debug_symbols = true; 67 | continue; 68 | } 69 | 70 | if (!strcmp(argv[i], "--help")) 71 | usage(0); 72 | 73 | if (argv[i][0] == '-' && argv[i][1] != '\0') { 74 | printf("unknown argument: %s\n", argv[i]); 75 | usage(1); 76 | } 77 | 78 | strarray_push(input_paths, argv[i], AL_Link); 79 | } 80 | 81 | if (input_paths->len == 0) { 82 | printf("no input files\n"); 83 | usage(1); 84 | } 85 | } 86 | 87 | #if X64WIN 88 | extern _Bool __stdcall SetConsoleMode(void*, int); 89 | extern void* __stdcall GetStdHandle(int); 90 | #endif 91 | 92 | int main(int argc, char** argv) { 93 | #if X64WIN 94 | SetConsoleMode(GetStdHandle(-11), 7); 95 | #endif 96 | 97 | alloc_init(AL_Link); 98 | 99 | StringArray include_paths = {0}; 100 | StringArray input_paths = {0}; 101 | char* entry_point_override = "main"; 102 | bool compile_only = false; 103 | bool debug_symbols = false; 104 | parse_args(argc, argv, &entry_point_override, &compile_only, &debug_symbols, &include_paths, 105 | &input_paths); 106 | strarray_push(&include_paths, NULL, AL_Link); 107 | strarray_push(&input_paths, NULL, AL_Link); 108 | 109 | DyibiccEnviromentData env_data = { 110 | .include_paths = (const char**)include_paths.data, 111 | .files = (const char**)input_paths.data, 112 | .load_file_contents = read_file, 113 | .get_function_address = NULL, 114 | .output_function = NULL, 115 | .use_ansi_codes = isatty(fileno(stdout)), 116 | .generate_debug_symbols = debug_symbols, 117 | }; 118 | 119 | DyibiccContext* ctx = dyibicc_set_environment(&env_data); 120 | 121 | alloc_reset(AL_Link); 122 | 123 | int result = 0; 124 | 125 | if (dyibicc_update(ctx, NULL, NULL)) { 126 | void* entry_point = dyibicc_find_export(ctx, entry_point_override); 127 | if (entry_point) { 128 | if (compile_only) { 129 | // Only doing a syntax check, just return 0 without running if we made it this far. 130 | result = 0; 131 | } else { 132 | int myargc = 1; 133 | char* myargv[] = {"prog", NULL}; 134 | result = ((int (*)(int, char**))entry_point)(myargc, myargv); 135 | } 136 | } else { 137 | printf("no entry point found\n"); 138 | result = 254; 139 | } 140 | } else { 141 | result = 255; 142 | } 143 | 144 | dyibicc_free(ctx); 145 | 146 | return result; 147 | } 148 | -------------------------------------------------------------------------------- /src/fuzz_entry.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "libdyibicc.h" 6 | 7 | static bool read_file(const char* path, char** contents, size_t* size) { 8 | FILE* fp = fopen(path, "rb"); 9 | if (!fp) { 10 | return false; 11 | } 12 | 13 | fseek(fp, 0, SEEK_END); 14 | *size = ftell(fp); 15 | rewind(fp); 16 | *contents = malloc(*size); 17 | fread(*contents, 1, *size, fp); 18 | fclose(fp); 19 | return true; 20 | } 21 | 22 | int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { 23 | const char* no_include_paths[] = {NULL}; 24 | const char* one_input_path[] = {"fuzz.c", NULL}; 25 | DyibiccEnviromentData env_data = { 26 | .include_paths = no_include_paths, 27 | .files = one_input_path, 28 | .load_file_contents = read_file, 29 | .get_function_address = NULL, 30 | .output_function = NULL, 31 | .use_ansi_codes = false, 32 | }; 33 | 34 | DyibiccContext* ctx = dyibicc_set_environment(&env_data); 35 | 36 | char* data_copy = malloc(Size + 1); 37 | memcpy(data_copy, Data, Size); 38 | data_copy[Size] = 0; 39 | dyibicc_update(ctx, "fuzz.c", data_copy); 40 | dyibicc_free(ctx); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /src/hashmap.c: -------------------------------------------------------------------------------- 1 | // This is an implementation of the open-addressing hash table. 2 | 3 | #include "dyibicc.h" 4 | 5 | // Initial hash bucket size 6 | #define INIT_SIZE 16 7 | 8 | // Rehash if the usage exceeds 70%. 9 | #define HIGH_WATERMARK 70 10 | 11 | // We'll keep the usage below 50% after rehashing. 12 | #define LOW_WATERMARK 50 13 | 14 | // Represents a deleted hash entry 15 | #define TOMBSTONE ((void*)-1) 16 | 17 | static uint64_t fnv_hash(char* s, int len) { 18 | uint64_t hash = 0xcbf29ce484222325; 19 | for (int i = 0; i < len; i++) { 20 | hash *= 0x100000001b3; 21 | hash ^= (unsigned char)s[i]; 22 | } 23 | return hash; 24 | } 25 | 26 | // Make room for new entires in a given hashmap by removing 27 | // tombstones and possibly extending the bucket size. 28 | static void rehash(HashMap* map) { 29 | // Compute the size of the new hashmap. 30 | int nkeys = 0; 31 | for (int i = 0; i < map->capacity; i++) 32 | if (map->buckets[i].key && map->buckets[i].key != TOMBSTONE) 33 | nkeys++; 34 | 35 | int cap = map->capacity; 36 | while ((nkeys * 100) / cap >= LOW_WATERMARK) 37 | cap = cap * 2; 38 | assert(cap > 0); 39 | 40 | // Create a new hashmap and copy all key-values. 41 | HashMap map2 = {0}; 42 | map2.buckets = bumpcalloc(cap, sizeof(HashEntry), map->alloc_lifetime); 43 | map2.capacity = cap; 44 | map2.alloc_lifetime = map->alloc_lifetime; 45 | 46 | for (int i = 0; i < map->capacity; i++) { 47 | HashEntry* ent = &map->buckets[i]; 48 | if (ent->key && ent->key != TOMBSTONE) { 49 | hashmap_put2(&map2, ent->key, ent->keylen, ent->val); 50 | } 51 | } 52 | 53 | assert(map2.used == nkeys); 54 | if (map->alloc_lifetime == AL_Manual) { 55 | alloc_free(map->buckets, map->alloc_lifetime); 56 | } 57 | *map = map2; 58 | } 59 | 60 | static bool match(HashEntry* ent, char* key, int keylen) { 61 | return ent->key && ent->key != TOMBSTONE && ent->keylen == keylen && 62 | memcmp(ent->key, key, keylen) == 0; 63 | } 64 | 65 | static HashEntry* get_entry(HashMap* map, char* key, int keylen) { 66 | if (!map->buckets) 67 | return NULL; 68 | 69 | uint64_t hash = fnv_hash(key, keylen); 70 | 71 | for (int i = 0; i < map->capacity; i++) { 72 | HashEntry* ent = &map->buckets[(hash + i) % map->capacity]; 73 | if (match(ent, key, keylen)) 74 | return ent; 75 | if (ent->key == NULL) 76 | return NULL; 77 | } 78 | unreachable(); 79 | } 80 | 81 | static HashEntry* get_or_insert_entry(HashMap* map, char* key, int keylen) { 82 | if (!map->buckets) { 83 | map->buckets = bumpcalloc(INIT_SIZE, sizeof(HashEntry), map->alloc_lifetime); 84 | map->capacity = INIT_SIZE; 85 | } else if ((map->used * 100) / map->capacity >= HIGH_WATERMARK) { 86 | rehash(map); 87 | } 88 | 89 | uint64_t hash = fnv_hash(key, keylen); 90 | 91 | for (int i = 0; i < map->capacity; i++) { 92 | HashEntry* ent = &map->buckets[(hash + i) % map->capacity]; 93 | 94 | if (match(ent, key, keylen)) { 95 | if (map->alloc_lifetime == AL_Manual) { 96 | free(ent->key); 97 | } 98 | ent->key = key; 99 | return ent; 100 | } 101 | 102 | // It is tempting to allow a TOMBSTONE entry to be reused here, but they 103 | // cannot be, see: https://github.com/rui314/chibicc/issues/135. 104 | 105 | if (ent->key == NULL) { 106 | ent->key = key; 107 | ent->keylen = keylen; 108 | map->used++; 109 | return ent; 110 | } 111 | } 112 | unreachable(); 113 | } 114 | 115 | IMPLSTATIC void* hashmap_get(HashMap* map, char* key) { 116 | return hashmap_get2(map, key, (int)strlen(key)); 117 | } 118 | 119 | IMPLSTATIC void* hashmap_get2(HashMap* map, char* key, int keylen) { 120 | HashEntry* ent = get_entry(map, key, keylen); 121 | return ent ? ent->val : NULL; 122 | } 123 | 124 | IMPLSTATIC void hashmap_put(HashMap* map, char* key, void* val) { 125 | hashmap_put2(map, key, (int)strlen(key), val); 126 | } 127 | 128 | IMPLSTATIC void hashmap_put2(HashMap* map, char* key, int keylen, void* val) { 129 | HashEntry* ent = get_or_insert_entry(map, key, keylen); 130 | ent->val = val; 131 | } 132 | 133 | IMPLSTATIC void hashmap_delete(HashMap* map, char* key) { 134 | hashmap_delete2(map, key, (int)strlen(key)); 135 | } 136 | 137 | IMPLSTATIC void hashmap_delete2(HashMap* map, char* key, int keylen) { 138 | HashEntry* ent = get_entry(map, key, keylen); 139 | if (ent) { 140 | if (map->alloc_lifetime == AL_Manual) { 141 | free(ent->key); 142 | } 143 | ent->key = TOMBSTONE; 144 | } 145 | } 146 | 147 | // keys strdup'd with AL_Manual, and values that are the data segment 148 | // allocations allocated by aligned_allocate. 149 | IMPLSTATIC void hashmap_clear_manual_key_owned_value_owned_aligned(HashMap* map) { 150 | assert(map->alloc_lifetime == AL_Manual); 151 | for (int i = 0; i < map->capacity; i++) { 152 | HashEntry* ent = &map->buckets[i]; 153 | if (ent->key && ent->key != TOMBSTONE) { 154 | alloc_free(ent->key, map->alloc_lifetime); 155 | aligned_free(ent->val); 156 | } 157 | } 158 | alloc_free(map->buckets, map->alloc_lifetime); 159 | map->buckets = NULL; 160 | map->used = 0; 161 | map->capacity = 0; 162 | } 163 | 164 | // keys strdup'd with AL_Manual, and values that point into the codeseg, so 165 | // aren't freed. 166 | IMPLSTATIC void hashmap_clear_manual_key_owned_value_unowned(HashMap* map) { 167 | assert(map->alloc_lifetime == AL_Manual); 168 | for (int i = 0; i < map->capacity; i++) { 169 | HashEntry* ent = &map->buckets[i]; 170 | if (ent->key && ent->key != TOMBSTONE) { 171 | alloc_free(ent->key, map->alloc_lifetime); 172 | // ent->val points into codeseg, not to be freed here. 173 | } 174 | } 175 | alloc_free(map->buckets, map->alloc_lifetime); 176 | map->buckets = NULL; 177 | map->used = 0; 178 | map->capacity = 0; 179 | } 180 | -------------------------------------------------------------------------------- /src/libdyibicc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // ANSI escape code decoding expected if use_ansi_codes is set in 8 | // DyibiccEnviromentData. 9 | typedef int (*DyibiccOutputFn)(const char* fmt, va_list ap); 10 | 11 | // Returns the address of a function by name. 12 | typedef void* (*DyibiccFunctionLookupFn)(const char* name); 13 | 14 | // Get the current contents of a file. Should return true on success with 15 | // *contents and *size filled out. *contents need not be null-terminated. 16 | // *contents ownership is taken and will be free()d. 17 | typedef bool (*DyibiccLoadFileContents)(const char* filename, char** contents, size_t* size); 18 | 19 | typedef struct DyibiccEnviromentData { 20 | // NULL-terminated list of user include paths to search. 21 | const char** include_paths; 22 | 23 | // NULL-terminated list of .c files to include in the project. 24 | const char** files; 25 | 26 | // Load the contents of a file by name (typically from disk). 27 | // 28 | // NOTE/TODO: There is currently a gotcha with this callback. It is used in 29 | // two situations: 30 | // 31 | // 1) for loading all .c files on initial startup (when update is 32 | // typically called as `dyibicc_update(ctx, NULL, NULL)`), and; 33 | // 34 | // 2) at all times when files are loaded by an `#include`. 35 | // 36 | // However! If a single file and contents are provided to update via 37 | // `dyibicc_update(ctx, "myfile.c", "...contents...")`, then the contents will 38 | // be used directly and there will be no callback to this function. 39 | DyibiccLoadFileContents load_file_contents; 40 | 41 | // Should resolve a function by name, for symbols that aren't defined by code 42 | // in |files|. i.e. to call system functionality. 43 | DyibiccFunctionLookupFn get_function_address; 44 | 45 | // Customizable output, all output from compiler error messages, etc. will be 46 | // vectored through this function. 47 | DyibiccOutputFn output_function; 48 | 49 | // Are simple ANSI colours supported by |output_function|. 50 | bool use_ansi_codes; 51 | 52 | // Should debug symbols (pdb) be generated. Only implemented on Windows. 53 | bool generate_debug_symbols; 54 | 55 | bool padding[6]; // Avoid C4820 padding warning on MSVC /Wall. 56 | } DyibiccEnviromentData; 57 | 58 | typedef struct DyibiccContext DyibiccContext; 59 | 60 | // Sets up the environment for the compiler. There can currently only be a 61 | // single active DyibiccContext, despite the implication that there could be 62 | // multiple. See notes in the structure about how it should be filled out. 63 | DyibiccContext* dyibicc_set_environment(DyibiccEnviromentData* env_data); 64 | 65 | // Called once on initialization with a file == NULL and contents == NULL, and 66 | // subsequently whenever any file contents are updated and the running code 67 | // should be recompiled/relinked. 68 | bool dyibicc_update(DyibiccContext* context, char* file, char* contents); 69 | 70 | // After a successful call to dyibicc_update(), retrieve the address of a 71 | // non-static function to call it. The returned function address cannot be 72 | // cached across dyibicc_update() calls. 73 | void* dyibicc_find_export(DyibiccContext* context, char* name); 74 | 75 | // Free all memory associated with the compiler context. 76 | void dyibicc_free(DyibiccContext* context); 77 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "dyibicc.h" 2 | 3 | #if X64WIN 4 | #include 5 | #endif 6 | 7 | #define C(x) compiler_state.main__##x 8 | #define L(x) linker_state.main__##x 9 | 10 | #if 0 // for -E call after preprocess(). 11 | static void print_tokens(Token* tok) { 12 | int line = 1; 13 | for (; tok->kind != TK_EOF; tok = tok->next) { 14 | if (line > 1 && tok->at_bol) 15 | printf("\n"); 16 | if (tok->has_space && !tok->at_bol) 17 | printf(" "); 18 | printf("%.*s", tok->len, tok->loc); 19 | line++; 20 | } 21 | printf("\n"); 22 | } 23 | #endif 24 | 25 | static int default_output_fn(const char* fmt, va_list ap) { 26 | int ret = vfprintf(stdout, fmt, ap); 27 | return ret; 28 | } 29 | 30 | static bool default_load_file_fn(const char* path, char** contents, size_t* size) { 31 | FILE* fp = fopen(path, "rb"); 32 | if (!fp) { 33 | return false; 34 | } 35 | 36 | fseek(fp, 0, SEEK_END); 37 | *size = ftell(fp); 38 | rewind(fp); 39 | *contents = malloc(*size); 40 | fread(*contents, 1, *size, fp); 41 | fclose(fp); 42 | return true; 43 | } 44 | 45 | DyibiccContext* dyibicc_set_environment(DyibiccEnviromentData* env_data) { 46 | // Set this up with a temporary value early, mostly so we can ABORT() below 47 | // with output if necessary. 48 | UserContext tmp_user_context = {0}; 49 | tmp_user_context.output_function = default_output_fn; 50 | tmp_user_context.load_file_contents = default_load_file_fn; 51 | user_context = &tmp_user_context; 52 | 53 | alloc_init(AL_Temp); 54 | 55 | // Clone env_data into allocated ctx 56 | 57 | size_t total_include_paths_len = 0; 58 | size_t num_include_paths = 0; 59 | for (const char** p = env_data->include_paths; *p; ++p) { 60 | total_include_paths_len += strlen(*p) + 1; 61 | ++num_include_paths; 62 | } 63 | 64 | StringArray sys_inc_paths = {0}; 65 | #if X64WIN 66 | strarray_push(&sys_inc_paths, "__include__/win", AL_Temp); 67 | strarray_push(&sys_inc_paths, "__include__/all", AL_Temp); 68 | 69 | #define GET_ENV_VAR(x) \ 70 | char* env_##x = getenv(#x); \ 71 | if (!env_##x) { \ 72 | ABORT("environment variable " #x " unset"); \ 73 | } 74 | 75 | GET_ENV_VAR(WindowsSdkDir); 76 | GET_ENV_VAR(WindowsSdkLibVersion); 77 | GET_ENV_VAR(VcToolsInstallDir); 78 | #undef GET_ENV_VAR 79 | 80 | strarray_push(&sys_inc_paths, 81 | format(AL_Temp, "%sInclude\\%sucrt", env_WindowsSdkDir, env_WindowsSdkLibVersion), 82 | AL_Temp); 83 | strarray_push(&sys_inc_paths, 84 | format(AL_Temp, "%sInclude\\%sum", env_WindowsSdkDir, env_WindowsSdkLibVersion), 85 | AL_Temp); 86 | strarray_push(&sys_inc_paths, 87 | format(AL_Temp, "%sInclude\\%sshared", env_WindowsSdkDir, env_WindowsSdkLibVersion), 88 | AL_Temp); 89 | strarray_push(&sys_inc_paths, format(AL_Temp, "%sinclude", env_VcToolsInstallDir), AL_Temp); 90 | 91 | #else 92 | strarray_push(&sys_inc_paths, "__include__/linux", AL_Temp); 93 | strarray_push(&sys_inc_paths, "__include__/all", AL_Temp); 94 | 95 | strarray_push(&sys_inc_paths, "/usr/local/include", AL_Temp); 96 | strarray_push(&sys_inc_paths, "/usr/include/x86_64-linux-gnu", AL_Temp); 97 | strarray_push(&sys_inc_paths, "/usr/include", AL_Temp); 98 | #endif 99 | 100 | for (int i = 0; i < sys_inc_paths.len; ++i) { 101 | total_include_paths_len += strlen(sys_inc_paths.data[i]) + 1; 102 | ++num_include_paths; 103 | } 104 | 105 | // Don't currently need dyibicc_include_dir once sys_inc_paths are added to. 106 | 107 | size_t total_source_files_len = 0; 108 | size_t num_files = 0; 109 | for (const char** p = env_data->files; *p; ++p) { 110 | total_source_files_len += strlen(*p) + 1; 111 | ++num_files; 112 | } 113 | 114 | size_t total_size = 115 | sizeof(UserContext) + // base structure 116 | (num_include_paths * sizeof(char*)) + // array in base structure 117 | (num_files * sizeof(FileLinkData)) + // array in base structure 118 | (total_include_paths_len * sizeof(char)) + // pointed to by include_paths 119 | (total_source_files_len * sizeof(char)) + // pointed to by FileLinkData.source_name 120 | ((num_files + 1) * sizeof(HashMap)) + // +1 beyond num_files for fully global dataseg 121 | ((num_files + 1) * sizeof(HashMap)) // +1 beyond num_files for fully global exports 122 | ; 123 | 124 | UserContext* data = calloc(1, total_size); 125 | 126 | data->load_file_contents = env_data->load_file_contents; 127 | if (!data->load_file_contents) { 128 | data->load_file_contents = default_load_file_fn; 129 | } 130 | data->get_function_address = env_data->get_function_address; 131 | data->output_function = env_data->output_function; 132 | if (!data->output_function) { 133 | data->output_function = default_output_fn; 134 | } 135 | data->use_ansi_codes = env_data->use_ansi_codes; 136 | data->generate_debug_symbols = env_data->generate_debug_symbols; 137 | 138 | char* d = (char*)(&data[1]); 139 | 140 | data->num_include_paths = num_include_paths; 141 | data->include_paths = (char**)d; 142 | d += sizeof(char*) * num_include_paths; 143 | 144 | data->num_files = num_files; 145 | data->files = (FileLinkData*)d; 146 | d += sizeof(FileLinkData) * num_files; 147 | 148 | data->global_data = (HashMap*)d; 149 | d += sizeof(HashMap) * (num_files + 1); 150 | 151 | data->exports = (HashMap*)d; 152 | d += sizeof(HashMap) * (num_files + 1); 153 | 154 | int i = 0; 155 | for (const char** p = env_data->include_paths; *p; ++p) { 156 | data->include_paths[i++] = d; 157 | strcpy(d, *p); 158 | d += strlen(*p) + 1; 159 | } 160 | for (int j = 0; j < sys_inc_paths.len; ++j) { 161 | data->include_paths[i++] = d; 162 | strcpy(d, sys_inc_paths.data[j]); 163 | d += strlen(sys_inc_paths.data[j]) + 1; 164 | } 165 | 166 | i = 0; 167 | for (const char** p = env_data->files; *p; ++p) { 168 | FileLinkData* dld = &data->files[i++]; 169 | dld->source_name = d; 170 | strcpy(dld->source_name, *p); 171 | d += strlen(*p) + 1; 172 | } 173 | 174 | // These maps store an arbitrary number of symbols, and they must persist 175 | // beyond AL_Link (to be saved for relink updates) so they must be manually 176 | // managed. 177 | for (size_t j = 0; j < num_files + 1; ++j) { 178 | data->global_data[j].alloc_lifetime = AL_Manual; 179 | data->exports[j].alloc_lifetime = AL_Manual; 180 | } 181 | data->reflect_types.alloc_lifetime = AL_UserContext; 182 | 183 | if ((size_t)(d - (char*)data) != total_size) { 184 | ABORT("incorrect size calculation"); 185 | } 186 | 187 | user_context = data; 188 | alloc_reset(AL_Temp); 189 | alloc_init(AL_UserContext); 190 | return (DyibiccContext*)data; 191 | } 192 | 193 | void dyibicc_free(DyibiccContext* context) { 194 | UserContext* ctx = (UserContext*)context; 195 | assert(ctx == user_context && "only one context currently supported"); 196 | for (size_t i = 0; i < ctx->num_files + 1; ++i) { 197 | hashmap_clear_manual_key_owned_value_owned_aligned(&ctx->global_data[i]); 198 | hashmap_clear_manual_key_owned_value_unowned(&ctx->exports[i]); 199 | } 200 | alloc_reset(AL_UserContext); 201 | 202 | for (size_t i = 0; i < ctx->num_files; ++i) { 203 | free_link_fixups(&ctx->files[i]); 204 | } 205 | #if X64WIN 206 | unregister_and_free_function_table_data(ctx); 207 | #endif 208 | free(ctx); 209 | user_context = NULL; 210 | } 211 | 212 | bool dyibicc_update(DyibiccContext* context, char* filename, char* contents) { 213 | if (setjmp(toplevel_update_jmpbuf) != 0) { 214 | codegen_free(); 215 | alloc_reset(AL_Compile); 216 | alloc_reset(AL_Temp); 217 | alloc_reset(AL_Link); 218 | memset(&compiler_state, 0, sizeof(compiler_state)); 219 | memset(&linker_state, 0, sizeof(linker_state)); 220 | return false; 221 | } 222 | 223 | UserContext* ctx = (UserContext*)context; 224 | bool link_result = true; 225 | 226 | assert(ctx == user_context && "only one context currently supported"); 227 | 228 | bool compiled_any = false; 229 | { 230 | for (size_t i = 0; i < ctx->num_files; ++i) { 231 | FileLinkData* dld = &ctx->files[i]; 232 | 233 | if (filename && strcmp(dld->source_name, filename) != 0) { 234 | // If a specific update is provided, we only compile that one. 235 | continue; 236 | } 237 | 238 | { 239 | alloc_init(AL_Compile); 240 | 241 | init_macros(); 242 | C(base_file) = dld->source_name; 243 | Token* tok; 244 | if (filename) { 245 | tok = tokenize_filecontents(filename, contents); 246 | } else { 247 | tok = tokenize_file(C(base_file)); 248 | } 249 | if (!tok) 250 | error("%s: %s", C(base_file), strerror(errno)); 251 | tok = preprocess(tok); 252 | tok = add_container_instantiations(tok); 253 | 254 | codegen_init(); // Initializes dynasm so that parse() can assign labels. 255 | 256 | Obj* prog = parse(tok); 257 | codegen(prog, i); 258 | 259 | compiled_any = true; 260 | 261 | alloc_reset(AL_Compile); 262 | } 263 | } 264 | 265 | if (compiled_any) { 266 | alloc_init(AL_Link); 267 | 268 | link_result = link_all_files(); 269 | 270 | alloc_reset(AL_Link); 271 | } 272 | } 273 | 274 | return link_result; 275 | } 276 | 277 | void* dyibicc_find_export(DyibiccContext* context, char* name) { 278 | UserContext* ctx = (UserContext*)context; 279 | return hashmap_get(&ctx->exports[ctx->num_files], name); 280 | } 281 | -------------------------------------------------------------------------------- /src/regen_container_headers.py: -------------------------------------------------------------------------------- 1 | # A bunch of hacks to get the magic containers we support built from the 2 | # standard STC distribution. 3 | # 4 | # Use STC's singleheader.py to get a standalone header for each of our containers 5 | # Remove all system <> includes (stdint, inttypes, etc.) 6 | # Rewrite c_assert (it's messy to include and our test code uses its own assert). 7 | # undef __attribute__ since some sys/cdefs.h defines __attribute__ away (o_O) 8 | # Add __attribute__((methodcall(...))) to appropriate places 9 | # Replace bool with _Bool (as stdbool is a define not a typedef) 10 | # Prefix in whatever prototypes/typedefs are necessary/were lost from dropping includes above 11 | # TODO: Strip the __cplusplus branches 12 | 13 | import os 14 | import re 15 | import subprocess 16 | import sys 17 | 18 | REPOROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 19 | SCRIPT_NAME = os.path.basename(__file__) 20 | 21 | COMMON_PREFIX = [ 22 | "// BEGIN " + SCRIPT_NAME + "\n", 23 | "#ifndef __dyibicc_internal_include__\n", 24 | "#error Can only be included by the compiler, or confusing errors will result!\n", 25 | "#endif\n", 26 | "#undef __attribute__\n", 27 | "typedef unsigned char uint8_t;\n", 28 | "typedef unsigned long long uint64_t;\n", 29 | "typedef unsigned long long size_t;\n", 30 | "typedef long long int64_t;\n", 31 | "typedef long long intptr_t;\n", 32 | "typedef unsigned int uint32_t;\n", 33 | "void* memset(void* dest, int ch, size_t count);\n", 34 | "void* memcpy(void* dest, const void* src, size_t count);\n", 35 | "int memcmp(const void* lhs, const void* rhs, size_t count);\n", 36 | "void* memmove(void* dest, const void* src, size_t count);\n", 37 | "size_t strlen(const char* str);\n", 38 | "void free(void* ptr);\n", 39 | "void *malloc(size_t size);\n", 40 | "void *calloc(size_t num, size_t size);\n", 41 | "void* realloc(void* ptr, size_t new_size);\n", 42 | "#define NULL ((void*)0) /* todo! */\n", 43 | "// ### END " + SCRIPT_NAME + "\n", 44 | ] 45 | 46 | CONTAINERS = [ 47 | { 48 | "in": os.path.join(REPOROOT, "scratch/STC/include/stc/cvec.h"), 49 | "out": os.path.join(REPOROOT, "include/all/_vec.h"), 50 | "prefix": COMMON_PREFIX, 51 | }, 52 | { 53 | "in": os.path.join(REPOROOT, "scratch/STC/include/stc/cmap.h"), 54 | "out": os.path.join(REPOROOT, "include/all/_map.h"), 55 | "prefix": COMMON_PREFIX, 56 | }, 57 | ] 58 | 59 | def main(): 60 | os.chdir(REPOROOT) 61 | if not os.path.isdir("scratch"): 62 | os.makedirs("scratch") 63 | os.chdir("scratch") 64 | subprocess.run(["git", "clone", "https://github.com/stclib/STC.git", "STC"]) 65 | os.chdir("STC") 66 | 67 | for c in CONTAINERS: 68 | subprocess.run([sys.executable, "src/singleheader.py", 69 | c["in"], c["out"]]) 70 | with open(c["out"], "r", encoding="utf-8") as f: 71 | contents = f.readlines() 72 | munged = [] 73 | for line in contents: 74 | 75 | m_inc = re.search('^\\s*# *include\\s*[<"](.+)[>"]', line) 76 | if m_inc: 77 | line = '// EXCLUDED BY %s' % SCRIPT_NAME + ' ' + line 78 | 79 | m_typedef = re.search('^\\s*typedef struct SELF {(.*)', line) 80 | if m_typedef: 81 | line = ('typedef struct __attribute__((methodcall(SELF##_))) SELF {' + 82 | m_typedef.group(1) + '\n') 83 | 84 | line = re.sub(r'\bbool\b', '_Bool', line) 85 | line = re.sub(r'\bfalse\b', '0', line) 86 | line = re.sub(r'\btrue\b', '1', line) 87 | 88 | m_defassert = re.search('^\\s*#define\\s+c_assert.*assert.*', line) 89 | if m_defassert: 90 | line = ('#ifndef STC_ASSERT\n' 91 | '#define STC_ASSERT(expr)\n' 92 | '#endif\n' 93 | ' #define c_assert(expr) STC_ASSERT(expr)\n') 94 | 95 | munged.append(line) 96 | contents = c["prefix"] + munged 97 | with open(c["out"], "w", encoding="utf-8", newline="\n") as f: 98 | f.writelines(contents) 99 | 100 | return 0 101 | 102 | if __name__ == "__main__": 103 | sys.exit(main()) 104 | -------------------------------------------------------------------------------- /src/testrun.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | import os 4 | import subprocess 5 | import sys 6 | 7 | def main(): 8 | root = os.path.abspath(sys.argv[1]) 9 | os.chdir(root) 10 | 11 | ccbin = os.path.abspath(sys.argv[2].replace('/', os.path.sep)) 12 | js = base64.b64decode(bytes(sys.argv[3], encoding='utf-8')) 13 | cmds = json.loads(js) 14 | 15 | # The default is 1, but we would prefer something a little more distinct. Windows will 16 | # return the exception code (big number) so out of 0..255, but Linux is always in that 17 | # range, so select something arbitrary as an ASAN signal that's more notable than the 18 | # default of 1. 19 | env = os.environ.copy() 20 | env['ASAN_OPTIONS'] = 'exitcode=117' 21 | 22 | if cmds['txt']: 23 | res = subprocess.run([ccbin] + cmds['run'].split(' '), cwd=root, capture_output=True, 24 | universal_newlines=True, env=env) 25 | out = res.stdout 26 | if out != cmds['txt']: 27 | print('got output:\n') 28 | print(out) 29 | print('but expected:\n') 30 | print(cmds['txt']) 31 | return 1 32 | else: 33 | res = subprocess.run([ccbin] + cmds['run'].split(' '), cwd=root, env=env) 34 | 35 | if cmds['ret'] == 'NOCRASH': 36 | if res.returncode >= 0 and res.returncode <= 255 and res.returncode != 117: 37 | return 0 38 | # Something out of range indicates crash, e.g Windows returns -1073741819 on GPF. 39 | # Linux is always in the range 0..255, so pick 117 arbitrarily for an ASAN signal. 40 | return 2 41 | else: 42 | if res.returncode != cmds['ret']: 43 | print('got return code %d, but expected %d' % (res.returncode, cmds['ret'])) 44 | return 2 45 | 46 | 47 | if __name__ == '__main__': 48 | sys.exit(main()) 49 | -------------------------------------------------------------------------------- /src/unicode.c: -------------------------------------------------------------------------------- 1 | #include "dyibicc.h" 2 | 3 | // Encode a given character in UTF-8. 4 | IMPLSTATIC int encode_utf8(char* buf, uint32_t c) { 5 | if (c <= 0x7F) { 6 | buf[0] = (char)c; 7 | return 1; 8 | } 9 | 10 | if (c <= 0x7FF) { 11 | buf[0] = (char)(0b11000000 | (c >> 6)); 12 | buf[1] = (char)(0b10000000 | (c & 0b00111111)); 13 | return 2; 14 | } 15 | 16 | if (c <= 0xFFFF) { 17 | buf[0] = (char)(0b11100000 | (c >> 12)); 18 | buf[1] = (char)(0b10000000 | ((c >> 6) & 0b00111111)); 19 | buf[2] = (char)(0b10000000 | (c & 0b00111111)); 20 | return 3; 21 | } 22 | 23 | buf[0] = (char)(0b11110000 | (c >> 18)); 24 | buf[1] = (char)(0b10000000 | ((c >> 12) & 0b00111111)); 25 | buf[2] = (char)(0b10000000 | ((c >> 6) & 0b00111111)); 26 | buf[3] = (char)(0b10000000 | (c & 0b00111111)); 27 | return 4; 28 | } 29 | 30 | // Read a UTF-8-encoded Unicode code point from a source file. 31 | // We assume that source files are always in UTF-8. 32 | // 33 | // UTF-8 is a variable-width encoding in which one code point is 34 | // encoded in one to four bytes. One byte UTF-8 code points are 35 | // identical to ASCII. Non-ASCII characters are encoded using more 36 | // than one byte. 37 | IMPLSTATIC uint32_t decode_utf8(char** new_pos, char* p) { 38 | if ((unsigned char)*p < 128) { 39 | *new_pos = p + 1; 40 | return *p; 41 | } 42 | 43 | char* start = p; 44 | int len; 45 | uint32_t c; 46 | 47 | if ((unsigned char)*p >= 0b11110000) { 48 | len = 4; 49 | c = *p & 0b111; 50 | } else if ((unsigned char)*p >= 0b11100000) { 51 | len = 3; 52 | c = *p & 0b1111; 53 | } else if ((unsigned char)*p >= 0b11000000) { 54 | len = 2; 55 | c = *p & 0b11111; 56 | } else { 57 | error_at(start, "invalid UTF-8 sequence"); 58 | } 59 | 60 | for (int i = 1; i < len; i++) { 61 | if ((unsigned char)p[i] >> 6 != 0b10) 62 | error_at(start, "invalid UTF-8 sequence"); 63 | c = (c << 6) | (p[i] & 0b111111); 64 | } 65 | 66 | *new_pos = p + len; 67 | return c; 68 | } 69 | 70 | static bool in_range(uint32_t* range, uint32_t c) { 71 | for (uint32_t i = 0; range[i] != (uint32_t)-1; i += 2) 72 | if (range[i] <= c && c <= range[i + 1]) 73 | return true; 74 | return false; 75 | } 76 | 77 | // [https://www.sigbus.info/n1570#D] C11 allows not only ASCII but 78 | // some multibyte characters in certan Unicode ranges to be used in an 79 | // identifier. 80 | // 81 | // This function returns true if a given character is acceptable as 82 | // the first character of an identifier. 83 | // 84 | // For example, ¾ (U+00BE) is a valid identifier because characters in 85 | // 0x00BE-0x00C0 are allowed, while neither ⟘ (U+27D8) nor ' ' 86 | // (U+3000, full-width space) are allowed because they are out of range. 87 | IMPLSTATIC bool is_ident1(uint32_t c) { 88 | // Slight performance improvement to early out before full test. 89 | if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) 90 | return true; 91 | 92 | static uint32_t range[] = { 93 | // '_', '_', 'a', 'z', 'A', 'Z', 94 | '$', '$', 0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF, 95 | 0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6, 0x00D8, 0x00F6, 96 | 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F, 0x1681, 0x180D, 0x180F, 0x1DBF, 97 | 0x1E00, 0x1FFF, 0x200B, 0x200D, 0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 98 | 0x2060, 0x206F, 0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793, 99 | 0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F, 0x3031, 0x303F, 100 | 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF, 0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 101 | 0xFE47, 0xFFFD, 0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD, 102 | 0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD, 0x90000, 0x9FFFD, 103 | 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD, 0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD, 104 | (uint32_t)-1, 105 | }; 106 | 107 | return in_range(range, c); 108 | } 109 | 110 | // Returns true if a given character is acceptable as a non-first 111 | // character of an identifier. 112 | IMPLSTATIC bool is_ident2(uint32_t c) { 113 | // Slight performance improvement to early out before full test. 114 | if (c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) 115 | return true; 116 | 117 | static uint32_t range[] = { 118 | '0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 119 | 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F, (uint32_t)-1, 120 | }; 121 | 122 | return is_ident1(c) || in_range(range, c); 123 | } 124 | 125 | // Returns the number of columns needed to display a given 126 | // character in a fixed-width font. 127 | // 128 | // Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c 129 | static int char_width(uint32_t c) { 130 | static uint32_t range1[] = { 131 | 0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486, 0x0488, 0x0489, 132 | 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2, 0x05C4, 0x05C5, 0x05C7, 0x05C7, 133 | 0x0600, 0x0603, 0x0610, 0x0615, 0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 134 | 0x06E7, 0x06E8, 0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A, 135 | 0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C, 0x0941, 0x0948, 136 | 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963, 0x0981, 0x0981, 0x09BC, 0x09BC, 137 | 0x09C1, 0x09C4, 0x09CD, 0x09CD, 0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 138 | 0x0A41, 0x0A42, 0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82, 139 | 0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD, 0x0AE2, 0x0AE3, 140 | 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F, 0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 141 | 0x0B56, 0x0B56, 0x0B82, 0x0B82, 0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 142 | 0x0C46, 0x0C48, 0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF, 143 | 0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43, 0x0D4D, 0x0D4D, 144 | 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6, 0x0E31, 0x0E31, 0x0E34, 0x0E3A, 145 | 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1, 0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 146 | 0x0F18, 0x0F19, 0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E, 147 | 0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC, 0x0FC6, 0x0FC6, 148 | 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037, 0x1039, 0x1039, 0x1058, 0x1059, 149 | 0x1160, 0x11FF, 0x135F, 0x135F, 0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 150 | 0x1772, 0x1773, 0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3, 151 | 0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922, 0x1927, 0x1928, 152 | 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18, 0x1B00, 0x1B03, 0x1B34, 0x1B34, 153 | 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C, 0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 154 | 0x1DFE, 0x1DFF, 0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F, 155 | 0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806, 0xA80B, 0xA80B, 156 | 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F, 0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 157 | 0xFFF9, 0xFFFB, 0x10A01, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 158 | 0x10A3F, 0x10A3F, 0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD, 159 | 0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF, (uint32_t)-1, 160 | }; 161 | 162 | if (in_range(range1, c)) 163 | return 0; 164 | 165 | static uint32_t range2[] = { 166 | 0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E, 0x3040, 0xA4CF, 167 | 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19, 0xFE30, 0xFE6F, 0xFF00, 0xFF60, 168 | 0xFFE0, 0xFFE6, 0x1F000, 0x1F644, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, (uint32_t)-1, 169 | }; 170 | 171 | if (in_range(range2, c)) 172 | return 2; 173 | return 1; 174 | } 175 | 176 | // Returns the number of columns needed to display a given 177 | // string in a fixed-width font. 178 | IMPLSTATIC int display_width(char* p, int len) { 179 | char* start = p; 180 | int w = 0; 181 | while (p - start < len) { 182 | uint32_t c = decode_utf8(&p, p); 183 | w += char_width(c); 184 | } 185 | return w; 186 | } 187 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "dyibicc.h" 2 | 3 | #ifdef _WIN64 4 | #include 5 | #else 6 | #include 7 | #include 8 | #endif 9 | 10 | IMPLSTATIC char* bumpstrndup(const char* s, size_t n, AllocLifetime lifetime) { 11 | size_t l = strnlen(s, n); 12 | char* d = bumpcalloc(1, l + 1, lifetime); 13 | if (!d) 14 | return NULL; 15 | memcpy(d, s, l); 16 | d[l] = 0; 17 | return d; 18 | } 19 | 20 | IMPLSTATIC char* bumpstrdup(const char* s, AllocLifetime lifetime) { 21 | size_t l = strlen(s); 22 | char* d = bumpcalloc(1, l + 1, lifetime); 23 | if (!d) 24 | return NULL; 25 | memcpy(d, s, l); 26 | d[l] = 0; 27 | return d; 28 | } 29 | 30 | IMPLSTATIC char* dirname(char* s) { 31 | size_t i; 32 | if (!s || !*s) 33 | return "."; 34 | i = strlen(s) - 1; 35 | for (; s[i] == '/' || s[i] == '\\'; i--) 36 | if (!i) 37 | return "/"; 38 | for (; s[i] != '/' || s[i] == '\\'; i--) 39 | if (!i) 40 | return "."; 41 | for (; s[i] == '/' || s[i] == '\\'; i--) 42 | if (!i) 43 | return "/"; 44 | s[i + 1] = 0; 45 | return s; 46 | } 47 | 48 | // Round up `n` to the nearest multiple of `align`. For instance, 49 | // align_to(5, 8) returns 8 and align_to(11, 8) returns 16. 50 | IMPLSTATIC uint64_t align_to_u(uint64_t n, uint64_t align) { 51 | return (n + align - 1) / align * align; 52 | } 53 | 54 | IMPLSTATIC int64_t align_to_s(int64_t n, int64_t align) { 55 | return (n + align - 1) / align * align; 56 | } 57 | 58 | IMPLSTATIC unsigned int get_page_size(void) { 59 | #if X64WIN 60 | SYSTEM_INFO system_info; 61 | GetSystemInfo(&system_info); 62 | return system_info.dwPageSize; 63 | #else 64 | return sysconf(_SC_PAGESIZE); 65 | #endif 66 | } 67 | 68 | IMPLSTATIC void strarray_push(StringArray* arr, char* s, AllocLifetime lifetime) { 69 | if (!arr->data) { 70 | arr->data = bumpcalloc(8, sizeof(char*), lifetime); 71 | arr->capacity = 8; 72 | } 73 | 74 | if (arr->capacity == arr->len) { 75 | arr->data = bumplamerealloc(arr->data, sizeof(char*) * arr->capacity, 76 | sizeof(char*) * arr->capacity * 2, lifetime); 77 | arr->capacity *= 2; 78 | for (int i = arr->len; i < arr->capacity; i++) 79 | arr->data[i] = NULL; 80 | } 81 | 82 | arr->data[arr->len++] = s; 83 | } 84 | 85 | IMPLSTATIC void strintarray_push(StringIntArray* arr, StringInt item, AllocLifetime lifetime) { 86 | if (!arr->data) { 87 | arr->data = bumpcalloc(8, sizeof(StringInt), lifetime); 88 | arr->capacity = 8; 89 | } 90 | 91 | if (arr->capacity == arr->len) { 92 | arr->data = bumplamerealloc(arr->data, sizeof(StringInt) * arr->capacity, 93 | sizeof(StringInt) * arr->capacity * 2, lifetime); 94 | arr->capacity *= 2; 95 | for (int i = arr->len; i < arr->capacity; i++) 96 | arr->data[i] = (StringInt){NULL, -1}; 97 | } 98 | 99 | arr->data[arr->len++] = item; 100 | } 101 | 102 | IMPLSTATIC void fileptrarray_push(FilePtrArray* arr, File* item, AllocLifetime lifetime) { 103 | if (!arr->data) { 104 | arr->data = bumpcalloc(8, sizeof(File*), lifetime); 105 | arr->capacity = 8; 106 | } 107 | 108 | if (arr->capacity == arr->len) { 109 | arr->data = bumplamerealloc(arr->data, sizeof(File*) * arr->capacity, 110 | sizeof(File*) * arr->capacity * 2, lifetime); 111 | arr->capacity *= 2; 112 | for (int i = arr->len; i < arr->capacity; i++) 113 | arr->data[i] = NULL; 114 | } 115 | 116 | arr->data[arr->len++] = item; 117 | } 118 | 119 | IMPLSTATIC void tokenptrarray_push(TokenPtrArray* arr, Token* item, AllocLifetime lifetime) { 120 | if (!arr->data) { 121 | arr->data = bumpcalloc(8, sizeof(Token*), lifetime); 122 | arr->capacity = 8; 123 | } 124 | 125 | if (arr->capacity == arr->len) { 126 | arr->data = bumplamerealloc(arr->data, sizeof(Token*) * arr->capacity, 127 | sizeof(Token*) * arr->capacity * 2, lifetime); 128 | arr->capacity *= 2; 129 | for (int i = arr->len; i < arr->capacity; i++) 130 | arr->data[i] = NULL; 131 | } 132 | 133 | arr->data[arr->len++] = item; 134 | } 135 | 136 | #if X64WIN 137 | IMPLSTATIC void intintintarray_push(IntIntIntArray* arr, IntIntInt item, AllocLifetime lifetime) { 138 | if (!arr->data) { 139 | arr->data = bumpcalloc(8, sizeof(IntIntInt), lifetime); 140 | arr->capacity = 8; 141 | } 142 | 143 | if (arr->capacity == arr->len) { 144 | arr->data = bumplamerealloc(arr->data, sizeof(IntIntInt) * arr->capacity, 145 | sizeof(IntIntInt) * arr->capacity * 2, lifetime); 146 | arr->capacity *= 2; 147 | for (int i = arr->len; i < arr->capacity; i++) 148 | arr->data[i] = (IntIntInt){-1, -1, -1}; 149 | } 150 | 151 | arr->data[arr->len++] = item; 152 | } 153 | #endif 154 | 155 | // Returns the contents of a given file. Doesn't support '-' for reading from 156 | // stdin. 157 | IMPLSTATIC char* read_file_wrap_user(char* path, AllocLifetime lifetime) { 158 | char* contents; 159 | size_t size; 160 | if (!user_context->load_file_contents(path, &contents, &size)) 161 | return NULL; 162 | 163 | char* buf = bumpcalloc(1, size + 1, lifetime); // TODO: doesn't really need a calloc 164 | memcpy(buf, contents, size); 165 | free(contents); 166 | buf[size] = 0; 167 | return buf; 168 | } 169 | 170 | // Takes a printf-style format string and returns a formatted string. 171 | IMPLSTATIC char* format(AllocLifetime lifetime, char* fmt, ...) { 172 | char buf[4096]; 173 | 174 | va_list ap; 175 | va_start(ap, fmt); 176 | vsprintf(buf, fmt, ap); 177 | va_end(ap); 178 | return bumpstrdup(buf, lifetime); 179 | } 180 | 181 | IMPLSTATIC int outaf(const char* fmt, ...) { 182 | va_list ap; 183 | va_start(ap, fmt); 184 | int ret = user_context->output_function(fmt, ap); 185 | va_end(ap); 186 | return ret; 187 | } 188 | 189 | #define ANSI_WHITE "\033[1;37m" 190 | #define ANSI_GREEN "\033[1;32m" 191 | #define ANSI_RED "\033[1;31m" 192 | #define ANSI_RESET "\033[0m" 193 | 194 | // Reports an error message in the following format. 195 | // 196 | // foo.c:10: x = y + 1; 197 | // ^ 198 | static void verror_at(char* filename, char* input, int line_no, char* loc, char* fmt, va_list ap) { 199 | // Find a line containing `loc`. 200 | char* line = loc; 201 | while (input < line && line[-1] != '\n') 202 | line--; 203 | 204 | char* end = loc; 205 | while (*end && *end != '\n') 206 | end++; 207 | 208 | // Print out the line. 209 | if (user_context->use_ansi_codes) 210 | outaf(ANSI_WHITE); 211 | 212 | int indent = outaf("%s:%d: ", filename, line_no); 213 | 214 | if (user_context->use_ansi_codes) 215 | outaf(ANSI_RESET); 216 | 217 | outaf("%.*s\n", (int)(end - line), line); 218 | 219 | // Show the error message. 220 | int pos = display_width(line, (int)(loc - line)) + indent; 221 | 222 | outaf("%*s", pos, ""); // print pos spaces. 223 | 224 | if (user_context->use_ansi_codes) 225 | outaf("%s^ %serror: %s", ANSI_GREEN, ANSI_RED, ANSI_WHITE); 226 | else 227 | outaf("^ error: "); 228 | 229 | user_context->output_function(fmt, ap); 230 | 231 | outaf("\n"); 232 | if (user_context->use_ansi_codes) 233 | outaf("%s", ANSI_RESET); 234 | } 235 | 236 | IMPLSTATIC void error_at(char* loc, char* fmt, ...) { 237 | File* cf = compiler_state.tokenize__current_file; 238 | 239 | int line_no = 1; 240 | for (char* p = cf->contents; p < loc; p++) 241 | if (*p == '\n') 242 | line_no++; 243 | 244 | va_list ap; 245 | va_start(ap, fmt); 246 | verror_at(cf->name, cf->contents, line_no, loc, fmt, ap); 247 | longjmp(toplevel_update_jmpbuf, 1); 248 | } 249 | 250 | IMPLSTATIC void error_tok(Token* tok, char* fmt, ...) { 251 | va_list ap; 252 | va_start(ap, fmt); 253 | verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, ap); 254 | longjmp(toplevel_update_jmpbuf, 1); 255 | } 256 | 257 | IMPLSTATIC void warn_tok(Token* tok, char* fmt, ...) { 258 | va_list ap; 259 | va_start(ap, fmt); 260 | verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, ap); 261 | va_end(ap); 262 | } 263 | 264 | // Reports an error and exit update. 265 | IMPLSTATIC void error(char* fmt, ...) { 266 | va_list ap; 267 | va_start(ap, fmt); 268 | if (!user_context || !user_context->output_function) { 269 | vfprintf(stderr, fmt, ap); 270 | } else { 271 | user_context->output_function(fmt, ap); 272 | outaf("\n"); 273 | } 274 | longjmp(toplevel_update_jmpbuf, 1); 275 | } 276 | 277 | IMPLSTATIC void error_internal(char* file, int line, char* msg) { 278 | outaf("%sinternal error at %s:%d: %s%s\n%s", ANSI_RED, file, line, ANSI_WHITE, msg, ANSI_RESET); 279 | longjmp(toplevel_update_jmpbuf, 1); 280 | } 281 | 282 | #if X64WIN 283 | IMPLSTATIC void register_function_table_data(UserContext* ctx, int func_count, char* base_addr) { 284 | if (ctx->generate_debug_symbols) { 285 | // We don't use RtlAddFunctionTable/RtlDeleteFunctionTable if using a pdb 286 | // dll, as the tables are already in the .pdata/.xdata section. 287 | return; 288 | } 289 | if (!RtlAddFunctionTable((RUNTIME_FUNCTION*)ctx->function_table_data, func_count, 290 | (DWORD64)base_addr)) { 291 | error("failed to RtlAddFunctionTable"); 292 | } 293 | } 294 | 295 | IMPLSTATIC void unregister_and_free_function_table_data(UserContext* ctx) { 296 | if (ctx->function_table_data) { 297 | // We don't use RtlAddFunctionTable/RtlDeleteFunctionTable if using a pdb 298 | // dll, as the tables are already in the .pdata/.xdata section. 299 | if (!ctx->generate_debug_symbols) { 300 | if (!RtlDeleteFunctionTable((RUNTIME_FUNCTION*)ctx->function_table_data)) { 301 | error("failed to RtlDeleteFunctionTable"); 302 | } 303 | } 304 | free(ctx->function_table_data); 305 | ctx->function_table_data = NULL; 306 | } 307 | } 308 | 309 | IMPLSTATIC char* get_temp_pdb_filename(AllocLifetime lifetime) { 310 | char name_template[1024] = "dyibicc-XXXXXX"; 311 | if (_mktemp_s(name_template, strlen(name_template) + 1) < 0) { 312 | error("failed to _mktemp_s"); 313 | } 314 | strcat(name_template, ".pdb"); 315 | return bumpstrdup(name_template, lifetime); 316 | } 317 | 318 | #define DYN_BASIC_PDB_IMPLEMENTATION 319 | #include "dyn_basic_pdb.h" 320 | 321 | #endif 322 | -------------------------------------------------------------------------------- /test/alignof.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int _Alignas(512) g1; 4 | int _Alignas(512) g2; 5 | char g3; 6 | int g4; 7 | long g5; 8 | char g6; 9 | 10 | int main() { 11 | ASSERT(1, _Alignof(char)); 12 | ASSERT(2, _Alignof(short)); 13 | ASSERT(4, _Alignof(int)); 14 | ASSERT(__SIZEOF_LONG__, _Alignof(long)); 15 | ASSERT(8, _Alignof(long long)); 16 | ASSERT(1, _Alignof(char[3])); 17 | ASSERT(4, _Alignof(int[3])); 18 | ASSERT(1, _Alignof(struct {char a; char b;}[2])); 19 | ASSERT(__SIZEOF_LONG__, _Alignof(struct {char a; long b;}[2])); 20 | 21 | ASSERT(1, ({ _Alignas(char) char x, y; &y-&x; })); 22 | ASSERT(__SIZEOF_LONG__, ({ _Alignas(long) char x, y; &y-&x; })); 23 | ASSERT(32, ({ _Alignas(32) char x, y; &y-&x; })); 24 | ASSERT(32, ({ _Alignas(32) int *x, *y; ((char *)&y)-((char *)&x); })); 25 | ASSERT(16, ({ struct { _Alignas(16) char x, y; } a; &a.y-&a.x; })); 26 | ASSERT(8, ({ struct T { _Alignas(8) char a; }; _Alignof(struct T); })); 27 | 28 | ASSERT(0, (long)(char *)&g1 % 512); 29 | ASSERT(0, (long)(char *)&g2 % 512); 30 | ASSERT(0, (long)(char *)&g4 % 4); 31 | ASSERT(0, (long)(char *)&g5 % 8); 32 | 33 | ASSERT(1, ({ char x; _Alignof(x); })); 34 | ASSERT(4, ({ int x; _Alignof(x); })); 35 | ASSERT(1, ({ char x; _Alignof x; })); 36 | ASSERT(4, ({ int x; _Alignof x; })); 37 | 38 | ASSERT(1, _Alignof(char) << 31 >> 31); 39 | ASSERT(1, _Alignof(char) << 63 >> 63); 40 | ASSERT(1, ({ char x; _Alignof(x) << 63 >> 63; })); 41 | 42 | ASSERT(0, ({ char x[16]; (unsigned long)&x % 16; })); 43 | ASSERT(0, ({ char x[17]; (unsigned long)&x % 16; })); 44 | ASSERT(0, ({ char x[100]; (unsigned long)&x % 16; })); 45 | ASSERT(0, ({ char x[101]; (unsigned long)&x % 16; })); 46 | 47 | printf("OK\n"); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /test/alloca.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | void *fn(int x, void *p, int y) { return p; } 4 | 5 | int main() { 6 | int i = 0; 7 | 8 | char *p1 = alloca(16); 9 | char *p2 = alloca(16); 10 | char *p3 = 1 + (char *)alloca(3) + 1; 11 | p3 -= 2; 12 | char *p4 = fn(1, alloca(16), 3); 13 | 14 | ASSERT(16, p1 - p2); 15 | ASSERT(16, p2 - p3); 16 | ASSERT(16, p3 - p4); 17 | 18 | memcpy(p1, "0123456789abcdef", 16); 19 | memcpy(p2, "ghijklmnopqrstuv", 16); 20 | memcpy(p3, "wxy", 3); 21 | memcpy(p4, "zippyzap", 8); 22 | 23 | ASSERT(0, memcmp(p1, "0123456789abcdef", 16)); 24 | ASSERT(0, memcmp(p2, "ghijklmnopqrstuv", 16)); 25 | ASSERT(0, memcmp(p3, "wxy", 3)); 26 | ASSERT(0, memcmp(p4, "zippyzap", 8)); 27 | 28 | printf("OK\n"); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /test/arith.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(0, 0); 5 | ASSERT(42, 42); 6 | ASSERT(21, 5+20-4); 7 | ASSERT(41, 12 + 34 - 5 ); 8 | ASSERT(47, 5+6*7); 9 | ASSERT(15, 5*(9-6)); 10 | ASSERT(4, (3+5)/2); 11 | ASSERT(10, -10+20); 12 | ASSERT(10, - -10); 13 | ASSERT(10, - - +10); 14 | 15 | ASSERT(0, 0==1); 16 | ASSERT(1, 42==42); 17 | ASSERT(1, 0!=1); 18 | ASSERT(0, 42!=42); 19 | 20 | ASSERT(1, 0<1); 21 | ASSERT(0, 1<1); 22 | ASSERT(0, 2<1); 23 | ASSERT(1, 0<=1); 24 | ASSERT(1, 1<=1); 25 | ASSERT(0, 2<=1); 26 | 27 | ASSERT(1, 1>0); 28 | ASSERT(0, 1>1); 29 | ASSERT(0, 1>2); 30 | ASSERT(1, 1>=0); 31 | ASSERT(1, 1>=1); 32 | ASSERT(0, 1>=2); 33 | 34 | ASSERT(0, 1073741824 * 100 / 100); 35 | 36 | ASSERT(7, ({ int i=2; i+=5; i; })); 37 | ASSERT(7, ({ int i=2; i+=5; })); 38 | ASSERT(3, ({ int i=5; i-=2; i; })); 39 | ASSERT(3, ({ int i=5; i-=2; })); 40 | ASSERT(6, ({ int i=3; i*=2; i; })); 41 | ASSERT(6, ({ int i=3; i*=2; })); 42 | ASSERT(3, ({ int i=6; i/=2; i; })); 43 | ASSERT(3, ({ int i=6; i/=2; })); 44 | 45 | ASSERT(3, ({ int i=2; ++i; })); 46 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; ++*p; })); 47 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; --*p; })); 48 | 49 | ASSERT(2, ({ int i=2; i++; })); 50 | ASSERT(2, ({ int i=2; i--; })); 51 | ASSERT(3, ({ int i=2; i++; i; })); 52 | ASSERT(1, ({ int i=2; i--; i; })); 53 | ASSERT(1, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; *p++; })); 54 | ASSERT(1, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; *p--; })); 55 | 56 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; a[0]; })); 57 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*(p--))--; a[1]; })); 58 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p)--; a[2]; })); 59 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p)--; p++; *p; })); 60 | 61 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; a[0]; })); 62 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; a[1]; })); 63 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; a[2]; })); 64 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; *p; })); 65 | 66 | ASSERT(0, !1); 67 | ASSERT(0, !2); 68 | ASSERT(1, !0); 69 | ASSERT(1, !(char)0); 70 | ASSERT(0, !(long)3); 71 | ASSERT(4, sizeof(!(char)0)); 72 | ASSERT(4, sizeof(!(long)0)); 73 | 74 | ASSERT(-1, ~0); 75 | ASSERT(0, ~-1); 76 | 77 | ASSERT(5, 17%6); 78 | ASSERT(5, ((long)17)%6); 79 | ASSERT(2, ({ int i=10; i%=4; i; })); 80 | ASSERT(2, ({ long i=10; i%=4; i; })); 81 | 82 | ASSERT(0, 0&1); 83 | ASSERT(1, 3&1); 84 | ASSERT(3, 7&3); 85 | ASSERT(10, -1&10); 86 | 87 | ASSERT(1, 0|1); 88 | ASSERT(0b10011, 0b10000|0b00011); 89 | 90 | ASSERT(0, 0^0); 91 | ASSERT(0, 0b1111^0b1111); 92 | ASSERT(0b110100, 0b111000^0b001100); 93 | 94 | ASSERT(2, ({ int i=6; i&=3; i; })); 95 | ASSERT(7, ({ int i=6; i|=3; i; })); 96 | ASSERT(10, ({ int i=15; i^=5; i; })); 97 | 98 | ASSERT(1, 1<<0); 99 | ASSERT(8, 1<<3); 100 | ASSERT(10, 5<<1); 101 | ASSERT(2, 5>>1); 102 | ASSERT(-1, -1>>1); 103 | ASSERT(1, ({ int i=1; i<<=0; i; })); 104 | ASSERT(8, ({ int i=1; i<<=3; i; })); 105 | ASSERT(10, ({ int i=5; i<<=1; i; })); 106 | ASSERT(2, ({ int i=5; i>>=1; i; })); 107 | ASSERT(-1, -1); 108 | ASSERT(-1, ({ int i=-1; i; })); 109 | ASSERT(-1, ({ int i=-1; i>>=1; i; })); 110 | 111 | ASSERT(2, 0?1:2); 112 | ASSERT(1, 1?1:2); 113 | ASSERT(-1, 0?-2:-1); 114 | ASSERT(-2, 1?-2:-1); 115 | ASSERT(4, sizeof(0?1:2)); 116 | ASSERT(__SIZEOF_LONG__, sizeof(0?(long)1:(long)2)); 117 | ASSERT(-1, 0?(long)-2:-1); 118 | ASSERT(-1, 0?-2:(long)-1); 119 | ASSERT(-2, 1?(long)-2:-1); 120 | ASSERT(-2, 1?-2:(long)-1); 121 | 122 | 1 ? -2 : (void)-1; 123 | 124 | ASSERT(20, ({ int x; int *p=&x; p+20-p; })); 125 | ASSERT(1, ({ int x; int *p=&x; p+20-p>0; })); 126 | ASSERT(-20, ({ int x; int *p=&x; p-20-p; })); 127 | ASSERT(1, ({ int x; int *p=&x; p-20-p<0; })); 128 | 129 | ASSERT(15, (char *)0xffffffffffffffff - (char *)0xfffffffffffffff0); 130 | ASSERT(-15, (char *)0xfffffffffffffff0 - (char *)0xffffffffffffffff); 131 | ASSERT(1, (void *)0xffffffffffffffff > (void *)0); 132 | 133 | ASSERT(3, 3?:5); 134 | ASSERT(5, 0?:5); 135 | ASSERT(4, ({ int i = 3; ++i?:10; })); 136 | 137 | ASSERT(3, (long double)3); 138 | ASSERT(5, (long double)3+2); 139 | ASSERT(6, (long double)3*2); 140 | ASSERT(5, (long double)3+2.0); 141 | 142 | printf("OK\n"); 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /test/asm.c: -------------------------------------------------------------------------------- 1 | // DISABLED 2 | // asm blocks are passed through to the system assembler in the original design, 3 | // and there isn't one of those now that we're jitting, so this functionality is 4 | // disabled for the time being. 5 | #include "test.h" 6 | 7 | char *asm_fn1(void) { 8 | asm("mov rax, 50\n\t" 9 | "mov rsp, rbp\n\t" 10 | "pop rbp\n\t" 11 | "ret"); 12 | } 13 | 14 | char *asm_fn2(void) { 15 | asm inline volatile("mov rax, 55\n\t" 16 | "mov rsp, rbp\n\t" 17 | "pop rbp\n\t" 18 | "ret"); 19 | } 20 | 21 | int main() { 22 | ASSERT(50, asm_fn1()); 23 | ASSERT(55, asm_fn2()); 24 | 25 | printf("OK\n"); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /test/atomic.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | #include 4 | #ifdef _WIN64 5 | // Not quite ready to brave windows.h yet. 6 | void* CreateThread(void* lpThreadAttributes, 7 | size_t dwStackSize, 8 | void* lpStartAddress, 9 | void* lpParameter, 10 | unsigned int dwCreationFlags, 11 | unsigned int* dwThreadId); 12 | unsigned int WaitForSingleObject(void* hHandle, unsigned int dwMilliseconds); 13 | unsigned int CloseHandle(void* hObject); 14 | #define INFINITE 0xFFFFFFFF 15 | #else 16 | #include 17 | #endif 18 | 19 | static int incr(_Atomic int *p) { 20 | int oldval = *p; 21 | int newval; 22 | do { 23 | newval = oldval + 1; 24 | } while (!atomic_compare_exchange_weak(p, &oldval, newval)); 25 | return newval; 26 | } 27 | 28 | static int add1(void *arg) { 29 | _Atomic int *x = arg; 30 | for (int i = 0; i < 1000*1000; i++) 31 | incr(x); 32 | return 0; 33 | } 34 | 35 | static int add2(void *arg) { 36 | _Atomic int *x = arg; 37 | for (int i = 0; i < 1000*1000; i++) 38 | (*x)++; 39 | return 0; 40 | } 41 | 42 | static int add3(void *arg) { 43 | _Atomic int *x = arg; 44 | for (int i = 0; i < 1000*1000; i++) 45 | *x += 5; 46 | return 0; 47 | } 48 | 49 | static int add_millions(void) { 50 | _Atomic int x = 0; 51 | 52 | #ifdef _WIN64 53 | void* thr1 = CreateThread(NULL, 0, add1, &x, 0, NULL); 54 | void* thr2 = CreateThread(NULL, 0, add2, &x, 0, NULL); 55 | void* thr3 = CreateThread(NULL, 0, add3, &x, 0, NULL); 56 | #else 57 | pthread_t thr1; 58 | pthread_t thr2; 59 | pthread_t thr3; 60 | 61 | pthread_create(&thr1, NULL, add1, &x); 62 | pthread_create(&thr2, NULL, add2, &x); 63 | pthread_create(&thr3, NULL, add3, &x); 64 | #endif 65 | 66 | for (int i = 0; i < 1000*1000; i++) 67 | x--; 68 | 69 | #ifdef _WIN64 70 | WaitForSingleObject(thr1, INFINITE); 71 | WaitForSingleObject(thr2, INFINITE); 72 | WaitForSingleObject(thr3, INFINITE); 73 | CloseHandle(thr1); 74 | CloseHandle(thr2); 75 | CloseHandle(thr3); 76 | #else 77 | pthread_join(thr1, NULL); 78 | pthread_join(thr2, NULL); 79 | pthread_join(thr3, NULL); 80 | #endif 81 | return x; 82 | } 83 | 84 | int main() { 85 | ASSERT(6*1000*1000, add_millions()); 86 | 87 | ASSERT(3, ({ int x=3; atomic_exchange(&x, 5); })); 88 | ASSERT(5, ({ int x=3; atomic_exchange(&x, 5); x; })); 89 | 90 | printf("OK\n"); 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /test/attribute.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | 4 | int main() { 5 | ASSERT(5, ({ struct { char a; int b; } __attribute__((packed)) x; sizeof(x); })); 6 | ASSERT(0, offsetof(struct __attribute__((packed)) { char a; int b; }, a)); 7 | ASSERT(1, offsetof(struct __attribute__((packed)) { char a; int b; }, b)); 8 | 9 | ASSERT(5, ({ struct __attribute__((packed)) { char a; int b; } x; sizeof(x); })); 10 | ASSERT(0, offsetof(struct { char a; int b; } __attribute__((packed)), a)); 11 | ASSERT(1, offsetof(struct { char a; int b; } __attribute__((packed)), b)); 12 | 13 | ASSERT(9, ({ typedef struct { char a; int b[2]; } __attribute__((packed)) T; sizeof(T); })); 14 | ASSERT(9, ({ typedef struct __attribute__((packed)) { char a; int b[2]; } T; sizeof(T); })); 15 | 16 | ASSERT(1, offsetof(struct __attribute__((packed)) T { char a; int b[2]; }, b)); 17 | ASSERT(1, _Alignof(struct __attribute__((packed)) { char a; int b[2]; })); 18 | 19 | ASSERT(8, ({ struct __attribute__((aligned(8))) { int a; } x; _Alignof(x); })); 20 | ASSERT(8, ({ struct { int a; } __attribute__((aligned(8))) x; _Alignof(x); })); 21 | 22 | ASSERT(8, ({ struct __attribute__((aligned(8), packed)) { char a; int b; } x; _Alignof(x); })); 23 | ASSERT(8, ({ struct { char a; int b; } __attribute__((aligned(8), packed)) x; _Alignof(x); })); 24 | ASSERT(1, offsetof(struct __attribute__((aligned(8), packed)) { char a; int b; }, b)); 25 | ASSERT(1, offsetof(struct { char a; int b; } __attribute__((aligned(8), packed)), b)); 26 | 27 | ASSERT(8, ({ struct __attribute__((aligned(8))) __attribute__((packed)) { char a; int b; } x; _Alignof(x); })); 28 | ASSERT(8, ({ struct { char a; int b; } __attribute__((aligned(8))) __attribute__((packed)) x; _Alignof(x); })); 29 | ASSERT(1, offsetof(struct __attribute__((aligned(8))) __attribute__((packed)) { char a; int b; }, b)); 30 | ASSERT(1, offsetof(struct { char a; int b; } __attribute__((aligned(8))) __attribute__((packed)), b)); 31 | 32 | ASSERT(8, ({ struct __attribute__((aligned(8))) { char a; int b; } __attribute__((packed)) x; _Alignof(x); })); 33 | ASSERT(1, offsetof(struct __attribute__((aligned(8))) { char a; int b; } __attribute__((packed)), b)); 34 | 35 | ASSERT(16, ({ struct __attribute__((aligned(8+8))) { char a; int b; } x; _Alignof(x); })); 36 | 37 | printf("OK\n"); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /test/bitfield.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | struct { 4 | char a; 5 | int b : 5; 6 | int c : 10; 7 | } g45 = {1, 2, 3}, g46={}; 8 | 9 | int main() { 10 | ASSERT(4, sizeof(struct {int x:1; })); 11 | ASSERT(__SIZEOF_LONG__, sizeof(struct {long x:1; })); 12 | 13 | struct bit1 { 14 | short a; 15 | char b; 16 | int c : 2; 17 | int d : 3; 18 | int e : 3; 19 | }; 20 | 21 | ASSERT(4, sizeof(struct bit1)); 22 | ASSERT(1, ({ struct bit1 x; x.a=1; x.b=2; x.c=3; x.d=4; x.e=5; x.a; })); 23 | ASSERT(1, ({ struct bit1 x={1,2,3,4,5}; x.a; })); 24 | ASSERT(2, ({ struct bit1 x={1,2,3,4,5}; x.b; })); 25 | ASSERT(-1, ({ struct bit1 x={1,2,3,4,5}; x.c; })); 26 | ASSERT(-4, ({ struct bit1 x={1,2,3,4,5}; x.d; })); 27 | ASSERT(-3, ({ struct bit1 x={1,2,3,4,5}; x.e; })); 28 | 29 | ASSERT(1, g45.a); 30 | ASSERT(2, g45.b); 31 | ASSERT(3, g45.c); 32 | 33 | ASSERT(0, g46.a); 34 | ASSERT(0, g46.b); 35 | ASSERT(0, g46.c); 36 | 37 | typedef struct { 38 | int a : 10; 39 | int b : 10; 40 | int c : 10; 41 | } T3; 42 | 43 | ASSERT(1, ({ T3 x={1,2,3}; x.a++; })); 44 | ASSERT(2, ({ T3 x={1,2,3}; x.b++; })); 45 | ASSERT(3, ({ T3 x={1,2,3}; x.c++; })); 46 | 47 | ASSERT(2, ({ T3 x={1,2,3}; ++x.a; })); 48 | ASSERT(3, ({ T3 x={1,2,3}; ++x.b; })); 49 | ASSERT(4, ({ T3 x={1,2,3}; ++x.c; })); 50 | 51 | ASSERT(4, sizeof(struct {int a:3; int c:1; int c:5;})); 52 | ASSERT(8, sizeof(struct {int a:3; int:0; int c:5;})); 53 | ASSERT(4, sizeof(struct {int a:3; int:0;})); 54 | 55 | printf("OK\n"); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /test/builtin.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(1, __builtin_types_compatible_p(int, int)); 5 | ASSERT(1, __builtin_types_compatible_p(double, double)); 6 | if (__SIZEOF_LONG__ == 8) { 7 | ASSERT(0, __builtin_types_compatible_p(int, long)); 8 | } else { 9 | ASSERT(1, __builtin_types_compatible_p(int, long)); 10 | } 11 | ASSERT(0, __builtin_types_compatible_p(long, float)); 12 | ASSERT(1, __builtin_types_compatible_p(int *, int *)); 13 | ASSERT(0, __builtin_types_compatible_p(short *, int *)); 14 | ASSERT(0, __builtin_types_compatible_p(int **, int *)); 15 | ASSERT(1, __builtin_types_compatible_p(const int, int)); 16 | ASSERT(0, __builtin_types_compatible_p(unsigned, int)); 17 | ASSERT(1, __builtin_types_compatible_p(signed, int)); 18 | ASSERT(0, __builtin_types_compatible_p(struct {int a;}, struct {int a;})); 19 | 20 | ASSERT(1, __builtin_types_compatible_p(int (*)(void), int (*)(void))); 21 | ASSERT(1, __builtin_types_compatible_p(void (*)(int), void (*)(int))); 22 | ASSERT(1, __builtin_types_compatible_p(void (*)(int, double), void (*)(int, double))); 23 | ASSERT(1, __builtin_types_compatible_p(int (*)(float, double), int (*)(float, double))); 24 | ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int)); 25 | ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int (*)(float))); 26 | ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int (*)(float, double, int))); 27 | ASSERT(1, __builtin_types_compatible_p(double (*)(...), double (*)(...))); 28 | ASSERT(0, __builtin_types_compatible_p(double (*)(...), double (*)(void))); 29 | 30 | ASSERT(1, ({ typedef struct {int a;} T; __builtin_types_compatible_p(T, T); })); 31 | ASSERT(1, ({ typedef struct {int a;} T; __builtin_types_compatible_p(T, const T); })); 32 | 33 | ASSERT(1, ({ struct {int a; int b;} x; __builtin_types_compatible_p(typeof(x.a), typeof(x.b)); })); 34 | 35 | printf("OK\n"); 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /test/cast.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(131585, (int)8590066177); 5 | ASSERT(513, (short)8590066177); 6 | ASSERT(1, (char)8590066177); 7 | ASSERT(1, (long)1); 8 | ASSERT(0, (long)&*(int *)0); 9 | ASSERT(513, ({ int x=512; *(char *)&x=1; x; })); 10 | #if __SIZEOF_LONG__ == 8 11 | ASSERT(5, ({ int x=5; long y=(long)&x; *(int*)y; })); 12 | #else 13 | ASSERT(5, ({ int x=5; long long y=(long long)&x; *(int*)y; })); 14 | #endif 15 | 16 | (void)1; 17 | 18 | ASSERT(-1, (char)255); 19 | ASSERT(-1, (signed char)255); 20 | ASSERT(255, (unsigned char)255); 21 | ASSERT(-1, (short)65535); 22 | ASSERT(65535, (unsigned short)65535); 23 | ASSERT(-1, (int)0xffffffff); 24 | ASSERT(0xffffffff, (unsigned)0xffffffff); 25 | 26 | ASSERT(1, -1<1); 27 | ASSERT(0, -1<(unsigned)1); 28 | ASSERT(254, (char)127+(char)127); 29 | ASSERT(65534, (short)32767+(short)32767); 30 | ASSERT(-1, -1>>1); 31 | ASSERT(-1, (unsigned long)-1); 32 | ASSERT(2147483647, ((unsigned)-1)>>1); 33 | ASSERT(-50, (-100)/2); 34 | ASSERT(2147483598, ((unsigned)-100)/2); 35 | #if __SIZEOF_LONG__ == 8 36 | ASSERT(9223372036854775758, ((unsigned long)-100)/2); 37 | #else 38 | ASSERT(9223372036854775758, ((unsigned long long)-100)/2); 39 | #endif 40 | #if __SIZEOF_LONG__ == 8 41 | ASSERT(0, ((long)-1)/(unsigned)100); 42 | #else 43 | ASSERT(0, ((long long)-1)/(unsigned)100); 44 | #endif 45 | ASSERT(-2, (-100)%7); 46 | ASSERT(2, ((unsigned)-100)%7); 47 | #if __SIZEOF_LONG__ == 8 48 | ASSERT(6, ((unsigned long)-100)%9); 49 | #else 50 | ASSERT(6, ((unsigned long long)-100)%9); 51 | #endif 52 | 53 | ASSERT(65535, (int)(unsigned short)65535); 54 | ASSERT(65535, ({ unsigned short x = 65535; x; })); 55 | ASSERT(65535, ({ unsigned short x = 65535; (int)x; })); 56 | 57 | ASSERT(-1, ({ typedef short T; T x = 65535; (int)x; })); 58 | ASSERT(65535, ({ typedef unsigned short T; T x = 65535; (int)x; })); 59 | 60 | ASSERT(0, (_Bool)0.0); 61 | ASSERT(1, (_Bool)0.1); 62 | ASSERT(3, (char)3.0); 63 | ASSERT(1000, (short)1000.3); 64 | ASSERT(3, (int)3.99); 65 | #if __SIZEOF_LONG__ == 8 66 | ASSERT(2000000000000000, (long)2e15); 67 | #else 68 | ASSERT(2000000000000000, (long long)2e15); 69 | #endif 70 | ASSERT(3, (float)3.5); 71 | ASSERT(5, (double)(float)5.5); 72 | ASSERT(3, (float)3); 73 | ASSERT(3, (double)3); 74 | ASSERT(3, (float)3L); 75 | ASSERT(3, (double)3L); 76 | 77 | printf("OK\n"); 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /test/common.c: -------------------------------------------------------------------------------- 1 | // DISABLED 2 | // This file is only compiled with others as a "library" 3 | #include 4 | #include 5 | #include 6 | 7 | void assert(int expected, int actual, char *code) { 8 | if (expected == actual) { 9 | printf("%s => %d\n", code, actual); 10 | } else { 11 | printf("%s => %d expected but got %d\n", code, expected, actual); 12 | exit(1); 13 | } 14 | } 15 | 16 | static int static_fn() { return 5; } 17 | int ext1 = 5; 18 | int *ext2 = &ext1; 19 | int ext3 = 7; 20 | int ext_fn1(int x) { return x; } 21 | int ext_fn2(int x) { return x; } 22 | int common_ext2 = 3; 23 | static int common_local; 24 | 25 | int false_fn() { return 512; } 26 | int true_fn() { return 513; } 27 | int char_fn() { return (2<<8)+3; } 28 | int short_fn() { return (2<<16)+5; } 29 | 30 | int uchar_fn() { return (2<<10)-1-4; } 31 | int ushort_fn() { return (2<<20)-1-7; } 32 | 33 | int schar_fn() { return (2<<10)-1-4; } 34 | int sshort_fn() { return (2<<20)-1-7; } 35 | 36 | int add_all(int n, ...) { 37 | va_list ap; 38 | va_start(ap, n); 39 | 40 | int sum = 0; 41 | for (int i = 0; i < n; i++) 42 | sum += va_arg(ap, int); 43 | return sum; 44 | } 45 | 46 | float add_float(float x, float y) { 47 | return x + y; 48 | } 49 | double add_double(double x, double y) { 50 | return x + y; 51 | } 52 | 53 | int add10_int(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10) { 54 | return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10; 55 | } 56 | 57 | float add10_float(float x1, float x2, float x3, float x4, float x5, float x6, float x7, float x8, float x9, float x10) { 58 | return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10; 59 | } 60 | 61 | double add10_double(double x1, double x2, double x3, double x4, double x5, double x6, double x7, double x8, double x9, double x10) { 62 | return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10; 63 | } 64 | 65 | typedef struct { int a,b; short c; char d; } Ty4; 66 | typedef struct { int a; float b; double c; } Ty5; 67 | typedef struct { unsigned char a[3]; } Ty6; 68 | typedef struct { long a, b, c; } Ty7; 69 | typedef struct { int a, b; } Ty8; 70 | 71 | int struct_test4(Ty4 x, int n) { 72 | switch (n) { 73 | case 0: return x.a; 74 | case 1: return x.b; 75 | case 2: return x.c; 76 | default: return x.d; 77 | } 78 | } 79 | 80 | int struct_test5(Ty5 x, int n) { 81 | switch (n) { 82 | case 0: return x.a; 83 | case 1: return x.b; 84 | default: return x.c; 85 | } 86 | } 87 | 88 | int struct_test6(Ty6 x, int n) { 89 | return x.a[n]; 90 | } 91 | 92 | int struct_test7(Ty7 x, int n) { 93 | switch (n) { 94 | case 0: return x.a; 95 | case 1: return x.b; 96 | default: return x.c; 97 | } 98 | } 99 | 100 | int struct_test8(Ty8 x, int n) { 101 | switch (n) { 102 | case 0: return x.a; 103 | default: return x.b; 104 | } 105 | } 106 | 107 | Ty4 struct_test24(void) { 108 | return (Ty4){10, 20, 30, 40}; 109 | } 110 | 111 | Ty5 struct_test25(void) { 112 | return (Ty5){10, 20, 30}; 113 | } 114 | 115 | Ty6 struct_test26(void) { 116 | return (Ty6){10, 20, 30}; 117 | } 118 | 119 | typedef struct { unsigned char a[10]; } Ty20; 120 | typedef struct { unsigned char a[20]; } Ty21; 121 | 122 | Ty20 struct_test27(void) { 123 | return (Ty20){10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; 124 | } 125 | 126 | Ty21 struct_test28(void) { 127 | return (Ty21){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; 128 | } 129 | 130 | $vec(int) container_test29($map(int, int)* py) { 131 | $vec(int) x = {0}; 132 | x..push_back(3); 133 | x..push_back(4); 134 | x..push_back(5); 135 | x..push_back(6); 136 | x..push_back(7); 137 | x..push_back(8); 138 | x..push_back(9); 139 | 140 | py..insert(1, 2); 141 | py..insert(3, 4); 142 | py..insert(5, 6); 143 | py..insert(7, 8); 144 | return x; 145 | } 146 | -------------------------------------------------------------------------------- /test/commonsym.c: -------------------------------------------------------------------------------- 1 | // DISABLED 2 | // Don't want to support this for now. 3 | #include "test.h" 4 | 5 | int x; 6 | int x = 5; 7 | int y = 7; 8 | int y; 9 | int common_ext1; 10 | int common_ext2; 11 | static int common_local; 12 | 13 | int main() { 14 | ASSERT(5, x); 15 | ASSERT(7, y); 16 | ASSERT(0, common_ext1); 17 | ASSERT(3, common_ext2); 18 | 19 | printf("OK\n"); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /test/compat.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | _Noreturn noreturn_fn(int restrict x) { 4 | exit(0); 5 | } 6 | 7 | void funcy_type(int arg[restrict static 3]) {} 8 | 9 | int main() { 10 | { volatile x; } 11 | { int volatile x; } 12 | { volatile int x; } 13 | { volatile int volatile volatile x; } 14 | { int volatile * volatile volatile x; } 15 | { auto ** restrict __restrict __restrict__ const volatile *x; } 16 | 17 | printf("OK\n"); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /test/complit.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | typedef struct Tree { 4 | int val; 5 | struct Tree *lhs; 6 | struct Tree *rhs; 7 | } Tree; 8 | 9 | Tree *tree = &(Tree){ 10 | 1, 11 | &(Tree){ 12 | 2, 13 | &(Tree){ 3, 0, 0 }, 14 | &(Tree){ 4, 0, 0 } 15 | }, 16 | 0 17 | }; 18 | 19 | int main() { 20 | ASSERT(1, (int){1}); 21 | ASSERT(2, ((int[]){0,1,2})[2]); 22 | ASSERT('a', ((struct {char a; int b;}){'a', 3}).a); 23 | ASSERT(3, ({ int x=3; (int){x}; })); 24 | (int){3} = 5; 25 | 26 | ASSERT(1, tree->val); 27 | ASSERT(2, tree->lhs->val); 28 | ASSERT(3, tree->lhs->lhs->val); 29 | ASSERT(4, tree->lhs->rhs->val); 30 | 31 | printf("OK\n"); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /test/const.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | { const x; } 5 | { int const x; } 6 | { const int x; } 7 | { const int const const x; } 8 | ASSERT(5, ({ const x = 5; x; })); 9 | ASSERT(8, ({ const x = 8; int *const y=&x; *y; })); 10 | ASSERT(6, ({ const x = 6; *(const * const)&x; })); 11 | 12 | printf("OK\n"); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /test/constexpr.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | float g40 = 1.5; 4 | double g41 = 0.0 ? 55 : (0, 1 + 1 * 5.0 / 2 * (double)2 * (int)2.0); 5 | 6 | int main() { 7 | ASSERT(10, ({ enum { ten=1+2+3+4 }; ten; })); 8 | ASSERT(1, ({ int i=0; switch(3) { case 5-2+0*3: i++; } i; })); 9 | ASSERT(8, ({ int x[1+1]; sizeof(x); })); 10 | ASSERT(6, ({ char x[8-2]; sizeof(x); })); 11 | ASSERT(6, ({ char x[2*3]; sizeof(x); })); 12 | ASSERT(3, ({ char x[12/4]; sizeof(x); })); 13 | ASSERT(2, ({ char x[12%10]; sizeof(x); })); 14 | ASSERT(0b100, ({ char x[0b110&0b101]; sizeof(x); })); 15 | ASSERT(0b111, ({ char x[0b110|0b101]; sizeof(x); })); 16 | ASSERT(0b110, ({ char x[0b111^0b001]; sizeof(x); })); 17 | ASSERT(4, ({ char x[1<<2]; sizeof(x); })); 18 | ASSERT(2, ({ char x[4>>1]; sizeof(x); })); 19 | ASSERT(2, ({ char x[(1==1)+1]; sizeof(x); })); 20 | ASSERT(1, ({ char x[(1!=1)+1]; sizeof(x); })); 21 | ASSERT(1, ({ char x[(1<1)+1]; sizeof(x); })); 22 | ASSERT(2, ({ char x[(1<=1)+1]; sizeof(x); })); 23 | ASSERT(2, ({ char x[1?2:3]; sizeof(x); })); 24 | ASSERT(3, ({ char x[0?2:3]; sizeof(x); })); 25 | ASSERT(3, ({ char x[(1,3)]; sizeof(x); })); 26 | ASSERT(2, ({ char x[!0+1]; sizeof(x); })); 27 | ASSERT(1, ({ char x[!1+1]; sizeof(x); })); 28 | ASSERT(2, ({ char x[~-3]; sizeof(x); })); 29 | ASSERT(2, ({ char x[(5||6)+1]; sizeof(x); })); 30 | ASSERT(1, ({ char x[(0||0)+1]; sizeof(x); })); 31 | ASSERT(2, ({ char x[(1&&1)+1]; sizeof(x); })); 32 | ASSERT(1, ({ char x[(1&&0)+1]; sizeof(x); })); 33 | ASSERT(3, ({ char x[(int)3]; sizeof(x); })); 34 | ASSERT(15, ({ char x[(char)0xffffff0f]; sizeof(x); })); 35 | ASSERT(0x10f, ({ char x[(short)0xffff010f]; sizeof(x); })); 36 | ASSERT(4, ({ char x[(int)0xfffffffffff+5]; sizeof(x); })); 37 | ASSERT(8, ({ char x[(int*)0+2]; sizeof(x); })); 38 | ASSERT(12, ({ char x[(int*)16-1]; sizeof(x); })); 39 | ASSERT(3, ({ char x[(int*)16-(int*)4]; sizeof(x); })); 40 | 41 | ASSERT(4, ({ char x[(-1>>31)+5]; sizeof(x); })); 42 | ASSERT(255, ({ char x[(unsigned char)0xffffffff]; sizeof(x); })); 43 | ASSERT(0x800f, ({ char x[(unsigned short)0xffff800f]; sizeof(x); })); 44 | ASSERT(1, ({ char x[(unsigned int)0xfffffffffff>>31]; sizeof(x); })); 45 | #if __SIZEOF_LONG__ == 8 46 | ASSERT(1, ({ char x[(long)-1/((long)1<<62)+1]; sizeof(x); })); 47 | ASSERT(4, ({ char x[(unsigned long)-1/((long)1<<62)+1]; sizeof(x); })); 48 | #else 49 | ASSERT(1, ({ char x[(long long)-1/((long long)1<<62)+1]; sizeof(x); })); 50 | ASSERT(4, ({ char x[(unsigned long long)-1/((long long)1<<62)+1]; sizeof(x); })); 51 | #endif 52 | ASSERT(1, ({ char x[(unsigned)1<-1]; sizeof(x); })); 53 | ASSERT(1, ({ char x[(unsigned)1<=-1]; sizeof(x); })); 54 | 55 | ASSERT(1, g40==1.5); 56 | ASSERT(1, g41==11); 57 | 58 | printf("OK\n"); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /test/container.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | $vec(int) x = {0}; 5 | x..push_back(33); 6 | x..push_back(44); 7 | 8 | ASSERT(44, *x..back()); 9 | 10 | $map(int, float) y = {0}; 11 | y..insert(3, 6.7f); 12 | y..insert(4, 3.14f); 13 | y..insert(5, 1.2f); 14 | 15 | ASSERT(3.14f, *y..at(4)); 16 | 17 | x..drop(); 18 | y..drop(); 19 | } 20 | -------------------------------------------------------------------------------- /test/container2.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main(void) { 4 | $vec(char*) vc = {0}; 5 | 6 | vc..push_back("zip"); 7 | vc..push_back("zap"); 8 | vc..push_back("zoot"); 9 | 10 | ASSERT(0, strcmp("zip", *vc..at(0))); 11 | ASSERT(0, strcmp("zap", *vc..at(1))); 12 | ASSERT(0, strcmp("zoot", *vc..at(2))); 13 | 14 | vc..drop(); 15 | } 16 | -------------------------------------------------------------------------------- /test/container3.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | $vec(int) container_test29($map(int, int)* py); 4 | 5 | int main(void) { 6 | $map(int, int) myy = {0}; 7 | 8 | $vec(int) myx = container_test29(&myy); 9 | 10 | ASSERT(7, myx..size()); 11 | ASSERT(9, *myx..back()); 12 | ASSERT(3, *myx..front()); 13 | 14 | ASSERT(4, myy..size()); 15 | 16 | ASSERT(2, *myy..at(1)); 17 | ASSERT(4, *myy..at(3)); 18 | ASSERT(6, *myy..at(5)); 19 | ASSERT(8, *myy..at(7)); 20 | 21 | myx..drop(); 22 | myy..drop(); 23 | } 24 | -------------------------------------------------------------------------------- /test/control.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | /* 4 | * This is a block comment. 5 | */ 6 | 7 | int main() { 8 | ASSERT(3, ({ int x; if (0) x=2; else x=3; x; })); 9 | ASSERT(3, ({ int x; if (1-1) x=2; else x=3; x; })); 10 | ASSERT(2, ({ int x; if (1) x=2; else x=3; x; })); 11 | ASSERT(2, ({ int x; if (2-1) x=2; else x=3; x; })); 12 | 13 | ASSERT(55, ({ int i=0; int j=0; for (i=0; i<=10; i=i+1) j=i+j; j; })); 14 | 15 | ASSERT(10, ({ int i=0; while(i<10) i=i+1; i; })); 16 | 17 | ASSERT(3, ({ 1; {2;} 3; })); 18 | ASSERT(5, ({ ;;; 5; })); 19 | 20 | ASSERT(10, ({ int i=0; while(i<10) i=i+1; i; })); 21 | ASSERT(55, ({ int i=0; int j=0; while(i<=10) {j=i+j; i=i+1;} j; })); 22 | 23 | ASSERT(3, (1,2,3)); 24 | ASSERT(5, ({ int i=2, j=3; (i=5,j)=6; i; })); 25 | ASSERT(6, ({ int i=2, j=3; (i=5,j)=6; j; })); 26 | 27 | ASSERT(55, ({ int j=0; for (int i=0; i<=10; i=i+1) j=j+i; j; })); 28 | ASSERT(3, ({ int i=3; int j=0; for (int i=0; i<=10; i=i+1) j=j+i; i; })); 29 | 30 | ASSERT(1, 0||1); 31 | ASSERT(1, 0||(2-2)||5); 32 | ASSERT(0, 0||0); 33 | ASSERT(0, 0||(2-2)); 34 | 35 | ASSERT(0, 0&&1); 36 | ASSERT(0, (2-2)&&5); 37 | ASSERT(1, 1&&5); 38 | 39 | ASSERT(3, ({ int i=0; goto a; a: i++; b: i++; c: i++; i; })); 40 | ASSERT(2, ({ int i=0; goto e; d: i++; e: i++; f: i++; i; })); 41 | ASSERT(1, ({ int i=0; goto i; g: i++; h: i++; i: i++; i; })); 42 | 43 | ASSERT(1, ({ typedef int foo; goto foo; foo:; 1; })); 44 | 45 | ASSERT(3, ({ int i=0; for(;i<10;i++) { if (i == 3) break; } i; })); 46 | ASSERT(4, ({ int i=0; while (1) { if (i++ == 3) break; } i; })); 47 | ASSERT(3, ({ int i=0; for(;i<10;i++) { for (;;) break; if (i == 3) break; } i; })); 48 | ASSERT(4, ({ int i=0; while (1) { while(1) break; if (i++ == 3) break; } i; })); 49 | 50 | ASSERT(10, ({ int i=0; int j=0; for (;i<10;i++) { if (i>5) continue; j++; } i; })); 51 | ASSERT(6, ({ int i=0; int j=0; for (;i<10;i++) { if (i>5) continue; j++; } j; })); 52 | ASSERT(10, ({ int i=0; int j=0; for(;!i;) { for (;j!=10;j++) continue; break; } j; })); 53 | ASSERT(11, ({ int i=0; int j=0; while (i++<10) { if (i>5) continue; j++; } i; })); 54 | ASSERT(5, ({ int i=0; int j=0; while (i++<10) { if (i>5) continue; j++; } j; })); 55 | ASSERT(11, ({ int i=0; int j=0; while(!i) { while (j++!=10) continue; break; } j; })); 56 | 57 | ASSERT(5, ({ int i=0; switch(0) { case 0:i=5;break; case 1:i=6;break; case 2:i=7;break; } i; })); 58 | ASSERT(6, ({ int i=0; switch(1) { case 0:i=5;break; case 1:i=6;break; case 2:i=7;break; } i; })); 59 | ASSERT(7, ({ int i=0; switch(2) { case 0:i=5;break; case 1:i=6;break; case 2:i=7;break; } i; })); 60 | ASSERT(0, ({ int i=0; switch(3) { case 0:i=5;break; case 1:i=6;break; case 2:i=7;break; } i; })); 61 | ASSERT(5, ({ int i=0; switch(0) { case 0:i=5;break; default:i=7; } i; })); 62 | ASSERT(7, ({ int i=0; switch(1) { case 0:i=5;break; default:i=7; } i; })); 63 | ASSERT(2, ({ int i=0; switch(1) { case 0: 0; case 1: 0; case 2: 0; i=2; } i; })); 64 | ASSERT(0, ({ int i=0; switch(3) { case 0: 0; case 1: 0; case 2: 0; i=2; } i; })); 65 | 66 | ASSERT(3, ({ int i=0; switch(-1) { case 0xffffffff: i=3; break; } i; })); 67 | 68 | ASSERT(7, ({ int i=0; int j=0; do { j++; } while (i++ < 6); j; })); 69 | ASSERT(4, ({ int i=0; int j=0; int k=0; do { if (++j > 3) break; continue; k++; } while (1); j; })); 70 | 71 | ASSERT(0, 0.0 && 0.0); 72 | ASSERT(0, 0.0 && 0.1); 73 | ASSERT(0, 0.3 && 0.0); 74 | ASSERT(1, 0.3 && 0.5); 75 | ASSERT(0, 0.0 || 0.0); 76 | ASSERT(1, 0.0 || 0.1); 77 | ASSERT(1, 0.3 || 0.0); 78 | ASSERT(1, 0.3 || 0.5); 79 | ASSERT(5, ({ int x; if (0.0) x=3; else x=5; x; })); 80 | ASSERT(3, ({ int x; if (0.1) x=3; else x=5; x; })); 81 | ASSERT(5, ({ int x=5; if (0.0) x=3; x; })); 82 | ASSERT(3, ({ int x=5; if (0.1) x=3; x; })); 83 | ASSERT(10, ({ double i=10.0; int j=0; for (; i; i--, j++); j; })); 84 | ASSERT(10, ({ double i=10.0; int j=0; do j++; while(--i); j; })); 85 | 86 | ASSERT(2, ({ int i=0; switch(7) { case 0 ... 5: i=1; break; case 6 ... 20: i=2; break; } i; })); 87 | ASSERT(1, ({ int i=0; switch(7) { case 0 ... 7: i=1; break; case 8 ... 10: i=2; break; } i; })); 88 | ASSERT(1, ({ int i=0; switch(7) { case 0: i=1; break; case 7 ... 7: i=1; break; } i; })); 89 | 90 | ASSERT(3, ({ void *p = &&v11; int i=0; goto *p; v11:i++; v12:i++; v13:i++; i; })); 91 | ASSERT(2, ({ void *p = &&v22; int i=0; goto *p; v21:i++; v22:i++; v23:i++; i; })); 92 | ASSERT(1, ({ void *p = &&v33; int i=0; goto *p; v31:i++; v32:i++; v33:i++; i; })); 93 | 94 | ASSERT(3, ({ static void *p[]={&&v41,&&v42,&&v43}; int i=0; goto *p[0]; v41:i++; v42:i++; v43:i++; i; })); 95 | ASSERT(2, ({ static void *p[]={&&v52,&&v52,&&v53}; int i=0; goto *p[1]; v51:i++; v52:i++; v53:i++; i; })); 96 | ASSERT(1, ({ static void *p[]={&&v62,&&v62,&&v63}; int i=0; goto *p[2]; v61:i++; v62:i++; v63:i++; i; })); 97 | 98 | printf("OK\n"); 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /test/decl.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(1, ({ char x; sizeof(x); })); 5 | ASSERT(2, ({ short int x; sizeof(x); })); 6 | ASSERT(2, ({ int short x; sizeof(x); })); 7 | ASSERT(4, ({ int x; sizeof(x); })); 8 | ASSERT(__SIZEOF_LONG__, ({ long int x; sizeof(x); })); 9 | ASSERT(__SIZEOF_LONG__, ({ int long x; sizeof(x); })); 10 | 11 | ASSERT(8, ({ long long x; sizeof(x); })); 12 | 13 | ASSERT(0, ({ _Bool x=0; x; })); 14 | ASSERT(1, ({ _Bool x=1; x; })); 15 | ASSERT(1, ({ _Bool x=2; x; })); 16 | ASSERT(1, (_Bool)1); 17 | ASSERT(1, (_Bool)2); 18 | ASSERT(0, (_Bool)(char)256); 19 | 20 | printf("OK\n"); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /test/enum.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(0, ({ enum { zero, one, two }; zero; })); 5 | ASSERT(1, ({ enum { zero, one, two }; one; })); 6 | ASSERT(2, ({ enum { zero, one, two }; two; })); 7 | ASSERT(5, ({ enum { five=5, six, seven }; five; })); 8 | ASSERT(6, ({ enum { five=5, six, seven }; six; })); 9 | ASSERT(0, ({ enum { zero, five=5, three=3, four }; zero; })); 10 | ASSERT(5, ({ enum { zero, five=5, three=3, four }; five; })); 11 | ASSERT(3, ({ enum { zero, five=5, three=3, four }; three; })); 12 | ASSERT(4, ({ enum { zero, five=5, three=3, four }; four; })); 13 | ASSERT(4, ({ enum { zero, one, two } x; sizeof(x); })); 14 | ASSERT(4, ({ enum t { zero, one, two }; enum t y; sizeof(y); })); 15 | 16 | printf("OK\n"); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /test/err_add_non_pointer.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:8: s + 3; 4 | // TXT: ^ error: invalid operands 5 | typedef struct Struct { int x; } Struct; 6 | int main() { 7 | Struct s; 8 | s + 3; 9 | } 10 | -------------------------------------------------------------------------------- /test/err_addvoid.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:8: c += f(); 4 | // TXT: ^ error: += expression with type void 5 | void f(void); 6 | int main() { 7 | double c; 8 | c += f(); 9 | } 10 | -------------------------------------------------------------------------------- /test/err_arrayelem.c: -------------------------------------------------------------------------------- 1 | // RET: 255 2 | // TXT: test/err_arrayelem.c:13: Thing* x = things[3]; 3 | // TXT: ^ error: value of type Thing can't be assigned to a pointer 4 | typedef struct Thing { 5 | double x; 6 | double y; 7 | double yspeed; 8 | } Thing; 9 | 10 | Thing things[5]; 11 | 12 | int main(void) { 13 | Thing* x = things[3]; 14 | } 15 | -------------------------------------------------------------------------------- /test/err_assign_incompatible_struct.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:10: abc = xyz; 4 | // TXT: ^ error: cannot assign incompatible structs 5 | typedef struct Blah { float x, y; } Blah; 6 | typedef struct Bloop { int x; int y; int z; } Bloop; 7 | Blah abc; 8 | Bloop xyz; 9 | int main(void) { 10 | abc = xyz; 11 | } 12 | -------------------------------------------------------------------------------- /test/err_assign_to_struct.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:8: abc = -4; 4 | // TXT: ^ error: cannot assign to struct 5 | typedef struct Blah { float x, y; } Blah; 6 | Blah abc; 7 | int main(void) { 8 | abc = -4; 9 | } 10 | -------------------------------------------------------------------------------- /test/err_identinclude.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:5: #include a 4 | // TXT: ^ error: expected a filename 5 | #include a 6 | -------------------------------------------------------------------------------- /test/err_incomplete_array_missing_initializer.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:5: int A[][] = ; 4 | // TXT: ^ error: array has incomplete element type 5 | int A[][] = ; 6 | -------------------------------------------------------------------------------- /test/err_incomplete_array_trailing.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:5: _[4u][]; 4 | // TXT: ^ error: incomplete type for array 5 | _[4u][]; 6 | -------------------------------------------------------------------------------- /test/err_incomplete_array_type.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:5: hhhhhh[]; 4 | // TXT: ^ error: incomplete type for array 5 | hhhhhh[]; 6 | -------------------------------------------------------------------------------- /test/err_large_array_designator.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:5: V[10]={[1000000000000000000000]=1}; 4 | // TXT: ^ error: array designator index exceeds array bounds 5 | V[10]={[1000000000000000000000]=1}; 6 | -------------------------------------------------------------------------------- /test/err_lshvoid.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:8: c <<= f(); 4 | // TXT: ^ error: <<= expression with type void 5 | void f(void); 6 | int main() { 7 | double c; 8 | c <<= f(); 9 | } 10 | -------------------------------------------------------------------------------- /test/err_methodcall_func_not_found.c: -------------------------------------------------------------------------------- 1 | // RET: 255 2 | // TXT: test/err_methodcall_func_not_found.c:13: XYZ_func 3 | // TXT: ^ error: undefined variable 4 | 5 | struct __attribute__((methodcall(XYZ_))) X { 6 | int a; 7 | int b; 8 | }; 9 | typedef struct X X; 10 | 11 | int main(void) { 12 | X x = {5, 6}; 13 | x..func(32); 14 | } 15 | -------------------------------------------------------------------------------- /test/err_negative_array_bounds.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:5: u[3][2][1][-4] = {[2][1]=1}; 4 | // TXT: ^ error: array declared with negative bounds 5 | u[3][2][1][-4] = {[2][1]=1}; 6 | -------------------------------------------------------------------------------- /test/err_nocode.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 254 3 | // TXT: no entry point found 4 | -------------------------------------------------------------------------------- /test/err_non_methodcall.c: -------------------------------------------------------------------------------- 1 | // RET: 255 2 | // TXT: test/err_non_methodcall.c:13: x..func(32); 3 | // TXT: ^ error: not an __attribute__((methodcall(prefix))) type 4 | 5 | struct X { 6 | int a; 7 | int b; 8 | }; 9 | typedef struct X X; 10 | 11 | int main(void) { 12 | X x = {5, 6}; 13 | x..func(32); 14 | } 15 | -------------------------------------------------------------------------------- /test/err_redefstruct.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: test/err_redefstruct.c:13: struct X { 4 | // TXT: ^ error: redefinition of type 5 | typedef struct A { 6 | int y; 7 | } A; 8 | 9 | struct X { 10 | int x; 11 | }; 12 | 13 | struct X { 14 | int x; 15 | }; 16 | -------------------------------------------------------------------------------- /test/err_sub_non_pointer.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:8: s - 3; 4 | // TXT: ^ error: invalid operands 5 | typedef struct Struct { int x; } Struct; 6 | int main() { 7 | Struct s; 8 | s - 3; 9 | } 10 | -------------------------------------------------------------------------------- /test/err_subvoid.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:8: c - f(); 4 | // TXT: ^ error: - expression with type void 5 | void f(void); 6 | int main() { 7 | double c; 8 | c - f(); 9 | } 10 | -------------------------------------------------------------------------------- /test/err_undefvar.c: -------------------------------------------------------------------------------- 1 | // RUN: {self} 2 | // RET: 255 3 | // TXT: {self}:6: in 4 | // TXT: ^ error: undefined variable 5 | int main() { 6 | in 7 | } 8 | -------------------------------------------------------------------------------- /test/extensions.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | struct __attribute__((methodcall(ZIPPY_))) X { 4 | int a; 5 | int b; 6 | }; 7 | typedef struct X X; 8 | 9 | void ZIPPY_func(X* x, int c) { 10 | ASSERT(5, x->a); 11 | ASSERT(6, x->b); 12 | ASSERT(32, c); 13 | } 14 | 15 | int main(void) { 16 | X x = {5, 6}; 17 | x..func(32); 18 | 19 | X* px = &x; 20 | px..func(32); 21 | 22 | X** ppx = &px; 23 | ppx..func(32); 24 | } 25 | -------------------------------------------------------------------------------- /test/extern.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | extern int ext1; 4 | extern int *ext2; 5 | 6 | inline int inline_fn(void) { 7 | return 3; 8 | } 9 | 10 | int main() { 11 | ASSERT(5, ext1); 12 | ASSERT(5, *ext2); 13 | 14 | extern int ext3; 15 | ASSERT(7, ext3); 16 | 17 | int ext_fn1(int x); 18 | ASSERT(5, ext_fn1(5)); 19 | 20 | extern int ext_fn2(int x); 21 | ASSERT(8, ext_fn2(8)); 22 | 23 | printf("OK\n"); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /test/float.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(35, (float)(char)35); 5 | ASSERT(35, (float)(short)35); 6 | ASSERT(35, (float)(int)35); 7 | ASSERT(35, (float)(long)35); 8 | ASSERT(35, (float)(unsigned char)35); 9 | ASSERT(35, (float)(unsigned short)35); 10 | ASSERT(35, (float)(unsigned int)35); 11 | ASSERT(35, (float)(unsigned long)35); 12 | 13 | ASSERT(35, (double)(char)35); 14 | ASSERT(35, (double)(short)35); 15 | ASSERT(35, (double)(int)35); 16 | ASSERT(35, (double)(long)35); 17 | ASSERT(35, (double)(unsigned char)35); 18 | ASSERT(35, (double)(unsigned short)35); 19 | ASSERT(35, (double)(unsigned int)35); 20 | ASSERT(35, (double)(unsigned long)35); 21 | 22 | ASSERT(35, (char)(float)35); 23 | ASSERT(35, (short)(float)35); 24 | ASSERT(35, (int)(float)35); 25 | ASSERT(35, (long)(float)35); 26 | ASSERT(35, (unsigned char)(float)35); 27 | ASSERT(35, (unsigned short)(float)35); 28 | ASSERT(35, (unsigned int)(float)35); 29 | ASSERT(35, (unsigned long)(float)35); 30 | 31 | ASSERT(35, (char)(double)35); 32 | ASSERT(35, (short)(double)35); 33 | ASSERT(35, (int)(double)35); 34 | ASSERT(35, (long)(double)35); 35 | ASSERT(35, (unsigned char)(double)35); 36 | ASSERT(35, (unsigned short)(double)35); 37 | ASSERT(35, (unsigned int)(double)35); 38 | ASSERT(35, (unsigned long)(double)35); 39 | 40 | ASSERT(-2147483648, (double)(unsigned long)(long)-1); 41 | 42 | ASSERT(14, (signed char)(long double)14); 43 | ASSERT(14, (unsigned char)(long double)14); 44 | 45 | ASSERT(14, (signed short)(long double)14); 46 | ASSERT(14, (unsigned short)(long double)14); 47 | ASSERT(414, (signed short)(long double)414); 48 | ASSERT(414, (unsigned short)(long double)414); 49 | 50 | ASSERT(14, (signed int)(long double)14); 51 | ASSERT(14, (unsigned int)(long double)14); 52 | ASSERT(414, (signed int)(long double)414); 53 | ASSERT(414, (unsigned int)(long double)414); 54 | ASSERT(555414, (signed int)(long double)555414); 55 | ASSERT(555414, (unsigned int)(long double)555414); 56 | 57 | ASSERT(14, (signed long long)(long double)14); 58 | ASSERT(14, (unsigned long long)(long double)14); 59 | ASSERT(414, (signed long long)(long double)414); 60 | ASSERT(414, (unsigned long long)(long double)414); 61 | ASSERT(555414, (signed long long)(long double)555414); 62 | ASSERT(555414, (unsigned long long)(long double)555414); 63 | ASSERT(123456789555414ull, (signed long long)(long double)123456789555414); 64 | ASSERT(123456789555414ull, (unsigned long long)(long double)123456789555414); 65 | 66 | ASSERT(-13, (signed char)(long double)-13); 67 | ASSERT(243, (unsigned char)(long double)-13); 68 | 69 | ASSERT(-13, (signed short)(long double)-13); 70 | ASSERT(65523, (unsigned short)(long double)-13); 71 | ASSERT(-413, (signed short)(long double)-413); 72 | ASSERT(65123, (unsigned short)(long double)-413); 73 | 74 | ASSERT(-13, (signed int)(long double)-13); 75 | ASSERT((unsigned int)-13, (unsigned int)(long double)-13); 76 | ASSERT(-413, (signed int)(long double)-413); 77 | ASSERT((unsigned int)-413, (unsigned int)(long double)-413); 78 | ASSERT(-555413, (signed int)(long double)-555413); 79 | ASSERT((unsigned int)-555413, (unsigned int)(long double)-555413); 80 | 81 | ASSERT(-13, (signed long long)(long double)-13); 82 | ASSERT((unsigned long long)-13, (unsigned long long)(long double)-13); 83 | ASSERT(-413, (signed long long)(long double)-413); 84 | ASSERT((unsigned long long)-413, (unsigned long long)(long double)-413); 85 | ASSERT(-555413, (signed long long)(long double)-555413); 86 | ASSERT((unsigned long long)-555413, (unsigned long long)(long double)-555413); 87 | ASSERT(-123456789555413ull, (signed long long)(long double)-123456789555413); 88 | ASSERT((unsigned long long)-123456789555413ull, 89 | (unsigned long long)(long double)-123456789555413); 90 | 91 | ASSERT(1, 2e3==2e3); 92 | ASSERT(0, 2e3==2e5); 93 | ASSERT(1, 2.0==2); 94 | ASSERT(0, 5.1<5); 95 | ASSERT(0, 5.0<5); 96 | ASSERT(1, 4.9<5); 97 | ASSERT(0, 5.1<=5); 98 | ASSERT(1, 5.0<=5); 99 | ASSERT(1, 4.9<=5); 100 | 101 | ASSERT(1, 2e3f==2e3); 102 | ASSERT(0, 2e3f==2e5); 103 | ASSERT(1, 2.0f==2); 104 | ASSERT(0, 5.1f<5); 105 | ASSERT(0, 5.0f<5); 106 | ASSERT(1, 4.9f<5); 107 | ASSERT(0, 5.1f<=5); 108 | ASSERT(1, 5.0f<=5); 109 | ASSERT(1, 4.9f<=5); 110 | 111 | ASSERT(6, 2.3+3.8); 112 | ASSERT(-1, 2.3-3.8); 113 | ASSERT(-3, -3.8); 114 | ASSERT(13, 3.3*4); 115 | ASSERT(2, 5.0/2); 116 | 117 | ASSERT(6, 2.3f+3.8f); 118 | ASSERT(6, 2.3f+3.8); 119 | ASSERT(-1, 2.3f-3.8); 120 | ASSERT(-3, -3.8f); 121 | ASSERT(13, 3.3f*4); 122 | ASSERT(2, 5.0f/2); 123 | 124 | ASSERT(0, 0.0/0.0 == 0.0/0.0); 125 | ASSERT(1, 0.0/0.0 != 0.0/0.0); 126 | 127 | ASSERT(0, 0.0/0.0 < 0); 128 | ASSERT(0, 0.0/0.0 <= 0); 129 | ASSERT(0, 0.0/0.0 > 0); 130 | ASSERT(0, 0.0/0.0 >= 0); 131 | 132 | ASSERT(0, !3.); 133 | ASSERT(1, !0.); 134 | ASSERT(0, !3.f); 135 | ASSERT(1, !0.f); 136 | 137 | ASSERT(5, 0.0 ? 3 : 5); 138 | ASSERT(3, 1.2 ? 3 : 5); 139 | 140 | printf("OK\n"); 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /test/funcstack.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | typedef struct StRegSized { 4 | unsigned char r; 5 | } StRegSized; 6 | 7 | 8 | void func1(int a, int b, int c, int d, StRegSized st) { 9 | ASSERT(1, a); 10 | ASSERT(2, b); 11 | ASSERT(3, c); 12 | ASSERT(4, d); 13 | ASSERT(99, st.r); 14 | } 15 | 16 | void func2(int a, int b, int c, int d, int e, StRegSized st) { 17 | ASSERT(1, a); 18 | ASSERT(2, b); 19 | ASSERT(3, c); 20 | ASSERT(4, d); 21 | ASSERT(5, e); 22 | ASSERT(99, st.r); 23 | } 24 | 25 | void func3(int a, int b, int c, int d, int e, int f, StRegSized st) { 26 | ASSERT(1, a); 27 | ASSERT(2, b); 28 | ASSERT(3, c); 29 | ASSERT(4, d); 30 | ASSERT(5, e); 31 | ASSERT(6, f); 32 | ASSERT(99, st.r); 33 | } 34 | 35 | void func4(int a, int b, int c, int d, int e, int f, int g, StRegSized st) { 36 | ASSERT(1, a); 37 | ASSERT(2, b); 38 | ASSERT(3, c); 39 | ASSERT(4, d); 40 | ASSERT(5, e); 41 | ASSERT(6, f); 42 | ASSERT(7, g); 43 | ASSERT(99, st.r); 44 | } 45 | 46 | void func5(int a, int b, int c, StRegSized st) { 47 | ASSERT(1, a); 48 | ASSERT(2, b); 49 | ASSERT(3, c); 50 | ASSERT(99, st.r); 51 | } 52 | 53 | void func6(int a, int b, StRegSized st) { 54 | ASSERT(1, a); 55 | ASSERT(2, b); 56 | ASSERT(99, st.r); 57 | } 58 | 59 | void func7(int a, StRegSized st) { 60 | ASSERT(1, a); 61 | ASSERT(99, st.r); 62 | } 63 | 64 | void func8(int a, int b, int c, StRegSized st, int d) { 65 | ASSERT(1, a); 66 | ASSERT(2, b); 67 | ASSERT(3, c); 68 | ASSERT(99, st.r); 69 | ASSERT(4, d); 70 | } 71 | 72 | void func9(int a, int b, StRegSized st, int c, int d) { 73 | ASSERT(1, a); 74 | ASSERT(2, b); 75 | ASSERT(99, st.r); 76 | ASSERT(3, c); 77 | ASSERT(4, d); 78 | } 79 | 80 | void funca(int a, StRegSized st, int b, int c, int d) { 81 | ASSERT(1, a); 82 | ASSERT(99, st.r); 83 | ASSERT(2, b); 84 | ASSERT(3, c); 85 | ASSERT(4, d); 86 | } 87 | 88 | void funcb(int a, StRegSized st, int b, StRegSized st2, int c) { 89 | ASSERT(1, a); 90 | ASSERT(99, st.r); 91 | ASSERT(2, b); 92 | ASSERT(98, st2.r); 93 | ASSERT(3, c); 94 | } 95 | 96 | void funcc(int a, StRegSized st, int b, StRegSized st2, StRegSized st3) { 97 | ASSERT(1, a); 98 | ASSERT(99, st.r); 99 | ASSERT(2, b); 100 | ASSERT(98, st2.r); 101 | ASSERT(97, st3.r); 102 | } 103 | 104 | void funcd(int a, StRegSized st, int b, StRegSized st2, StRegSized st3, int c) { 105 | ASSERT(1, a); 106 | ASSERT(99, st.r); 107 | ASSERT(2, b); 108 | ASSERT(98, st2.r); 109 | ASSERT(97, st3.r); 110 | ASSERT(3, c); 111 | } 112 | 113 | 114 | int main(void) { 115 | StRegSized x = {99}; 116 | StRegSized y = {98}; 117 | StRegSized z = {97}; 118 | func1(1, 2, 3, 4, x); 119 | func2(1, 2, 3, 4, 5, x); 120 | func3(1, 2, 3, 4, 5, 6, x); 121 | func4(1, 2, 3, 4, 5, 6, 7, x); 122 | func5(1, 2, 3, x); 123 | func6(1, 2, x); 124 | func7(1, x); 125 | func8(1, 2, 3, x, 4); 126 | func9(1, 2, x, 3, 4); 127 | funca(1, x, 2, 3, 4); 128 | funcb(1, x, 2, y, 3); 129 | funcc(1, x, 2, y, z); 130 | funcd(1, x, 2, y, z, 3); 131 | } 132 | -------------------------------------------------------------------------------- /test/fuzzcases/array-too-large.c: -------------------------------------------------------------------------------- 1 | Jp[8e8];- -------------------------------------------------------------------------------- /test/fuzzcases/degenerate-function-decl.c: -------------------------------------------------------------------------------- 1 | __;b(()())o;; -------------------------------------------------------------------------------- /test/fuzzcases/double-amp-initializer.c: -------------------------------------------------------------------------------- 1 | H= -&&_/ 08 -------------------------------------------------------------------------------- /test/fuzzcases/empty-child-array-unspecified-init.c: -------------------------------------------------------------------------------- 1 | v94[][]= -------------------------------------------------------------------------------- /test/fuzzcases/incomplete-array-element-type.c: -------------------------------------------------------------------------------- 1 | l[6][][8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000][8][6]=68][8][6]=6;[~;zzz;zl; -------------------------------------------------------------------------------- /test/fuzzcases/incomplete-designator.c: -------------------------------------------------------------------------------- 1 | OO[52][26][]=[01][6] -------------------------------------------------------------------------------- /test/fuzzcases/init-expr-div-by-zero.c: -------------------------------------------------------------------------------- 1 | D=3<4%!3 -------------------------------------------------------------------------------- /test/fuzzcases/init-expr-div-by-zero2.c: -------------------------------------------------------------------------------- 1 | D=4/0u>8^7u%18^7u%1u -------------------------------------------------------------------------------- /test/fuzzcases/invalid-token-in-pp-if.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgraham/dyibicc/e02820c9b18a867cf3f109bd62385d7450f70258/test/fuzzcases/invalid-token-in-pp-if.c -------------------------------------------------------------------------------- /test/fuzzcases/label-initializer.c: -------------------------------------------------------------------------------- 1 | z=~~&z~~~~~~|~~~$z~~~= -------------------------------------------------------------------------------- /test/fuzzcases/mm-during-preprocessor-expression.c: -------------------------------------------------------------------------------- 1 | #if 2&--------------g}110/11[""[[""["[[""["(.2(-7 -------------------------------------------------------------------------------- /test/fuzzcases/more-zero-size-array-init.c: -------------------------------------------------------------------------------- 1 | OO[52][26][]=[01][6] -------------------------------------------------------------------------------- /test/fuzzcases/non-constant-array-reference.c: -------------------------------------------------------------------------------- 1 | v[2][&6]=v[2]T -------------------------------------------------------------------------------- /test/fuzzcases/postfix-inc-dec-preprocessor.c: -------------------------------------------------------------------------------- 1 | #if&hif&h++0f& -------------------------------------------------------------------------------- /test/fuzzcases/unclosed-char-literal-timeout.c: -------------------------------------------------------------------------------- 1 | l' '7'\ -------------------------------------------------------------------------------- /test/fuzzcases/unterminated-comment.c: -------------------------------------------------------------------------------- 1 | H// -------------------------------------------------------------------------------- /test/fuzzcases/unterminated-ifdef.c: -------------------------------------------------------------------------------- 1 | #ifdef 2 | -------------------------------------------------------------------------------- /test/fuzzcases/unterminated-ifndef.c: -------------------------------------------------------------------------------- 1 | #ifndef 2 | -------------------------------------------------------------------------------- /test/fuzzcases/unterminated-pp-line-directive.c: -------------------------------------------------------------------------------- 1 | #00- -------------------------------------------------------------------------------- /test/generic.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(1, _Generic(100.0, double: 1, int *: 2, int: 3, float: 4)); 5 | ASSERT(2, _Generic((int *)0, double: 1, int *: 2, int: 3, float: 4)); 6 | ASSERT(2, _Generic((int[3]){}, double: 1, int *: 2, int: 3, float: 4)); 7 | ASSERT(3, _Generic(100, double: 1, int *: 2, int: 3, float: 4)); 8 | ASSERT(4, _Generic(100f, double: 1, int *: 2, int: 3, float: 4)); 9 | 10 | printf("OK\n"); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /test/include1.h: -------------------------------------------------------------------------------- 1 | #include "include2.h" 2 | 3 | char *include1_filename = __FILE__; 4 | int include1_line = __LINE__; 5 | 6 | int include1 = 5; 7 | -------------------------------------------------------------------------------- /test/include2.h: -------------------------------------------------------------------------------- 1 | int include2 = 7; 2 | -------------------------------------------------------------------------------- /test/include3.h: -------------------------------------------------------------------------------- 1 | #define foo 3 2 | -------------------------------------------------------------------------------- /test/include4.h: -------------------------------------------------------------------------------- 1 | #define foo 4 2 | -------------------------------------------------------------------------------- /test/line.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | #line 500 "foo" 5 | ASSERT(501, __LINE__); 6 | ASSERT(0, strcmp(__FILE__, "foo")); 7 | 8 | #line 800 "bar" 9 | ASSERT(801, __LINE__); 10 | ASSERT(0, strcmp(__FILE__, "bar")); 11 | 12 | #line 1 13 | ASSERT(2, __LINE__); 14 | 15 | # 200 "xyz" 2 3 16 | ASSERT(201, __LINE__); 17 | ASSERT(0, strcmp(__FILE__, "xyz")); 18 | 19 | printf("OK\n"); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /test/line_directive_bug.c: -------------------------------------------------------------------------------- 1 | #if 1 2 | #line 1 "file" 3 | #endif 4 | int main() {} 5 | -------------------------------------------------------------------------------- /test/literal.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(97, 'a'); 5 | ASSERT(10, '\n'); 6 | ASSERT(-128, '\x80'); 7 | 8 | ASSERT(511, 0777); 9 | ASSERT(0, 0x0); 10 | ASSERT(10, 0xa); 11 | ASSERT(10, 0XA); 12 | ASSERT(48879, 0xbeef); 13 | ASSERT(48879, 0xBEEF); 14 | ASSERT(48879, 0XBEEF); 15 | ASSERT(0, 0b0); 16 | ASSERT(1, 0b1); 17 | ASSERT(47, 0b101111); 18 | ASSERT(47, 0B101111); 19 | 20 | ASSERT(4, sizeof(0)); 21 | ASSERT(8, sizeof(0L)); 22 | ASSERT(8, sizeof(0LU)); 23 | ASSERT(8, sizeof(0UL)); 24 | ASSERT(8, sizeof(0LL)); 25 | ASSERT(8, sizeof(0LLU)); 26 | ASSERT(8, sizeof(0Ull)); 27 | #ifdef _WIN64 28 | ASSERT(8, sizeof(0ui64)); 29 | ASSERT(8, sizeof(0i64)); 30 | ASSERT(8, sizeof(1ui64)); 31 | ASSERT(8, sizeof(1i64)); 32 | #endif 33 | ASSERT(8, sizeof(0l)); 34 | ASSERT(8, sizeof(0ll)); 35 | ASSERT(8, sizeof(0x0L)); 36 | ASSERT(8, sizeof(0b0L)); 37 | ASSERT(4, sizeof(2147483647)); 38 | ASSERT(8, sizeof(2147483648)); 39 | ASSERT(-1, 0xffffffffffffffff); 40 | ASSERT(8, sizeof(0xffffffffffffffff)); 41 | ASSERT(4, sizeof(4294967295U)); 42 | ASSERT(8, sizeof(4294967296U)); 43 | 44 | ASSERT(3, -1U>>30); 45 | ASSERT(3, -1Ul>>62); 46 | ASSERT(3, -1ull>>62); 47 | 48 | ASSERT(1, 0xffffffffffffffffl>>63); 49 | ASSERT(1, 0xffffffffffffffffll>>63); 50 | 51 | ASSERT(-1, 18446744073709551615); 52 | ASSERT(8, sizeof(18446744073709551615)); 53 | ASSERT(-1, 18446744073709551615>>63); 54 | 55 | ASSERT(-1, 0xffffffffffffffff); 56 | ASSERT(8, sizeof(0xffffffffffffffff)); 57 | ASSERT(1, 0xffffffffffffffff>>63); 58 | 59 | ASSERT(-1, 01777777777777777777777); 60 | ASSERT(8, sizeof(01777777777777777777777)); 61 | ASSERT(1, 01777777777777777777777>>63); 62 | 63 | ASSERT(-1, 0b1111111111111111111111111111111111111111111111111111111111111111); 64 | ASSERT(8, sizeof(0b1111111111111111111111111111111111111111111111111111111111111111)); 65 | ASSERT(1, 0b1111111111111111111111111111111111111111111111111111111111111111>>63); 66 | 67 | ASSERT(8, sizeof(2147483648)); 68 | ASSERT(4, sizeof(2147483647)); 69 | 70 | ASSERT(8, sizeof(0x1ffffffff)); 71 | ASSERT(4, sizeof(0xffffffff)); 72 | ASSERT(1, 0xffffffff>>31); 73 | 74 | ASSERT(8, sizeof(040000000000)); 75 | ASSERT(4, sizeof(037777777777)); 76 | ASSERT(1, 037777777777>>31); 77 | 78 | ASSERT(8, sizeof(0b111111111111111111111111111111111)); 79 | ASSERT(4, sizeof(0b11111111111111111111111111111111)); 80 | ASSERT(1, 0b11111111111111111111111111111111>>31); 81 | 82 | ASSERT(-1, 1 << 31 >> 31); 83 | ASSERT(-1, 01 << 31 >> 31); 84 | ASSERT(-1, 0x1 << 31 >> 31); 85 | ASSERT(-1, 0b1 << 31 >> 31); 86 | 87 | 0.0; 88 | 1.0; 89 | 3e+8; 90 | 0x10.1p0; 91 | .1E4f; 92 | 93 | ASSERT(4, sizeof(8f)); 94 | ASSERT(4, sizeof(0.3F)); 95 | ASSERT(8, sizeof(0.)); 96 | ASSERT(8, sizeof(.0)); 97 | ASSERT(__SIZEOF_LONG_DOUBLE__, sizeof(5.l)); 98 | ASSERT(__SIZEOF_LONG_DOUBLE__, sizeof(2.0L)); 99 | 100 | assert(1, size\ 101 | of(char), \ 102 | "sizeof(char)"); 103 | 104 | ASSERT(4, sizeof(L'\0')); 105 | ASSERT(97, L'a'); 106 | 107 | printf("OK\n"); 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /test/macro.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include "include1.h" 3 | 4 | char *main_filename1 = __FILE__; 5 | int main_line1 = __LINE__; 6 | #define LINE() __LINE__ 7 | int main_line2 = LINE(); 8 | 9 | # 10 | 11 | /* */ # 12 | 13 | int ret3(void) { return 3; } 14 | int dbl(int x) { return x*x; } 15 | 16 | int add2(int x, int y) { 17 | return x + y; 18 | } 19 | 20 | int add6(int a, int b, int c, int d, int e, int f) { 21 | return a + b + c + d + e + f; 22 | } 23 | 24 | int main() { 25 | ASSERT(5, include1); 26 | ASSERT(7, include2); 27 | 28 | #if 0 29 | #include "/no/such/file" 30 | ASSERT(0, 1); 31 | #if nested 32 | #endif 33 | #endif 34 | 35 | int m = 0; 36 | 37 | #if 1 38 | m = 5; 39 | #endif 40 | ASSERT(5, m); 41 | 42 | #if 1 43 | # if 0 44 | # if 1 45 | foo bar 46 | # endif 47 | # endif 48 | m = 3; 49 | #endif 50 | ASSERT(3, m); 51 | 52 | #if 1-1 53 | # if 1 54 | # endif 55 | # if 1 56 | # else 57 | # endif 58 | # if 0 59 | # else 60 | # endif 61 | m = 2; 62 | #else 63 | # if 1 64 | m = 3; 65 | # endif 66 | #endif 67 | ASSERT(3, m); 68 | 69 | #if 1 70 | m = 2; 71 | #else 72 | m = 3; 73 | #endif 74 | ASSERT(2, m); 75 | 76 | #if 1 77 | m = 2; 78 | #else 79 | m = 3; 80 | #endif 81 | ASSERT(2, m); 82 | 83 | #if 0 84 | m = 1; 85 | #elif 0 86 | m = 2; 87 | #elif 3+5 88 | m = 3; 89 | #elif 1*5 90 | m = 4; 91 | #endif 92 | ASSERT(3, m); 93 | 94 | #if 1+5 95 | m = 1; 96 | #elif 1 97 | m = 2; 98 | #elif 3 99 | m = 2; 100 | #endif 101 | ASSERT(1, m); 102 | 103 | #if 0 104 | m = 1; 105 | #elif 1 106 | # if 1 107 | m = 2; 108 | # else 109 | m = 3; 110 | # endif 111 | #else 112 | m = 5; 113 | #endif 114 | ASSERT(2, m); 115 | 116 | int M1 = 5; 117 | 118 | #define M1 3 119 | ASSERT(3, M1); 120 | #define M1 4 121 | ASSERT(4, M1); 122 | 123 | #define M1 3+4+ 124 | ASSERT(12, M1 5); 125 | 126 | #define M1 3+4 127 | ASSERT(23, M1*5); 128 | 129 | #define ASSERT_ assert( 130 | #define if 5 131 | #define five "5" 132 | #define END ) 133 | ASSERT_ 5, if, five END; 134 | 135 | #undef ASSERT_ 136 | #undef if 137 | #undef five 138 | #undef END 139 | 140 | if (0); 141 | 142 | #define M 5 143 | #if M 144 | m = 5; 145 | #else 146 | m = 6; 147 | #endif 148 | ASSERT(5, m); 149 | 150 | #define M 5 151 | #if M-5 152 | m = 6; 153 | #elif M 154 | m = 5; 155 | #endif 156 | ASSERT(5, m); 157 | 158 | int M2 = 6; 159 | #define M2 M2 + 3 160 | ASSERT(9, M2); 161 | 162 | #define M3 M2 + 3 163 | ASSERT(12, M3); 164 | 165 | int M4 = 3; 166 | #define M4 M5 * 5 167 | #define M5 M4 + 2 168 | ASSERT(13, M4); 169 | 170 | #ifdef M6 171 | m = 5; 172 | #else 173 | m = 3; 174 | #endif 175 | ASSERT(3, m); 176 | 177 | #define M6 178 | #ifdef M6 179 | m = 5; 180 | #else 181 | m = 3; 182 | #endif 183 | ASSERT(5, m); 184 | 185 | #ifndef M7 186 | m = 3; 187 | #else 188 | m = 5; 189 | #endif 190 | ASSERT(3, m); 191 | 192 | #define M7 193 | #ifndef M7 194 | m = 3; 195 | #else 196 | m = 5; 197 | #endif 198 | ASSERT(5, m); 199 | 200 | #if 0 201 | #ifdef NO_SUCH_MACRO 202 | #endif 203 | #ifndef NO_SUCH_MACRO 204 | #endif 205 | #else 206 | #endif 207 | 208 | #define M7() 1 209 | int M7 = 5; 210 | ASSERT(1, M7()); 211 | ASSERT(5, M7); 212 | 213 | #define M7 () 214 | ASSERT(3, ret3 M7); 215 | 216 | #define M8(x,y) x+y 217 | ASSERT(7, M8(3, 4)); 218 | 219 | #define M8(x,y) x*y 220 | ASSERT(24, M8(3+4, 4+5)); 221 | 222 | #define M8(x,y) (x)*(y) 223 | ASSERT(63, M8(3+4, 4+5)); 224 | 225 | #define M8(x,y) x y 226 | ASSERT(9, M8(, 4+5)); 227 | 228 | #define M8(x,y) x*y 229 | ASSERT(20, M8((2+3), 4)); 230 | 231 | #define M8(x,y) x*y 232 | ASSERT(12, M8((2,3), 4)); 233 | 234 | #define dbl(x) M10(x) * x 235 | #define M10(x) dbl(x) + 3 236 | ASSERT(10, dbl(2)); 237 | 238 | #define M11(x) #x 239 | ASSERT('a', M11( a!b `""c)[0]); 240 | ASSERT('!', M11( a!b `""c)[1]); 241 | ASSERT('b', M11( a!b `""c)[2]); 242 | ASSERT(' ', M11( a!b `""c)[3]); 243 | ASSERT('`', M11( a!b `""c)[4]); 244 | ASSERT('"', M11( a!b `""c)[5]); 245 | ASSERT('"', M11( a!b `""c)[6]); 246 | ASSERT('c', M11( a!b `""c)[7]); 247 | ASSERT(0, M11( a!b `""c)[8]); 248 | 249 | #define paste(x,y) x##y 250 | ASSERT(15, paste(1,5)); 251 | ASSERT(255, paste(0,xff)); 252 | ASSERT(3, ({ int foobar=3; paste(foo,bar); })); 253 | ASSERT(5, paste(5,)); 254 | ASSERT(5, paste(,5)); 255 | 256 | #define i 5 257 | ASSERT(101, ({ int i3=100; paste(1+i,3); })); 258 | #undef i 259 | 260 | #define paste2(x) x##5 261 | ASSERT(26, paste2(1+2)); 262 | 263 | #define paste3(x) 2##x 264 | ASSERT(23, paste3(1+2)); 265 | 266 | #define paste4(x, y, z) x##y##z 267 | ASSERT(123, paste4(1,2,3)); 268 | 269 | #define M12 270 | #if defined(M12) 271 | m = 3; 272 | #else 273 | m = 4; 274 | #endif 275 | ASSERT(3, m); 276 | 277 | #define M12 278 | #if defined M12 279 | m = 3; 280 | #else 281 | m = 4; 282 | #endif 283 | ASSERT(3, m); 284 | 285 | #if defined(M12) - 1 286 | m = 3; 287 | #else 288 | m = 4; 289 | #endif 290 | ASSERT(4, m); 291 | 292 | #if defined(NO_SUCH_MACRO) 293 | m = 3; 294 | #else 295 | m = 4; 296 | #endif 297 | ASSERT(4, m); 298 | 299 | #if no_such_symbol == 0 300 | m = 5; 301 | #else 302 | m = 6; 303 | #endif 304 | ASSERT(5, m); 305 | 306 | #define STR(x) #x 307 | #define M12(x) STR(x) 308 | #define M13(x) M12(foo.x) 309 | ASSERT(0, strcmp(M13(bar), "foo.bar")); 310 | 311 | #define M13(x) M12(foo. x) 312 | ASSERT(0, strcmp(M13(bar), "foo. bar")); 313 | 314 | #define M12 foo 315 | #define M13(x) STR(x) 316 | #define M14(x) M13(x.M12) 317 | ASSERT(0, strcmp(M14(bar), "bar.foo")); 318 | 319 | #define M14(x) M13(x. M12) 320 | ASSERT(0, strcmp(M14(bar), "bar. foo")); 321 | 322 | #include "include3.h" 323 | ASSERT(3, foo); 324 | 325 | #include "include4.h" 326 | ASSERT(4, foo); 327 | 328 | #define M13 "include3.h" 329 | #include M13 330 | ASSERT(3, foo); 331 | 332 | #define M13 < include4.h 333 | #include M13 > 334 | ASSERT(4, foo); 335 | 336 | #undef foo 337 | 338 | ASSERT(1, __STDC__); 339 | 340 | ASSERT(0, strcmp(main_filename1, "test/macro.c")); 341 | ASSERT(5, main_line1); 342 | ASSERT(7, main_line2); 343 | ASSERT(0, strcmp(include1_filename, "test/include1.h")); 344 | ASSERT(4, include1_line); 345 | 346 | #define M14(...) 3 347 | ASSERT(3, M14()); 348 | 349 | #define M14(...) __VA_ARGS__ 350 | ASSERT(2, M14() 2); 351 | ASSERT(5, M14(5)); 352 | 353 | #define M14(...) add2(__VA_ARGS__) 354 | ASSERT(8, M14(2, 6)); 355 | 356 | #define M14(...) add6(1,2,__VA_ARGS__,6) 357 | ASSERT(21, M14(3,4,5)); 358 | 359 | #define M14(x, ...) add6(1,2,x,__VA_ARGS__,6) 360 | ASSERT(21, M14(3,4,5)); 361 | 362 | #define M14(args...) 3 363 | ASSERT(3, M14()); 364 | 365 | #define M14(x, ...) x 366 | ASSERT(5, M14(5)); 367 | 368 | #define M14(args...) args 369 | ASSERT(2, M14() 2); 370 | ASSERT(5, M14(5)); 371 | 372 | #define M14(args...) add2(args) 373 | ASSERT(8, M14(2, 6)); 374 | 375 | #define M14(args...) add6(1,2,args,6) 376 | ASSERT(21, M14(3,4,5)); 377 | 378 | #define M14(x, args...) add6(1,2,x,args,6) 379 | ASSERT(21, M14(3,4,5)); 380 | 381 | #define M14(x, args...) x 382 | ASSERT(5, M14(5)); 383 | 384 | #define CONCAT(x,y) x##y 385 | ASSERT(5, ({ int f0zz=5; CONCAT(f,0zz); })); 386 | ASSERT(5, ({ CONCAT(4,.57) + 0.5; })); 387 | 388 | ASSERT(11, strlen(__DATE__)); 389 | ASSERT(8, strlen(__TIME__)); 390 | 391 | ASSERT(0, __COUNTER__); 392 | ASSERT(1, __COUNTER__); 393 | ASSERT(2, __COUNTER__); 394 | 395 | ASSERT(24, strlen(__TIMESTAMP__)); 396 | 397 | ASSERT(0, strcmp(__BASE_FILE__, "test/macro.c")); 398 | 399 | #define M30(buf, fmt, ...) sprintf(buf, fmt __VA_OPT__(,) __VA_ARGS__) 400 | ASSERT(0, ({ char buf[100]; M30(buf, "foo"); strcmp(buf, "foo"); })); 401 | ASSERT(0, ({ char buf[100]; M30(buf, "foo%d", 3); strcmp(buf, "foo3"); })); 402 | ASSERT(0, ({ char buf[100]; M30(buf, "foo%d%d", 3, 5); strcmp(buf, "foo35"); })); 403 | 404 | #define M31(buf, fmt, ...) sprintf(buf, fmt, ## __VA_ARGS__) 405 | ASSERT(0, ({ char buf[100]; M31(buf, "foo"); strcmp(buf, "foo"); })); 406 | ASSERT(0, ({ char buf[100]; M31(buf, "foo%d", 3); strcmp(buf, "foo3"); })); 407 | ASSERT(0, ({ char buf[100]; M31(buf, "foo%d%d", 3, 5); strcmp(buf, "foo35"); })); 408 | 409 | #define M31(x, y) (1, ##x y) 410 | ASSERT(3, M31(, 3)); 411 | 412 | printf("OK\n"); 413 | return 0; 414 | } 415 | -------------------------------------------------------------------------------- /test/offsetof.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | 4 | typedef struct { 5 | int a; 6 | char b; 7 | int c; 8 | double d; 9 | } T; 10 | 11 | int main() { 12 | ASSERT(0, offsetof(T, a)); 13 | ASSERT(4, offsetof(T, b)); 14 | ASSERT(8, offsetof(T, c)); 15 | ASSERT(16, offsetof(T, d)); 16 | 17 | printf("OK\n"); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /test/pointer.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(3, ({ int x=3; *&x; })); 5 | ASSERT(3, ({ int x=3; int *y=&x; int **z=&y; **z; })); 6 | ASSERT(5, ({ int x=3; int y=5; *(&x+1); })); 7 | ASSERT(3, ({ int x=3; int y=5; *(&y-1); })); 8 | ASSERT(5, ({ int x=3; int y=5; *(&x-(-1)); })); 9 | ASSERT(5, ({ int x=3; int *y=&x; *y=5; x; })); 10 | ASSERT(7, ({ int x=3; int y=5; *(&x+1)=7; y; })); 11 | ASSERT(7, ({ int x=3; int y=5; *(&y-2+1)=7; x; })); 12 | ASSERT(5, ({ int x=3; (&x+2)-&x+3; })); 13 | ASSERT(8, ({ int x, y; x=3; y=5; x+y; })); 14 | ASSERT(8, ({ int x=3, y=5; x+y; })); 15 | 16 | ASSERT(3, ({ int x[2]; int *y=&x; *y=3; *x; })); 17 | 18 | ASSERT(3, ({ int x[3]; *x=3; *(x+1)=4; *(x+2)=5; *x; })); 19 | ASSERT(4, ({ int x[3]; *x=3; *(x+1)=4; *(x+2)=5; *(x+1); })); 20 | ASSERT(5, ({ int x[3]; *x=3; *(x+1)=4; *(x+2)=5; *(x+2); })); 21 | 22 | ASSERT(0, ({ int x[2][3]; int *y=x; *y=0; **x; })); 23 | ASSERT(1, ({ int x[2][3]; int *y=x; *(y+1)=1; *(*x+1); })); 24 | ASSERT(2, ({ int x[2][3]; int *y=x; *(y+2)=2; *(*x+2); })); 25 | ASSERT(3, ({ int x[2][3]; int *y=x; *(y+3)=3; **(x+1); })); 26 | ASSERT(4, ({ int x[2][3]; int *y=x; *(y+4)=4; *(*(x+1)+1); })); 27 | ASSERT(5, ({ int x[2][3]; int *y=x; *(y+5)=5; *(*(x+1)+2); })); 28 | 29 | ASSERT(3, ({ int x[3]; *x=3; x[1]=4; x[2]=5; *x; })); 30 | ASSERT(4, ({ int x[3]; *x=3; x[1]=4; x[2]=5; *(x+1); })); 31 | ASSERT(5, ({ int x[3]; *x=3; x[1]=4; x[2]=5; *(x+2); })); 32 | ASSERT(5, ({ int x[3]; *x=3; x[1]=4; x[2]=5; *(x+2); })); 33 | ASSERT(5, ({ int x[3]; *x=3; x[1]=4; 2[x]=5; *(x+2); })); 34 | 35 | ASSERT(0, ({ int x[2][3]; int *y=x; y[0]=0; x[0][0]; })); 36 | ASSERT(1, ({ int x[2][3]; int *y=x; y[1]=1; x[0][1]; })); 37 | ASSERT(2, ({ int x[2][3]; int *y=x; y[2]=2; x[0][2]; })); 38 | ASSERT(3, ({ int x[2][3]; int *y=x; y[3]=3; x[1][0]; })); 39 | ASSERT(4, ({ int x[2][3]; int *y=x; y[4]=4; x[1][1]; })); 40 | ASSERT(5, ({ int x[2][3]; int *y=x; y[5]=5; x[1][2]; })); 41 | 42 | printf("OK\n"); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /test/pragma-once.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #pragma once 4 | 5 | #include "test/pragma-once.c" 6 | 7 | int main() { 8 | printf("OK\n"); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /test/reflect.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "test.h" 6 | 7 | bool SomeFunc(void) {} 8 | int SomeFunc2(char a, float b, short c, double d) {} 9 | 10 | typedef struct Xyz { 11 | int a; 12 | } Xyz; 13 | 14 | struct NoTypedef { 15 | int x; 16 | }; 17 | 18 | typedef struct SelfRef { 19 | int zippy; 20 | float zappy; 21 | double blorp; 22 | struct SelfRef* next; 23 | } SelfRef; 24 | 25 | int main(void) { 26 | _ReflectType* t_int = _ReflectTypeOf(int); 27 | ASSERT(0, strcmp(t_int->name, "int")); 28 | ASSERT(_REFLECT_KIND_INT, t_int->kind); 29 | ASSERT(4, t_int->size); 30 | ASSERT(4, t_int->align); 31 | 32 | _ReflectType* t_uint = _ReflectTypeOf(unsigned int); 33 | ASSERT(0, strcmp(t_uint->name, "unsigned int")); 34 | ASSERT(_REFLECT_KIND_INT, t_uint->kind); 35 | ASSERT(4, t_uint->size); 36 | ASSERT(4, t_uint->align); 37 | ASSERT(_REFLECT_TYPEFLAG_UNSIGNED, t_uint->flags); 38 | 39 | _ReflectType* t_expr_int = _ReflectTypeOf(4+5); 40 | ASSERT(1, t_expr_int == t_int); 41 | _ReflectType* t_expr_uint = _ReflectTypeOf(4u+5); 42 | ASSERT(1, t_expr_uint == t_uint); 43 | 44 | _ReflectType* t_bool0 = _ReflectTypeOf(_Bool); 45 | _ReflectType* t_bool1 = _ReflectTypeOf(bool); 46 | // "false" and "true" are just 0 and 1, so getting their type is int, not bool. 47 | ASSERT(0, strcmp(t_bool0->name, "bool")); 48 | ASSERT(_REFLECT_KIND_BOOL, t_bool0->kind); 49 | ASSERT(1, t_bool0->size); 50 | ASSERT(1, t_bool0->align); 51 | ASSERT(1, t_bool0 == t_bool1); 52 | 53 | _ReflectType* t_char = _ReflectTypeOf(char); 54 | ASSERT(0, strcmp(t_char->name, "char")); 55 | ASSERT(_REFLECT_KIND_CHAR, t_char->kind); 56 | ASSERT(1, t_char->size); 57 | ASSERT(1, t_char->align); 58 | 59 | _ReflectType* t_short = _ReflectTypeOf(short); 60 | ASSERT(0, strcmp(t_short->name, "short")); 61 | ASSERT(_REFLECT_KIND_SHORT, t_short->kind); 62 | ASSERT(2, t_short->size); 63 | ASSERT(2, t_short->align); 64 | 65 | _ReflectType* t_float = _ReflectTypeOf(float); 66 | ASSERT(0, strcmp(t_float->name, "float")); 67 | ASSERT(_REFLECT_KIND_FLOAT, t_float->kind); 68 | ASSERT(4, t_float->size); 69 | ASSERT(4, t_float->align); 70 | 71 | _ReflectType* t_double = _ReflectTypeOf(double); 72 | ASSERT(0, strcmp(t_double->name, "double")); 73 | ASSERT(_REFLECT_KIND_DOUBLE, t_double->kind); 74 | ASSERT(8, t_double->size); 75 | ASSERT(8, t_double->align); 76 | 77 | _ReflectType* t_int64 = _ReflectTypeOf(int64_t); 78 | ASSERT(_REFLECT_KIND_LONG, t_int64->kind); 79 | #if __SIZEOF_LONG__ == 4 80 | ASSERT(0, strcmp(t_int64->name, "long long")); 81 | #else 82 | ASSERT(0, strcmp(t_int64->name, "long")); 83 | #endif 84 | ASSERT(8, t_int64->size); 85 | ASSERT(8, t_int64->align); 86 | 87 | _ReflectType* t_charp = _ReflectTypeOf(char*); 88 | ASSERT(0, strcmp(t_charp->name, "char*")); 89 | ASSERT(_REFLECT_KIND_PTR, t_charp->kind); 90 | ASSERT(8, t_charp->size); 91 | ASSERT(8, t_charp->align); 92 | ASSERT(1, t_charp->ptr.base == t_char); 93 | 94 | _ReflectType* t_charpp = _ReflectTypeOf(char**); 95 | ASSERT(0, strcmp(t_charpp->name, "char**")); 96 | ASSERT(_REFLECT_KIND_PTR, t_charpp->kind); 97 | ASSERT(8, t_charpp->size); 98 | ASSERT(8, t_charpp->align); 99 | ASSERT(1, t_charpp->ptr.base == t_charp); 100 | 101 | char* x = "zippy"; 102 | ASSERT(1, _ReflectTypeOf(x) == t_charp); 103 | 104 | _ReflectType* t_intp = _ReflectTypeOf(int*); 105 | ASSERT(0, strcmp(t_intp->name, "int*")); 106 | ASSERT(_REFLECT_KIND_PTR, t_intp->kind); 107 | ASSERT(8, t_intp->size); 108 | ASSERT(8, t_intp->align); 109 | ASSERT(1, t_intp->ptr.base == t_int); 110 | 111 | int y[5]; 112 | _ReflectType* t_intarr5 = _ReflectTypeOf(y); 113 | ASSERT(0, strcmp(t_intarr5->name, "int [5]")); 114 | ASSERT(_REFLECT_KIND_ARRAY, t_intarr5->kind); 115 | ASSERT(5*sizeof(int), t_intarr5->size); 116 | ASSERT(4, t_intarr5->align); 117 | ASSERT(1, t_intarr5->arr.base == t_int); 118 | ASSERT(5, t_intarr5->arr.len); 119 | 120 | float(**ppz)[5]; 121 | _ReflectType* t_pparr5 = _ReflectTypeOf(ppz); 122 | ASSERT(0, strcmp(t_pparr5->name, "float (**) [5]")); 123 | ASSERT(_REFLECT_KIND_PTR, t_pparr5->kind); 124 | ASSERT(8, t_pparr5->size); 125 | ASSERT(8, t_pparr5->align); 126 | 127 | _ReflectType* t_func = _ReflectTypeOf(SomeFunc); 128 | ASSERT(0, strcmp(t_func->name, "bool (void)")); 129 | 130 | _ReflectType* t_func2 = _ReflectTypeOf(SomeFunc2); 131 | ASSERT(0, strcmp(t_func2->name, "int (char, float, short, double)")); 132 | 133 | int (*Funcy)(void); 134 | _ReflectType* t_funcptrvoid = _ReflectTypeOf(Funcy); 135 | ASSERT(0, strcmp(t_funcptrvoid->name, "int (*)(void)")); 136 | 137 | int (*(*Funcy2)(void))[3]; 138 | _ReflectType* t_fpretparr = _ReflectTypeOf(Funcy2); 139 | ASSERT(0, strcmp(t_fpretparr->name, "int (* (*)(void)) [3]")); 140 | 141 | float(**(*lsd)(int (*)(double)))[5][6]; 142 | _ReflectType* t_ptr_to_func_taking_fptr_returning_pp_5_6_float = _ReflectTypeOf(lsd); 143 | ASSERT(0, strcmp(t_ptr_to_func_taking_fptr_returning_pp_5_6_float->name, 144 | "float (** (*)(int (*)(double))) [5][6]")); 145 | 146 | _ReflectType* t_xyz = _ReflectTypeOf(Xyz); 147 | ASSERT(0, strcmp(t_xyz->name, "Xyz")); 148 | ASSERT(_REFLECT_KIND_STRUCT, t_xyz->kind); 149 | ASSERT(4, t_xyz->size); 150 | ASSERT(4, t_xyz->align); 151 | ASSERT(0, t_xyz->flags); 152 | ASSERT(1, t_xyz->su.num_members); 153 | ASSERT(1, t_xyz->su.members[0].type == t_int); 154 | ASSERT(0, strcmp(t_xyz->su.members[0].name, "a")); 155 | ASSERT(4, t_xyz->su.members[0].align); 156 | ASSERT(0, t_xyz->su.members[0].offset); 157 | ASSERT(-1, t_xyz->su.members[0].bit_width); 158 | ASSERT(-1, t_xyz->su.members[0].bit_offset); 159 | 160 | _ReflectType* t_pxyz = _ReflectTypeOf(Xyz*); 161 | ASSERT(0, strcmp(t_pxyz->name, "Xyz*")); 162 | ASSERT(1, t_pxyz->ptr.base == t_xyz); 163 | 164 | _ReflectType* t_selfref = _ReflectTypeOf(SelfRef); 165 | _ReflectType* t_pselfref = _ReflectTypeOf(SelfRef*); 166 | ASSERT(0, strcmp(t_selfref->name, "SelfRef")); 167 | ASSERT(_REFLECT_KIND_STRUCT, t_selfref->kind); 168 | ASSERT(24, t_selfref->size); 169 | ASSERT(8, t_selfref->align); 170 | ASSERT(0, t_selfref->flags); 171 | ASSERT(4, t_selfref->su.num_members); 172 | 173 | ASSERT(1, t_selfref->su.members[0].type == t_int); 174 | ASSERT(0, strcmp(t_selfref->su.members[0].name, "zippy")); 175 | ASSERT(4, t_selfref->su.members[0].align); 176 | ASSERT(0, t_selfref->su.members[0].offset); 177 | ASSERT(-1, t_selfref->su.members[0].bit_width); 178 | ASSERT(-1, t_selfref->su.members[0].bit_offset); 179 | 180 | ASSERT(1, t_selfref->su.members[1].type == t_float); 181 | ASSERT(0, strcmp(t_selfref->su.members[1].name, "zappy")); 182 | ASSERT(4, t_selfref->su.members[1].align); 183 | ASSERT(4, t_selfref->su.members[1].offset); 184 | ASSERT(-1, t_selfref->su.members[1].bit_width); 185 | ASSERT(-1, t_selfref->su.members[1].bit_offset); 186 | 187 | ASSERT(1, t_selfref->su.members[2].type == t_double); 188 | ASSERT(0, strcmp(t_selfref->su.members[2].name, "blorp")); 189 | ASSERT(8, t_selfref->su.members[2].align); 190 | ASSERT(8, t_selfref->su.members[2].offset); 191 | ASSERT(-1, t_selfref->su.members[2].bit_width); 192 | ASSERT(-1, t_selfref->su.members[2].bit_offset); 193 | 194 | ASSERT(1, t_selfref->su.members[3].type == t_pselfref); 195 | ASSERT(0, strcmp(t_selfref->su.members[3].name, "next")); 196 | ASSERT(8, t_selfref->su.members[3].align); 197 | ASSERT(16, t_selfref->su.members[3].offset); 198 | ASSERT(-1, t_selfref->su.members[3].bit_width); 199 | ASSERT(-1, t_selfref->su.members[3].bit_offset); 200 | 201 | _ReflectType* t_notypedef = _ReflectTypeOf(struct NoTypedef); 202 | ASSERT(0, strcmp(t_notypedef->name, "NoTypedef")); // "struct NoTypedef"? 203 | 204 | _ReflectType* t_pnotypedef = _ReflectTypeOf(struct NoTypedef*); 205 | ASSERT(0, strcmp(t_pnotypedef->name, "NoTypedef*")); 206 | 207 | return 0; 208 | } 209 | -------------------------------------------------------------------------------- /test/sizeof.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(1, sizeof(char)); 5 | ASSERT(2, sizeof(short)); 6 | ASSERT(2, sizeof(short int)); 7 | ASSERT(2, sizeof(int short)); 8 | ASSERT(4, sizeof(int)); 9 | ASSERT(__SIZEOF_LONG__, sizeof(long)); 10 | ASSERT(__SIZEOF_LONG__, sizeof(long int)); 11 | ASSERT(__SIZEOF_LONG__, sizeof(long int)); 12 | ASSERT(8, sizeof(char *)); 13 | ASSERT(8, sizeof(int *)); 14 | ASSERT(8, sizeof(long *)); 15 | ASSERT(8, sizeof(int **)); 16 | ASSERT(8, sizeof(int(*)[4])); 17 | ASSERT(32, sizeof(int*[4])); 18 | ASSERT(16, sizeof(int[4])); 19 | ASSERT(48, sizeof(int[3][4])); 20 | ASSERT(8, sizeof(struct {int a; int b;})); 21 | 22 | ASSERT(__SIZEOF_LONG__, sizeof(-10 + (long)5)); 23 | ASSERT(__SIZEOF_LONG__, sizeof(-10 - (long)5)); 24 | ASSERT(__SIZEOF_LONG__, sizeof(-10 * (long)5)); 25 | ASSERT(__SIZEOF_LONG__, sizeof(-10 / (long)5)); 26 | ASSERT(__SIZEOF_LONG__, sizeof((long)-10 + 5)); 27 | ASSERT(__SIZEOF_LONG__, sizeof((long)-10 - 5)); 28 | ASSERT(__SIZEOF_LONG__, sizeof((long)-10 * 5)); 29 | ASSERT(__SIZEOF_LONG__, sizeof((long)-10 / 5)); 30 | 31 | ASSERT(1, ({ char i; sizeof(++i); })); 32 | ASSERT(1, ({ char i; sizeof(i++); })); 33 | 34 | ASSERT(8, sizeof(int(*)[10])); 35 | ASSERT(8, sizeof(int(*)[][10])); 36 | 37 | ASSERT(4, sizeof(struct { int x, y[]; })); 38 | 39 | ASSERT(1, sizeof(char)); 40 | ASSERT(1, sizeof(signed char)); 41 | ASSERT(1, sizeof(signed char signed)); 42 | ASSERT(1, sizeof(unsigned char)); 43 | ASSERT(1, sizeof(unsigned char unsigned)); 44 | 45 | ASSERT(2, sizeof(short)); 46 | ASSERT(2, sizeof(int short)); 47 | ASSERT(2, sizeof(short int)); 48 | ASSERT(2, sizeof(signed short)); 49 | ASSERT(2, sizeof(int short signed)); 50 | ASSERT(2, sizeof(unsigned short)); 51 | ASSERT(2, sizeof(int short unsigned)); 52 | 53 | ASSERT(4, sizeof(int)); 54 | ASSERT(4, sizeof(signed int)); 55 | ASSERT(4, sizeof(signed)); 56 | ASSERT(4, sizeof(signed signed)); 57 | ASSERT(4, sizeof(unsigned int)); 58 | ASSERT(4, sizeof(unsigned)); 59 | ASSERT(4, sizeof(unsigned unsigned)); 60 | 61 | ASSERT(__SIZEOF_LONG__, sizeof(long)); 62 | ASSERT(__SIZEOF_LONG__, sizeof(signed long)); 63 | ASSERT(__SIZEOF_LONG__, sizeof(signed long int)); 64 | ASSERT(__SIZEOF_LONG__, sizeof(unsigned long)); 65 | ASSERT(__SIZEOF_LONG__, sizeof(unsigned long int)); 66 | 67 | ASSERT(8, sizeof(long long)); 68 | ASSERT(8, sizeof(signed long long)); 69 | ASSERT(8, sizeof(signed long long int)); 70 | ASSERT(8, sizeof(unsigned long long)); 71 | ASSERT(8, sizeof(unsigned long long int)); 72 | 73 | ASSERT(1, sizeof((char)1)); 74 | ASSERT(2, sizeof((short)1)); 75 | ASSERT(4, sizeof((int)1)); 76 | ASSERT(__SIZEOF_LONG__, sizeof((long)1)); 77 | 78 | ASSERT(4, sizeof((char)1 + (char)1)); 79 | ASSERT(4, sizeof((short)1 + (short)1)); 80 | ASSERT(4, sizeof(1?2:3)); 81 | ASSERT(4, sizeof(1?(short)2:(char)3)); 82 | ASSERT(__SIZEOF_LONG__, sizeof(1?(long)2:(char)3)); 83 | 84 | ASSERT(1, sizeof(char) << 31 >> 31); 85 | ASSERT(1, sizeof(char) << 63 >> 63); 86 | 87 | ASSERT(4, sizeof(float)); 88 | ASSERT(8, sizeof(double)); 89 | 90 | ASSERT(4, sizeof(1f+2)); 91 | ASSERT(8, sizeof(1.0+2)); 92 | ASSERT(4, sizeof(1f-2)); 93 | ASSERT(8, sizeof(1.0-2)); 94 | ASSERT(4, sizeof(1f*2)); 95 | ASSERT(8, sizeof(1.0*2)); 96 | ASSERT(4, sizeof(1f/2)); 97 | ASSERT(8, sizeof(1.0/2)); 98 | 99 | ASSERT(__SIZEOF_LONG_DOUBLE__, sizeof(long double)); 100 | 101 | ASSERT(1, sizeof(main)); 102 | 103 | printf("OK\n"); 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /test/stdhdr.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | printf("OK\n"); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /test/string.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(0, ""[0]); 5 | ASSERT(1, sizeof("")); 6 | 7 | ASSERT(97, "abc"[0]); 8 | ASSERT(98, "abc"[1]); 9 | ASSERT(99, "abc"[2]); 10 | ASSERT(0, "abc"[3]); 11 | ASSERT(4, sizeof("abc")); 12 | 13 | ASSERT(7, "\a"[0]); 14 | ASSERT(8, "\b"[0]); 15 | ASSERT(9, "\t"[0]); 16 | ASSERT(10, "\n"[0]); 17 | ASSERT(11, "\v"[0]); 18 | ASSERT(12, "\f"[0]); 19 | ASSERT(13, "\r"[0]); 20 | ASSERT(27, "\e"[0]); 21 | 22 | ASSERT(106, "\j"[0]); 23 | ASSERT(107, "\k"[0]); 24 | ASSERT(108, "\l"[0]); 25 | 26 | ASSERT(7, "\ax\ny"[0]); 27 | ASSERT(120, "\ax\ny"[1]); 28 | ASSERT(10, "\ax\ny"[2]); 29 | ASSERT(121, "\ax\ny"[3]); 30 | 31 | ASSERT(0, "\0"[0]); 32 | ASSERT(16, "\20"[0]); 33 | ASSERT(65, "\101"[0]); 34 | ASSERT(104, "\1500"[0]); 35 | ASSERT(0, "\x00"[0]); 36 | ASSERT(119, "\x77"[0]); 37 | 38 | ASSERT(7, sizeof("abc" "def")); 39 | ASSERT(9, sizeof("abc" "d" "efgh")); 40 | ASSERT(0, strcmp("abc" "d" "\nefgh", "abcd\nefgh")); 41 | ASSERT(0, !strcmp("abc" "d", "abcd\nefgh")); 42 | ASSERT(0, strcmp("\x9" "0", "\t0")); 43 | 44 | ASSERT(16, sizeof(L"abc" "")); 45 | 46 | ASSERT(28, sizeof(L"abc" "def")); 47 | ASSERT(28, sizeof(L"abc" L"def")); 48 | ASSERT(14, sizeof(u"abc" "def")); 49 | ASSERT(14, sizeof(u"abc" u"def")); 50 | 51 | ASSERT(L'a', (L"abc" "def")[0]); 52 | ASSERT(L'd', (L"abc" "def")[3]); 53 | ASSERT(L'\0', (L"abc" "def")[6]); 54 | 55 | ASSERT(u'a', (u"abc" "def")[0]); 56 | ASSERT(u'd', (u"abc" "def")[3]); 57 | ASSERT(u'\0', (u"abc" "def")[6]); 58 | 59 | ASSERT(L'あ', ("あ" L"")[0]); 60 | ASSERT(0343, ("\343\201\202" L"")[0]); 61 | ASSERT(0201, ("\343\201\202" L"")[1]); 62 | ASSERT(0202, ("\343\201\202" L"")[2]); 63 | ASSERT(0, ("\343\201\202" L"")[3]); 64 | 65 | ASSERT(L'a', ("a" "b" L"c")[0]); 66 | ASSERT(L'b', ("a" "b" L"c")[1]); 67 | ASSERT(L'c', ("a" "b" L"c")[2]); 68 | ASSERT(0, ("a" "b" L"c")[3]); 69 | 70 | printf("OK\n"); 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /test/struct.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(1, ({ struct {int a; int b;} x; x.a=1; x.b=2; x.a; })); 5 | ASSERT(2, ({ struct {int a; int b;} x; x.a=1; x.b=2; x.b; })); 6 | ASSERT(1, ({ struct {char a; int b; char c;} x; x.a=1; x.b=2; x.c=3; x.a; })); 7 | ASSERT(2, ({ struct {char a; int b; char c;} x; x.b=1; x.b=2; x.c=3; x.b; })); 8 | ASSERT(3, ({ struct {char a; int b; char c;} x; x.a=1; x.b=2; x.c=3; x.c; })); 9 | 10 | ASSERT(0, ({ struct {char a; char b;} x[3]; char *p=x; p[0]=0; x[0].a; })); 11 | ASSERT(1, ({ struct {char a; char b;} x[3]; char *p=x; p[1]=1; x[0].b; })); 12 | ASSERT(2, ({ struct {char a; char b;} x[3]; char *p=x; p[2]=2; x[1].a; })); 13 | ASSERT(3, ({ struct {char a; char b;} x[3]; char *p=x; p[3]=3; x[1].b; })); 14 | 15 | ASSERT(6, ({ struct {char a[3]; char b[5];} x; char *p=&x; x.a[0]=6; p[0]; })); 16 | ASSERT(7, ({ struct {char a[3]; char b[5];} x; char *p=&x; x.b[0]=7; p[3]; })); 17 | 18 | ASSERT(6, ({ struct { struct { char b; } a; } x; x.a.b=6; x.a.b; })); 19 | 20 | ASSERT(4, ({ struct {int a;} x; sizeof(x); })); 21 | ASSERT(8, ({ struct {int a; int b;} x; sizeof(x); })); 22 | ASSERT(8, ({ struct {int a, b;} x; sizeof(x); })); 23 | ASSERT(12, ({ struct {int a[3];} x; sizeof(x); })); 24 | ASSERT(16, ({ struct {int a;} x[4]; sizeof(x); })); 25 | ASSERT(24, ({ struct {int a[3];} x[2]; sizeof(x); })); 26 | ASSERT(2, ({ struct {char a; char b;} x; sizeof(x); })); 27 | ASSERT(0, ({ struct {} x; sizeof(x); })); 28 | ASSERT(8, ({ struct {char a; int b;} x; sizeof(x); })); 29 | ASSERT(8, ({ struct {int a; char b;} x; sizeof(x); })); 30 | 31 | ASSERT(8, ({ struct t {int a; int b;} x; struct t y; sizeof(y); })); 32 | ASSERT(8, ({ struct t {int a; int b;}; struct t y; sizeof(y); })); 33 | ASSERT(2, ({ struct t {char a[2];}; { struct t {char a[4];}; } struct t y; sizeof(y); })); 34 | ASSERT(3, ({ struct t {int x;}; int t=1; struct t y; y.x=2; t+y.x; })); 35 | 36 | ASSERT(3, ({ struct t {char a;} x; struct t *y = &x; x.a=3; y->a; })); 37 | ASSERT(3, ({ struct t {char a;} x; struct t *y = &x; y->a=3; x.a; })); 38 | 39 | ASSERT(3, ({ struct {int a,b;} x,y; x.a=3; y=x; y.a; })); 40 | ASSERT(7, ({ struct t {int a,b;}; struct t x; x.a=7; struct t y; struct t *z=&y; *z=x; y.a; })); 41 | ASSERT(7, ({ struct t {int a,b;}; struct t x; x.a=7; struct t y, *p=&x, *q=&y; *q=*p; y.a; })); 42 | ASSERT(5, ({ struct t {char a, b;} x, y; x.a=5; y=x; y.a; })); 43 | 44 | ASSERT(3, ({ struct {int a,b;} x,y; x.a=3; y=x; y.a; })); 45 | ASSERT(7, ({ struct t {int a,b;}; struct t x; x.a=7; struct t y; struct t *z=&y; *z=x; y.a; })); 46 | ASSERT(7, ({ struct t {int a,b;}; struct t x; x.a=7; struct t y, *p=&x, *q=&y; *q=*p; y.a; })); 47 | ASSERT(5, ({ struct t {char a, b;} x, y; x.a=5; y=x; y.a; })); 48 | 49 | ASSERT(8, ({ struct t {int a; int b;} x; struct t y; sizeof(y); })); 50 | ASSERT(8, ({ struct t {int a; int b;}; struct t y; sizeof(y); })); 51 | 52 | if (__SIZEOF_LONG__ == 8) { 53 | ASSERT(16, ({ struct {char a; long b;} x; sizeof(x); })); 54 | } else { 55 | ASSERT(8, ({ struct {char a; long b;} x; sizeof(x); })); 56 | } 57 | ASSERT(4, ({ struct {char a; short b;} x; sizeof(x); })); 58 | 59 | ASSERT(8, ({ struct foo *bar; sizeof(bar); })); 60 | ASSERT(4, ({ struct T *foo; struct T {int x;}; sizeof(struct T); })); 61 | ASSERT(1, ({ struct T { struct T *next; int x; } a; struct T b; b.x=1; a.next=&b; a.next->x; })); 62 | ASSERT(4, ({ typedef struct T T; struct T { int x; }; sizeof(T); })); 63 | 64 | ASSERT(2, ({ struct {int a;} x={1}, y={2}; (x=y).a; })); 65 | ASSERT(1, ({ struct {int a;} x={1}, y={2}; (1?x:y).a; })); 66 | ASSERT(2, ({ struct {int a;} x={1}, y={2}; (0?x:y).a; })); 67 | 68 | ASSERT(7, ({struct S { union { int a; }; }; struct S x = (struct S){.a = 7}; x.a; })); 69 | 70 | printf("OK\n"); 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /test/struct_bug17.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | struct StructA { 4 | int x1, x2, x3; 5 | }; 6 | 7 | void func(int dummy1, 8 | int dummy2, 9 | int dummy3, 10 | int dummy4, 11 | struct StructA trigger, 12 | struct StructA bug_occurred) { 13 | // Crash was occurring due to incorrect calculation of parameter offsets when 14 | // passed by reference beyond register slots. 15 | ASSERT(0, bug_occurred.x1); 16 | ASSERT(1, bug_occurred.x2); 17 | ASSERT(2, bug_occurred.x3); 18 | } 19 | 20 | extern void XXXXX(int dummy1, 21 | int dummy2, 22 | int dummy3, 23 | int dummy4, 24 | struct StructA trigger, 25 | struct StructA bug_occurred); 26 | int main(void) { 27 | struct StructA var = {0, 1, 2}; 28 | func(1, 2, 3, 4, var, var); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /test/struct_copy.c: -------------------------------------------------------------------------------- 1 | #include 2 | static void printint(int x) { 3 | printf("%d\n", x); 4 | } 5 | 6 | struct B { 7 | int b; 8 | int c; 9 | int x; 10 | }; 11 | 12 | struct A { 13 | struct B b; 14 | }; 15 | 16 | void A(struct B b) { 17 | struct A a; 18 | a.b = b; 19 | printint(a.b.b); 20 | printint(a.b.c); 21 | printint(a.b.x); 22 | } 23 | 24 | int main(void) { 25 | struct B b = (struct B){3, 2, 1}; 26 | A(b); 27 | } 28 | -------------------------------------------------------------------------------- /test/struct_string.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | #include 4 | struct $Str { 5 | char* ptr; 6 | int64_t len; 7 | }; 8 | 9 | #define $Str$__lit__(s) $Str$from_n(s, sizeof(s) - 1) 10 | 11 | struct $Str $Str$from_n(char* data, size_t len) { 12 | struct $Str s = {malloc(len + 1), len}; 13 | memcpy(s.ptr, data, len + 1); 14 | return s; 15 | } 16 | 17 | static void printstr(struct $Str s) { 18 | ASSERT('h', s.ptr[0]); 19 | ASSERT('e', s.ptr[1]); 20 | ASSERT('l', s.ptr[2]); 21 | ASSERT('l', s.ptr[3]); 22 | ASSERT('o', s.ptr[4]); 23 | ASSERT(0, s.ptr[5]); 24 | ASSERT(5, s.len); 25 | free(s.ptr); 26 | } 27 | 28 | int main(void) { 29 | { 30 | printstr($Str$__lit__("hello")); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/substructs.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct B { 4 | int x; 5 | int y; 6 | int z; 7 | }; 8 | 9 | struct C { 10 | int x; 11 | int y; 12 | int z; 13 | int w; 14 | }; 15 | 16 | struct D { 17 | int x; 18 | int y; 19 | int z; 20 | int w; 21 | int v; 22 | }; 23 | 24 | void f(struct B b) { 25 | printf("%d %d %d\n", b.x, b.y, b.z); 26 | } 27 | 28 | void g(struct C c) { 29 | printf("%d %d %d %d\n", c.x, c.y, c.z, c.w); 30 | } 31 | 32 | void h(struct D d) { 33 | printf("%d %d %d %d %d\n", d.x, d.y, d.z, d.w, d.v); 34 | } 35 | 36 | int main(void) { 37 | struct B b = {10, 20, 30}; 38 | f(b); 39 | struct C c = {10, 20, 30, 40}; 40 | g(c); 41 | struct D d = {10, 20, 30, 40, 50}; 42 | h(d); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #define ASSERT(x, y) assert(x, y, #y) 2 | 3 | void assert(int expected, int actual, char *code); 4 | int printf(char *fmt, ...); 5 | int sprintf(char *buf, char *fmt, ...); 6 | int vsprintf(char *buf, char *fmt, void *ap); 7 | int strcmp(char *p, char *q); 8 | int strncmp(char *p, char *q, long n); 9 | int memcmp(char *p, char *q, long n); 10 | void exit(int n); 11 | int vsprintf(); 12 | long strlen(char *s); 13 | void *memcpy(void *dest, void *src, long n); 14 | void *memset(void *s, int c, long n); 15 | -------------------------------------------------------------------------------- /test/test_helpers_for_update.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | _MAIN_TEMPLATE = r''' 5 | #include "libdyibicc.h" 6 | #include 7 | #include 8 | #include 9 | 10 | void* get_host_helper_func(const char* name) { 11 | (void)name; 12 | %(helper_lookups)s 13 | } 14 | 15 | static bool get_file_by_name(const char* filename, char** contents, size_t* size) { 16 | %(initial_file_contents)s 17 | 18 | // Otherwise, fallback to normal file loading (for includes, etc.) 19 | FILE* fp = fopen(filename, "rb"); 20 | if (!fp) { 21 | return false; 22 | } 23 | 24 | fseek(fp, 0, SEEK_END); 25 | *size = ftell(fp); 26 | rewind(fp); 27 | *contents = malloc(*size); 28 | fread(*contents, 1, *size, fp); 29 | fclose(fp); 30 | return true; 31 | } 32 | 33 | int main(void) { 34 | char* include_paths[] = { 35 | %(include_paths)s 36 | }; 37 | char* input_paths[] = { 38 | %(input_paths)s 39 | }; 40 | 41 | DyibiccEnviromentData env_data = { 42 | .include_paths = (const char**)include_paths, 43 | .files = (const char**)input_paths, 44 | .load_file_contents = get_file_by_name, 45 | .get_function_address = get_host_helper_func, 46 | .output_function = NULL, 47 | .use_ansi_codes = false, 48 | .generate_debug_symbols = false, 49 | }; 50 | 51 | DyibiccContext* ctx = dyibicc_set_environment(&env_data); 52 | 53 | int final_result = 0; 54 | 55 | if (!dyibicc_update(ctx, NULL, NULL)) { 56 | printf("initial update failed\n"); 57 | final_result = 255; 58 | goto fail; 59 | } 60 | 61 | %(steps)s 62 | 63 | printf("OK\n"); 64 | fail: 65 | dyibicc_free(ctx); 66 | return final_result; 67 | } 68 | ''' 69 | 70 | _UPDATE_FILE_TEMPLATE = r''' 71 | static char contents_step%(step)d[] = %(contents)s; 72 | if (!dyibicc_update(ctx, "%(filename)s", contents_step%(step)d)) { 73 | final_result = 255; 74 | goto fail; 75 | } 76 | ''' 77 | 78 | _CALL_ENTRY_TEMPLATE = r''' 79 | { 80 | void* entry_point = dyibicc_find_export(ctx, "main"); 81 | if (entry_point) { 82 | int myargc = 1; 83 | char* myargv[] = {"prog", NULL}; 84 | int result = ((int (*)(int, char**))entry_point)(myargc, myargv); 85 | if (result != %(desired_result)d) { 86 | printf("%(exp_file)s:%(exp_line)d: got %%d, but expected %%d\n", result, %(desired_result)d); 87 | final_result = 253; 88 | goto fail; 89 | } else { 90 | printf("%(exp_file)s:%(exp_line)d: OK (%%d)\n", result); 91 | } 92 | } else { 93 | printf("no entry point found\n"); 94 | final_result = 254; 95 | goto fail; 96 | } 97 | } 98 | ''' 99 | 100 | 101 | _steps = [] 102 | _current = {} 103 | _is_dirty = {} 104 | _include_paths = [] 105 | _initial_file_contents = {} 106 | _extra_host = [] 107 | _host_helper_funcs = [] 108 | 109 | 110 | def _string_as_c_array(s): 111 | result = [] 112 | for ch in s: 113 | result.append(hex(ord(ch))) 114 | return ','.join(result) + ",'\\0'" 115 | 116 | 117 | def add_to_host(code): 118 | global _extra_host 119 | _extra_host.append(code) 120 | 121 | 122 | def add_host_helper_func(*funcnames): 123 | _host_helper_funcs.extend(funcnames) 124 | 125 | 126 | def initial(file_to_contents): 127 | global _steps 128 | global _current 129 | for f, c in file_to_contents.items(): 130 | _current[f] = c 131 | _is_dirty[f] = True 132 | _initial_file_contents[f] = c 133 | update_ok() 134 | 135 | 136 | def include_path(path): 137 | global _include_paths 138 | _include_paths.append(path) 139 | 140 | 141 | def update_ok(): 142 | global _steps 143 | global _current 144 | for f, dirty in _is_dirty.items(): 145 | if dirty: 146 | _steps.append(_UPDATE_FILE_TEMPLATE % { 147 | 'filename': f, 148 | 'contents': '{' + _string_as_c_array(_current[f]) + '}', 149 | 'step': len(_steps)}) 150 | _is_dirty[f] = False 151 | 152 | 153 | def expect(rv): 154 | import inspect 155 | previous_frame = inspect.currentframe().f_back 156 | (filename, line_number, _, _, _) = inspect.getframeinfo(previous_frame) 157 | filename = os.path.split(filename)[1] 158 | global _steps 159 | _steps.append(_CALL_ENTRY_TEMPLATE % { 160 | 'desired_result': rv, 161 | 'exp_file': filename, 162 | 'exp_line': line_number}) 163 | 164 | 165 | def sub(filename, line, find, replace_with): 166 | global _current 167 | cur = _current[filename] 168 | lines = cur.splitlines() 169 | lines[line - 1] = lines[line - 1].replace(find, replace_with) 170 | _current[filename] = '\n'.join(lines) 171 | _is_dirty[filename] = True 172 | 173 | 174 | def done(): 175 | global _steps 176 | global _current 177 | global _include_paths 178 | global _initial_file_contents 179 | files = ['"%s"' % x for x in _current.keys()] + ['NULL'] 180 | incs = ['"%s"' % x for x in _include_paths] + ['NULL'] 181 | _include_paths.append('NULL') 182 | initials = '' 183 | counter = 0 184 | for f, c in _initial_file_contents.items(): 185 | initials += ' if (strcmp("%s", filename) == 0) {\n' % f 186 | initials += (' static char initial_%d[] = {' % counter) + _string_as_c_array(c) + '};\n' 187 | # Has to be malloc+copied because the compiler assumes it should free. 188 | initials += ' *contents = malloc(%d);\n' % len(c) 189 | initials += ' memcpy(*contents, initial_%d, %d);\n' % (counter, len(c)) 190 | initials += ' *size = %d;\n' % len(c) 191 | initials += ' return true;\n' 192 | initials += ' }\n\n' 193 | counter += 1 194 | helper_lookups = '' 195 | for x in _host_helper_funcs: 196 | helper_lookups += ' if (strcmp("%s", name) == 0) return (void*)%s;\n' % (x, x) 197 | helper_lookups += ' return NULL;\n' 198 | with open(sys.argv[1], 'w', newline='\n') as f: 199 | f.write('\n'.join(_extra_host)) 200 | f.write(_MAIN_TEMPLATE % { 201 | 'initial_file_contents': initials, 202 | 'helper_lookups': helper_lookups, 203 | 'include_paths': ', '.join(incs), 204 | 'input_paths': ', '.join(files), 205 | 'steps': '\n'.join(_steps)}) 206 | -------------------------------------------------------------------------------- /test/tls.c: -------------------------------------------------------------------------------- 1 | // DISABLED 2 | // TLS isn't implemented yet. 3 | #include "test.h" 4 | #include 5 | #include 6 | 7 | _Thread_local int v1; 8 | _Thread_local int v2 = 5; 9 | int v3 = 7; 10 | 11 | int thread_main(void *unused) { 12 | ASSERT(0, v1); 13 | ASSERT(5, v2); 14 | ASSERT(7, v3); 15 | 16 | v1 = 1; 17 | v2 = 2; 18 | v3 = 3; 19 | 20 | ASSERT(1, v1); 21 | ASSERT(2, v2); 22 | ASSERT(3, v3); 23 | 24 | return 0; 25 | } 26 | 27 | int main() { 28 | pthread_t thr; 29 | 30 | ASSERT(0, v1); 31 | ASSERT(5, v2); 32 | ASSERT(7, v3); 33 | 34 | ASSERT(0, pthread_create(&thr, NULL, thread_main, NULL)); 35 | ASSERT(0, pthread_join(thr, NULL)); 36 | 37 | ASSERT(0, v1); 38 | ASSERT(5, v2); 39 | ASSERT(3, v3); 40 | 41 | printf("OK\n"); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /test/typedef.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | typedef int MyInt, MyInt2[4]; 4 | typedef int; 5 | 6 | int main() { 7 | ASSERT(1, ({ typedef int t; t x=1; x; })); 8 | ASSERT(1, ({ typedef struct {int a;} t; t x; x.a=1; x.a; })); 9 | ASSERT(1, ({ typedef int t; t t=1; t; })); 10 | ASSERT(2, ({ typedef struct {int a;} t; { typedef int t; } t x; x.a=2; x.a; })); 11 | ASSERT(4, ({ typedef t; t x; sizeof(x); })); 12 | ASSERT(3, ({ MyInt x=3; x; })); 13 | ASSERT(16, ({ MyInt2 x; sizeof(x); })); 14 | 15 | printf("OK\n"); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /test/typeof.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(3, ({ typeof(int) x=3; x; })); 5 | ASSERT(3, ({ typeof(1) x=3; x; })); 6 | ASSERT(4, ({ int x; typeof(x) y; sizeof(y); })); 7 | ASSERT(8, ({ int x; typeof(&x) y; sizeof(y); })); 8 | ASSERT(4, ({ typeof("foo") x; sizeof(x); })); 9 | ASSERT(12, sizeof(typeof(struct { int a,b,c; }))); 10 | 11 | printf("OK\n"); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /test/unicode.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #define STR(x) #x 4 | 5 | typedef unsigned short char16_t; 6 | typedef unsigned int char32_t; 7 | typedef int wchar_t; 8 | 9 | int π = 3; 10 | 11 | int main() { 12 | ASSERT(4, sizeof(L'\0')); 13 | ASSERT(97, L'a'); 14 | 15 | ASSERT(0, strcmp("αβγ", "\u03B1\u03B2\u03B3")); 16 | ASSERT(0, strcmp("日本語", "\u65E5\u672C\u8A9E")); 17 | ASSERT(0, strcmp("日本語", "\U000065E5\U0000672C\U00008A9E")); 18 | ASSERT(0, strcmp("🌮", "\U0001F32E")); 19 | 20 | ASSERT(-1, L'\xffffffff'>>31); 21 | ASSERT(946, L'β'); 22 | ASSERT(12354, L'あ'); 23 | ASSERT(127843, L'🍣'); 24 | 25 | ASSERT(2, sizeof(u'\0')); 26 | ASSERT(1, u'\xffff'>>15); 27 | ASSERT(97, u'a'); 28 | ASSERT(946, u'β'); 29 | ASSERT(12354, u'あ'); 30 | ASSERT(62307, u'🍣'); 31 | 32 | ASSERT(0, strcmp(STR(u'a'), "u'a'")); 33 | 34 | ASSERT(4, sizeof(U'\0')); 35 | ASSERT(1, U'\xffffffff'>>31); 36 | ASSERT(97, U'a'); 37 | ASSERT(946, U'β'); 38 | ASSERT(12354, U'あ'); 39 | ASSERT(127843, U'🍣'); 40 | 41 | ASSERT(0, strcmp(STR(U'a'), "U'a'")); 42 | 43 | ASSERT(4, sizeof(u8"abc")); 44 | ASSERT(0, strcmp(u8"abc", "abc")); 45 | 46 | ASSERT(0, strcmp(STR(u8"a"), "u8\"a\"")); 47 | 48 | ASSERT(2, sizeof(u"")); 49 | ASSERT(10, sizeof(u"\xffzzz")); 50 | ASSERT(0, memcmp(u"", "\0\0", 2)); 51 | ASSERT(0, memcmp(u"abc", "a\0b\0c\0\0\0", 8)); 52 | ASSERT(0, memcmp(u"日本語", "\345e,g\236\212\0\0", 8)); 53 | ASSERT(0, memcmp(u"🍣", "<\330c\337\0\0", 6)); 54 | ASSERT(u'β', u"βb"[0]); 55 | ASSERT(u'b', u"βb"[1]); 56 | ASSERT(0, u"βb"[2]); 57 | 58 | ASSERT(0, strcmp(STR(u"a"), "u\"a\"")); 59 | 60 | ASSERT(4, sizeof(U"")); 61 | ASSERT(20, sizeof(U"\xffzzz")); 62 | ASSERT(0, memcmp(U"", "\0\0\0\0", 4)); 63 | ASSERT(0, memcmp(U"abc", "a\0\0\0b\0\0\0c\0\0\0\0\0\0\0", 16)); 64 | ASSERT(0, memcmp(U"日本語", "\345e\0\0,g\0\0\236\212\0\0\0\0\0\0", 16)); 65 | ASSERT(0, memcmp(U"🍣", "c\363\001\0\0\0\0\0", 8)); 66 | ASSERT(u'β', U"βb"[0]); 67 | ASSERT(u'b', U"βb"[1]); 68 | ASSERT(0, U"βb"[2]); 69 | ASSERT(1, U"\xffffffff"[0] >> 31); 70 | 71 | ASSERT(0, strcmp(STR(U"a"), "U\"a\"")); 72 | 73 | ASSERT(4, sizeof(L"")); 74 | ASSERT(20, sizeof(L"\xffzzz")); 75 | ASSERT(0, memcmp(L"", "\0\0\0\0", 4)); 76 | ASSERT(0, memcmp(L"abc", "a\0\0\0b\0\0\0c\0\0\0\0\0\0\0", 16)); 77 | ASSERT(0, memcmp(L"日本語", "\345e\0\0,g\0\0\236\212\0\0\0\0\0\0", 16)); 78 | ASSERT(0, memcmp(L"🍣", "c\363\001\0\0\0\0\0", 8)); 79 | ASSERT(u'β', L"βb"[0]); 80 | ASSERT(u'b', L"βb"[1]); 81 | ASSERT(0, L"βb"[2]); 82 | ASSERT(-1, L"\xffffffff"[0] >> 31); 83 | 84 | ASSERT(0, strcmp(STR(L"a"), "L\"a\"")); 85 | 86 | ASSERT(u'α', ({ char16_t x[] = u"αβ"; x[0]; })); 87 | ASSERT(u'β', ({ char16_t x[] = u"αβ"; x[1]; })); 88 | ASSERT(6, ({ char16_t x[] = u"αβ"; sizeof(x); })); 89 | 90 | ASSERT(U'🤔', ({ char32_t x[] = U"🤔x"; x[0]; })); 91 | ASSERT(U'x', ({ char32_t x[] = U"🤔x"; x[1]; })); 92 | ASSERT(12, ({ char32_t x[] = U"🤔x"; sizeof(x); })); 93 | 94 | ASSERT(L'🤔', ({ wchar_t x[] = L"🤔x"; x[0]; })); 95 | ASSERT(L'x', ({ wchar_t x[] = L"🤔x"; x[1]; })); 96 | ASSERT(12, ({ wchar_t x[] = L"🤔x"; sizeof(x); })); 97 | 98 | ASSERT(3, π); 99 | ASSERT(3, ({ int あβ0¾=3; あβ0¾; })); 100 | ASSERT(5, ({ int $$$=5; $$$; })); 101 | 102 | printf("OK\n"); 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /test/union.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(8, ({ union { int a; char b[6]; } x; sizeof(x); })); 5 | ASSERT(3, ({ union { int a; char b[4]; } x; x.a = 515; x.b[0]; })); 6 | ASSERT(2, ({ union { int a; char b[4]; } x; x.a = 515; x.b[1]; })); 7 | ASSERT(0, ({ union { int a; char b[4]; } x; x.a = 515; x.b[2]; })); 8 | ASSERT(0, ({ union { int a; char b[4]; } x; x.a = 515; x.b[3]; })); 9 | 10 | ASSERT(3, ({ union {int a,b;} x,y; x.a=3; y.a=5; y=x; y.a; })); 11 | ASSERT(3, ({ union {struct {int a,b;} c;} x,y; x.c.b=3; y.c.b=5; y=x; y.c.b; })); 12 | 13 | ASSERT(0xef, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.a; })); 14 | ASSERT(0xbe, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.b; })); 15 | ASSERT(0xad, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.c; })); 16 | ASSERT(0xde, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.d; })); 17 | 18 | ASSERT(3, ({struct { union { int a,b; }; union { int c,d; }; } x; x.a=3; x.b; })); 19 | ASSERT(5, ({struct { union { int a,b; }; union { int c,d; }; } x; x.d=5; x.c; })); 20 | 21 | printf("OK\n"); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /test/update_basic.py: -------------------------------------------------------------------------------- 1 | from test_helpers_for_update import * 2 | 3 | SRC = '''\ 4 | int main(void) { 5 | return 0; 6 | } 7 | ''' 8 | 9 | initial({'main.c': SRC}) 10 | update_ok() 11 | expect(0) 12 | 13 | sub('main.c', 2, '0', '1') 14 | update_ok() 15 | expect(1) 16 | 17 | sub('main.c', 2, '1', '99') 18 | update_ok() 19 | expect(99) 20 | 21 | done() 22 | -------------------------------------------------------------------------------- /test/update_morestruct.py: -------------------------------------------------------------------------------- 1 | from test_helpers_for_update import * 2 | 3 | HOST = r''' 4 | #include 5 | 6 | typedef struct Color { 7 | unsigned char r; // Color red value 8 | unsigned char g; // Color green value 9 | unsigned char b; // Color blue value 10 | unsigned char a; // Color alpha value 11 | } Color; 12 | 13 | Color Fade(Color color, float alpha) 14 | { 15 | if (alpha < 0.0f) alpha = 0.0f; 16 | else if (alpha > 1.0f) alpha = 1.0f; 17 | 18 | return (Color){ color.r, color.g, color.b, (unsigned char)(255.0f*alpha) }; 19 | } 20 | 21 | ''' 22 | 23 | SRC = r''' 24 | typedef struct Color { 25 | unsigned char r; // Color red value 26 | unsigned char g; // Color green value 27 | unsigned char b; // Color blue value 28 | unsigned char a; // Color alpha value 29 | } Color; 30 | 31 | Color Fade(Color color, float alpha); 32 | 33 | #define RED (Color){ 230, 41, 55, 255 } 34 | #define GRID 18 35 | 36 | int printf(); 37 | 38 | void stuff(void) { 39 | Color x = Fade(RED, 1.f); 40 | 41 | // This is a regression test for a return buffer of only 4 bytes. Previously 42 | // the first local was being calculated incorrectly and happened to mostly work, 43 | // as long as it was 8 bytes large. This struct return of only 4 bytes was 44 | // writing to rbp-4 rather, overwriting the saved stack frame. 45 | } 46 | 47 | int main(void) { 48 | stuff(); 49 | return 0; 50 | } 51 | ''' 52 | 53 | # This is not really an "update" test, but because the update tests happen to 54 | # build a separate host binary rather than using the standard dyibicc.exe, it 55 | # gives us a way to have C code to call that's test-specific and compiled by 56 | # the host compiler, rather than by ours. 57 | 58 | add_to_host(HOST); 59 | add_host_helper_func("Fade") 60 | 61 | initial({'main.c': SRC}) 62 | update_ok() 63 | expect(0) 64 | 65 | done() 66 | -------------------------------------------------------------------------------- /test/update_structabi.py: -------------------------------------------------------------------------------- 1 | from test_helpers_for_update import * 2 | 3 | HOST = '''\ 4 | #include 5 | typedef struct TestVec2 { float x; float y; } TestVec2; 6 | typedef struct TestCam { TestVec2 offset; TestVec2 target; float rotation; float zoom; } TestCam; 7 | 8 | #ifndef DEG2RAD 9 | #define DEG2RAD (PI/180.0f) 10 | #endif 11 | #ifndef PI 12 | #define PI 3.14159265358979323846f 13 | #endif 14 | 15 | typedef struct TestVec3 { 16 | float x; // Vector x component 17 | float y; // Vector y component 18 | float z; // Vector z component 19 | } TestVec3; 20 | 21 | typedef struct TestMat { 22 | float m0, m4, m8, m12; // Matrix first row (4 components) 23 | float m1, m5, m9, m13; // Matrix second row (4 components) 24 | float m2, m6, m10, m14; // Matrix third row (4 components) 25 | float m3, m7, m11, m15; // Matrix fourth row (4 components) 26 | } TestMat; 27 | 28 | TestMat MatrixMultiply(TestMat left, TestMat right) 29 | { 30 | TestMat result = { 0 }; 31 | 32 | result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; 33 | result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; 34 | result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; 35 | result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; 36 | result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; 37 | result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; 38 | result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; 39 | result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; 40 | result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; 41 | result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; 42 | result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; 43 | result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; 44 | result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; 45 | result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; 46 | result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; 47 | result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; 48 | 49 | return result; 50 | } 51 | 52 | TestMat GetCameraMatrix2D(TestCam camera) 53 | { 54 | (void)camera; 55 | TestMat matTransform = { 0 }; 56 | matTransform = MatrixMultiply(matTransform, matTransform); 57 | return matTransform; 58 | } 59 | 60 | TestVec3 TestVec3Transform(TestVec3 v, TestMat mat) 61 | { 62 | TestVec3 result = { 0 }; 63 | 64 | float x = v.x; 65 | float y = v.y; 66 | float z = v.z; 67 | 68 | result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; 69 | result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; 70 | result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; 71 | 72 | return result; 73 | } 74 | 75 | TestVec2 GetScreenToWorld2D(TestCam camera) 76 | { 77 | TestMat x = GetCameraMatrix2D(camera); 78 | 79 | return (TestVec2){ x.m0, x.m1 }; 80 | } 81 | 82 | ''' 83 | 84 | SRC = '''\ 85 | #include "test.h" 86 | #include 87 | 88 | typedef struct TestVec2 { 89 | float x; 90 | float y; 91 | } TestVec2; 92 | 93 | typedef struct TestCam { 94 | TestVec2 offset; 95 | TestVec2 target; 96 | float rotation; 97 | float zoom; 98 | } TestCam; 99 | 100 | TestVec2 GetScreenToWorld2D(TestCam camera); 101 | 102 | int main(void) { 103 | TestCam cam = {(TestVec2){0.f, 0.f}, (TestVec2){0.f, 0.f}, 0.f, 1.f}; 104 | TestVec2 w = GetScreenToWorld2D(cam); 105 | if (w.x != 0.f || w.y != 0.f) abort(); 106 | 107 | // This test was a regression test for non-stack alignment that was 108 | // only happening in Windows /Ox builds. It's not a great test, but 109 | // we hope that it'll crash if the bug manifests again. 110 | 111 | printf("OK\\n"); 112 | return 0; 113 | } 114 | ''' 115 | 116 | # This is not really an "update" test, but because the update tests happen to 117 | # build a separate host binary rather than using the standard dyibicc.exe, it 118 | # gives us a way to have C code to call that's test-specific and compiled by 119 | # the host compiler, rather than by ours. 120 | 121 | add_to_host(HOST); 122 | add_host_helper_func("GetScreenToWorld2D", "abort") 123 | include_path("../../test") 124 | 125 | initial({'main.c': SRC}) 126 | update_ok() 127 | expect(0) 128 | 129 | done() 130 | -------------------------------------------------------------------------------- /test/update_structabi2.py: -------------------------------------------------------------------------------- 1 | from test_helpers_for_update import * 2 | 3 | HOST = '''\ 4 | #include 5 | 6 | struct B { 7 | int x; 8 | int y; 9 | int z; 10 | }; 11 | 12 | void test_f(struct B b) { 13 | printf("%d %d %d\\n", b.x, b.y, b.z); 14 | } 15 | ''' 16 | 17 | SRC = '''\ 18 | #include 19 | 20 | struct B { 21 | int x; 22 | int y; 23 | int z; 24 | }; 25 | 26 | extern void test_f(struct B b); 27 | 28 | // This was causing stack un-alignment due to how struct copying was 29 | // implemented in codegen. 30 | int main(void) { 31 | struct B b = {10, 20, 30}; 32 | test_f(b); 33 | printf("OK\\n"); 34 | return 0; 35 | } 36 | ''' 37 | 38 | # This is not really an "update" test, but because the update tests happen to 39 | # build a separate host binary rather than using the standard dyibicc.exe, it 40 | # gives us a way to have C code to call that's test-specific and compiled by 41 | # the host compiler, rather than by ours. 42 | 43 | add_to_host(HOST); 44 | add_host_helper_func("test_f") 45 | include_path("../../test") 46 | 47 | initial({'main.c': SRC}) 48 | update_ok() 49 | expect(0) 50 | 51 | done() 52 | 53 | -------------------------------------------------------------------------------- /test/update_twofiles.py: -------------------------------------------------------------------------------- 1 | from test_helpers_for_update import * 2 | 3 | SRC1 = '''\ 4 | extern int other(void); 5 | int main(void) { 6 | return other(); 7 | } 8 | ''' 9 | 10 | SRC2 = '''\ 11 | int other(void) { 12 | return 100; 13 | } 14 | ''' 15 | 16 | initial({'main.c': SRC1, 'second.c': SRC2}) 17 | update_ok() 18 | expect(100) 19 | 20 | # Update only the second file; main needs to relink against the import. 21 | sub('second.c', 2, '100', '99') 22 | update_ok() 23 | expect(99) 24 | 25 | done() 26 | -------------------------------------------------------------------------------- /test/usualconv.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | static int ret10(void) { return 10; } 4 | 5 | int main() { 6 | ASSERT((long)-5, -10 + (long)5); 7 | ASSERT((long)-15, -10 - (long)5); 8 | ASSERT((long)-50, -10 * (long)5); 9 | ASSERT((long)-2, -10 / (long)5); 10 | 11 | ASSERT(1, -2 < (long)-1); 12 | ASSERT(1, -2 <= (long)-1); 13 | ASSERT(0, -2 > (long)-1); 14 | ASSERT(0, -2 >= (long)-1); 15 | 16 | ASSERT(1, (long)-2 < -1); 17 | ASSERT(1, (long)-2 <= -1); 18 | ASSERT(0, (long)-2 > -1); 19 | ASSERT(0, (long)-2 >= -1); 20 | 21 | ASSERT(0, 2147483647 + 2147483647 + 2); 22 | ASSERT((long)-1, ({ long x; x=-1; x; })); 23 | 24 | ASSERT(1, ({ char x[3]; x[0]=0; x[1]=1; x[2]=2; char *y=x+1; y[0]; })); 25 | ASSERT(0, ({ char x[3]; x[0]=0; x[1]=1; x[2]=2; char *y=x+1; y[-1]; })); 26 | ASSERT(5, ({ struct t {char a;} x, y; x.a=5; y=x; y.a; })); 27 | 28 | ASSERT(10, (1 ? ret10 : (void *)0)()); 29 | 30 | printf("OK\n"); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /test/varargs.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | 4 | int sum1(int x, ...) { 5 | va_list ap; 6 | va_start(ap, x); 7 | 8 | for (;;) { 9 | int y = va_arg(ap, int); 10 | if (y == 0) 11 | return x; 12 | x += y; 13 | } 14 | } 15 | 16 | int sum2(int x, ...) { 17 | va_list ap; 18 | va_start(ap, x); 19 | 20 | for (;;) { 21 | double y = va_arg(ap, double); 22 | x += y; 23 | 24 | int z = va_arg(ap, int); 25 | if (z == 0) 26 | return x; 27 | x += z; 28 | } 29 | } 30 | 31 | void fmt(char *buf, char *fmt, ...) { 32 | va_list ap; 33 | va_start(ap, fmt); 34 | 35 | va_list ap2; 36 | va_copy(ap2, ap); 37 | vsprintf(buf, fmt, ap2); 38 | va_end(buf); 39 | } 40 | 41 | int main() { 42 | ASSERT(6, sum1(1, 2, 3, 0)); 43 | ASSERT(55, sum1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0)); 44 | ASSERT(21, sum2(1, 2.0, 3, 4.0, 5, 6.0, 0)); 45 | ASSERT(21, sum2(1, 2.0, 3, 4.0, 5, 6.0, 0)); 46 | ASSERT(210, sum2(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13, 14.0, 15, 16.0, 17, 18.0, 19, 20.0, 0)); 47 | ASSERT(0, ({ char buf[100]; fmt(buf, "%d %d", 2, 3); strcmp(buf, "2 3"); })); 48 | 49 | printf("OK\n"); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /test/variable.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int g1, g2[4]; 4 | static int g3 = 3; 5 | 6 | int main() { 7 | ASSERT(3, ({ int a; a=3; a; })); 8 | ASSERT(3, ({ int a=3; a; })); 9 | ASSERT(8, ({ int a=3; int z=5; a+z; })); 10 | 11 | ASSERT(3, ({ int a=3; a; })); 12 | ASSERT(8, ({ int a=3; int z=5; a+z; })); 13 | ASSERT(6, ({ int a; int b; a=b=3; a+b; })); 14 | ASSERT(3, ({ int foo=3; foo; })); 15 | ASSERT(8, ({ int foo123=3; int bar=5; foo123+bar; })); 16 | 17 | ASSERT(4, ({ int x; sizeof(x); })); 18 | ASSERT(4, ({ int x; sizeof x; })); 19 | ASSERT(8, ({ int *x; sizeof(x); })); 20 | ASSERT(16, ({ int x[4]; sizeof(x); })); 21 | ASSERT(48, ({ int x[3][4]; sizeof(x); })); 22 | ASSERT(16, ({ int x[3][4]; sizeof(*x); })); 23 | ASSERT(4, ({ int x[3][4]; sizeof(**x); })); 24 | ASSERT(5, ({ int x[3][4]; sizeof(**x) + 1; })); 25 | ASSERT(5, ({ int x[3][4]; sizeof **x + 1; })); 26 | ASSERT(4, ({ int x[3][4]; sizeof(**x + 1); })); 27 | ASSERT(4, ({ int x=1; sizeof(x=2); })); 28 | ASSERT(1, ({ int x=1; sizeof(x=2); x; })); 29 | 30 | ASSERT(0, g1); 31 | ASSERT(3, ({ g1=3; g1; })); 32 | ASSERT(0, ({ g2[0]=0; g2[1]=1; g2[2]=2; g2[3]=3; g2[0]; })); 33 | ASSERT(1, ({ g2[0]=0; g2[1]=1; g2[2]=2; g2[3]=3; g2[1]; })); 34 | ASSERT(2, ({ g2[0]=0; g2[1]=1; g2[2]=2; g2[3]=3; g2[2]; })); 35 | ASSERT(3, ({ g2[0]=0; g2[1]=1; g2[2]=2; g2[3]=3; g2[3]; })); 36 | 37 | ASSERT(4, sizeof(g1)); 38 | ASSERT(16, sizeof(g2)); 39 | 40 | ASSERT(1, ({ char x=1; x; })); 41 | ASSERT(1, ({ char x=1; char y=2; x; })); 42 | ASSERT(2, ({ char x=1; char y=2; y; })); 43 | 44 | ASSERT(1, ({ char x; sizeof(x); })); 45 | ASSERT(10, ({ char x[10]; sizeof(x); })); 46 | 47 | ASSERT(2, ({ int x=2; { int x=3; } x; })); 48 | ASSERT(2, ({ int x=2; { int x=3; } int y=4; x; })); 49 | ASSERT(3, ({ int x=2; { x=3; } x; })); 50 | 51 | ASSERT(7, ({ int x; int y; char z; char *a=&y; char *b=&z; b-a; })); 52 | ASSERT(1, ({ int x; char y; int z; char *a=&y; char *b=&z; b-a; })); 53 | 54 | ASSERT(__SIZEOF_LONG__, ({ long x; sizeof(x); })); 55 | ASSERT(2, ({ short x; sizeof(x); })); 56 | 57 | ASSERT(24, ({ char *x[3]; sizeof(x); })); 58 | ASSERT(8, ({ char (*x)[3]; sizeof(x); })); 59 | ASSERT(1, ({ char (x); sizeof(x); })); 60 | ASSERT(3, ({ char (x)[3]; sizeof(x); })); 61 | ASSERT(12, ({ char (x[3])[4]; sizeof(x); })); 62 | ASSERT(4, ({ char (x[3])[4]; sizeof(x[0]); })); 63 | ASSERT(3, ({ char *x[3]; char y; x[0]=&y; y=3; x[0][0]; })); 64 | ASSERT(4, ({ char x[3]; char (*y)[3]=x; y[0][0]=4; y[0][0]; })); 65 | 66 | { void *x; } 67 | 68 | ASSERT(3, g3); 69 | 70 | printf("OK\n"); 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /test/vla.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | ASSERT(20, ({ int n=5; int x[n]; sizeof(x); })); 5 | ASSERT((5+1)*(8*2)*4, ({ int m=5, n=8; int x[m+1][n*2]; sizeof(x); })); 6 | 7 | ASSERT(8, ({ char n=10; int (*x)[n][n+2]; sizeof(x); })); 8 | ASSERT(480, ({ char n=10; int (*x)[n][n+2]; sizeof(*x); })); 9 | ASSERT(48, ({ char n=10; int (*x)[n][n+2]; sizeof(**x); })); 10 | ASSERT(4, ({ char n=10; int (*x)[n][n+2]; sizeof(***x); })); 11 | 12 | ASSERT(60, ({ char n=3; int x[5][n]; sizeof(x); })); 13 | ASSERT(12, ({ char n=3; int x[5][n]; sizeof(*x); })); 14 | 15 | ASSERT(60, ({ char n=3; int x[n][5]; sizeof(x); })); 16 | ASSERT(20, ({ char n=3; int x[n][5]; sizeof(*x); })); 17 | 18 | ASSERT(0, ({ int n=10; int x[n+1][n+6]; int *p=x; for (int i = 0; i