├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── LICENSE.txt ├── README.md ├── a.lua ├── build.lua ├── common ├── README.txt ├── arena.h ├── arena_array.h ├── buffer.h ├── common.c ├── common.h ├── dyn_array.h ├── emitter.c ├── file_map.h ├── futex.h ├── hash_map.h ├── hashes.h ├── log.c ├── log.h ├── nbhs.h ├── new_hash_map.h ├── perf.c ├── perf.h ├── pool.c ├── pool.h ├── spall.h ├── spall_native_auto.h └── str.h ├── cuik_c ├── README.txt ├── ast_dump.c ├── atoms.c ├── atoms.h ├── compilation_unit.c ├── crash_handler.c ├── cuik.c ├── decl_parser.h ├── driver │ ├── driver.c │ ├── driver_arg_parse.h │ ├── driver_args.h │ ├── driver_fs.h │ └── driver_sched.h ├── expr_fold.h ├── expr_parser.h ├── glsl_keywords.h ├── glsl_parser.h ├── ir_gen.c ├── ir_gen.h ├── ir_gen2.c ├── libcuik.c ├── linker.c ├── parser.c ├── parser.h ├── path.c ├── sema.c ├── sema.h ├── stmt_parser.h ├── targets │ ├── aarch64_desc.c │ ├── mips_desc.c │ ├── target_generic.c │ ├── targets.h │ ├── wasm_desc.c │ └── x64_desc.c ├── tls.c ├── toolchains │ ├── darwin.c │ ├── gnu.c │ └── msvc.c ├── top_level_parser.h ├── types.c └── visitors.c ├── cuik_pp ├── cpp.c ├── cpp_dbg.h ├── cpp_directive.h ├── cpp_expand.h ├── cpp_expr.h ├── cpp_fs.h ├── cpp_iters.h ├── cpp_symtab.h ├── dfa.h ├── diagnostic.c ├── diagnostic.h ├── keywords.h ├── lexer.c ├── lexer.h └── stb_sprintf.h ├── docs ├── BUILDING.txt ├── IR.txt ├── NOTES.txt ├── README.txt └── STRUCTURE.txt ├── foo.c ├── freestanding ├── emmintrin.h ├── float.h ├── immintrin.h ├── intrin.h ├── limits.h ├── mm_malloc.h ├── stdalign.h ├── stdarg.h ├── stdatomic.h ├── stdbool.h ├── stddef.h ├── stdint.h ├── x86intrin.h └── xmmintrin.h ├── include ├── cuik.h ├── cuik_ast.h ├── cuik_driver.h ├── cuik_fs.h ├── cuik_irgen.h ├── cuik_lex.h ├── cuik_parse.h ├── cuik_prelude.h ├── cuik_symtab.h ├── prelude.h ├── sdg.h ├── tb.h ├── tb_coff.h ├── tb_elf.h ├── tb_formats.h ├── tb_linker.h └── tb_x64.h ├── linker ├── README.txt ├── archives.c ├── elf_linker.c ├── ld_script.c ├── linker.c ├── linker.h └── pe_linker.c ├── logo └── cuik.svg ├── main ├── README.md ├── bindgen.h ├── link.h ├── live.h ├── main_driver.c ├── objdump.h └── stb_ds.h ├── mat.c ├── mat.lua ├── mat.s ├── meta ├── a64_dfa.lua ├── debugger.lua ├── dsl.lua ├── hexembed.c ├── inspect.lua ├── jason.lua ├── lexgen.c ├── uops.lua └── xmlparser.lua ├── tb ├── .editorconfig ├── .gitignore ├── NOTES.txt ├── aarch64 │ ├── a64.machine │ ├── a64_gen.h │ ├── aarch64_emitter.h │ └── aarch64_target.c ├── abi.c ├── briggs_ra.c ├── builtins.h ├── codegen.c ├── codegen.h ├── codegen_impl.h ├── debug │ ├── cv.c │ ├── cv.h │ ├── cv_type_builder.c │ └── sdg.c ├── debug_builder.c ├── disasm.c ├── emitter.h ├── exporter.c ├── hash.c ├── host.h ├── jit.c ├── libtb.c ├── mips │ ├── mips_nodes.inc │ ├── mips_target.c │ └── targets.txt ├── new_builder.c ├── objects │ ├── coff.c │ ├── coff.h │ ├── coff_parse.c │ ├── elf64.c │ ├── lib_parse.h │ ├── macho.c │ ├── macho.h │ ├── parse_prelude.h │ └── wasm_obj.c ├── opt │ ├── bb_placement.h │ ├── branches.h │ ├── cfg.h │ ├── compact.h │ ├── dbg.h │ ├── fold.h │ ├── gcm.h │ ├── gvn.h │ ├── interp.h │ ├── ipo.h │ ├── lattice.h │ ├── libcalls.h │ ├── list_sched.h │ ├── loop.h │ ├── mem2reg.h │ ├── mem_opt.h │ ├── optimizer.c │ ├── passes.h │ ├── peep_float.h │ ├── peep_int.h │ ├── peep_mem.h │ ├── peeps.h │ ├── print.h │ ├── print_dumb.h │ ├── print_svg.h │ ├── properties.h │ ├── rpo_sched.h │ ├── slp.h │ ├── sroa.h │ ├── verify.h │ └── worklist.h ├── rogers_ra.c ├── set.h ├── tb.c ├── tb_builder.c ├── tb_internal.h ├── tb_platform.h ├── wasm │ └── wasm_target.c └── x64 │ ├── x64.h │ ├── x64.machine │ ├── x64_disasm.c │ ├── x64_emitter.h │ ├── x64_insts.inc │ ├── x64_nodes.inc │ └── x64_target.c ├── tests.lua └── tests ├── addr.c ├── csmith ├── f0.c ├── f0.clang.txt ├── f1.c ├── platform.info └── print.c ├── donut ├── donut.c └── donut2.c ├── doq ├── doq.c ├── example.doq ├── golden.html └── readme.md ├── gold ├── bar.txt ├── bar.txxt ├── ex0.c ├── ex1.c ├── ex2.c ├── ex3.c ├── ex4.c ├── ex5.c ├── ex6.c ├── ex7.c ├── foo.txt ├── nbody.c ├── out.svg └── test.lua ├── nbody.c └── win32_dx ├── main.c └── stb_image.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Unix style 2 | * text=lf 3 | # Explicitly declare text files you want to always be normalized and converted 4 | # to native line endings on checkout. 5 | *.c text eol=lf 6 | *.cpp text eol=lf 7 | *.txt text eol=lf 8 | *.h text eol=lf 9 | *.svg text eol=lf 10 | *.md text eol=lf 11 | # Denote all files that are truly binary and should not be modified. 12 | *.o binary 13 | *.obj binary 14 | *.png binary 15 | *.lib filter=lfs diff=lfs merge=lfs -text 16 | *.a binary 17 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: RealNeGate 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | .tup/ 3 | 4 | build.ninja 5 | .ninja_log 6 | .ninja_deps 7 | 8 | bin/ 9 | bin2/ 10 | Truct/ 11 | inspector/SDL2/ 12 | 13 | # *.lib 14 | *.a 15 | 16 | # Prerequisites 17 | *.d 18 | 19 | # Object files 20 | *.o 21 | *.ko 22 | *.obj 23 | *.elf 24 | 25 | # Linker output 26 | *.ilk 27 | *.map 28 | *.exp 29 | 30 | # Precompiled Headers 31 | *.gch 32 | *.pch 33 | 34 | # Libraries 35 | *.la 36 | *.lo 37 | 38 | # Shared objects (inc. Windows DLLs) 39 | *.dll 40 | *.so 41 | *.so.* 42 | *.dylib 43 | 44 | # Executables 45 | *.exe 46 | *.out 47 | *.app 48 | *.i*86 49 | *.x86_64 50 | *.hex 51 | 52 | # Debug files 53 | *.dSYM/ 54 | *.su 55 | *.idb 56 | *.pdb 57 | 58 | # NeGate crap 59 | *.rdbg 60 | *.i 61 | *.swp 62 | *.dmp 63 | *.json 64 | *.flint 65 | *.ttf 66 | cuik 67 | tb_unittests 68 | 69 | tests/the_fuzzer/ 70 | tests/stb_image.c 71 | tests/test5.c 72 | tests/test5_2.c 73 | preprocessed.c 74 | 75 | # metaprogram artifacts 76 | tb/x64/x64_gen.h 77 | 78 | project.4coder 79 | *.cache 80 | tests/golden/tmp.txt 81 | tests/golden/tmp2.txt 82 | build.ninja 83 | *.lib 84 | *.csv 85 | *.spall 86 | tests/collection/foo.txt 87 | tests/gold/cuik.txt 88 | tests/gold/clang.txt 89 | meta/a64dfa.c 90 | meta/Instructions.json 91 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mimalloc"] 2 | path = mimalloc 3 | url = https://github.com/microsoft/mimalloc 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Yasser Arguelles Snape 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **warning: unfinished and buggy...** 4 | 5 | ## Resources 6 | 7 | * [discord](https://discord.gg/UFuHChZswc) 8 | * [build from source](https://github.com/RealNeGate/Cuik/blob/master/docs/BUILDING.txt) 9 | * [code structure](https://github.com/RealNeGate/Cuik/blob/master/docs/STRUCTURE.txt) 10 | 11 | ## Summary 12 | 13 | Cuik (pronounced 'Quick') is a new fast compiler toolchain built to replace GCC, MSVC, and LLVM. It's built in a modular design, the C11 frontend is one library (`libCuik`) along with the backend (`tb`) which can both be compiled separately. 14 | -------------------------------------------------------------------------------- /common/README.txt: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Cuik repository common utilities 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | This is used by all kinds of components within the project, almost everything 6 | will be including it. It has nice macros and includes. 7 | 8 | TODO: 9 | 10 | * move everything to using the iterator macros -------------------------------------------------------------------------------- /common/arena.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef TB_API 7 | # ifdef __cplusplus 8 | # define TB_EXTERN extern "C" 9 | # else 10 | # define TB_EXTERN 11 | # endif 12 | # ifdef TB_DLL 13 | # ifdef TB_IMPORT_DLL 14 | # define TB_API TB_EXTERN __declspec(dllimport) 15 | # else 16 | # define TB_API TB_EXTERN __declspec(dllexport) 17 | # endif 18 | # else 19 | # define TB_API TB_EXTERN 20 | # endif 21 | #endif 22 | 23 | enum { 24 | // this is overshooting by a bit but essentially my arena chunks 25 | // are in an arena so i should be leaving a bit of slack for the 26 | // object headers. 27 | TB_ARENA_ALLOC_HEAD_SLACK = 32, 28 | // usually chunks are small unless it asks for a lot of memory (big arrays usually) 29 | TB_ARENA_NORMAL_CHUNK_SIZE = (32 * 1024), 30 | // just a decent alignment amount, in practice 8 would prolly be fine 31 | TB_ARENA_ALIGNMENT = 16, 32 | }; 33 | 34 | typedef struct TB_ArenaChunk TB_ArenaChunk; 35 | struct TB_ArenaChunk { 36 | TB_ArenaChunk* prev; 37 | char* avail; 38 | char* limit; 39 | char* pad; 40 | 41 | char data[]; 42 | }; 43 | 44 | #ifndef TB_OPAQUE_ARENA_DEF 45 | #define TB_OPAQUE_ARENA_DEF 46 | typedef struct TB_ArenaChunk TB_ArenaChunk; 47 | typedef struct { 48 | TB_ArenaChunk* top; 49 | 50 | #ifndef NDEBUG 51 | const char* tag; 52 | uint32_t allocs; 53 | uint32_t alloc_bytes; 54 | #endif 55 | } TB_Arena; 56 | 57 | typedef struct TB_ArenaSavepoint { 58 | TB_ArenaChunk* top; 59 | char* avail; 60 | } TB_ArenaSavepoint; 61 | #endif // TB_OPAQUE_ARENA_DEF 62 | 63 | #define TB_ARENA_ALLOC(arena, T) tb_arena_alloc(arena, sizeof(T)) 64 | #define TB_ARENA_ARR_ALLOC(arena, count, T) tb_arena_alloc(arena, (count) * sizeof(T)) 65 | 66 | TB_API void tb_arena_create(TB_Arena* restrict arena, const char* optional_tag); 67 | TB_API void tb_arena_destroy(TB_Arena* restrict arena); 68 | TB_API void tb_arena_clear(TB_Arena* restrict arena); 69 | 70 | TB_API void* tb_arena_unaligned_alloc(TB_Arena* restrict arena, size_t size); 71 | TB_API void* tb_arena_alloc(TB_Arena* restrict arena, size_t size); 72 | 73 | TB_API void* tb_arena_realloc(TB_Arena* restrict arena, void* old, size_t old_size, size_t size); 74 | 75 | // return false on failure 76 | TB_API bool tb_arena_free(TB_Arena* restrict arena, void* ptr, size_t size); 77 | TB_API void tb_arena_pop(TB_Arena* restrict arena, void* ptr, size_t size); 78 | 79 | // in case you wanna mix unaligned and aligned arenas 80 | TB_API void tb_arena_realign(TB_Arena* restrict arena); 81 | 82 | TB_API size_t tb_arena_chunk_size(TB_ArenaChunk* arena); 83 | TB_API size_t tb_arena_current_size(TB_Arena* arena); 84 | 85 | // savepoints 86 | TB_API TB_ArenaSavepoint tb_arena_base(TB_Arena* arena); 87 | TB_API TB_ArenaSavepoint tb_arena_save(TB_Arena* arena); 88 | TB_API void tb_arena_restore(TB_Arena* arena, TB_ArenaSavepoint sp); 89 | TB_API bool tb_arena_is_top(TB_Arena* arena, TB_ArenaSavepoint sp); 90 | -------------------------------------------------------------------------------- /common/arena_array.h: -------------------------------------------------------------------------------- 1 | // Just a dynamic array that fits well into the arena 2 | #pragma once 3 | #include 4 | #include 5 | 6 | typedef struct AArray { 7 | TB_Arena* arena; 8 | uint32_t length, capacity; 9 | char data[]; 10 | } AArray; 11 | 12 | static void* aarray__create(TB_Arena* arena, size_t type_size, size_t cap) { 13 | AArray* header = tb_arena_alloc(arena, sizeof(AArray) + (type_size * cap)); 14 | *header = (AArray){ .arena = arena, .capacity = cap }; 15 | return &header->data[0]; 16 | } 17 | 18 | static void* aarray__reserve(void* ptr, size_t type_size, size_t min_size) { 19 | AArray* header = ((AArray*)ptr) - 1; 20 | if (min_size >= header->capacity) { 21 | size_t old = header->capacity; 22 | header->capacity = min_size * 2; 23 | 24 | AArray* new_ptr = tb_arena_realloc(header->arena, header, sizeof(AArray) + (type_size * old), sizeof(AArray) + (type_size * header->capacity)); 25 | if (!new_ptr) { 26 | fprintf(stderr, "error: out of memory!"); 27 | abort(); 28 | } 29 | return &new_ptr->data[0]; 30 | } 31 | 32 | return ptr; 33 | } 34 | 35 | static void* aarray__reserve2(void* ptr, size_t type_size, size_t min_size) { 36 | AArray* header = ((AArray*)ptr) - 1; 37 | 38 | if (min_size >= header->capacity) { 39 | size_t old = header->capacity; 40 | header->capacity = min_size * 2; 41 | 42 | AArray* new_ptr = tb_arena_realloc(header->arena, header, sizeof(AArray) + (type_size * old), sizeof(AArray) + (type_size * header->capacity)); 43 | if (!new_ptr) { 44 | fprintf(stderr, "error: out of memory!"); 45 | abort(); 46 | } 47 | 48 | ptr = &new_ptr->data[0]; 49 | header = new_ptr; 50 | } 51 | 52 | // zero out the space up to the min_size 53 | if (min_size > header->length) { 54 | memset(&header->data[header->length * type_size], 0, (min_size - header->length) * type_size); 55 | header->length = min_size; 56 | } 57 | 58 | return ptr; 59 | } 60 | 61 | #define ArenaArray(T) T* 62 | #define aarray_create(arena, T, cap) (T*) aarray__create(arena, sizeof(T), cap) 63 | #define aarray_length(arr) ((((AArray*) (arr)) - 1)->length) 64 | #define aarray_set_length(arr, len) ((((AArray*) (arr)) - 1)->length = (len)) 65 | #define aarray_clear(arr) ((((AArray*) (arr)) - 1)->length = 0) 66 | #define aarray_insert(arr, i, ...) ((arr) = aarray__reserve2(arr, sizeof(*(arr)), (i)+1), (arr)[i] = __VA_ARGS__) 67 | #define aarray_push(arr, ...) ((arr) = aarray__reserve(arr, sizeof(*(arr)), aarray_length(arr)), (arr)[aarray_length(arr)++] = __VA_ARGS__) 68 | #define aarray_pop(arr) ((arr)[(((AArray*)(arr)) - 1)->length -= 1]) 69 | #define aarray_top(arr) ((arr)[(((AArray*)(arr)) - 1)->length - 1]) 70 | #define aarray_reserve(arr, i) ((arr) = aarray__reserve(arr, sizeof(*(arr)), (i)), aarray_length(arr) = (i)) 71 | #define aarray_for(i, arr) for (ptrdiff_t i = 0, end_ = aarray_length(arr); i < end_; i++) 72 | #define aarray_for_rev(i, arr) for (ptrdiff_t i = aarray_length(arr); i--;) 73 | #define aarray_remove(arr, i) ((arr)[i] = (arr)[(((AArray*)(arr)) - 1)->length -= 1]) 74 | -------------------------------------------------------------------------------- /common/buffer.h: -------------------------------------------------------------------------------- 1 | // String Buffer 2 | #ifndef NL_BUFFER_H 3 | #define NL_BUFFER_H 4 | 5 | #include "stdarg.h" 6 | 7 | struct nl_buffer_t; 8 | typedef struct nl_buffer_t nl_buffer_t; 9 | 10 | struct nl_buffer_t { 11 | char *buf; 12 | int len; 13 | int alloc; 14 | }; 15 | 16 | nl_buffer_t *nl_buffer_new(void); 17 | void nl_buffer_alloc(nl_buffer_t *buf); 18 | 19 | void nl_buffer_format(nl_buffer_t *buf, const char *fmt, ...); 20 | char *nl_buffer_get(nl_buffer_t *buf); 21 | 22 | #ifdef NL_BUFFER_IMPL 23 | 24 | nl_buffer_t *nl_buffer_new(void) { 25 | nl_buffer_t *buf = NL_MALLOC(sizeof(nl_buffer_t)); 26 | buf->alloc = 16; 27 | buf->buf = NL_MALLOC(buf->alloc); 28 | buf->buf[0] = '\0'; 29 | buf->len = 0; 30 | return buf; 31 | } 32 | 33 | void nl_buffer_format(nl_buffer_t *buf, const char *restrict fmt, ...) { 34 | while (true) { 35 | int avail = buf->alloc - buf->len; 36 | va_list ap; 37 | va_start(ap, fmt); 38 | int written = vsnprintf(&buf->buf[buf->len], avail, fmt, ap); 39 | va_end(ap); 40 | if (avail <= written) { 41 | buf->alloc = buf->alloc * 2 + 16; 42 | buf->buf = NL_REALLOC(buf->buf, buf->alloc); 43 | continue; 44 | } 45 | buf->len += written; 46 | break; 47 | } 48 | } 49 | 50 | char *nl_buffer_get(nl_buffer_t *buf) { 51 | return buf->buf; 52 | } 53 | 54 | #endif 55 | #endif 56 | -------------------------------------------------------------------------------- /common/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef CONFIG_HAS_MIMALLOC 10 | #include 11 | 12 | #define cuik_malloc(size) mi_malloc(size) 13 | #define cuik_calloc(count, size) mi_calloc(count, size) 14 | #define cuik_free(ptr) mi_free(ptr) 15 | #define cuik_realloc(ptr, size) mi_realloc(ptr, size) 16 | #define cuik_strdup(x) mi_strdup(x) 17 | #else 18 | #define cuik_malloc(size) malloc(size) 19 | #define cuik_calloc(count, size) calloc(count, size) 20 | #define cuik_free(size) free(size) 21 | #define cuik_realloc(ptr, size) realloc(ptr, size) 22 | 23 | #ifdef _WIN32 24 | #define cuik_strdup(x) _strdup(x) 25 | #else 26 | #define cuik_strdup(x) strdup(x) 27 | #endif 28 | #endif 29 | 30 | #if defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64__) || defined(__x86_64) 31 | #define CUIK__IS_X64 1 32 | #elif defined(__aarch64__) 33 | #define CUIK__IS_AARCH64 1 34 | #endif 35 | 36 | // Cuik doesn't have SIMD intrinsics yet... sadge 37 | #if defined(__CUIK__) 38 | #define USE_INTRIN 0 39 | #else 40 | #define USE_INTRIN 1 41 | #endif 42 | 43 | #define FOR_N(it, start, end) \ 44 | for (ptrdiff_t it = (start), end__ = (end); it < end__; ++it) 45 | 46 | #define FOR_REV_N(it, start, end) \ 47 | for (ptrdiff_t it = (end), start__ = (start); (it--) > start__;) 48 | 49 | #define FOR_BIT(it, start, bits) \ 50 | for (uint64_t _bits_ = (bits), it = (start); _bits_; _bits_ >>= 1, ++it) if (_bits_ & 1) 51 | 52 | #define STR2(x) #x 53 | #define STR(x) STR2(x) 54 | 55 | #ifndef COUNTOF 56 | #define COUNTOF(...) (sizeof(__VA_ARGS__) / sizeof(__VA_ARGS__[0])) 57 | #endif 58 | 59 | #ifdef NDEBUG 60 | #define ASSUME(x) ((x) ? 0 : __builtin_unreachable()) 61 | #else 62 | #define ASSUME(x) ((x) ? 0 : (fprintf(stderr, __FILE__ ": " STR(__LINE__) ": bad assumption: " #x "\n"))) 63 | #endif 64 | 65 | #define LIKELY(x) __builtin_expect(!!(x), 1) 66 | #define UNLIKELY(x) __builtin_expect(!!(x), 0) 67 | 68 | #ifdef NDEBUG 69 | #define TODO() __builtin_unreachable() 70 | #else 71 | #define TODO() (assert(0 && "TODO"), __builtin_unreachable()) 72 | #endif 73 | 74 | // just because we use a threads fallback layer which can include windows 75 | // and such which is annoying... eventually need to modify that out or something 76 | #ifndef thread_local 77 | #define thread_local _Thread_local 78 | #endif 79 | 80 | #define SWAP(T, a, b) \ 81 | do { \ 82 | T temp = a; \ 83 | a = b; \ 84 | b = temp; \ 85 | } while (0) 86 | 87 | void cuik_init_terminal(void); 88 | 89 | void tls_init(void); 90 | void tls_reset(void); 91 | void* tls_push(size_t size); 92 | void* tls_pop(size_t size); 93 | void* tls_save(void); 94 | void tls_restore(void* p); 95 | 96 | void* cuik__valloc(size_t sz); 97 | void cuik__vfree(void* p, size_t sz); 98 | -------------------------------------------------------------------------------- /common/file_map.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _WIN32 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | 7 | typedef struct { 8 | HANDLE file; 9 | HANDLE mapping; 10 | size_t size; 11 | void* data; 12 | } FileMap; 13 | 14 | static FileMap open_file_map_write(const char* filepath, size_t size) { 15 | HANDLE file = CreateFileA(filepath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 16 | if (file == INVALID_HANDLE_VALUE) { 17 | return (FileMap){ INVALID_HANDLE_VALUE }; 18 | } 19 | 20 | HANDLE mapping = CreateFileMappingA(file, NULL, PAGE_READWRITE, 0, size, NULL); 21 | if (mapping == NULL) { 22 | CloseHandle(file); 23 | return (FileMap){ INVALID_HANDLE_VALUE }; 24 | } 25 | 26 | void* output = MapViewOfFileEx(mapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size, NULL); 27 | if (output == NULL) { 28 | CloseHandle(mapping); 29 | CloseHandle(file); 30 | return (FileMap){ INVALID_HANDLE_VALUE }; 31 | } 32 | 33 | return (FileMap){ 34 | .file = file, 35 | .mapping = mapping, 36 | .size = size, 37 | .data = output 38 | }; 39 | } 40 | 41 | static FileMap open_file_map_read(const char* filepath) { 42 | HANDLE file = CreateFileA(filepath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); 43 | if (file == INVALID_HANDLE_VALUE) { 44 | return (FileMap){ INVALID_HANDLE_VALUE }; 45 | } 46 | 47 | LARGE_INTEGER size; 48 | if (!GetFileSizeEx(file, &size)) { 49 | CloseHandle(file); 50 | return (FileMap){ INVALID_HANDLE_VALUE }; 51 | } 52 | 53 | HANDLE mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, 0); 54 | if (!mapping) { 55 | fprintf(stderr, "Could not map file! %s", filepath); 56 | CloseHandle(file); 57 | return (FileMap){ INVALID_HANDLE_VALUE }; 58 | } 59 | 60 | void* memory = MapViewOfFileEx(mapping, FILE_MAP_READ, 0, 0, 0, 0); 61 | if (!memory) { 62 | fprintf(stderr, "Could not view mapped file! %s", filepath); 63 | CloseHandle(mapping); 64 | CloseHandle(file); 65 | return (FileMap){ INVALID_HANDLE_VALUE }; 66 | } 67 | 68 | return (FileMap){ 69 | .file = file, 70 | .mapping = mapping, 71 | .size = size.QuadPart, 72 | .data = memory 73 | }; 74 | } 75 | 76 | static void close_file_map(FileMap* file_map) { 77 | UnmapViewOfFile(file_map->data); 78 | CloseHandle(file_map->mapping); 79 | CloseHandle(file_map->file); 80 | } 81 | #else 82 | #include 83 | #include 84 | #include 85 | #include 86 | 87 | typedef struct { 88 | int fd; 89 | size_t size; 90 | void* data; 91 | } FileMap; 92 | 93 | static FileMap open_file_map_write(const char* filepath, size_t size) { 94 | int fd = open(filepath, O_CREAT | O_RDWR, 0666); 95 | if (fd <= 0) { 96 | return (FileMap){ 0 }; 97 | } 98 | 99 | ftruncate(fd, size); 100 | 101 | void* output = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 102 | if (output == MAP_FAILED) { 103 | return (FileMap){ 0 }; 104 | } 105 | 106 | return (FileMap){ fd, size, output }; 107 | } 108 | 109 | static FileMap open_file_map_read(const char* filepath) { 110 | int fd = open(filepath, O_RDONLY); 111 | if (fd <= 0) { 112 | return (FileMap){ 0 }; 113 | } 114 | 115 | struct stat file_stats; 116 | if (fstat(fd, &file_stats) == -1) { 117 | return (FileMap){ 0 }; 118 | } 119 | 120 | void* buffer = mmap(NULL, file_stats.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 121 | if (buffer == MAP_FAILED) { 122 | return (FileMap){ 0 }; 123 | } 124 | 125 | return (FileMap){ fd, file_stats.st_size, buffer }; 126 | } 127 | 128 | static void close_file_map(FileMap* file_map) { 129 | munmap(file_map->data, file_map->size); 130 | close(file_map->fd); 131 | } 132 | #endif 133 | -------------------------------------------------------------------------------- /common/futex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __APPLE__ 6 | typedef _Atomic int32_t Futex; 7 | 8 | void futex_wait(Futex* f, int32_t val); // leaves if *f != val 9 | void futex_wait_eq(Futex* f, int32_t val); // leaves if *f == val 10 | #else 11 | typedef _Atomic int64_t Futex; 12 | 13 | void futex_wait(Futex* f, int64_t val); // leaves if *f != val 14 | void futex_wait_eq(Futex* f, int64_t val); // leaves if *f == val 15 | #endif 16 | 17 | void futex_signal(Futex* f); 18 | void futex_broadcast(Futex* f); 19 | -------------------------------------------------------------------------------- /common/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `log.c` for details. 6 | * 7 | * Modified by NeGate to enforce C11 mutexes 8 | */ 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define TB_ENABLE_LOG 0 18 | #define LOG_VERSION "0.1.0" 19 | 20 | typedef struct { 21 | va_list ap; 22 | const char *fmt; 23 | const char *file; 24 | uint64_t time; 25 | void *udata; 26 | int tid; 27 | int line; 28 | int level; 29 | } log_Event; 30 | 31 | typedef void (*log_LogFn)(log_Event *ev); 32 | 33 | enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; 34 | 35 | #if TB_ENABLE_LOG 36 | #define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) 37 | #define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 38 | #define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 39 | #define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) 40 | #define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) 41 | #define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) 42 | #define log_watch(fmt, var) log_log(LOG_DEBUG, __FILE__, __LINE__, #var " = " fmt, (uint64_t)var) 43 | #else 44 | #define log_trace(...) (__VA_ARGS__, 0) 45 | #define log_debug(...) (__VA_ARGS__, 0) 46 | #define log_info(...) (__VA_ARGS__, 0) 47 | #define log_warn(...) (__VA_ARGS__, 0) 48 | #define log_error(...) (__VA_ARGS__, 0) 49 | #define log_fatal(...) (__VA_ARGS__, 0) 50 | #define log_watch(fmt, var) (var, 0) 51 | #endif 52 | 53 | const char* log_level_string(int level); 54 | void log_set_level(int level); 55 | void log_set_quiet(bool enable); 56 | int log_add_callback(log_LogFn fn, void *udata, int level); 57 | int log_add_fp(FILE *fp, int level); 58 | 59 | void log_log(int level, const char *file, int line, const char *fmt, ...); 60 | -------------------------------------------------------------------------------- /common/perf.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////// 2 | // Profiler 3 | //////////////////////////////////////////// 4 | // The callbacks are global and a user may even hook in using the cuikperf_start 5 | // and cuikperf_stop, or by using CUIK_TIMED_BLOCK 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // DONT USE THIS :((( 14 | void cuik_init_timer_system(void); 15 | 16 | void cuikperf_start(const char* path); 17 | void cuikperf_stop(void); 18 | bool cuikperf_is_active(void); 19 | 20 | // the absolute values here don't have to mean anything, it's just about being able 21 | // to measure between two points. 22 | uint64_t cuik_time_in_nanos(void); 23 | 24 | void cuikperf_thread_start(void); 25 | void cuikperf_thread_stop(void); 26 | 27 | // Reports a region of time to the profiler callback 28 | void cuikperf_region_start(const char* fmt, const char* extra); 29 | void cuikperf_region_start2(const char* fmt, size_t extra_len, const char* extra); 30 | void cuikperf_region_end(void); 31 | 32 | // Usage: 33 | // CUIK_TIMED_BLOCK("Beans %d", 5) { 34 | // ... 35 | // } 36 | #define CUIK_TIMED_BLOCK(label) for (uint64_t __i = (cuikperf_region_start(label, NULL), 0); __i < 1; __i++, cuikperf_region_end()) 37 | #define CUIK_TIMED_BLOCK_ARGS(label, extra) for (uint64_t __i = (cuikperf_region_start(label, extra), 0); __i < 1; __i++, cuikperf_region_end()) 38 | -------------------------------------------------------------------------------- /common/pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define TPool_Thread_Local _Thread_local 6 | #define TPool_Atomic _Atomic 7 | 8 | #if defined(__APPLE__) || defined(_WIN32) 9 | typedef TPool_Atomic int64_t TPool_Futex; 10 | #else 11 | typedef TPool_Atomic int32_t TPool_Futex; 12 | #endif 13 | 14 | typedef struct TPool_Thread TPool_Thread; 15 | typedef struct TPool TPool; 16 | 17 | typedef void tpool_task_proc(TPool* pool, void** args); 18 | 19 | typedef struct TPool_Task { 20 | tpool_task_proc *do_work; 21 | void *args[3]; 22 | } TPool_Task; 23 | 24 | struct TPool { 25 | struct TPool_Thread *threads; 26 | 27 | int thread_count; 28 | TPool_Atomic bool running; 29 | 30 | TPool_Futex tasks_available; 31 | TPool_Futex tasks_left; 32 | }; 33 | 34 | void tpool_init(TPool *pool, int child_thread_count); 35 | void tpool_add_task(TPool *pool, tpool_task_proc* fn, void* val); 36 | void tpool_add_task2(TPool *pool, tpool_task_proc* fn, int arg_count, void** args); 37 | void tpool_wait(TPool *pool); 38 | void tpool_destroy(TPool *pool); 39 | 40 | -------------------------------------------------------------------------------- /common/str.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | String string_cstr(const char* str); 8 | String string_from_range(const unsigned char* start, const unsigned char* end); 9 | bool string_equals(const String* a, const String* b); 10 | bool string_equals_cstr(const String* a, const char* b); 11 | -------------------------------------------------------------------------------- /cuik_c/README.txt: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // C Language Front-end 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | This is Cuik (pronounced Quick), the fast C compiler front-end in the form of a 6 | library. IT IS NOT PRODUCTION-READY YET SO PLEASE DONT ACT LIKE IT IS. 7 | 8 | """ 9 | The world needs better compiler tools, tools which are built as libraries. This 10 | design point allows reuse of the tools in new and novel ways. However, building 11 | the tools as libraries isn't enough: they must have clean APIs, be as 12 | decoupled from each other as possible, and be easy to modify/extend. This 13 | requires clean layering, decent design, and avoiding tying the libraries to a 14 | specific use. Oh yeah, did I mention that we want the resultant libraries to 15 | be as fast as possible? :) 16 | 17 | - Chris Lattner, 2007 18 | """ 19 | 20 | Code overview: 21 | 22 | AST - this is the package that provides AST construction, printing and 23 | walking, it doesn't do parsing itself. depends on Common. 24 | 25 | Lex - provides a Lexer capable of C (potentially C++) along with keyword 26 | recognition for C and the preprocessor, depends on Common. 27 | 28 | Parse - the C parser, does some parsing due to the properties of C but 29 | only for top-level statements. depends on Preproc. 30 | 31 | Sema - type checker for C. depends on Parse. 32 | 33 | IRGen - handles converting type checked AST into TB IR. depends on AST. 34 | 35 | Driver - handles calling libCuik as an end-to-end program, depends on... 36 | everything? 37 | 38 | Key features: 39 | 40 | * Compiler-as-library design. 41 | * Faster compilation than Clang. 42 | * Powerful diagnostics engine. 43 | 44 | Active TODO: 45 | 46 | * Fix up API (i wanna make it stable eventually :P) 47 | 48 | Future Goals: 49 | 50 | * Incremental parser 51 | * Support for more versions of C (currently it's C11 N1590 mostly) 52 | 53 | -------------------------------------------------------------------------------- /cuik_c/atoms.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "atoms.h" 3 | #include "../cuik_pp/lexer.h" 4 | 5 | enum { INTERNER_EXP = 24 }; 6 | 7 | thread_local static TB_Arena atoms_arena; 8 | thread_local static Atom* interner; 9 | 10 | void atoms_free(void) { 11 | CUIK_TIMED_BLOCK("free atoms") { 12 | tb_arena_destroy(&atoms_arena); 13 | cuik__vfree(interner, (1u << INTERNER_EXP) * sizeof(Atom)); 14 | interner = NULL; 15 | } 16 | } 17 | 18 | size_t atoms_len(Atom str) { 19 | return *(uint32_t*) &str[-4]; 20 | } 21 | 22 | Atom atoms_put(size_t len, const unsigned char* str) { 23 | if (interner == NULL) { 24 | CUIK_TIMED_BLOCK("alloc atoms") { 25 | interner = cuik__valloc((1u << INTERNER_EXP) * sizeof(Atom)); 26 | tb_arena_create(&atoms_arena, "Atoms"); 27 | } 28 | } 29 | 30 | uint32_t mask = (1 << INTERNER_EXP) - 1; 31 | uint32_t hash = tb__murmur3_32(str, len); 32 | size_t first = hash & mask, i = first; 33 | 34 | do { 35 | // linear probe 36 | if (LIKELY(interner[i] == NULL)) { 37 | Atom newstr = tb_arena_unaligned_alloc(&atoms_arena, len + 5); 38 | uint32_t len32 = len; 39 | memcpy(newstr, &len32, 4); 40 | memcpy(newstr + 4, str, len); 41 | newstr[len + 4] = 0; 42 | 43 | interner[i] = newstr + 4; 44 | return &newstr[4]; 45 | } else if (len == atoms_len(interner[i]) && memcmp(str, interner[i], len) == 0) { 46 | return interner[i]; 47 | } 48 | 49 | i = (i + 1) & mask; 50 | } while (i != first); 51 | 52 | log_error("atoms arena: out of memory!\n"); 53 | abort(); 54 | } 55 | 56 | Atom atoms_putc(const char* str) { 57 | return atoms_put(strlen(str), (const unsigned char*) str); 58 | } 59 | 60 | Atom atoms_putuc(const unsigned char* str) { 61 | return atoms_put(strlen((const char*) str), str); 62 | } 63 | 64 | Atom atoms_eat_token(TokenStream* restrict s) { 65 | Token* t = tokens_get(s); 66 | if (t->type != TOKEN_IDENTIFIER) { 67 | return NULL; 68 | } 69 | 70 | tokens_next(s); 71 | return atoms_put(t->content.length, t->content.data); 72 | } 73 | -------------------------------------------------------------------------------- /cuik_c/atoms.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "arena.h" 3 | #include "common.h" 4 | 5 | typedef char* Atom; 6 | 7 | void atoms_free(void); 8 | Atom atoms_put(size_t len, const unsigned char* str); 9 | Atom atoms_putuc(const unsigned char* str); 10 | Atom atoms_putc(const char* str); 11 | 12 | // length of an atom 13 | size_t atoms_len(Atom str); 14 | 15 | -------------------------------------------------------------------------------- /cuik_c/compilation_unit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "parser.h" 5 | 6 | CompilationUnit* cuik_create_compilation_unit(void) { 7 | CompilationUnit* cu = cuik_calloc(1, sizeof(CompilationUnit)); 8 | mtx_init(&cu->lock, mtx_plain); 9 | return cu; 10 | } 11 | 12 | TranslationUnit* cuik_first_translation_unit(CompilationUnit* restrict cu) { 13 | return cu->head; 14 | } 15 | 16 | #ifdef CONFIG_HAS_TB 17 | void cuik_compilation_unit_set_tb_module(CompilationUnit* restrict cu, TB_Module* mod) { 18 | cu->ir_mod = mod; 19 | } 20 | 21 | TB_Module* cuik_compilation_unit_tb_module(CompilationUnit* restrict cu) { 22 | return cu->ir_mod; 23 | } 24 | #endif 25 | 26 | void cuik_add_to_compilation_unit(CompilationUnit* restrict cu, TranslationUnit* restrict tu) { 27 | assert(tu->next == NULL && "somehow the TU is already attached to something..."); 28 | mtx_lock(&cu->lock); 29 | 30 | #ifdef CONFIG_HAS_TB 31 | tu->ir_mod = cu->ir_mod; 32 | #endif 33 | 34 | tu->parent = cu; 35 | 36 | if (cu->tail == NULL) cu->head = tu; 37 | else cu->tail->next = tu; 38 | cu->tail = tu; 39 | cu->count += 1; 40 | 41 | mtx_unlock(&cu->lock); 42 | } 43 | 44 | void cuik_destroy_compilation_unit(CompilationUnit* restrict cu) { 45 | if (cu == NULL) { 46 | return; 47 | } 48 | 49 | // walk all the TUs and free them (if they're not freed already) 50 | TranslationUnit* tu = cu->head; 51 | while (tu != NULL) { 52 | TranslationUnit* next = tu->next; 53 | cuik_destroy_translation_unit(tu); 54 | tu = next; 55 | } 56 | 57 | mtx_destroy(&cu->lock); 58 | cuik_free(cu); 59 | } 60 | 61 | size_t cuik_num_of_translation_units_in_compilation_unit(CompilationUnit* restrict cu) { 62 | return cu->count; 63 | } 64 | -------------------------------------------------------------------------------- /cuik_c/crash_handler.c: -------------------------------------------------------------------------------- 1 | #if _WIN32 2 | #include "common.h" 3 | #include 4 | 5 | // because we undef'd it earlier 6 | #define VOID void 7 | #define TokenStream FuckYouWindows 8 | #include 9 | #pragma comment(lib, "Dbghelp.lib") 10 | 11 | #include 12 | 13 | static mtx_t crash_mutex; 14 | 15 | static LONG WINAPI unhandled_exception_handler(EXCEPTION_POINTERS* e) { 16 | mtx_lock(&crash_mutex); 17 | 18 | HANDLE process = GetCurrentProcess(); 19 | SymInitialize(process, NULL, TRUE); 20 | 21 | void* stack[100]; 22 | uint16_t frames = CaptureStackBackTrace(0, 100, stack, NULL); 23 | SYMBOL_INFO* symbol = (SYMBOL_INFO*) cuik_calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1); 24 | 25 | symbol->MaxNameLen = 255; 26 | symbol->SizeOfStruct = sizeof(SYMBOL_INFO); 27 | 28 | switch (e->ExceptionRecord->ExceptionCode) { 29 | case EXCEPTION_ACCESS_VIOLATION: { 30 | uintptr_t addr = e->ExceptionRecord->ExceptionInformation[1]; 31 | 32 | if (e->ExceptionRecord->ExceptionInformation[0] == 8) { 33 | printf("\nIllegal jump to %#llx\n", addr); 34 | } else { 35 | bool is_write = e->ExceptionRecord->ExceptionInformation[0] == 1; 36 | if (addr <= 0xFFFF) { 37 | printf("\nIllegal %s from null pointer (%#llx)\n", is_write ? "write" : "read", addr); 38 | } else { 39 | printf("\nIllegal %s from %#llx\n", is_write ? "write" : "read", addr); 40 | } 41 | } 42 | break; 43 | } 44 | 45 | default: 46 | printf("\nCrash dump:\n"); 47 | break; 48 | } 49 | 50 | size_t i = 0; 51 | 52 | // skip exception stack frames 53 | for (; i < frames; i++) { 54 | SymFromAddr(process, (DWORD64) stack[i], 0, symbol); 55 | if (strcmp(symbol->Name, "KiUserExceptionDispatcher") == 0) { 56 | i += 1; 57 | break; 58 | } 59 | } 60 | 61 | for (; i < frames; i++) { 62 | SymFromAddr(process, (DWORD64) stack[i], 0, symbol); 63 | 64 | DWORD disp; 65 | IMAGEHLP_LINE64 line; 66 | if (SymGetLineFromAddr64(process, (DWORD64) stack[i], &disp, &line)) { 67 | printf(" %-40s - 0x%llX - %s:%lu%+ld\n", symbol->Name, symbol->Address, line.FileName, line.LineNumber, disp); 68 | } else { 69 | printf(" %-40s - 0x%llX\n", symbol->Name, symbol->Address); 70 | } 71 | 72 | if (strcmp(symbol->Name, "main") == 0) { 73 | break; 74 | } 75 | } 76 | 77 | exit(1); 78 | return EXCEPTION_CONTINUE_SEARCH; 79 | } 80 | 81 | void hook_crash_handler(void) { 82 | mtx_init(&crash_mutex, mtx_plain); 83 | SetUnhandledExceptionFilter(unhandled_exception_handler); 84 | } 85 | #else 86 | // #error "Implement crash handler on this platform" 87 | void hook_crash_handler(void) { 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /cuik_c/driver/driver_args.h: -------------------------------------------------------------------------------- 1 | X(HELP, "h", false, "print help") 2 | X(VERBOSE, "V", false, "print verbose messages") 3 | X(LIVE, "live", false, "runs compiler persistently, repeats compile step whenever the input changes") 4 | // preprocessor 5 | X(DEFINE, "D", true, "defines a macro before compiling") 6 | X(UNDEF, "U", true, "undefines a macro before compiling") 7 | X(INCLUDE, "I", true, "add directory to the include searches") 8 | X(PPTEST, "Pp", false, "test preprocessor") 9 | X(PP, "P", false, "print preprocessor output to stdout") 10 | // parser 11 | X(LANG, "lang", true, "choose the language (c11, c23, glsl)") 12 | X(AST, "ast", false, "print AST into stdout") 13 | X(SYNTAX, "xe", false, "type check only") 14 | // dep tracking 15 | X(DEPS, "MD", false, "write header dependencies") 16 | X(DEPFILE, "MF", true, "write depout from -MD into a file") 17 | // optimizer 18 | X(OPTLVL, "O", false, "no optimizations") 19 | // backend 20 | X(EMITIR, "emit-ir", false, "print IR into stdout") 21 | X(OUTPUT, "o", true, "set the output filepath") 22 | X(OBJECT, "c", false, "output object file") 23 | X(ASSEMBLY, "S", false, "output assembly to stdout") 24 | X(DEBUG, "g", false, "compile with debug information") 25 | X(NOCHKSTK, "nochkstk", false, "disable buffer checks (think of MSVC's /GS-)") 26 | // linker 27 | X(NOLIBC, "nostdlib", false, "don't include and link against the default CRT") 28 | X(LIB, "l", true, "add library name to the linking") 29 | X(LIBDIR, "L", true, "add library directory to search paths") 30 | X(BASED, "based", false, "use the TB linker (EXPERIMENTAL)") 31 | X(SUBSYSTEM, "subsystem",true, "set windows subsystem (windows only... of course)") 32 | X(ENTRY, "e", true, "set entrypoint") 33 | // misc 34 | X(TARGET, "target", true, "change the target system and arch") 35 | X(THREADS, "j", false, "enabled multithreaded compilation") 36 | X(TIME, "T", false, "profile the compile times") 37 | X(THINK, "think", false, "aids in thinking about serious problems") 38 | // run 39 | X(RUN, "r", false, "JIT the executable (NOT READY)") 40 | #undef X 41 | -------------------------------------------------------------------------------- /cuik_c/driver/driver_fs.h: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | #define WIN32_LEAN_AND_MEAN 3 | #include 4 | #else 5 | #include 6 | #endif 7 | 8 | static bool str_ends_with(const char* cstr, const char* postfix) { 9 | const size_t cstr_len = strlen(cstr); 10 | const size_t postfix_len = strlen(postfix); 11 | 12 | return postfix_len <= cstr_len && strcmp(cstr + cstr_len - postfix_len, postfix) == 0; 13 | } 14 | 15 | // handles the **.c *.c type stuff 16 | static void filtered_append(Cuik_DriverArgs* args, const char* path, bool recursive) { 17 | const char* slash = path; 18 | for (const char* p = path; *p; p++) { 19 | if (*p == '/' || *p == '\\') { 20 | slash = p; 21 | } 22 | } 23 | 24 | #ifdef _WIN32 25 | WIN32_FIND_DATA find_data; 26 | HANDLE find_handle = FindFirstFile(path, &find_data); 27 | 28 | // loops through normal files 29 | if (find_handle != INVALID_HANDLE_VALUE) { 30 | do { 31 | char tmp[FILENAME_MAX]; 32 | if (slash == path) { 33 | sprintf_s(tmp, MAX_PATH, "%s", find_data.cFileName); 34 | } else { 35 | sprintf_s(tmp, MAX_PATH, "%.*s%s", (int)(slash - path) + 1, path, find_data.cFileName); 36 | } 37 | 38 | Cuik_Path* new_path = cuik_malloc(sizeof(Cuik_Path)); 39 | if (!cuikfs_canonicalize(new_path, tmp, false)) { 40 | fprintf(stderr, "Invalid filepath! %s\n", tmp); 41 | } 42 | 43 | if (cuik_path_has_ext(new_path, "a") || cuik_path_has_ext(new_path, "lib")) { 44 | dyn_array_put(args->libraries, new_path); 45 | } else { 46 | dyn_array_put(args->sources, new_path); 47 | } 48 | } while (FindNextFile(find_handle, &find_data)); 49 | } 50 | FindClose(find_handle); 51 | 52 | if (recursive) { 53 | char dir_path[MAX_PATH]; 54 | sprintf_s(dir_path, sizeof(dir_path), "%.*s*", (int)(slash - path) + 1, path); 55 | HANDLE dir = FindFirstFile(dir_path, &find_data); 56 | 57 | if (dir != INVALID_HANDLE_VALUE) { 58 | do { 59 | if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && find_data.cFileName[0] != '.') { 60 | char new_pattern[FILENAME_MAX]; 61 | sprintf_s(new_pattern, sizeof(new_pattern), "%.*s%s%s", (int)(slash - path) + 1, path, find_data.cFileName, slash); 62 | 63 | filtered_append(args, new_pattern, true); 64 | } 65 | } while (FindNextFile(dir, &find_data)); 66 | } 67 | FindClose(dir); 68 | } 69 | #else 70 | fprintf(stderr, "todo: glob search not implemented on this platform yet!\n"); 71 | exit(1); 72 | #endif 73 | } 74 | 75 | static void append_input_path(Cuik_DriverArgs* args, const char* path) { 76 | // we don't check this very well because we're based 77 | const char* star = NULL; 78 | for (const char* p = path; *p; p++) { 79 | if (*p == '*') { 80 | star = p; 81 | break; 82 | } 83 | } 84 | 85 | if (star != NULL) { 86 | filtered_append(args, path, star[1] == '*'); 87 | } else { 88 | Cuik_Path* newstr = cuik_malloc(sizeof(Cuik_Path)); 89 | if (cuikfs_canonicalize(newstr, path, args->toolchain.case_insensitive)) { 90 | if (cuik_path_has_ext(newstr, "a") || cuik_path_has_ext(newstr, "lib")) { 91 | dyn_array_put(args->libraries, newstr); 92 | } else { 93 | dyn_array_put(args->sources, newstr); 94 | } 95 | } else { 96 | fprintf(stderr, "Invalid filepath! %s\n", path); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /cuik_c/driver/driver_sched.h: -------------------------------------------------------------------------------- 1 | 2 | #ifdef CONFIG_HAS_TB 3 | typedef struct { 4 | Futex* done; 5 | 6 | size_t count; 7 | TB_Function** arr; 8 | 9 | void* arg; 10 | 11 | CuikSched_PerFunction func; 12 | } PerFunction; 13 | 14 | static void per_func_task(TPool* tp, void** arg) { 15 | PerFunction task = *((PerFunction*) arg[0]); 16 | for (size_t i = 0; i < task.count; i++) { 17 | task.func(task.arr[i], task.arg); 18 | } 19 | 20 | atomic_fetch_sub(task.done, 1); 21 | futex_signal(task.done); 22 | } 23 | 24 | static size_t good_batch_size(size_t n, size_t jobs) { 25 | // we cap out at 8192 for the batch size but when there's less input 26 | // we might pick something which can get some good division of labor. 27 | // 28 | // each thread is gonna get 4 batches so: job_count / (N * 4) 29 | /*size_t batch_size = jobs / (n * 4); 30 | if (batch_size < 128) return 100; 31 | if (batch_size > 8192) return 8192; 32 | 33 | // next power of two 34 | return 1ull << (64ull - __builtin_clzll(batch_size - 1ull));*/ 35 | 36 | return 8192; 37 | } 38 | 39 | void cuiksched_per_function(TPool* tp, CompilationUnit* cu, TB_Module* mod, void* arg, CuikSched_PerFunction func) { 40 | if (tp != NULL) { 41 | Futex done = 0; 42 | size_t count = 0; 43 | 44 | PerFunction task = { .done = &done, .arg = arg, .func = func }; 45 | size_t func_count = dyn_array_length(cu->worklist); 46 | 47 | size_t batch_size = 10000; 48 | for (size_t i = 0; i < func_count; i += batch_size) { 49 | size_t end = i + batch_size; 50 | if (end >= func_count) end = func_count; 51 | 52 | // btw the struct gets copied by the thread pool 53 | task.count = end - i; 54 | task.arr = &cu->worklist[i]; 55 | 56 | // leaking!!! also bad allocation pattern!!! 57 | PerFunction* t = cuik_malloc(sizeof(PerFunction)); 58 | *t = task; 59 | 60 | tpool_add_task(tp, per_func_task, t); 61 | count++; 62 | } 63 | 64 | futex_wait_eq(&done, count); 65 | } else { 66 | size_t func_count = dyn_array_length(cu->worklist); 67 | for (size_t i = 0; i < func_count; i++) { 68 | func(cu->worklist[i], arg); 69 | } 70 | } 71 | } 72 | #endif 73 | -------------------------------------------------------------------------------- /cuik_c/glsl_keywords.h: -------------------------------------------------------------------------------- 1 | // GLSL does a lot of things with pseudo keywords so let's automate some of that 2 | X(binding) 3 | X(location) 4 | X(offset) 5 | #undef X -------------------------------------------------------------------------------- /cuik_c/ir_gen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef CONFIG_HAS_TB 4 | #include 5 | #include 6 | #include 7 | #include "parser.h" 8 | 9 | #include 10 | 11 | typedef enum IRValType { 12 | RVALUE, 13 | RVALUE_PHI, 14 | 15 | LVALUE, 16 | LVALUE_SYM, 17 | LVALUE_BITS, 18 | LVALUE_EXPR, 19 | LVALUE_LABEL, 20 | } IRValType; 21 | 22 | typedef struct { 23 | IRValType kind : 16; 24 | int mem_var : 16; 25 | struct { 26 | uint16_t offset; 27 | uint16_t width; 28 | } bits; 29 | union { 30 | TB_Node* n; 31 | Subexpr* e; 32 | }; 33 | Cuik_QualType type; 34 | Cuik_QualType cast_type; 35 | } ValDesc; 36 | 37 | typedef struct IRVal { 38 | IRValType value_type; 39 | Cuik_QualType type, cast_type; 40 | 41 | union { 42 | TB_Node* reg; 43 | Stmt* sym; 44 | Subexpr* e; 45 | struct { 46 | TB_Node* reg; 47 | 48 | short offset; 49 | short width; 50 | } bits; 51 | struct { 52 | TB_Node* if_true; 53 | TB_Node* if_false; 54 | TB_Node* merger; 55 | } phi; 56 | }; 57 | } IRVal; 58 | 59 | static TB_DataType ctype_to_tbtype(const Cuik_Type* t) { 60 | switch (t->kind) { 61 | case KIND_VOID: 62 | return TB_TYPE_VOID; 63 | case KIND_BOOL: 64 | return TB_TYPE_BOOL; 65 | 66 | case KIND_CHAR: 67 | case KIND_SHORT: 68 | case KIND_INT: 69 | case KIND_LONG: 70 | case KIND_LLONG: { 71 | switch (t->size) { 72 | case 1: return TB_TYPE_I8; 73 | case 2: return TB_TYPE_I16; 74 | case 4: return TB_TYPE_I32; 75 | case 8: return TB_TYPE_I64; 76 | default: abort(); 77 | } 78 | } 79 | 80 | case KIND_FLOAT: 81 | return TB_TYPE_F32; 82 | case KIND_DOUBLE: 83 | return TB_TYPE_F64; 84 | case KIND_ENUM: 85 | return TB_TYPE_I32; 86 | 87 | case KIND_PTR: 88 | case KIND_FUNC: 89 | case KIND_ARRAY: 90 | return TB_TYPE_PTR; 91 | 92 | case KIND_STRUCT: 93 | case KIND_UNION: 94 | return TB_TYPE_PTR; 95 | 96 | default: 97 | abort(); // TODO 98 | } 99 | } 100 | 101 | TB_Node* cvt2rval(TranslationUnit* tu, TB_Function* func, IRVal* v); 102 | int count_max_tb_init_objects(InitNode* root_node); 103 | TB_DebugType* cuik__as_tb_debug_type(TB_Module* mod, Cuik_Type* t); 104 | 105 | TB_Node* as_rval(TranslationUnit* tu, TB_GraphBuilder* g, const ValDesc* v); 106 | 107 | static void irgen_stmt(TranslationUnit* tu, TB_Function* func, Stmt* restrict s); 108 | static TB_Node* irgen_as_rvalue(TranslationUnit* tu, TB_Function* func, Cuik_Expr* e); 109 | static IRVal irgen_expr(TranslationUnit* tu, TB_Function* func, Cuik_Expr* e); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /cuik_c/libcuik.c: -------------------------------------------------------------------------------- 1 | // Our precious libCuik unity build, ideally a lot of the smaller components will 2 | // become single header libs maybe even the entire thing. 3 | #include "tls.c" 4 | #include "path.c" 5 | 6 | // Used around the place 7 | #include "hashes.h" 8 | 9 | #include 10 | #include 11 | 12 | #define CUIK_FS_IMPL 13 | #include 14 | 15 | #define CUIK_AST_IMPL 16 | #include 17 | 18 | // Parser 19 | #define CUIK_SYMTAB_IMPL 20 | #include 21 | 22 | #include "ast_dump.c" 23 | #include "atoms.c" 24 | #include "types.c" 25 | #include "parser.c" 26 | #include "sema.c" 27 | #include "visitors.c" 28 | 29 | // IR Gen 30 | #ifdef CONFIG_HAS_TB 31 | #include "ir_gen.c" 32 | #include "ir_gen2.c" 33 | #endif 34 | 35 | // Linker support 36 | #include "linker.c" 37 | 38 | // Targets 39 | #include "targets/target_generic.c" 40 | 41 | // Compilation units 42 | #include "compilation_unit.c" 43 | #include "driver/driver.c" 44 | #include "cuik.c" 45 | 46 | // Must be at the bottom because it does weird windows stuff 47 | #include "crash_handler.c" 48 | -------------------------------------------------------------------------------- /cuik_c/linker.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef _WIN32 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | #endif 8 | 9 | void cuiklink_deinit(Cuik_Linker* l) { 10 | dyn_array_destroy(l->inputs); 11 | dyn_array_destroy(l->libpaths); 12 | } 13 | 14 | void cuiklink_apply_toolchain_libs(Cuik_Linker* l, bool nocrt) { 15 | l->toolchain.add_libraries(l->toolchain.ctx, nocrt, l); 16 | } 17 | 18 | void cuiklink_add_libpath(Cuik_Linker* l, const char filepath[]) { 19 | char* out = cuik_strdup(filepath); 20 | for (char* s = out; *s; s++) { 21 | if (*s == '\\') *s = '/'; 22 | } 23 | dyn_array_put(l->libpaths, out); 24 | } 25 | 26 | void cuiklink_add_libpathf(Cuik_Linker* l, const char* fmt, ...) { 27 | char* out = cuik_malloc(FILENAME_MAX); 28 | 29 | va_list ap; 30 | va_start(ap, fmt); 31 | vsnprintf(out, FILENAME_MAX, fmt, ap); 32 | va_end(ap); 33 | 34 | for (char* s = out; *s; s++) { 35 | if (*s == '\\') *s = '/'; 36 | } 37 | dyn_array_put(l->libpaths, out); 38 | } 39 | 40 | void cuiklink_add_input_file(Cuik_Linker* l, const char* filepath) { 41 | dyn_array_put(l->inputs, cuik_strdup(filepath)); 42 | } 43 | 44 | bool cuiklink_find_library(Cuik_Linker* l, char output[FILENAME_MAX], const char* filepath) { 45 | dyn_array_for(i, l->libpaths) { 46 | const char* lp = l->libpaths[i]; 47 | snprintf(output, FILENAME_MAX, "%s%s%s", lp, lp[strlen(lp) - 1] != '/' ? "/" : "", filepath); 48 | 49 | FILE* f = fopen(output, "rb"); 50 | if (f) { 51 | fclose(f); 52 | return true; 53 | } 54 | } 55 | 56 | return false; 57 | } 58 | 59 | bool cuiklink_invoke(Cuik_Linker* l, Cuik_DriverArgs* args, const char* output_file, const char* filename) { 60 | return args->toolchain.invoke_link(args->toolchain.ctx, args, l, output_file, filename); 61 | } 62 | -------------------------------------------------------------------------------- /cuik_c/path.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static ptrdiff_t find_last_slash(const char* str) { 4 | size_t len = strlen(str); 5 | for (size_t i = len; i--;) { 6 | if (str[i] == '/' || str[i] == '\\') return i; 7 | } 8 | 9 | return -1; 10 | } 11 | 12 | static ptrdiff_t find_ext(size_t len, const char* str) { 13 | for (size_t i = len; i--;) { 14 | if (str[i] == '/' || str[i] == '\\') return -1; 15 | if (str[i] == '.') return i; 16 | } 17 | 18 | return -1; 19 | } 20 | 21 | bool cuik_path_set_no_ext(Cuik_Path* restrict dst, const char* src) { 22 | ptrdiff_t slash_pos = find_ext(strlen(src), src); 23 | if (slash_pos >= 0) { 24 | // copy everything before that last slash, then normalize the slash to the OS 25 | memcpy(dst->data, src, slash_pos); 26 | dst->data[slash_pos] = CUIK_PATH_SLASH_SEP; 27 | dst->length = slash_pos + 1; 28 | } else { 29 | dst->length = 0; 30 | dst->data[0] = '\0'; 31 | } 32 | 33 | return true; 34 | } 35 | 36 | bool cuik_path_set(Cuik_Path* restrict dst, const char* src) { 37 | size_t len = src ? strlen(src) : 0; 38 | if (len >= FILENAME_MAX) { 39 | return false; 40 | } 41 | 42 | memcpy(dst->data, src, len); 43 | dst->data[len] = 0; 44 | dst->length = len; 45 | return true; 46 | } 47 | 48 | bool cuik_path_set_ext(Cuik_Path* restrict dst, Cuik_Path* restrict src, size_t ext_len, const char* ext) { 49 | ptrdiff_t dot_pos = find_ext(src->length, src->data); 50 | 51 | return cuik_path_append2(dst, dot_pos >= 0 ? dot_pos : src->length, src->data, ext_len, ext); 52 | } 53 | 54 | bool cuik_path_set_dir(Cuik_Path* restrict dst, const char* src) { 55 | ptrdiff_t slash_pos = find_last_slash(src); 56 | if (slash_pos >= 0) { 57 | // copy everything before that last slash, then normalize the slash to the OS 58 | memcpy(dst->data, src, slash_pos); 59 | dst->data[slash_pos] = CUIK_PATH_SLASH_SEP; 60 | dst->length = slash_pos + 1; 61 | } else { 62 | dst->length = 0; 63 | dst->data[0] = '\0'; 64 | } 65 | 66 | return true; 67 | } 68 | 69 | bool cuik_path_append2(Cuik_Path* restrict dst, size_t a_len, const char a[], size_t b_len, const char b[]) { 70 | size_t end = a_len + b_len; 71 | if (end >= FILENAME_MAX) { 72 | return false; 73 | } 74 | 75 | memcpy(dst->data, a, a_len); 76 | memcpy(dst->data + a_len, b, b_len); 77 | dst->data[end] = 0; 78 | dst->length = end; 79 | return true; 80 | } 81 | 82 | bool cuik_path_append(Cuik_Path* restrict dst, const Cuik_Path* restrict a, size_t b_len, const char b[]) { 83 | size_t end = a->length + b_len; 84 | if (end >= FILENAME_MAX) { 85 | return false; 86 | } 87 | 88 | memcpy(dst->data, a->data, a->length); 89 | memcpy(dst->data + a->length, b, b_len); 90 | dst->data[end] = 0; 91 | dst->length = end; 92 | return true; 93 | } 94 | 95 | bool cuik_path_has_ext(const Cuik_Path* restrict src, const char* ext) { 96 | size_t ext_len = strlen(ext); 97 | 98 | return src->length > ext_len + 1 99 | && src->data[src->length - ext_len - 1] == '.' 100 | && memcmp(&src->data[src->length - ext_len], ext, ext_len) == 0; 101 | } 102 | 103 | bool cuik_path_has_ext2(const Cuik_Path* restrict src) { 104 | for (size_t i = src->length; i--;) { 105 | if (src->data[i] == '/') return false; 106 | if (src->data[i] == '\\') return false; 107 | 108 | if (src->data[i] == '.') return true; 109 | } 110 | 111 | return false; 112 | } 113 | 114 | bool cuik_path_is_in(const Cuik_Path* restrict dst, const char* src) { 115 | size_t src_len = src ? strlen(src) : 0; 116 | if (src_len > 0 && (src[src_len - 1] == '/' || src[src_len - 1] == '\\')) { 117 | src_len -= 1; 118 | } 119 | 120 | return dst->length >= src_len + 1 121 | && memcmp(dst->data, src, src_len) == 0 122 | && (dst->data[src_len] == '/' || dst->data[src_len] == '\\'); 123 | } 124 | -------------------------------------------------------------------------------- /cuik_c/sema.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "parser.h" 6 | 7 | bool type_compatible(TranslationUnit* tu, Cuik_Type* a, Cuik_Type* b, Subexpr* a_expr); 8 | 9 | // check that each type matches the cast type 10 | bool cuik__type_check_args(TranslationUnit* tu, Cuik_Expr* e, int arg_count, Subexpr** args); 11 | Cuik_QualType cuik__sema_expr(TranslationUnit* tu, Cuik_Expr* restrict e); 12 | void sema_stmt(TranslationUnit* tu, Stmt* restrict s); 13 | -------------------------------------------------------------------------------- /cuik_c/targets/aarch64_desc.c: -------------------------------------------------------------------------------- 1 | #include "targets.h" 2 | #include "../sema.h" 3 | 4 | static void set_defines(const Cuik_Target* target, Cuik_CPP* cpp) { 5 | target_generic_set_defines(cpp, target->system, true, true); 6 | 7 | cuikpp_define_cstr(cpp, "__arm", "1"); 8 | cuikpp_define_cstr(cpp, "__arm__", "1"); 9 | cuikpp_define_cstr(cpp, "__ARM_ARCH_8__", "1"); 10 | 11 | if (target->system == CUIK_SYSTEM_WINDOWS) { 12 | cuikpp_define_cstr(cpp, "_M_ARM64", "1"); 13 | } 14 | } 15 | 16 | #ifdef CONFIG_HAS_TB 17 | // on Win64 all structs that have a size of 1,2,4,8 18 | // or any scalars are passed via registers 19 | static bool win64_should_pass_via_reg(TranslationUnit* tu, Cuik_Type* type) { 20 | if (type->kind == KIND_STRUCT || type->kind == KIND_UNION) { 21 | return type->size <= 8; 22 | } else { 23 | return true; 24 | } 25 | } 26 | 27 | static TB_FunctionPrototype* create_prototype(TranslationUnit* tu, Cuik_Type* type) { 28 | return target_generic_create_prototype(win64_should_pass_via_reg, tu, type); 29 | } 30 | 31 | static TB_Node* compile_builtin(TranslationUnit* tu, TB_GraphBuilder* g, const char* name, int arg_count, ValDesc* args) { 32 | return NULL; 33 | } 34 | #endif /* CONFIG_HAS_TB */ 35 | 36 | Cuik_Target* cuik_target_aarch64(Cuik_System system, Cuik_Environment env) { 37 | BuiltinTable builtins; 38 | nl_map_create(builtins, 128); 39 | 40 | target_generic_fill_builtin_table(&builtins); 41 | 42 | // #define X(name, format) nl_map_put_cstr(builtins, #name, format); 43 | // #undef X 44 | 45 | Cuik_Target* t = cuik_malloc(sizeof(Cuik_Target)); 46 | *t = (Cuik_Target){ 47 | .env = env, 48 | .system = system, 49 | 50 | .int_bits = { 8, 16, 32, 64, 64 }, 51 | .pointer_byte_size = 8, 52 | 53 | #ifdef CONFIG_HAS_TB 54 | .arch = TB_ARCH_AARCH64, 55 | #endif 56 | 57 | .builtin_func_map = builtins, 58 | .set_defines = set_defines, 59 | #ifdef CONFIG_HAS_TB 60 | .compile_builtin = compile_builtin, 61 | #endif /* CONFIG_HAS_TB */ 62 | }; 63 | 64 | // on MSVC, long means 32bit and this is necessary because of stuff like 65 | // DWORD being defined as unsigned long. 66 | if (env == CUIK_ENV_MSVC) { 67 | t->int_bits[CUIK_BUILTIN_LONG] = 32; 68 | } 69 | 70 | cuik_target_build(t); 71 | 72 | // bake out size_t and ptrdiff_t after long long is ready 73 | t->size_type = t->unsigned_ints[CUIK_BUILTIN_LLONG]; 74 | t->size_type.also_known_as = "size_t"; 75 | 76 | t->ptrdiff_type = t->signed_ints[CUIK_BUILTIN_LLONG]; 77 | t->ptrdiff_type.also_known_as = "ptrdiff_t"; 78 | 79 | return t; 80 | } 81 | -------------------------------------------------------------------------------- /cuik_c/targets/mips_desc.c: -------------------------------------------------------------------------------- 1 | #include "targets.h" 2 | #include "../sema.h" 3 | 4 | static void set_defines(const Cuik_Target* target, Cuik_CPP* cpp) { 5 | target_generic_set_defines(cpp, target->system, true, true); 6 | 7 | cuikpp_define_cstr(cpp, "mips", "1"); 8 | cuikpp_define_cstr(cpp, "__mips", "1"); 9 | cuikpp_define_cstr(cpp, "__mips__", "1"); 10 | cuikpp_define_cstr(cpp, "__MIPS__", "1"); 11 | } 12 | 13 | #ifdef CONFIG_HAS_TB 14 | static bool win64_should_pass_via_reg(TranslationUnit* tu, Cuik_Type* type) { 15 | if (type->kind == KIND_STRUCT || type->kind == KIND_UNION) { 16 | return type->size <= tu->target->pointer_byte_size; 17 | } else { 18 | return true; 19 | } 20 | } 21 | 22 | static TB_FunctionPrototype* create_prototype(TranslationUnit* tu, Cuik_Type* type) { 23 | return target_generic_create_prototype(win64_should_pass_via_reg, tu, type); 24 | } 25 | 26 | static TB_Node* compile_builtin(TranslationUnit* tu, TB_GraphBuilder* g, const char* name, int arg_count, ValDesc* args) { 27 | return NULL; 28 | } 29 | #endif /* CONFIG_HAS_TB */ 30 | 31 | static Cuik_Target* cuik_target_mips(Cuik_System system, Cuik_Environment env, bool is64bit) { 32 | BuiltinTable builtins; 33 | nl_map_create(builtins, 128); 34 | target_generic_fill_builtin_table(&builtins); 35 | 36 | // #define X(name, format) nl_map_put_cstr(builtins, #name, format); 37 | // #undef X 38 | 39 | Cuik_Target* t = cuik_malloc(sizeof(Cuik_Target)); 40 | *t = (Cuik_Target){ 41 | .env = env, 42 | .system = system, 43 | 44 | .int_bits = { 8, 16, 32, 64, 64 }, 45 | .pointer_byte_size = 8, 46 | 47 | #ifdef CONFIG_HAS_TB 48 | .arch = is64bit ? TB_ARCH_MIPS64 : TB_ARCH_MIPS32, 49 | #endif 50 | 51 | .builtin_func_map = builtins, 52 | .set_defines = set_defines, 53 | #ifdef CONFIG_HAS_TB 54 | .compile_builtin = compile_builtin, 55 | #endif /* CONFIG_HAS_TB */ 56 | }; 57 | 58 | cuik_target_build(t); 59 | 60 | // bake out size_t and ptrdiff_t after long long is ready 61 | t->size_type = t->unsigned_ints[CUIK_BUILTIN_LLONG]; 62 | t->size_type.also_known_as = "size_t"; 63 | 64 | t->ptrdiff_type = t->signed_ints[CUIK_BUILTIN_LLONG]; 65 | t->ptrdiff_type.also_known_as = "ptrdiff_t"; 66 | 67 | return t; 68 | } 69 | 70 | Cuik_Target* cuik_target_mips32(Cuik_System system, Cuik_Environment env) { 71 | return cuik_target_mips(system, env, false); 72 | } 73 | 74 | Cuik_Target* cuik_target_mips64(Cuik_System system, Cuik_Environment env) { 75 | return cuik_target_mips(system, env, true); 76 | } 77 | -------------------------------------------------------------------------------- /cuik_c/targets/targets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef CONFIG_HAS_TB 8 | #include "../ir_gen.h" 9 | #endif 10 | 11 | enum { 12 | CUIK_BUILTIN_CHAR, 13 | CUIK_BUILTIN_SHORT, 14 | CUIK_BUILTIN_INT, 15 | CUIK_BUILTIN_LONG, 16 | CUIK_BUILTIN_LLONG 17 | }; 18 | 19 | typedef NL_Strmap(const char*) BuiltinTable; 20 | 21 | struct Cuik_Target { 22 | Cuik_Environment env; 23 | Cuik_System system; 24 | 25 | #ifdef CONFIG_HAS_TB 26 | TB_Arch arch; 27 | #endif 28 | 29 | bool is_big_endian; 30 | 31 | // tells us if a name is maps to a builtin 32 | BuiltinTable builtin_func_map; 33 | 34 | // we don't have any enforcements on primitive integers other than what 35 | // the spec might say. 36 | // sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long) 37 | uint32_t int_bits[5]; 38 | Cuik_Type signed_ints[5], unsigned_ints[5]; 39 | 40 | // this is size_t and ptrdiff_t 41 | Cuik_Type size_type, ptrdiff_type; 42 | 43 | // this is used for uintptr_t, intptr_t and pointer types 44 | size_t pointer_byte_size; 45 | 46 | // initializes some target specific macro defines 47 | void (*set_defines)(const Cuik_Target* self, Cuik_CPP* cpp); 48 | 49 | #ifdef CONFIG_HAS_TB 50 | // when one of the builtins are triggered we call this to generate it's code 51 | TB_Node* (*compile_builtin)(TranslationUnit* tu, TB_GraphBuilder* g, const char* name, int arg_count, ValDesc* args); 52 | #endif /* CONFIG_HAS_TB */ 53 | }; 54 | 55 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN 56 | #define TARGET_NEEDS_BYTESWAP(target) (!(target)->is_big_endian) 57 | #else 58 | #define TARGET_NEEDS_BYTESWAP(target) ((target)->is_big_endian) 59 | #endif 60 | 61 | // Called after initializing the integer sizes along with the callbacks. 62 | // Verify that the integer sizes are compliant and initialize real pointers 63 | // to Cuik_Types for them. 64 | void cuik_target_build(Cuik_Target* target); 65 | 66 | #ifdef CONFIG_HAS_TB 67 | typedef bool (*ShouldPassViaReg)(TranslationUnit* tu, Cuik_Type* type); 68 | TB_FunctionPrototype* target_generic_create_prototype(ShouldPassViaReg fn, TranslationUnit* tu, Cuik_Type* type); 69 | #endif 70 | 71 | void target_generic_set_defines(Cuik_CPP* cpp, Cuik_System sys, bool is_64bit, bool is_little_endian); 72 | void target_generic_fill_builtin_table(BuiltinTable* builtins); 73 | -------------------------------------------------------------------------------- /cuik_c/targets/wasm_desc.c: -------------------------------------------------------------------------------- 1 | #include "targets.h" 2 | #include "../sema.h" 3 | 4 | static void set_defines(const Cuik_Target* target, Cuik_CPP* cpp) { 5 | target_generic_set_defines(cpp, target->system, true, true); 6 | } 7 | 8 | #ifdef CONFIG_HAS_TB 9 | static bool win64_should_pass_via_reg(TranslationUnit* tu, Cuik_Type* type) { 10 | if (type->kind == KIND_STRUCT || type->kind == KIND_UNION) { 11 | return type->size <= tu->target->pointer_byte_size; 12 | } else { 13 | return true; 14 | } 15 | } 16 | 17 | static TB_FunctionPrototype* create_prototype(TranslationUnit* tu, Cuik_Type* type) { 18 | return target_generic_create_prototype(win64_should_pass_via_reg, tu, type); 19 | } 20 | 21 | static TB_Node* compile_builtin(TranslationUnit* tu, TB_GraphBuilder* g, const char* name, int arg_count, ValDesc* args) { 22 | return NULL; 23 | } 24 | #endif /* CONFIG_HAS_TB */ 25 | 26 | Cuik_Target* cuik_target_wasm32(Cuik_System system, Cuik_Environment env) { 27 | BuiltinTable builtins; 28 | nl_map_create(builtins, 128); 29 | target_generic_fill_builtin_table(&builtins); 30 | 31 | // #define X(name, format) nl_map_put_cstr(builtins, #name, format); 32 | // #undef X 33 | 34 | Cuik_Target* t = cuik_malloc(sizeof(Cuik_Target)); 35 | *t = (Cuik_Target){ 36 | .env = env, 37 | .system = system, 38 | 39 | .int_bits = { 8, 16, 32, 64, 64 }, 40 | .pointer_byte_size = 4, 41 | 42 | #ifdef CONFIG_HAS_TB 43 | .arch = TB_ARCH_WASM32, 44 | #endif 45 | 46 | .builtin_func_map = builtins, 47 | .set_defines = set_defines, 48 | #ifdef CONFIG_HAS_TB 49 | .compile_builtin = compile_builtin, 50 | #endif /* CONFIG_HAS_TB */ 51 | }; 52 | 53 | cuik_target_build(t); 54 | 55 | // bake out size_t and ptrdiff_t after int is ready 56 | t->size_type = t->unsigned_ints[CUIK_BUILTIN_INT]; 57 | t->size_type.also_known_as = "size_t"; 58 | 59 | t->ptrdiff_type = t->signed_ints[CUIK_BUILTIN_INT]; 60 | t->ptrdiff_type.also_known_as = "ptrdiff_t"; 61 | 62 | return t; 63 | } 64 | -------------------------------------------------------------------------------- /cuik_c/targets/x64_desc.c: -------------------------------------------------------------------------------- 1 | #include "targets.h" 2 | #include "../sema.h" 3 | 4 | static void set_defines(const Cuik_Target* target, Cuik_CPP* cpp) { 5 | target_generic_set_defines(cpp, target->system, true, true); 6 | 7 | cuikpp_define_cstr(cpp, "__x86_64", "1"); 8 | cuikpp_define_cstr(cpp, "__x86_64__", "1"); 9 | cuikpp_define_cstr(cpp, "__amd64", "1"); 10 | cuikpp_define_cstr(cpp, "__amd64__", "1"); 11 | 12 | if (target->system == CUIK_SYSTEM_WINDOWS) { 13 | cuikpp_define_cstr(cpp, "_M_X64", "100"); 14 | cuikpp_define_cstr(cpp, "_AMD64_", "100"); 15 | cuikpp_define_cstr(cpp, "_M_AMD64", "100"); 16 | } 17 | } 18 | 19 | #ifdef CONFIG_HAS_TB 20 | // on Win64 all structs that have a size of 1,2,4,8 21 | // or any scalars are passed via registers 22 | static bool win64_should_pass_via_reg(TranslationUnit* tu, Cuik_Type* type) { 23 | if (type->kind == KIND_STRUCT || type->kind == KIND_UNION) { 24 | return type->size <= 8; 25 | } else { 26 | return true; 27 | } 28 | } 29 | 30 | static TB_FunctionPrototype* create_prototype(TranslationUnit* tu, Cuik_Type* type) { 31 | return target_generic_create_prototype(win64_should_pass_via_reg, tu, type); 32 | } 33 | 34 | static TB_Node* compile_builtin(TranslationUnit* tu, TB_GraphBuilder* g, const char* name, int arg_count, ValDesc* args) { 35 | // x64 specific builtins 36 | if (strcmp(name, "_mm_setcsr") == 0) { 37 | return tb_builder_x86_ldmxcsr(g, as_rval(tu, g, &args[1])); 38 | } else if (strcmp(name, "_mm_getcsr") == 0) { 39 | return tb_builder_x86_stmxcsr(g); 40 | } else if (strcmp(name, "__rdtsc") == 0) { 41 | return tb_builder_cycle_counter(g); 42 | } else if (strcmp(name, "__readgsqword") == 0) { 43 | // TODO(NeGate): implement readgs/writegs with all the type variants 44 | return tb_builder_uint(g, TB_TYPE_I16, 0); 45 | } else { 46 | assert(0 && "unimplemented builtin!"); 47 | return 0; 48 | } 49 | } 50 | #endif /* CONFIG_HAS_TB */ 51 | 52 | Cuik_Target* cuik_target_x64(Cuik_System system, Cuik_Environment env) { 53 | BuiltinTable builtins; 54 | nl_map_create(builtins, 128); 55 | 56 | target_generic_fill_builtin_table(&builtins); 57 | 58 | #define X(name, format) nl_map_put_cstr(builtins, #name, format); 59 | if (system == CUIK_SYSTEM_WINDOWS) { 60 | X(__va_start, "c**T v"); 61 | X(__va_arg, "c*T v"); 62 | } else { 63 | X(__va_start, "v*T v"); 64 | X(__va_arg, "v*T v"); 65 | } 66 | X(_mm_getcsr, "v i"); 67 | X(_mm_setcsr, "i v"); 68 | X(__readgsqword, " s"); 69 | X(__rdtsc, " L"); 70 | #undef X 71 | 72 | Cuik_Target* t = cuik_malloc(sizeof(Cuik_Target)); 73 | *t = (Cuik_Target){ 74 | .env = env, 75 | .system = system, 76 | 77 | .int_bits = { 8, 16, 32, 64, 64 }, 78 | .pointer_byte_size = 8, 79 | 80 | #ifdef CONFIG_HAS_TB 81 | .arch = TB_ARCH_X86_64, 82 | #endif 83 | 84 | .builtin_func_map = builtins, 85 | .set_defines = set_defines, 86 | #ifdef CONFIG_HAS_TB 87 | .compile_builtin = compile_builtin, 88 | #endif /* CONFIG_HAS_TB */ 89 | }; 90 | 91 | // on MSVC, long means 32bit and this is necessary because of stuff like 92 | // DWORD being defined as unsigned long. 93 | if (env == CUIK_ENV_MSVC) { 94 | t->int_bits[CUIK_BUILTIN_LONG] = 32; 95 | } 96 | 97 | cuik_target_build(t); 98 | 99 | // bake out size_t and ptrdiff_t after long long is ready 100 | t->size_type = t->unsigned_ints[CUIK_BUILTIN_LLONG]; 101 | t->size_type.also_known_as = "size_t"; 102 | 103 | t->ptrdiff_type = t->signed_ints[CUIK_BUILTIN_LLONG]; 104 | t->ptrdiff_type.also_known_as = "ptrdiff_t"; 105 | 106 | return t; 107 | } 108 | -------------------------------------------------------------------------------- /cuik_c/tls.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | 4 | #define TEMPORARY_STORAGE_SIZE (32 << 20) 5 | 6 | typedef struct TemporaryStorage { 7 | size_t used; 8 | size_t committed; 9 | 10 | uint8_t data[]; 11 | } TemporaryStorage; 12 | 13 | static _Thread_local TemporaryStorage* temp_storage; 14 | 15 | void tls_init(void) { 16 | if (temp_storage == NULL) { 17 | temp_storage = cuik__valloc(TEMPORARY_STORAGE_SIZE); 18 | if (temp_storage == NULL) { 19 | fprintf(stderr, "error: could not reserve temporary storage\n"); 20 | abort(); 21 | } 22 | } 23 | 24 | temp_storage->used = 0; 25 | } 26 | 27 | void tls_reset(void) { 28 | temp_storage->used = 0; 29 | } 30 | 31 | void* tls_push(size_t size) { 32 | if (sizeof(TemporaryStorage) + temp_storage->used + size >= TEMPORARY_STORAGE_SIZE) { 33 | printf("temporary storage: out of memory!\n"); 34 | abort(); 35 | } 36 | 37 | void* ptr = &temp_storage->data[temp_storage->used]; 38 | temp_storage->used += size; 39 | return ptr; 40 | } 41 | 42 | void* tls_pop(size_t size) { 43 | assert(sizeof(TemporaryStorage) + temp_storage->used > size); 44 | 45 | temp_storage->used -= size; 46 | return &temp_storage->data[temp_storage->used]; 47 | } 48 | 49 | void* tls_save(void) { 50 | assert(sizeof(TemporaryStorage) + temp_storage->used); 51 | 52 | //size_t align_mask = sizeof(max_align_t) - 1; 53 | //temp_storage->used = (temp_storage->used + align_mask) & ~align_mask; 54 | 55 | return &temp_storage->data[temp_storage->used]; 56 | } 57 | 58 | void tls_restore(void* p) { 59 | size_t i = ((uint8_t*)p) - temp_storage->data; 60 | assert(i <= temp_storage->used); 61 | 62 | temp_storage->used = i; 63 | } 64 | -------------------------------------------------------------------------------- /cuik_c/toolchains/darwin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void add_libraries(void* ctx, bool nocrt, Cuik_Linker* l) { 5 | } 6 | 7 | static void set_preprocessor(void* ctx, bool nocrt, Cuik_CPP* cpp) { 8 | cuikpp_define_cstr(cpp, "__APPLE__", "1"); 9 | cuikpp_define_cstr(cpp, "__MACH__" , "1"); 10 | cuikpp_define_cstr(cpp, "__weak", "// __attribute__((objc_gc(weak))"); 11 | cuikpp_define_cstr(cpp, "__apple_build_version__", "14000029"); 12 | } 13 | 14 | static bool invoke_link(void* ctx, const Cuik_DriverArgs* args, Cuik_Linker* linker, const char* output, const char* filename) { 15 | return false; 16 | } 17 | 18 | static void* init(void) { 19 | return NULL; 20 | } 21 | 22 | Cuik_Toolchain cuik_toolchain_darwin(void) { 23 | return (Cuik_Toolchain){ 24 | .init = init, 25 | .set_preprocessor = set_preprocessor, 26 | .add_libraries = add_libraries, 27 | .invoke_link = invoke_link 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /cuik_c/toolchains/gnu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void add_libraries(void* ctx, bool nocrt, Cuik_Linker* l) { 6 | } 7 | 8 | static void set_preprocessor(void* ctx, bool nocrt, Cuik_CPP* cpp) { 9 | if (!nocrt) { 10 | cuikpp_add_include_directory(cpp, true, "/usr/local/include/"); 11 | cuikpp_add_include_directory(cpp, true, "/usr/include/x86_64-linux-gnu/"); 12 | cuikpp_add_include_directory(cpp, true, "/usr/include/"); 13 | } 14 | 15 | // things we don't handle yet so we just remove them 16 | cuikpp_define_empty_cstr(cpp, "__THROWNL"); 17 | cuikpp_define_empty_cstr(cpp, "__USER_LABEL_PREFIX__"); 18 | 19 | cuikpp_define_cstr(cpp, "_LP64", "1"); 20 | cuikpp_define_cstr(cpp, "__C99_MACRO_WITH_VA_ARGS", "1"); 21 | cuikpp_define_cstr(cpp, "__ELF__", "1"); 22 | cuikpp_define_cstr(cpp, "__LP64__", "1"); 23 | cuikpp_define_cstr(cpp, "__SIZEOF_DOUBLE__", "8"); 24 | cuikpp_define_cstr(cpp, "__SIZEOF_FLOAT__", "4"); 25 | cuikpp_define_cstr(cpp, "__SIZEOF_INT__", "4"); 26 | cuikpp_define_cstr(cpp, "__SIZEOF_LONG_DOUBLE__", "8"); 27 | cuikpp_define_cstr(cpp, "__SIZEOF_LONG_LONG__", "8"); 28 | cuikpp_define_cstr(cpp, "__SIZEOF_LONG__", "8"); 29 | cuikpp_define_cstr(cpp, "__SIZEOF_POINTER__", "8"); 30 | cuikpp_define_cstr(cpp, "__SIZEOF_PTRDIFF_T__", "8"); 31 | cuikpp_define_cstr(cpp, "__SIZEOF_SHORT__", "2"); 32 | cuikpp_define_cstr(cpp, "__SIZEOF_SIZE_T__", "8"); 33 | cuikpp_define_cstr(cpp, "__SIZE_TYPE__", "unsigned long"); 34 | cuikpp_define_cstr(cpp, "__STDC_HOSTED__", "1"); 35 | cuikpp_define_cstr(cpp, "__STDC_NO_COMPLEX__", "1"); 36 | cuikpp_define_cstr(cpp, "__STDC_UTF_16__", "1"); 37 | cuikpp_define_cstr(cpp, "__STDC_UTF_32__", "1"); 38 | cuikpp_define_cstr(cpp, "__STDC_VERSION__", "201112L"); 39 | cuikpp_define_cstr(cpp, "__STDC__", "1"); 40 | cuikpp_define_cstr(cpp, "__alignof__", "_Alignof"); 41 | cuikpp_define_cstr(cpp, "__const__", "const"); 42 | cuikpp_define_cstr(cpp, "__gnu_linux__", "1"); 43 | cuikpp_define_cstr(cpp, "__inline__", "inline"); 44 | cuikpp_define_cstr(cpp, "__linux", "1"); 45 | cuikpp_define_cstr(cpp, "__linux__", "1"); 46 | cuikpp_define_cstr(cpp, "__signed__", "signed"); 47 | cuikpp_define_cstr(cpp, "__typeof__", "typeof"); 48 | cuikpp_define_cstr(cpp, "__unix", "1"); 49 | cuikpp_define_cstr(cpp, "__unix__", "1"); 50 | cuikpp_define_cstr(cpp, "__volatile__", "volatile"); 51 | cuikpp_define_cstr(cpp, "linux", "1"); 52 | cuikpp_define_cstr(cpp, "unix", "1"); 53 | } 54 | 55 | static bool invoke_link(void* ctx, const Cuik_DriverArgs* args, Cuik_Linker* linker, const char* output, const char* filename) { 56 | enum { CMD_LINE_MAX = 4096 }; 57 | 58 | char cmd_line[CMD_LINE_MAX]; 59 | int cmd_line_len = snprintf(cmd_line, CMD_LINE_MAX, "clang %s -lm -o %s ", args->debug_info ? "-g" : "", output); 60 | 61 | dyn_array_for(i, linker->inputs) { 62 | cmd_line_len += snprintf(&cmd_line[cmd_line_len], CMD_LINE_MAX - cmd_line_len, "%s ", linker->inputs[i]); 63 | } 64 | 65 | printf("Command: %s\n", cmd_line); 66 | int exit_code = system(cmd_line); 67 | if (exit_code != 0) { 68 | fprintf(stderr, "Linker exited with code %d\n", exit_code); 69 | return false; 70 | } 71 | 72 | return true; 73 | } 74 | 75 | static void* init(void) { 76 | return NULL; 77 | } 78 | 79 | Cuik_Toolchain cuik_toolchain_gnu(void) { 80 | return (Cuik_Toolchain){ 81 | .init = init, 82 | .set_preprocessor = set_preprocessor, 83 | .add_libraries = add_libraries, 84 | .invoke_link = invoke_link 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /cuik_pp/cpp_dbg.h: -------------------------------------------------------------------------------- 1 | // This is an optional internal tool for debugging the preprocessor output 2 | #define CPP_DBG 1 3 | 4 | static char* breakpoints[100]; 5 | static MacroArgs* cppdbg__arglist; 6 | static int cppdbg__log; 7 | 8 | static bool strprefix(const char* str, const char* pre) { 9 | return strncmp(pre, str, strlen(pre)) == 0; 10 | } 11 | 12 | static int cppdbg__break(void) { 13 | cppdbg__log = 0; 14 | 15 | char line[255]; 16 | for (;;) { 17 | // read 18 | printf("> "); 19 | while (fgets(line, 255, stdin) == NULL) {} 20 | 21 | char* nl = strchr(line, '\n'); 22 | if (nl) *nl = 0; 23 | 24 | if (strprefix(line, "b ")) { 25 | for (size_t i = 0; i < 100; i++) if (breakpoints[i] == NULL) { 26 | fprintf(stderr, "set breakpoint: %s\n", line + 2); 27 | breakpoints[i] = cuik_strdup(line + 2); 28 | break; 29 | } 30 | } else if (strcmp(line, "debugger") == 0) { 31 | __builtin_debugtrap(); 32 | break; 33 | } else if (strcmp(line, "args") == 0) { 34 | // printf("FUNCTION MACRO: %.*s %.*s\n", (int)t.content.length, t.content.data, (int)def.length, def.data); 35 | if (cppdbg__arglist) { 36 | for (size_t i = 0; i < cppdbg__arglist->value_count; i++) { 37 | printf(" ['%.*s'] = '%.*s'\n", (int) cppdbg__arglist->keys[i].length, cppdbg__arglist->keys[i].data, (int) cppdbg__arglist->values[i].content.length, cppdbg__arglist->values[i].content.data); 38 | } 39 | } else { 40 | printf("NONE\n"); 41 | } 42 | } else if (strcmp(line, "log") == 0) { 43 | printf("Logging will continue until next break...\n"); 44 | cppdbg__log = 1; 45 | } else if (strcmp(line, "dump") == 0) { 46 | return 1; 47 | } else if (strcmp(line, "next") == 0) { 48 | return 0; 49 | } else { 50 | fprintf(stderr, "unknown command: %s\n", line); 51 | } 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /cuik_pp/cpp_fs.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if USE_INTRIN && CUIK__IS_X64 4 | #include 5 | #endif 6 | 7 | typedef struct InternalFile InternalFile; 8 | struct InternalFile { 9 | InternalFile* next; 10 | const char* name; 11 | size_t size; 12 | char data[]; 13 | }; 14 | 15 | extern InternalFile* cuik__ifiles_root; 16 | 17 | InternalFile* find_internal_file(const char* name) { 18 | InternalFile* f = cuik__ifiles_root; 19 | for (; f != NULL; f = f->next) { 20 | if (strcmp(name, f->name) == 0) return f; 21 | } 22 | 23 | return NULL; 24 | } 25 | 26 | bool cuikpp_locate_file(void* user_data, const Cuik_Path* restrict input, Cuik_Path* output, bool case_insensitive) { 27 | if (cuik_path_is_in(input, "$cuik")) { 28 | InternalFile* f = find_internal_file(input->data + sizeof("$cuik")); 29 | if (f) { 30 | *output = *input; 31 | return true; 32 | } else { 33 | return false; 34 | } 35 | } else { 36 | cuikfs_canonicalize(output, input->data, case_insensitive); 37 | return cuikfs_exists(output->data); 38 | } 39 | } 40 | 41 | bool cuikpp_default_fs(void* user_data, const Cuik_Path* restrict input, Cuik_FileResult* output, bool case_insensitive) { 42 | if (input->length == 0) { 43 | if (user_data == NULL) return false; 44 | String source = *(String*) user_data; 45 | 46 | // allocate proper buffer for source 47 | char* buffer = cuik__valloc(source.length + 17); 48 | memcpy(buffer, source.data, source.length); 49 | 50 | cuiklex_canonicalize(source.length, buffer); 51 | 52 | output->length = source.length; 53 | output->data = buffer; 54 | return true; 55 | } else if (cuik_path_is_in(input, "$cuik")) { 56 | InternalFile* f = find_internal_file(input->data + sizeof("$cuik")); 57 | if (f == NULL) return false; 58 | 59 | output->length = f->size; 60 | output->data = f->data; 61 | return true; 62 | } else { 63 | Cuik_Path path; 64 | cuikfs_canonicalize(&path, input->data, case_insensitive); 65 | 66 | // read entire file into virtual memory block 67 | Cuik_File* file = cuikfs_open(path.data, false); 68 | if (file == NULL) return false; 69 | 70 | size_t length; 71 | if (!cuikfs_get_length(file, &length)) goto err; 72 | 73 | char* buffer = cuik__valloc(length + 17); 74 | if (!cuikfs_read(file, buffer, length)) goto err; 75 | 76 | cuiklex_canonicalize(length, buffer); 77 | 78 | output->length = length; 79 | output->data = buffer; 80 | cuikfs_close(file); 81 | return true; 82 | 83 | err: 84 | cuikfs_close(file); 85 | return false; 86 | } 87 | } 88 | 89 | void cuiklex_canonicalize(size_t length, char* data) { 90 | uint8_t* text = (uint8_t*) data; 91 | 92 | #if USE_INTRIN 93 | size_t simd_end = length; 94 | length = length & 15; 95 | #endif 96 | 97 | for (size_t i = 0; i < length; i++) { 98 | if (text[i] == '\t') text[i] = ' '; 99 | if (text[i] == '\v') text[i] = ' '; 100 | if (text[i] == 12) text[i] = ' '; 101 | } 102 | 103 | #if USE_INTRIN 104 | // NOTE(NeGate): This code requires SSE4.1, it's not impossible to make 105 | // ARM variants and such but yea. 106 | // log_debug("SIMD starts at %zu (ends at %zu) such that the iterations are a multiple of 16", length, simd_end); 107 | for (size_t i = length; i < simd_end; i += 16) { 108 | __m128i bytes = _mm_loadu_si128((__m128i*)&text[i]); 109 | 110 | // Replace all \t and \v with spaces 111 | __m128i test_ident = _mm_cmpeq_epi8(bytes, _mm_set1_epi8('\t')); 112 | test_ident = _mm_or_si128(test_ident, _mm_cmpeq_epi8(bytes, _mm_set1_epi8('\v'))); 113 | test_ident = _mm_or_si128(test_ident, _mm_cmpeq_epi8(bytes, _mm_set1_epi8(12))); 114 | 115 | bytes = _mm_blendv_epi8(bytes, _mm_set1_epi8(' '), test_ident); 116 | _mm_storeu_si128((__m128i*)&text[i], bytes); 117 | } 118 | #endif 119 | } 120 | -------------------------------------------------------------------------------- /cuik_pp/cpp_iters.h: -------------------------------------------------------------------------------- 1 | 2 | CUIK_API void cuikpp_add_include_directory(Cuik_CPP* ctx, bool is_system, const char dir[]) { 3 | Cuik_IncludeDir d = { is_system, gimme_the_shtuffs(ctx, sizeof(Cuik_Path)) }; 4 | cuik_path_set(d.path, dir); 5 | 6 | char last = d.path->data[d.path->length - 1]; 7 | if (last != '/' && last != '\\') { 8 | d.path->data[d.path->length++] = CUIK_PATH_SLASH_SEP; 9 | d.path->data[d.path->length] = 0; 10 | } 11 | 12 | dyn_array_put(ctx->system_include_dirs, d); 13 | } 14 | 15 | CUIK_API void cuikpp_add_include_directoryf(Cuik_CPP* ctx, bool is_system, const char* fmt, ...) { 16 | Cuik_IncludeDir d = { is_system, gimme_the_shtuffs(ctx, sizeof(Cuik_Path)) }; 17 | 18 | va_list ap; 19 | va_start(ap, fmt); 20 | d.path->length = vsnprintf(d.path->data, FILENAME_MAX, fmt, ap); 21 | va_end(ap); 22 | 23 | char last = d.path->data[d.path->length - 1]; 24 | if (last != '/' && last != '\\') { 25 | d.path->data[d.path->length++] = CUIK_PATH_SLASH_SEP; 26 | d.path->data[d.path->length] = 0; 27 | } 28 | 29 | dyn_array_put(ctx->system_include_dirs, d); 30 | } 31 | 32 | CUIK_API bool cuikpp_find_include_include(Cuik_CPP* ctx, char output[FILENAME_MAX], const char* path) { 33 | dyn_array_for(i, ctx->system_include_dirs) { 34 | sprintf_s(output, FILENAME_MAX, "%s%s", ctx->system_include_dirs[i].path->data, path); 35 | FILE* f = fopen(output, "r"); 36 | if (f != NULL) { 37 | fclose(f); 38 | return true; 39 | } 40 | } 41 | 42 | return false; 43 | } 44 | 45 | Cuik_FileEntry* cuikpp_next_file(Cuik_CPP* ctx, Cuik_FileEntry* f) { 46 | // first element 47 | if (f == NULL) return &ctx->tokens.files[0]; 48 | 49 | size_t len = dyn_array_length(ctx->tokens.files); 50 | size_t i = f - ctx->tokens.files; 51 | const char* filename = f->filename; 52 | 53 | // skip any sequential file chunks from the same file 54 | while (i < len && ctx->tokens.files[i].filename == filename) { 55 | i += 1; 56 | } 57 | 58 | return &ctx->tokens.files[i]; 59 | } 60 | 61 | CUIK_API Cuik_IncludeDir* cuikpp_get_include_dirs(Cuik_CPP* ctx) { 62 | return &ctx->system_include_dirs[0]; 63 | } 64 | 65 | CUIK_API size_t cuikpp_get_include_dir_count(Cuik_CPP* ctx) { 66 | return dyn_array_length(ctx->system_include_dirs); 67 | } 68 | 69 | Cuik_DefineIter cuikpp_first_define(Cuik_CPP* ctx) { 70 | size_t cap = 1u << ctx->macros.exp; 71 | for (size_t i = 0; i < cap; i++) { 72 | if (ctx->macros.keys[i].length != 0 && ctx->macros.keys[i].length != MACRO_DEF_TOMBSTONE) { 73 | return (Cuik_DefineIter){ 74 | .index = i, 75 | .key = ctx->macros.keys[i], 76 | .value = ctx->macros.vals[i].value, 77 | .loc = ctx->macros.vals[i].loc, 78 | }; 79 | } 80 | } 81 | 82 | return (Cuik_DefineIter){ .index = 0 }; 83 | } 84 | 85 | bool cuikpp_next_define(Cuik_CPP* ctx, Cuik_DefineIter* it) { 86 | size_t cap = 1u << ctx->macros.exp; 87 | size_t e = it->index; 88 | if (e >= cap) return false; 89 | 90 | it->loc = ctx->macros.vals[e].loc; 91 | it->key = ctx->macros.keys[e]; 92 | it->value = ctx->macros.vals[e].value; 93 | 94 | for (size_t i = it->index + 1; i < cap; i++) { 95 | if (ctx->macros.keys[i].length != 0 && ctx->macros.keys[i].length != MACRO_DEF_TOMBSTONE) { 96 | it->index = i; 97 | it->key = ctx->macros.keys[i]; 98 | it->value = ctx->macros.vals[i].value; 99 | it->loc = ctx->macros.vals[i].loc; 100 | return true; 101 | } 102 | } 103 | 104 | it->index = cap; 105 | return true; 106 | } 107 | -------------------------------------------------------------------------------- /cuik_pp/diagnostic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | #include "lexer.h" 4 | #include 5 | #include 6 | 7 | typedef struct DiagFixit { 8 | SourceRange loc; 9 | int offset; 10 | const char* hint; 11 | } DiagFixit; 12 | 13 | typedef struct { 14 | TokenStream* tokens; 15 | ResolvedSourceLoc base; 16 | 17 | const char* line_start; 18 | const char* line_end; 19 | 20 | size_t dist_from_line_start; 21 | size_t cursor; 22 | } DiagWriter; 23 | 24 | struct Cuik_Diagnostics { 25 | Cuik_DiagCallback callback; 26 | void* userdata; 27 | 28 | TB_Arena buffer; 29 | 30 | // We write the text output to a buffer such that we 31 | // can do ordered output. 32 | Cuik_Parser* parser; 33 | 34 | // Incremented atomically by the diagnostics engine 35 | _Atomic int error_tally; 36 | }; 37 | 38 | typedef enum Cuik_ReportLevel { 39 | REPORT_VERBOSE, 40 | REPORT_INFO, 41 | REPORT_WARNING, 42 | REPORT_ERROR, 43 | REPORT_MAX 44 | } Cuik_ReportLevel; 45 | 46 | int cuikdg_error_count(TokenStream* s); 47 | void cuikdg_tally_error(TokenStream* s); 48 | 49 | Cuik_Diagnostics* cuikdg_make(Cuik_DiagCallback callback, void* userdata); 50 | void cuikdg_free(Cuik_Diagnostics* diag); 51 | 52 | //////////////////////////////// 53 | // Complex diagnostic builder 54 | //////////////////////////////// 55 | void diag_header(TokenStream* tokens, DiagType type, const char* fmt, ...); 56 | 57 | DiagWriter diag_writer(TokenStream* tokens); 58 | void diag_writer_highlight(DiagWriter* writer, SourceRange loc); 59 | bool diag_writer_is_compatible(DiagWriter* writer, SourceRange loc); 60 | void diag_writer_done(DiagWriter* writer); 61 | 62 | static void report_two_spots(Cuik_ReportLevel level, TokenStream* tokens, SourceLoc loc, SourceLoc loc2, const char* msg, const char* loc_msg, const char* loc_msg2, const char* interjection) {} 63 | static void report(Cuik_ReportLevel level, TokenStream* tokens, SourceLoc loc, const char* fmt, ...) {} 64 | static void report_ranged(Cuik_ReportLevel level, TokenStream* tokens, SourceLoc start_loc, SourceLoc end_loc, const char* fmt, ...) {} 65 | static void report_fix(Cuik_ReportLevel level, TokenStream* tokens, SourceLoc loc, const char* tip, const char* fmt, ...) {} 66 | 67 | // Report primitives 68 | static void report_header(Cuik_ReportLevel level, const char* fmt, ...) {} 69 | static void report_line(TokenStream* tokens, SourceLoc loci, int indent) {} 70 | -------------------------------------------------------------------------------- /cuik_pp/keywords.h: -------------------------------------------------------------------------------- 1 | TOKEN_KW_auto = 0x10000000, 2 | TOKEN_KW_break, 3 | TOKEN_KW_case, 4 | TOKEN_KW_char, 5 | TOKEN_KW_const, 6 | TOKEN_KW_continue, 7 | TOKEN_KW_default, 8 | TOKEN_KW_do, 9 | TOKEN_KW_double, 10 | TOKEN_KW_else, 11 | TOKEN_KW_enum, 12 | TOKEN_KW_extern, 13 | TOKEN_KW_float, 14 | TOKEN_KW_for, 15 | TOKEN_KW_goto, 16 | TOKEN_KW_if, 17 | TOKEN_KW_inline, 18 | TOKEN_KW_int, 19 | TOKEN_KW_long, 20 | TOKEN_KW_register, 21 | TOKEN_KW_restrict, 22 | TOKEN_KW_return, 23 | TOKEN_KW_short, 24 | TOKEN_KW_signed, 25 | TOKEN_KW_sizeof, 26 | TOKEN_KW_static, 27 | TOKEN_KW_struct, 28 | TOKEN_KW_switch, 29 | TOKEN_KW_typedef, 30 | TOKEN_KW_union, 31 | TOKEN_KW_unsigned, 32 | TOKEN_KW_void, 33 | TOKEN_KW_volatile, 34 | TOKEN_KW_while, 35 | TOKEN_KW_Alignas, 36 | TOKEN_KW_Alignof, 37 | TOKEN_KW_Atomic, 38 | TOKEN_KW_Bool, 39 | TOKEN_KW_Complex, 40 | TOKEN_KW_Embed, 41 | TOKEN_KW_Generic, 42 | TOKEN_KW_Imaginary, 43 | TOKEN_KW_Pragma, 44 | TOKEN_KW_Noreturn, 45 | TOKEN_KW_Static_assert, 46 | TOKEN_KW_Thread_local, 47 | TOKEN_KW_Typeof, 48 | TOKEN_KW_Vector, 49 | TOKEN_KW_asm, 50 | TOKEN_KW_attribute, 51 | TOKEN_KW_cdecl, 52 | TOKEN_KW_stdcall, 53 | TOKEN_KW_declspec, 54 | TOKEN_KW_discard, 55 | TOKEN_KW_layout, 56 | TOKEN_KW_in, 57 | TOKEN_KW_out, 58 | TOKEN_KW_inout, 59 | TOKEN_KW_uint, 60 | TOKEN_KW_buffer, 61 | TOKEN_KW_uniform, 62 | TOKEN_KW_flat, 63 | TOKEN_KW_smooth, 64 | TOKEN_KW_noperspective, 65 | TOKEN_KW_vec2, 66 | TOKEN_KW_vec3, 67 | TOKEN_KW_vec4, 68 | TOKEN_KW_ivec2, 69 | TOKEN_KW_ivec3, 70 | TOKEN_KW_ivec4, 71 | TOKEN_KW_uvec2, 72 | TOKEN_KW_uvec3, 73 | TOKEN_KW_uvec4, 74 | TOKEN_KW_dvec2, 75 | TOKEN_KW_dvec3, 76 | TOKEN_KW_dvec4, 77 | -------------------------------------------------------------------------------- /docs/BUILDING.txt: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | First of all, lets' grab mimalloc: 4 | 5 | ``` 6 | git submodule update --init --recursive 7 | ``` 8 | 9 | You will need Clang/GCC, Ninja, and Lua (Luajit works) 10 | 11 | For the most part doing `lua build.lua -driver` will build the CLI app, if you wish to build 12 | the library by itself add `-cuik` and/or `-tb` depending on what is wanted. If you wish 13 | for a shared object (DLL/SO) add `-shared` which by default will compile with Cuik and TB. 14 | -------------------------------------------------------------------------------- /docs/IR.txt: -------------------------------------------------------------------------------- 1 | # Sea of Nodes (SoN) 2 | 3 | https://www.oracle.com/technetwork/java/javase/tech/c2-ir95-150110.pdf 4 | 5 | SoN is an SSA where ordering is relaxed in the form of explicit dependencies 6 | as opposed to local ordering inside basic blocks, for instance pure operations 7 | like addition will not have an exact placement only the constraint that it must 8 | be resolved after it's inputs. This makes it easier to perform local optimizations 9 | without a care for scheduling, this is especially helpful because of how many 10 | optimizations we've moved to peepholes. 11 | 12 | note: edges going down from A to B means B is dependent on A. 13 | 14 | Reassociation 15 | 16 | x+2+4 17 | 18 | x 2 2 4 19 | \ / \ / 20 | + 4 => x + 21 | \ / \ / 22 | + + 23 | 24 | GVN 25 | 26 | A*B + A*B 27 | 28 | A B A B 29 | |\ /| \ / 30 | | X | * 31 | |/ \| => / \ 32 | * * \ / 33 | \ / + 34 | + 35 | Load elimination 36 | 37 | *x = 16 38 | return *x 39 | 40 | x_ 41 | | \ 42 | | \ 43 | | \ x 44 | | | | 45 | memory | 16 | => memory | 16 46 | \ | | | \ | / | 47 | Store | Store | 48 | | | / 49 | | / / 50 | | / / 51 | | / / 52 | Load | 53 | | | 54 | V V 55 | 56 | note: we're not showing the control edge memory operations have for simplicit but 57 | both of these are sharing a control edge. Stores produce more memory but don't produce 58 | more control flow and Loads use memory but don't produce more (these are both non-volatile) 59 | -------------------------------------------------------------------------------- /docs/NOTES.txt: -------------------------------------------------------------------------------- 1 | I'm going to be on a diagnostics improvement grind so i wanna write down some stuff i need to get in: 2 | Struct field shadowing (a field in a struct matches another one, sometimes this fails because of it being inside of an unnamed struct) 3 | 4 | ~~ Enum values aren't being resolved by the new parser, fix that, they all turn out as 0 ~~ 5 | 6 | I'm on my selfhost grind so this is the self script for now: 7 | ``` 8 | cuik main/src/main_driver.c libCuik/lib/**.c c11threads/threads_msvc.c tilde-backend/tildebackend.lib -I common -I c11threads -I libCuik/include -I libCuik/lib -I tilde-backend/include -DCUIK_USE_TB -o self/cuik.exe -j 9 | ``` 10 | run in the main repo folder 11 | -------------------------------------------------------------------------------- /docs/README.txt: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Tilde Backend (Tilde or TB for short) 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | TB is compiler backend in the form of a C library. This is built as an 6 | alternative to other larger compiler toolchains while providing the 7 | optimizations, machine code generation and object file export functionality 8 | necessary for the development of compilers. 9 | 10 | # Roadmap 11 | 12 | Code generation: 13 | We're starting with x64 but will be moving focus to Aarch64 soon. I wanna 14 | 15 | Optimizer: 16 | It's almost complete with all the -O1 level passes (mostly missing inlining). 17 | After that we can move towards -O2 level stuff (the goal is to compete with 18 | LLVM so we need to be a bit ambitious). 19 | 20 | Debug info: 21 | Codeview support and DWARF has not yet been started, there's plans on making a 22 | new debug info format eventually. 23 | 24 | Output targets: 25 | We currently have basic ELF64, COFF64, some current work is being done for 26 | PE and Macho-O. We got exporting object files but I wanna go further because 27 | linkers ain't supposed to be separate programs. 28 | 29 | -------------------------------------------------------------------------------- /docs/STRUCTURE.txt: -------------------------------------------------------------------------------- 1 | # LibCuik 2 | 3 | The library is compiled as a unity build found in `lib/libcuik.c` 4 | 5 | Defines: 6 | 7 | -DCUIK_USE_TB compiles the IRgen code. 8 | -DCUIK_ALLOW_THREADS compiles threadpool. 9 | 10 | Components: 11 | 12 | preproc/ C preprocessor 13 | 14 | front/ Parser & type checker 15 | 16 | back/ TB integration (irgen) 17 | 18 | driver/ Compiler caller helpers (compile args, traditional compiler 19 | dispatch) 20 | 21 | Flow: 22 | 23 | At any point in the standard pipeline you can stop and introspect the data. multiple 24 | options laid out vertically means they're different ways to generate such data 25 | 26 | _______________PREPROCESSOR_________________ 27 | / \ 28 | source -cuik_driver_preprocess_str-> Cuik_CPP* 29 | cuik_driver_preprocess 30 | cuikpp_make 31 | 32 | ___________________________________PARSER_______________________________________ 33 | / \ 34 | Cuik_CPP* -cuikpp_get_token_stream-> TokenStream -cuikparse_run-> Cuik_ParseResult 35 | 36 | 37 | ________________TYPE CHECK_____________________ 38 | / \ 39 | Cuik_ParseResult.tu --cuiksema_run-> *mutates TU* 40 | 41 | -------------------------------------------------------------------------------- /foo.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void matmul(float* dst, float* a, float* b); 4 | 5 | static float a[256]; 6 | static float b[256]; 7 | static float dst[256]; 8 | 9 | int main() { 10 | matmul(dst, a, b); 11 | return 0; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /freestanding/emmintrin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef _Vector(unsigned char, 16) __m128i; 4 | 5 | // m128i generic ops 6 | static __m128i _mm_loadu_si128(const __m128i* src) { return *src; } 7 | static __m128i _mm_load_si128(const __m128i* src) { return *src; } 8 | static void _mm_store_si128(__m128i* dst, __m128i src) { *dst = src; } 9 | -------------------------------------------------------------------------------- /freestanding/float.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DECIMAL_DIG 10 4 | #define FLT_ROUNDS 1 5 | #define FLT_EVAL_METHOD 0 6 | 7 | #define FLT_RADIX 2 8 | #define FLT_DECIMAL_DIG 6 9 | #define FLT_MIN 0x1.000000p-126 10 | #define FLT_MAX 0x1.7ffffep+127 11 | #define FLT_TRUE_MIN 0x1.000000p-149 12 | #define FLT_EPSILON 0x1.000002p+0 13 | #define FLT_MANT_DIG 23 14 | #define FLT_MIN_EXP -125 15 | #define FLT_MAX_EXP +128 16 | #define FLT_MIN_10_EXP -37 17 | #define FLT_MAX_10_EXP +38 18 | #define FLT_HAS_SUBNORM 1 19 | 20 | #define DBL_DECIMAL_DIG 10 21 | #define DBL_MIN 0x1.0000000000000p-1021 22 | #define DBL_MAX 0x1.fffffffffffffp+1023 23 | #define DBL_TRUE_MIN 0x1.0000000000000p-1074 24 | #define DBL_EPSILON 0x1.0000000000001p+0 25 | #define DBL_MANT_DIG 11 26 | #define DBL_MIN_EXP -1022 27 | #define DBL_MAX_EXP +1024 28 | #define DBL_MIN_10_EXP -307 29 | #define DBL_MAX_10_EXP +308 30 | #define DBL_HAS_SUBNORM 1 31 | 32 | #define LDBL_DECIMAL_DIG DBL_DECIMAL_DIG 33 | #define LDBL_MIN DBL_MIN 34 | #define LDBL_MAX DBL_MAX 35 | #define LDBL_TRUE_MIN DBL_TRUE_MIN 36 | #define LDBL_EPSILON DBL_EPSILON 37 | #define LDBL_MANT_DIG DBL_MANT_DIG 38 | #define LDBL_MIN_EXP DBL_MIN_EXP 39 | #define LDBL_MIN_10_EXP DBL_MIN_10_EXP 40 | #define LDBL_MAX_EXP DBL_MAX_EXP 41 | #define LDBL_MAX_10_EXP DBL_MAX_10_EXP 42 | #define LDBL_HAS_SUBNORM DBL_HAS_SUBNORM 43 | -------------------------------------------------------------------------------- /freestanding/immintrin.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /freestanding/intrin.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void __debugbreak(void); 4 | -------------------------------------------------------------------------------- /freestanding/limits.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #define MB_LEN_MAX 5 5 | 6 | #define CHAR_BIT 8 7 | #define BOOL_WIDTH 8 8 | 9 | #define UCHAR_WIDTH 8 10 | #define SCHAR_WIDTH 8 11 | #define UCHAR_MAX 0xff 12 | #define SCHAR_MIN -0x80 13 | #define SCHAR_MAX +0x7f 14 | 15 | #if defined(__CHAR_UNSIGNED__) || defined(_CHAR_UNSIGNED) 16 | #define CHAR_MIN UCHAR_MIN 17 | #define CHAR_MAX UCHAR_MAX 18 | #else 19 | #define CHAR_MIN SCHAR_MIN 20 | #define CHAR_MAX SCHAR_MAX 21 | #endif 22 | 23 | #define USHRT_WIDTH 16 24 | #define SHRT_WIDTH 16 25 | #define USHRT_MAX 0xffff 26 | #define SHRT_MIN -0x8000 27 | #define SHRT_MAX +0x7fff 28 | 29 | #define UINT_WIDTH 32 30 | #define INT_WIDTH 32 31 | #define UINT_MAX 0xffffffffu 32 | #define INT_MIN -0x80000000 33 | #define INT_MAX +0x7fffffff 34 | 35 | #if defined(_WIN64) 36 | #define ULONG_WIDTH 32 37 | #define LONG_WIDTH 32 38 | #define ULONG_MAX +0xffffffff 39 | #define LONG_MIN -0x80000000 40 | #define LONG_MAX +0x7fffffff 41 | #else 42 | #define ULONG_WIDTH 64 43 | #define LONG_WIDTH 64 44 | #define ULONG_MAX 0xffffffffffffffffull 45 | #define LONG_MIN -0x8000000000000000ll 46 | #define LONG_MAX +0x7fffffffffffffffll 47 | #endif 48 | 49 | #define ULLONG_WIDTH 64 50 | #define LLONG_WIDTH 64 51 | #define ULLONG_MAX 0xffffffffffffffffull 52 | #define LLONG_MIN -0x8000000000000000ll 53 | #define LLONG_MAX +0x7fffffffffffffffll 54 | 55 | #define PTRDIFF_WIDTH LLONG_WIDTH 56 | #define PTRDIFF_MIN LLONG_MIN 57 | #define PTRDIFF_MAX LLONG_MAX 58 | 59 | #define SIZE_WIDTH ULLONG_WIDTH 60 | #define SIZE_MAX ULLONG_MAX 61 | 62 | #define SIG_ATOMIC_WIDTH LLONG_WIDTH 63 | #define SIG_ATOMIC_MIN LLONG_MIN 64 | #define SIG_ATOMIC_MAX LLONG_MAX 65 | 66 | #define WINT_WIDTH INT_WIDTH 67 | #define WINT_MIN INT_MIN 68 | #define WINT_MAX INT_MAX 69 | 70 | #define WCHAR_WIDTH USHORT_WIDTH 71 | #define WCHAR_MIN USHORT_WIDTH 72 | #define WCHAR_MAX USHORT_WIDTH 73 | 74 | #define _I8_MIN (-127 - 1) 75 | #define _I8_MAX 127 76 | #define _UI8_MAX 0xff 77 | 78 | #define _I16_MIN (-32767 - 1) 79 | #define _I16_MAX 32767 80 | #define _UI16_MAX 0xffff 81 | 82 | #define _I32_MIN (-2147483647 - 1) 83 | #define _I32_MAX 2147483647 84 | #define _UI32_MAX 0xffffffffu 85 | 86 | #define _I64_MIN (-9223372036854775807ll - 1) 87 | #define _I64_MAX 9223372036854775807ll 88 | #define _UI64_MAX 0xffffffffffffffffull 89 | 90 | -------------------------------------------------------------------------------- /freestanding/mm_malloc.h: -------------------------------------------------------------------------------- 1 | #define _mm_free(p) _aligned_free(p) 2 | #define _mm_malloc(s, a) _aligned_malloc(s, a) 3 | -------------------------------------------------------------------------------- /freestanding/stdalign.h: -------------------------------------------------------------------------------- 1 | // 7.15 Alignment 2 | #pragma once 3 | 4 | #define __alignas_is_defined 1 5 | #define alignas(n) _Alignas(n) 6 | #define alignof(T) _Alignof(T) 7 | -------------------------------------------------------------------------------- /freestanding/stdarg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _WIN32 4 | // Win64 ABI 5 | typedef char* va_list; 6 | 7 | #define va_start(ap, a) __va_start(&ap, a) 8 | #define va_arg(ap, ty) __va_arg(&ap, ty) 9 | #define va_end() ((void)0) 10 | #define va_copy(destination, source) ((destination) = (source)) 11 | #else 12 | // SystemV ABI 13 | typedef struct { 14 | unsigned int gp_offset; 15 | unsigned int fp_offset; 16 | void *overflow_arg_area; 17 | void *reg_save_area; 18 | } __va_elem; 19 | 20 | typedef __va_elem va_list[1]; 21 | 22 | static void *__va_arg_mem(__va_elem *ap, int sz, int align) { 23 | void *p = ap->overflow_arg_area; 24 | if (align > 8) 25 | p = (p + 15) / 16 * 16; 26 | ap->overflow_arg_area = ((unsigned long)p + sz + 7) / 8 * 8; 27 | return p; 28 | } 29 | 30 | static void *__va_arg_gp(__va_elem *ap, int sz, int align) { 31 | if (ap->gp_offset >= 48) 32 | return __va_arg_mem(ap, sz, align); 33 | 34 | void *r = ap->reg_save_area + ap->gp_offset; 35 | ap->gp_offset += 8; 36 | return r; 37 | } 38 | 39 | static void *__va_arg_fp(__va_elem *ap, int sz, int align) { 40 | if (ap->fp_offset >= 112) 41 | return __va_arg_mem(ap, sz, align); 42 | 43 | void *r = ap->reg_save_area + ap->fp_offset; 44 | ap->fp_offset += 8; 45 | return r; 46 | } 47 | 48 | #define va_arg(ap, ty) *((ty*) __va_arg(ap, ty)) 49 | #define va_start(ap, a) __va_start(ap, a) 50 | #define va_end(ap) 51 | #define va_copy(dest, src) ((dest)[0] = (src)[0]) 52 | 53 | #define __GNUC_VA_LIST 1 54 | typedef va_list __gnuc_va_list; 55 | #endif 56 | -------------------------------------------------------------------------------- /freestanding/stdbool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // usually Cuik will just define these by default 4 | #ifndef __bool_true_false_are_defined 5 | # define __bool_true_false_are_defined 1 6 | # define bool _Bool 7 | # define false 0 8 | # define true 1 9 | #endif 10 | -------------------------------------------------------------------------------- /freestanding/stddef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef NULL 4 | #define NULL ((void*)0) 5 | #endif 6 | 7 | #ifdef _WIN32 8 | // windows does it :p 9 | //#include 10 | #endif 11 | 12 | // 7.19 Common definitions 13 | typedef long long ptrdiff_t; 14 | typedef unsigned long long size_t; 15 | typedef long double max_align_t; 16 | 17 | typedef long long intptr_t; 18 | typedef unsigned long long uintptr_t; 19 | 20 | #ifdef _WIN32 21 | typedef unsigned short wchar_t; 22 | #else 23 | typedef int wchar_t; 24 | #endif 25 | 26 | #define offsetof(s,m) ((size_t)&(((s*)0)->m)) 27 | // #define offsetof(s,m) __builtin_offsetof(s,m) 28 | -------------------------------------------------------------------------------- /freestanding/stdint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef signed char int8_t; 4 | typedef short int16_t; 5 | typedef int int32_t; 6 | typedef long long int64_t; 7 | typedef unsigned char uint8_t; 8 | typedef unsigned short uint16_t; 9 | typedef unsigned int uint32_t; 10 | typedef unsigned long long uint64_t; 11 | 12 | typedef signed char int_least8_t; 13 | typedef short int_least16_t; 14 | typedef int int_least32_t; 15 | typedef long long int_least64_t; 16 | typedef unsigned char uint_least8_t; 17 | typedef unsigned short uint_least16_t; 18 | typedef unsigned int uint_least32_t; 19 | typedef unsigned long long uint_least64_t; 20 | 21 | typedef signed char int_fast8_t; 22 | typedef int int_fast16_t; 23 | typedef int int_fast32_t; 24 | typedef long long int_fast64_t; 25 | typedef unsigned char uint_fast8_t; 26 | typedef unsigned int uint_fast16_t; 27 | typedef unsigned int uint_fast32_t; 28 | typedef unsigned long long uint_fast64_t; 29 | 30 | typedef long long intmax_t; 31 | typedef unsigned long long uintmax_t; 32 | 33 | #define INT8_MIN (-127 - 1) 34 | #define INT16_MIN (-32767 - 1) 35 | #define INT32_MIN (-2147483647 - 1) 36 | #define INT64_MIN (-9223372036854775807ll - 1) 37 | #define INT8_MAX 127 38 | #define INT16_MAX 32767 39 | #define INT32_MAX 2147483647 40 | #define INT64_MAX 9223372036854775807ll 41 | #define UINT8_MAX 0xffu 42 | #define UINT16_MAX 0xffffu 43 | #define UINT32_MAX 0xffffffffu 44 | #define UINT64_MAX 0xffffffffffffffffull 45 | 46 | #define INT_LEAST8_MIN INT8_MIN 47 | #define INT_LEAST16_MIN INT16_MIN 48 | #define INT_LEAST32_MIN INT32_MIN 49 | #define INT_LEAST64_MIN INT64_MIN 50 | #define INT_LEAST8_MAX INT8_MAX 51 | #define INT_LEAST16_MAX INT16_MAX 52 | #define INT_LEAST32_MAX INT32_MAX 53 | #define INT_LEAST64_MAX INT64_MAX 54 | #define UINT_LEAST8_MAX UINT8_MAX 55 | #define UINT_LEAST16_MAX UINT16_MAX 56 | #define UINT_LEAST32_MAX UINT32_MAX 57 | #define UINT_LEAST64_MAX UINT64_MAX 58 | 59 | #define INT_FAST8_MIN INT8_MIN 60 | #define INT_FAST16_MIN INT32_MIN 61 | #define INT_FAST32_MIN INT32_MIN 62 | #define INT_FAST64_MIN INT64_MIN 63 | #define INT_FAST8_MAX INT8_MAX 64 | #define INT_FAST16_MAX INT32_MAX 65 | #define INT_FAST32_MAX INT32_MAX 66 | #define INT_FAST64_MAX INT64_MAX 67 | #define UINT_FAST8_MAX UINT8_MAX 68 | #define UINT_FAST16_MAX UINT32_MAX 69 | #define UINT_FAST32_MAX UINT32_MAX 70 | #define UINT_FAST64_MAX UINT64_MAX 71 | 72 | #ifdef _CUIK_TARGET_64BIT_ 73 | #define INTPTR_MIN INT64_MIN 74 | #define INTPTR_MAX INT64_MAX 75 | #define UINTPTR_MAX UINT64_MAX 76 | #else 77 | #define INTPTR_MIN INT32_MIN 78 | #define INTPTR_MAX INT32_MAX 79 | #define UINTPTR_MAX UINT32_MAX 80 | #endif 81 | 82 | #define INTMAX_MIN INT64_MIN 83 | #define INTMAX_MAX INT64_MAX 84 | #define UINTMAX_MAX UINT64_MAX 85 | 86 | #define PTRDIFF_MIN INTPTR_MIN 87 | #define PTRDIFF_MAX INTPTR_MAX 88 | 89 | #ifndef SIZE_MAX 90 | // SIZE_MAX definition must match exactly with limits.h for modules support. 91 | #ifdef _CUIK_TARGET_64BIT_ 92 | #define SIZE_MAX 0xffffffffffffffffui64 93 | #else 94 | #define SIZE_MAX 0xffffffffui32 95 | #endif 96 | #endif 97 | 98 | #define SIG_ATOMIC_MIN INT32_MIN 99 | #define SIG_ATOMIC_MAX INT32_MAX 100 | 101 | #define WCHAR_MIN 0x0000 102 | #define WCHAR_MAX 0xffff 103 | 104 | #define WINT_MIN 0x0000 105 | #define WINT_MAX 0xffff 106 | 107 | #define INT8_C(x) (x) 108 | #define INT16_C(x) (x) 109 | #define INT32_C(x) (x) 110 | #define INT64_C(x) (x ## LL) 111 | 112 | #define UINT8_C(x) (x) 113 | #define UINT16_C(x) (x) 114 | #define UINT32_C(x) (x ## U) 115 | #define UINT64_C(x) (x ## ULL) 116 | 117 | #define INTMAX_C(x) INT64_C(x) 118 | #define UINTMAX_C(x) UINT64_C(x) 119 | -------------------------------------------------------------------------------- /freestanding/x86intrin.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /freestanding/xmmintrin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef _Vector(float, 4) __m128; 4 | typedef _Vector(double, 2) __m128d; 5 | 6 | // actual builtins 7 | float __builtin_ia32_sqrtss(__m128 a); 8 | __m128 __builtin_ia32_sqrtps(__m128 a); 9 | float __builtin_ia32_rsqrtss(__m128 a); 10 | __m128 __builtin_ia32_rsqrtps(__m128 a); 11 | __m128 __builtin_ia32_rcpss(__m128 a, __m128 b); 12 | __m128 __builtin_ia32_rcpps(__m128 a, __m128 b); 13 | __m128 __builtin_ia32_minss(__m128 a, __m128 b); 14 | __m128 __builtin_ia32_minps(__m128 a, __m128 b); 15 | __m128 __builtin_ia32_maxss(__m128 a, __m128 b); 16 | __m128 __builtin_ia32_maxps(__m128 a, __m128 b); 17 | 18 | static __m128 _mm_add_ss(__m128 a, __m128 b) { 19 | a[0] += b[0]; 20 | return a; 21 | } 22 | 23 | static __m128 _mm_sub_ss(__m128 a, __m128 b) { 24 | a[0] -= b[0]; 25 | return a; 26 | } 27 | 28 | static __m128 _mm_mul_ss(__m128 a, __m128 b) { 29 | a[0] *= b[0]; 30 | return a; 31 | } 32 | 33 | static __m128 _mm_div_ss(__m128 a, __m128 b) { 34 | a[0] /= b[0]; 35 | return a; 36 | } 37 | 38 | static __m128 _mm_add_ps(__m128 a, __m128 b) { return a + b; } 39 | static __m128 _mm_sub_ps(__m128 a, __m128 b) { return a - b; } 40 | static __m128 _mm_mul_ps(__m128 a, __m128 b) { return a * b; } 41 | static __m128 _mm_div_ps(__m128 a, __m128 b) { return a / b; } 42 | static __m128 _mm_and_ps(__m128 a, __m128 b) { return a & b; } 43 | static __m128 _mm_or_ps(__m128 a, __m128 b) { return a | b; } 44 | static __m128 _mm_xor_ps(__m128 a, __m128 b) { return a ^ b; } 45 | static __m128 _mm_andnot_ps(__m128 a, __m128 b) { return ~(a & b); } 46 | 47 | static __m128 _mm_sqrt_ss(__m128 a) { return (__m128)__builtin_ia32_sqrtss(a); } 48 | static __m128 _mm_sqrt_ps(__m128 a) { return __builtin_ia32_sqrtps(a); } 49 | 50 | static __m128 _mm_rsqrt_ss(__m128 a) { return (__m128)__builtin_ia32_rsqrtss(a); } 51 | static __m128 _mm_rsqrt_ps(__m128 a) { return __builtin_ia32_rsqrtps(a); } 52 | 53 | static __m128 _mm_rcp_ss(__m128 a) { return (__m128)__builtin_ia32_rcpss(a); } 54 | static __m128 _mm_rcp_ps(__m128 a) { return __builtin_ia32_rcpps(a); } 55 | 56 | static __m128 _mm_min_ss(__m128 a, __m128 b) { return __builtin_ia32_minss(a, b); } 57 | static __m128 _mm_min_ps(__m128 a, __m128 b) { return __builtin_ia32_minps(a, b); } 58 | static __m128 _mm_max_ss(__m128 a, __m128 b) { return __builtin_ia32_maxss(a, b); } 59 | static __m128 _mm_max_ps(__m128 a, __m128 b) { return __builtin_ia32_maxps(a, b); } 60 | 61 | static __m128 _mm_setr_ps(float z, float y, float x, float w) { return (__m128){ z, y, x, w }; } 62 | static __m128 _mm_setzero_ps(void) { return (__m128){ 0 }; } 63 | -------------------------------------------------------------------------------- /include/cuik.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "cuik_prelude.h" 3 | #include 4 | #include 5 | 6 | #ifndef _WIN32 7 | int sprintf_s(char* buffer, size_t len, const char* format, ...); 8 | #endif 9 | 10 | #ifdef CONFIG_HAS_TB 11 | #include 12 | #endif 13 | 14 | typedef struct CompilationUnit CompilationUnit; 15 | typedef struct TranslationUnit TranslationUnit; 16 | typedef struct Cuik_Toolchain Cuik_Toolchain; 17 | typedef struct Cuik_DriverArgs Cuik_DriverArgs; 18 | 19 | //////////////////////////////////////////// 20 | // Target descriptor 21 | //////////////////////////////////////////// 22 | typedef enum Cuik_System { 23 | CUIK_SYSTEM_WINDOWS, 24 | CUIK_SYSTEM_LINUX, 25 | CUIK_SYSTEM_MACOS, 26 | CUIK_SYSTEM_ANDROID, 27 | CUIK_SYSTEM_WEB, 28 | } Cuik_System; 29 | 30 | typedef enum Cuik_Subsystem { 31 | CUIK_SUBSYSTEM_PC, 32 | } Cuik_Subsystem; 33 | 34 | typedef enum Cuik_Environment { 35 | CUIK_ENV_MSVC, 36 | CUIK_ENV_GNU, 37 | CUIK_ENV_WEB, 38 | } Cuik_Environment; 39 | 40 | typedef struct Cuik_TargetDesc { 41 | Cuik_System sys; 42 | } Cuik_TargetDesc; 43 | 44 | typedef struct Cuik_Target Cuik_Target; 45 | 46 | // the naming convention of the targets here is 47 | // 48 | // cuik_target_XXX_YYY_ZZZ 49 | // 50 | // where XXX is arch, YYY is system, ZZZ is environment 51 | // 52 | CUIK_API Cuik_Target* cuik_target_x64(Cuik_System system, Cuik_Environment env); 53 | CUIK_API Cuik_Target* cuik_target_aarch64(Cuik_System system, Cuik_Environment env); 54 | CUIK_API Cuik_Target* cuik_target_mips32(Cuik_System system, Cuik_Environment env); 55 | CUIK_API Cuik_Target* cuik_target_mips64(Cuik_System system, Cuik_Environment env); 56 | CUIK_API Cuik_Target* cuik_target_wasm32(Cuik_System system, Cuik_Environment env); 57 | CUIK_API void cuik_free_target(Cuik_Target* target); 58 | 59 | CUIK_API Cuik_System cuik_get_target_system(const Cuik_Target* t); 60 | CUIK_API Cuik_Environment cuik_get_target_env(const Cuik_Target* t); 61 | 62 | CUIK_API Cuik_Target* cuik_target_host(void); 63 | 64 | //////////////////////////////////////////// 65 | // General Cuik stuff 66 | //////////////////////////////////////////// 67 | CUIK_API void cuik_init(bool use_crash_handler); 68 | 69 | //////////////////////////////////////////// 70 | // Compilation unit management 71 | //////////////////////////////////////////// 72 | #define CUIK_FOR_EACH_TU(it, cu) for (TranslationUnit* it = cuik_first_translation_unit(cu); it; it = cuik_next_translation_unit(it)) 73 | 74 | // if the translation units are in a compilation unit you can walk this chain of pointers 75 | // to read them 76 | CUIK_API TranslationUnit* cuik_first_translation_unit(CompilationUnit* restrict cu); 77 | CUIK_API TranslationUnit* cuik_next_translation_unit(TranslationUnit* restrict tu); 78 | 79 | CUIK_API CompilationUnit* cuik_create_compilation_unit(void); 80 | CUIK_API void cuik_lock_compilation_unit(CompilationUnit* restrict cu); 81 | CUIK_API void cuik_unlock_compilation_unit(CompilationUnit* restrict cu); 82 | CUIK_API void cuik_add_to_compilation_unit(CompilationUnit* restrict cu, TranslationUnit* restrict tu); 83 | CUIK_API void cuik_destroy_compilation_unit(CompilationUnit* restrict cu); 84 | CUIK_API size_t cuik_num_of_translation_units_in_compilation_unit(CompilationUnit* restrict cu); 85 | 86 | #ifdef CONFIG_HAS_TB 87 | CUIK_API void cuik_compilation_unit_set_tb_module(CompilationUnit* restrict cu, TB_Module* mod); 88 | CUIK_API TB_Module* cuik_compilation_unit_tb_module(CompilationUnit* restrict cu); 89 | #endif 90 | 91 | #include // from common 92 | #include "cuik_lex.h" 93 | #include "cuik_ast.h" 94 | #include "cuik_parse.h" 95 | #include "cuik_driver.h" 96 | #include "cuik_fs.h" 97 | 98 | #ifdef CONFIG_HAS_TB 99 | #include "cuik_irgen.h" 100 | #endif 101 | -------------------------------------------------------------------------------- /include/cuik_irgen.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////// 2 | // TB IR generation 3 | //////////////////////////////////////////// 4 | // This is the necessary code to integrate TB with Cuik, it'll generate IR for top level 5 | // statements. 6 | // 7 | // Notes: 8 | // * cuikcg_function and cuikcg_decl can be run on multiple threads 9 | // for the same module so long as the AST node is different. 10 | // 11 | // * the generator functions will return NULL if the AST node is incompatible. 12 | // 13 | // * unused AST nodes do not NEED be compiled. 14 | // 15 | #pragma once 16 | #include "cuik_prelude.h" 17 | #include 18 | 19 | // allocates all the functions and globals necessary to do IR gen 20 | CUIK_API void cuikcg_allocate_ir(TranslationUnit* tu, TB_Module* m, bool debug); 21 | 22 | // returns NULL on failure 23 | CUIK_API TB_Symbol* cuikcg_top_level(TranslationUnit* restrict tu, TB_Module* m, Stmt* restrict s); 24 | 25 | -------------------------------------------------------------------------------- /include/cuik_parse.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////// 2 | // C parser 3 | //////////////////////////////// 4 | // This module can parse C code (currently C11 with Microsoft and GNU extensions) 5 | #pragma once 6 | #include "cuik_prelude.h" 7 | 8 | typedef struct Cuik_Warnings { 9 | // implicitly converting between types and losing information 10 | bool data_loss : 1; 11 | bool unused_decls : 1; 12 | bool unused_funcs : 1; 13 | } Cuik_Warnings; 14 | 15 | typedef enum Cuik_Entrypoint { 16 | CUIK_ENTRYPOINT_NONE, 17 | 18 | CUIK_ENTRYPOINT_MAIN, 19 | CUIK_ENTRYPOINT_WINMAIN, 20 | } Cuik_Entrypoint; 21 | 22 | // This is generated from 23 | // #pragma comment(lib, "somelib.lib") 24 | typedef struct Cuik_ImportRequest { 25 | struct Cuik_ImportRequest* next; 26 | const char* lib_name; 27 | } Cuik_ImportRequest; 28 | 29 | typedef struct Cuik_ParseResult { 30 | int error_count; 31 | 32 | TranslationUnit* tu; // if error_count == 0, then tu is a valid TU. 33 | Cuik_ImportRequest* imports; // linked list of imported libs. 34 | } Cuik_ParseResult; 35 | 36 | CUIK_API Cuik_ParseResult cuikparse_run(Cuik_Version version, TokenStream* restrict s, Cuik_Target* target, bool only_code_index); 37 | 38 | CUIK_API void cuik_tu_set_ordinal(TranslationUnit* restrict tu, int ordinal); 39 | CUIK_API int cuik_tu_get_ordinal(TranslationUnit* restrict tu); 40 | 41 | // sets the user data field, returns the old value 42 | CUIK_API void* cuik_set_translation_unit_user_data(TranslationUnit* restrict tu, void* ud); 43 | 44 | // returns user data field 45 | CUIK_API void* cuik_get_translation_unit_user_data(TranslationUnit* restrict tu); 46 | 47 | // force delete the translation unit regardless of references held 48 | CUIK_API void cuik_destroy_translation_unit(TranslationUnit* restrict tu); 49 | 50 | // does this translation unit have a main? what type? 51 | CUIK_API Cuik_Entrypoint cuik_get_entrypoint_status(TranslationUnit* restrict tu); 52 | 53 | CUIK_API TokenStream* cuik_get_token_stream_from_tu(TranslationUnit* restrict tu); 54 | 55 | CUIK_API Stmt** cuik_get_top_level_stmts(TranslationUnit* restrict tu); 56 | CUIK_API size_t cuik_num_of_top_level_stmts(TranslationUnit* restrict tu); 57 | 58 | CUIK_API Cuik_Parser* cuikdg_get_parser(Cuik_Diagnostics* diag); 59 | 60 | -------------------------------------------------------------------------------- /include/cuik_prelude.h: -------------------------------------------------------------------------------- 1 | // All Cuik includes use this file 2 | #pragma once 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef CUIK_DLL 11 | # ifdef CUIK_IMPORT_DLL 12 | # define CUIK_API __declspec(dllimport) 13 | # else 14 | # define CUIK_API __declspec(dllexport) 15 | # endif 16 | #else 17 | # define CUIK_API 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /include/prelude.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef TB_API 13 | # ifdef __cplusplus 14 | # define TB_EXTERN extern "C" 15 | # else 16 | # define TB_EXTERN 17 | # endif 18 | # ifdef TB_DLL 19 | # ifdef TB_IMPORT_DLL 20 | # define TB_API TB_EXTERN __declspec(dllimport) 21 | # else 22 | # define TB_API TB_EXTERN __declspec(dllexport) 23 | # endif 24 | # else 25 | # define TB_API TB_EXTERN 26 | # endif 27 | #endif 28 | 29 | // just represents some region of bytes, usually in file parsing crap 30 | typedef struct { 31 | const uint8_t* data; 32 | size_t length; 33 | } TB_Slice; 34 | 35 | typedef enum TB_WindowsSubsystem { 36 | TB_WIN_SUBSYSTEM_UNKNOWN, 37 | TB_WIN_SUBSYSTEM_WINDOWS, 38 | TB_WIN_SUBSYSTEM_CONSOLE, 39 | TB_WIN_SUBSYSTEM_EFI_APP, 40 | } TB_WindowsSubsystem; 41 | 42 | typedef enum TB_OutputFlavor { 43 | TB_FLAVOR_OBJECT, // .o .obj 44 | TB_FLAVOR_SHARED, // .so .dll 45 | TB_FLAVOR_STATIC, // .a .lib 46 | TB_FLAVOR_EXECUTABLE, // .exe 47 | } TB_OutputFlavor; 48 | 49 | typedef enum { 50 | TB_EXECUTABLE_UNKNOWN, 51 | TB_EXECUTABLE_PE, 52 | TB_EXECUTABLE_ELF, 53 | } TB_ExecutableType; 54 | 55 | typedef enum TB_Arch { 56 | TB_ARCH_UNKNOWN, 57 | 58 | TB_ARCH_X86_64, 59 | TB_ARCH_AARCH64, 60 | 61 | // they're almost identical so might as well do both. 62 | TB_ARCH_MIPS32, 63 | TB_ARCH_MIPS64, 64 | 65 | TB_ARCH_WASM32, 66 | 67 | TB_ARCH_MAX, 68 | } TB_Arch; 69 | 70 | typedef enum TB_System { 71 | TB_SYSTEM_WINDOWS, 72 | TB_SYSTEM_LINUX, 73 | TB_SYSTEM_MACOS, 74 | TB_SYSTEM_ANDROID, // Not supported yet 75 | TB_SYSTEM_WASM, 76 | 77 | TB_SYSTEM_MAX, 78 | } TB_System; 79 | 80 | typedef enum TB_ABI { 81 | // Used on 64bit Windows platforms 82 | TB_ABI_WIN64, 83 | 84 | // Used on Mac, BSD and Linux platforms 85 | TB_ABI_SYSTEMV, 86 | } TB_ABI; 87 | 88 | typedef struct TB_Emitter { 89 | size_t capacity, count; 90 | uint8_t* data; 91 | } TB_Emitter; 92 | 93 | void* tb_out_reserve(TB_Emitter* o, size_t count); 94 | void tb_out_commit(TB_Emitter* o, size_t count); 95 | 96 | // reserves & commits 97 | void* tb_out_grab(TB_Emitter* o, size_t count); 98 | size_t tb_out_grab_i(TB_Emitter* o, size_t count); 99 | size_t tb_out_get_pos(TB_Emitter* o, void* p); 100 | 101 | // Adds null terminator onto the end and returns the starting position of the string 102 | size_t tb_outstr_nul_UNSAFE(TB_Emitter* o, const char* str); 103 | 104 | void tb_out1b_UNSAFE(TB_Emitter* o, uint8_t i); 105 | void tb_out4b_UNSAFE(TB_Emitter* o, uint32_t i); 106 | void tb_outstr_UNSAFE(TB_Emitter* o, const char* str); 107 | void tb_outs_UNSAFE(TB_Emitter* o, size_t len, const void* str); 108 | size_t tb_outs(TB_Emitter* o, size_t len, const void* str); 109 | void* tb_out_get(TB_Emitter* o, size_t pos); 110 | 111 | // fills region with zeros 112 | void tb_out_zero(TB_Emitter* o, size_t len); 113 | 114 | void tb_out1b(TB_Emitter* o, uint8_t i); 115 | void tb_out2b(TB_Emitter* o, uint16_t i); 116 | void tb_out4b(TB_Emitter* o, uint32_t i); 117 | void tb_out8b(TB_Emitter* o, uint64_t i); 118 | void tb_patch1b(TB_Emitter* o, uint32_t pos, uint8_t i); 119 | void tb_patch2b(TB_Emitter* o, uint32_t pos, uint16_t i); 120 | void tb_patch4b(TB_Emitter* o, uint32_t pos, uint32_t i); 121 | void tb_patch8b(TB_Emitter* o, uint32_t pos, uint64_t i); 122 | 123 | uint8_t tb_get1b(TB_Emitter* o, uint32_t pos); 124 | uint16_t tb_get2b(TB_Emitter* o, uint32_t pos); 125 | uint32_t tb_get4b(TB_Emitter* o, uint32_t pos); 126 | 127 | -------------------------------------------------------------------------------- /include/sdg.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // builtin primitives (the custom types start at 0x100) 4 | // 5 | // if the top bit is set, we're using a pointer to these 6 | // types rather than a direct type. 7 | typedef enum { 8 | SDG_PRIM_VOID, 9 | 10 | // builtin bools 11 | SDG_PRIM_BOOL8, SDG_PRIM_BOOL16, SDG_PRIM_BOOL32, SDG_PRIM_BOOL64, 12 | 13 | // builtin char 14 | SDG_PRIM_CHAR8, SDG_PRIM_CHAR16, SDG_PRIM_CHAR32, 15 | 16 | // builtin integers 17 | SDG_PRIM_INT8, SDG_PRIM_UINT8, 18 | SDG_PRIM_INT16, SDG_PRIM_UINT16, 19 | SDG_PRIM_INT32, SDG_PRIM_UINT32, 20 | SDG_PRIM_INT64, SDG_PRIM_UINT64, 21 | 22 | // builtin floats 23 | SDG_PRIM_FLOAT, SDG_PRIM_DOUBLE, SDG_PRIM_LONG_DOUBLE, 24 | 25 | // NOTE(NeGate): if set, the type is now a pointer to the described type. 26 | SDG_PRIM_POINTER = 0x80, 27 | 28 | CUSTOM_TYPE_START = 0x100, 29 | } SDG_TypeIndex; 30 | 31 | typedef struct { 32 | enum { 33 | SDG_TYPE_PTR, 34 | SDG_TYPE_FUNC, 35 | } tag; 36 | 37 | int arg_count; 38 | SDG_TypeIndex base; 39 | SDG_TypeIndex args[]; 40 | } SDG_Type; 41 | 42 | // symbol table 43 | typedef enum { 44 | // normal symbols 45 | SDG_SYMBOL_PROC, 46 | SDG_SYMBOL_GLOBAL, 47 | 48 | // magic symbols 49 | SDG_SYMBOL_FILE, 50 | SDG_SYMBOL_MODULE, 51 | } SDG_SymbolTag; 52 | 53 | typedef struct { 54 | uint8_t tag; 55 | // number of bytes to skip to reach the first element in the body of the symbol. 56 | // this only applies for procedures because they have nested elements. 57 | uint16_t content_ptr; 58 | uint32_t kid_count; 59 | // 0 if doesn't have one 60 | uint32_t next; 61 | } SDG_Symbol; 62 | 63 | typedef struct { 64 | SDG_Symbol super; 65 | // type 66 | SDG_TypeIndex type; 67 | // in program's memory 68 | uint32_t rva, size; 69 | char name[]; 70 | } SDG_NormalSymbol; 71 | 72 | typedef struct { 73 | SDG_Symbol super; 74 | // type 75 | SDG_TypeIndex type; 76 | // locals consist of pieces (for now, eventually 77 | // some location -> pieces) 78 | char name[]; 79 | } SDG_Local; 80 | 81 | // Each piece can do a limited amount of processing 82 | // on a variable to get it copied into the logical 83 | // view of the variable. 84 | typedef struct { 85 | // place to copy into the logical view. 86 | uint64_t offset; 87 | 88 | // indirection: 89 | // 90 | int32_t disp; 91 | uint8_t base; 92 | 93 | // bit extraction: 94 | // (x >> bit_offset) & (~0 >> (64 - bit_len)) 95 | // 96 | uint8_t bit_offset, bit_len; 97 | } SDG_Piece; 98 | 99 | typedef struct { 100 | SDG_Symbol super; 101 | // used to track changes 102 | uint32_t last_write; 103 | char name[]; 104 | } SDG_File; 105 | 106 | typedef struct { 107 | SDG_Symbol super; 108 | uint32_t type_table; 109 | uint32_t type_count; 110 | char name[]; 111 | } SDG_Module; 112 | -------------------------------------------------------------------------------- /include/tb_formats.h: -------------------------------------------------------------------------------- 1 | // This handles the generalized executable/object format parsing stuff 2 | #ifndef TB_OBJECT_H 3 | #define TB_OBJECT_H 4 | 5 | #include 6 | 7 | typedef enum { 8 | TB_OBJECT_RELOC_NONE, // how? 9 | 10 | // Target independent 11 | TB_OBJECT_RELOC_ADDR32, 12 | TB_OBJECT_RELOC_ADDR64, // unsupported on 32bit platforms 13 | TB_OBJECT_RELOC_SECREL, 14 | TB_OBJECT_RELOC_SECTION, 15 | 16 | // COFF only 17 | TB_OBJECT_RELOC_ADDR32NB, // Relative virtual address 18 | 19 | // Global offset table (ELF) 20 | TB_OBJECT_RELOC_GOTPCREL, 21 | TB_OBJECT_RELOC_REX_GOTPCRELX, 22 | 23 | // x64 only 24 | TB_OBJECT_RELOC_REL32, // relative 32bit displacement 25 | 26 | // Aarch64 only 27 | TB_OBJECT_RELOC_BRANCH26, // 26bit displacement for B and BL instructions 28 | TB_OBJECT_RELOC_REL21, // for ADR instructions 29 | 30 | // TODO(NeGate): fill in the rest of this later 31 | } TB_ObjectRelocType; 32 | 33 | typedef struct { 34 | TB_ObjectRelocType type; 35 | uint32_t symbol_index; 36 | size_t virtual_address; 37 | size_t addend; 38 | } TB_ObjectReloc; 39 | 40 | typedef enum { 41 | TB_OBJECT_SYMBOL_UNKNOWN, 42 | TB_OBJECT_SYMBOL_EXTERN, // exported 43 | TB_OBJECT_SYMBOL_WEAK_EXTERN, // weak 44 | TB_OBJECT_SYMBOL_IMPORT, // forward decl 45 | TB_OBJECT_SYMBOL_STATIC, // local 46 | TB_OBJECT_SYMBOL_SECTION, // local 47 | } TB_ObjectSymbolType; 48 | 49 | typedef struct { 50 | TB_ObjectSymbolType type; 51 | int section_num; 52 | 53 | uint32_t ordinal; 54 | uint32_t value; 55 | 56 | TB_Slice name; 57 | 58 | // for COFF, this is the auxillary 59 | void* extra; 60 | 61 | // this is zeroed out by the loader and left for the user to do crap with 62 | void* user_data; 63 | } TB_ObjectSymbol; 64 | 65 | typedef struct { 66 | TB_Slice name; 67 | uint32_t flags; 68 | 69 | size_t virtual_address; 70 | size_t virtual_size; 71 | 72 | // You can have a virtual size without having a raw 73 | // data size, that's how the BSS section works 74 | TB_Slice raw_data; 75 | 76 | size_t relocation_count; 77 | size_t relocation_offset; 78 | 79 | // legacy... HACK... i'll get rid of it later 80 | TB_ObjectReloc* relocations; 81 | 82 | // this is zeroed out by the loader and left for the user to do crap with 83 | void* user_data; 84 | } TB_ObjectSection; 85 | 86 | typedef enum { 87 | TB_OBJECT_FILE_UNKNOWN, 88 | 89 | TB_OBJECT_FILE_COFF, 90 | TB_OBJECT_FILE_ELF64 91 | } TB_ObjectFileType; 92 | 93 | typedef struct { 94 | TB_ObjectFileType type; 95 | int arch; 96 | 97 | TB_Slice name; 98 | TB_Slice ar_name; 99 | 100 | size_t symbol_count; 101 | TB_ObjectSymbol* symbols; 102 | 103 | size_t section_count; 104 | TB_ObjectSection sections[]; 105 | } TB_ObjectFile; 106 | 107 | //////////////////////////////// 108 | // Archive parser 109 | //////////////////////////////// 110 | typedef struct { 111 | TB_Slice name; 112 | 113 | // if import_name is empty, we're dealing with an object file 114 | TB_Slice import_name; 115 | uint16_t ordinal; 116 | 117 | TB_Slice content; 118 | } TB_ArchiveEntry; 119 | 120 | typedef struct { 121 | TB_Slice file; 122 | size_t pos; 123 | 124 | size_t member_count; 125 | uint32_t* members; 126 | 127 | size_t symbol_count; 128 | uint16_t* symbols; 129 | char* symbol_strtab; 130 | 131 | TB_Slice strtbl; 132 | } TB_ArchiveFileParser; 133 | 134 | // We do this to parse the header 135 | TB_API bool tb_archive_parse(TB_Slice file, TB_ArchiveFileParser* restrict out_parser); 136 | // After that we can enumerate any symbol entries to resolve imports 137 | TB_API size_t tb_archive_parse_entries(TB_ArchiveFileParser* restrict parser, size_t i, size_t count, TB_ArchiveEntry* out_entry); 138 | 139 | TB_API TB_ExecutableType tb_system_executable_format(TB_System s); 140 | 141 | #endif // TB_OBJECT_H 142 | -------------------------------------------------------------------------------- /include/tb_linker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "tb_formats.h" 11 | 12 | #ifndef TB_API 13 | # ifdef __cplusplus 14 | # define TB_EXTERN extern "C" 15 | # else 16 | # define TB_EXTERN 17 | # endif 18 | # ifdef TB_DLL 19 | # ifdef TB_IMPORT_DLL 20 | # define TB_API TB_EXTERN __declspec(dllimport) 21 | # else 22 | # define TB_API TB_EXTERN __declspec(dllexport) 23 | # endif 24 | # else 25 | # define TB_API TB_EXTERN 26 | # endif 27 | #endif 28 | 29 | //////////////////////////////// 30 | // Linker exporter 31 | //////////////////////////////// 32 | // This is used to export shared objects or executables 33 | typedef struct TB_Linker TB_Linker; 34 | typedef struct TB_LinkerSection TB_LinkerSection; 35 | typedef struct TB_LinkerSectionPiece TB_LinkerSectionPiece; 36 | 37 | // tp can be NULL, it just means we're single-threaded 38 | TB_API TB_Linker* tb_linker_create(TB_ExecutableType type, TB_Arch arch, TPool* tp); 39 | TB_API bool tb_linker_export(TB_Linker* l, const char* file_name); 40 | 41 | // wait for any pending jobs to finish 42 | TB_API void tb_linker_barrier(TB_Linker* l); 43 | 44 | // windows only 45 | TB_API void tb_linker_set_subsystem(TB_Linker* l, TB_WindowsSubsystem subsystem); 46 | 47 | TB_API void tb_linker_set_entrypoint(TB_Linker* l, const char* name); 48 | 49 | TB_API void tb_linker_add_libpath(TB_Linker* l, const char* path); 50 | 51 | #ifdef CONFIG_HAS_TB 52 | // Links compiled module into output 53 | typedef struct TB_Module TB_Module; 54 | TB_API void tb_linker_append_module(TB_Linker* l, TB_Module* m); 55 | #endif 56 | 57 | // Adds object file to output 58 | TB_API void tb_linker_append_object(TB_Linker* l, const char* file_name); 59 | 60 | // Adds static library to output 61 | // this can include imports (wrappers for DLL symbols) along with 62 | // normal sections. 63 | TB_API void tb_linker_append_library(TB_Linker* l, const char* file_name); 64 | -------------------------------------------------------------------------------- /include/tb_x64.h: -------------------------------------------------------------------------------- 1 | #ifndef TB_X64_H 2 | #define TB_X64_H 3 | 4 | #include 5 | #include 6 | 7 | #ifndef TB_API 8 | # ifdef __cplusplus 9 | # define TB_EXTERN extern "C" 10 | # else 11 | # define TB_EXTERN 12 | # endif 13 | # ifdef TB_DLL 14 | # ifdef TB_IMPORT_DLL 15 | # define TB_API TB_EXTERN __declspec(dllimport) 16 | # else 17 | # define TB_API TB_EXTERN __declspec(dllexport) 18 | # endif 19 | # else 20 | # define TB_API TB_EXTERN 21 | # endif 22 | #endif 23 | 24 | typedef enum { 25 | // r/m is a memory operand 26 | TB_X86_INSTR_INDIRECT = (1u << 0u), 27 | 28 | // LOCK prefix is present 29 | TB_X86_INSTR_LOCK = (1u << 1u), 30 | 31 | // uses an immediate 32 | TB_X86_INSTR_IMMEDIATE = (1u << 2u), 33 | 34 | // set if the r/m can be found on the right hand side 35 | TB_X86_INSTR_DIRECTION = (1u << 3u), 36 | 37 | // REP prefix is present 38 | TB_X86_INSTR_REP = (1u << 5u), 39 | 40 | // REPNE prefix is present 41 | TB_X86_INSTR_REPNE = (1u << 6u), 42 | } TB_X86_InstFlags; 43 | 44 | typedef enum { 45 | TB_X86_RAX, TB_X86_RCX, TB_X86_RDX, TB_X86_RBX, TB_X86_RSP, TB_X86_RBP, TB_X86_RSI, TB_X86_RDI, 46 | TB_X86_R8, TB_X86_R9, TB_X86_R10, TB_X86_R11, TB_X86_R12, TB_X86_R13, TB_X86_R14, TB_X86_R15, 47 | } TB_X86_GPR; 48 | 49 | typedef enum { 50 | TB_X86_SEGMENT_DEFAULT = 0, 51 | 52 | TB_X86_SEGMENT_ES, TB_X86_SEGMENT_CS, 53 | TB_X86_SEGMENT_SS, TB_X86_SEGMENT_DS, 54 | TB_X86_SEGMENT_GS, TB_X86_SEGMENT_FS, 55 | } TB_X86_Segment; 56 | 57 | typedef enum { 58 | TB_X86_NONE = 0, 59 | 60 | TB_X86_BYTE, // 1 61 | TB_X86_WORD, // 2 62 | TB_X86_DWORD, // 4 63 | TB_X86_QWORD, // 8 64 | 65 | TB_X86_XMMWORD, // the generic idea of them 66 | 67 | TB_X86_F32x1, // ss 68 | TB_X86_F64x1, // sd 69 | TB_X86_F32x4, // ps 70 | TB_X86_F64x2, // pd 71 | 72 | TB_X86_PBYTE, // int8 x 16 = 16 73 | TB_X86_PWORD, // int16 x 8 = 16 74 | TB_X86_PDWORD, // int32 x 4 = 16 75 | TB_X86_PQWORD, // int64 x 2 = 16 76 | } TB_X86_DataType; 77 | 78 | typedef struct { 79 | uint16_t opcode; 80 | 81 | // packed 16bits 82 | uint16_t scale : 2; 83 | uint16_t flags : 6; 84 | uint16_t dt : 4; 85 | uint16_t dt2 : 4; 86 | 87 | // each 8bits are a different reg (lowest bits to higher): 88 | // base, index, rx, extra 89 | uint32_t regs; 90 | int32_t disp; 91 | 92 | // bitpacking amirite 93 | TB_X86_Segment segment : 4; 94 | uint8_t length : 4; 95 | uint8_t disp_pos : 4; 96 | 97 | // immediate operand 98 | int64_t imm; 99 | } TB_X86_Inst; 100 | 101 | TB_API bool tb_x86_disasm(TB_X86_Inst* restrict inst, size_t length, const uint8_t* data); 102 | TB_API const char* tb_x86_reg_name(int8_t reg, TB_X86_DataType dt); 103 | TB_API const char* tb_x86_type_name(TB_X86_DataType dt); 104 | TB_API const char* tb_x86_mnemonic(TB_X86_Inst* inst); 105 | 106 | #endif /* TB_X64_H */ 107 | -------------------------------------------------------------------------------- /linker/README.txt: -------------------------------------------------------------------------------- 1 | Linkers are mostly OS-indepedent ngl, like yea i gotta parse the shitty object files into execs but layouting, 2 | generating code stubs, and doing relocations are basically the same everywhere once the parsing is done. 3 | 4 | Pipeline: 5 | 6 | * Importing (Extremely parallel): Call import functions, each of these will extract the symbols & relocations into memory. 7 | The section data isn't read but we do keep track of which files and what ranges they exist in 8 | for layouting reasons. 9 | 10 | * Layout step (Not so parallel): We use whatever user-defined ordinals to sort the data into a consistent ordering, from there 11 | we know the complete size of the final file (useful for file map writing). 12 | 13 | * Export step (Extremely parallel): 14 | 15 | -------------------------------------------------------------------------------- /linker/archives.c: -------------------------------------------------------------------------------- 1 | #include "linker.h" 2 | #include "../tb/objects/lib_parse.h" 3 | 4 | void append_archive(TPool* pool, TB_LinkerObject* lib, int slash) { 5 | log_debug("linking against %.*s (as archive)", (int) (lib->name.length - slash), lib->name.data + slash); 6 | 7 | TB_Linker* l = lib->linker; 8 | TB_Slice ar_file = lib->content; 9 | 10 | TB_ArchiveFileParser ar_parser = { 0 }; 11 | if (!tb_archive_parse(ar_file, &ar_parser)) { 12 | cuikperf_region_end(); 13 | return; 14 | } 15 | 16 | // populate lazy symbols, distribute work to other threads... because we can :) 17 | CUIK_TIMED_BLOCK("lazy") { 18 | uint64_t t = lib->time; 19 | char* strtab = ar_parser.symbol_strtab; 20 | 21 | size_t i = 0, j = 0; 22 | while (i < ar_parser.symbol_count) { 23 | uint16_t offset_index = ar_parser.symbols[i] - 1; 24 | 25 | const char* name = &strtab[j]; 26 | size_t len = ideally_fast_strlen(name); 27 | 28 | TB_ArchiveEntry e = tb_archive_member_get(&ar_parser, offset_index); 29 | 30 | // We don't *really* care about this info beyond nicer errors (use an arena tho) 31 | TB_LinkerObject* obj_file = tb_arena_alloc(&linker_perm_arena, sizeof(TB_LinkerObject)); 32 | *obj_file = (TB_LinkerObject){ e.name, l, e.content, t + offset_index*65536, lib }; 33 | 34 | TB_LinkerObject* k = namehs_intern(&l->objects, obj_file); 35 | if (k != obj_file) { 36 | tb_arena_free(&linker_perm_arena, k, sizeof(TB_LinkerObject)); 37 | obj_file = k; 38 | } 39 | 40 | printf("%s : %u : %#x (%.*s)\n", name, offset_index, ar_parser.members[offset_index], (int) e.name.length, e.name.data); 41 | TB_LinkerSymbol* s = tb_arena_alloc(&linker_perm_arena, sizeof(TB_LinkerSymbol)); 42 | *s = (TB_LinkerSymbol){ 43 | .name = { (const uint8_t*) name, len }, 44 | .tag = TB_LINKER_SYMBOL_LAZY, 45 | .lazy = { obj_file }, 46 | }; 47 | 48 | TB_LinkerSymbol* new_s = tb_linker_symbol_insert(l, s); 49 | if (new_s != s) { 50 | tb_arena_free(&linker_perm_arena, s, sizeof(TB_LinkerSymbol)); 51 | s = new_s; 52 | } 53 | i += 1, j += len + 1; 54 | } 55 | } 56 | 57 | __debugbreak(); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /linker/ld_script.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //////////////////////////////// 4 | // Parsing GNU ld scripts 5 | //////////////////////////////// 6 | void elf_append_script(TPool* pool, TB_LinkerObject* lib, int slash) { 7 | log_debug("linking against %.*s (as LD script)", (int) (lib->name.length - slash), lib->name.data + slash); 8 | 9 | TB_Linker* l = lib->linker; 10 | TB_Slice content = lib->content; 11 | 12 | printf("%.*s\n", (int) content.length, content.data); 13 | } 14 | 15 | 16 | -------------------------------------------------------------------------------- /logo/cuik.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /main/README.md: -------------------------------------------------------------------------------- 1 | # Main driver 2 | 3 | This is the CLI driver for libCuik. It's a mostly CC-like interface with some minor changes along with some behavioral changes. LibCuik is capable of multithreading within one process which means that if you pass multiple source files into Cuik we may compile them on separate threads (unless --threads=1 is specified). 4 | -------------------------------------------------------------------------------- /main/live.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct { 3 | uint64_t og_last_write; 4 | } LiveCompiler; 5 | 6 | #if _WIN32 7 | #define WIN32_LEAN_AND_MEAN 8 | #include 9 | 10 | static uint64_t get_last_write_time(const char* filepath) { 11 | WIN32_FIND_DATA data; 12 | HANDLE handle = FindFirstFile(filepath, &data); 13 | 14 | ULARGE_INTEGER i; 15 | i.LowPart = data.ftLastWriteTime.dwLowDateTime; 16 | i.HighPart = data.ftLastWriteTime.dwHighDateTime; 17 | 18 | FindClose(handle); 19 | return i.QuadPart; 20 | } 21 | 22 | // returns false if no changes are made, true if the file is ready to be reloaded 23 | static bool live_compile_watch(LiveCompiler* l, const char* source_file) { 24 | // Wait for the user to save again 25 | uint64_t current_last_write; 26 | if (current_last_write = get_last_write_time(source_file), l->og_last_write == current_last_write) { 27 | return false; 28 | } 29 | 30 | l->og_last_write = current_last_write; 31 | 32 | // wait for it to finish writing before trying to compile 33 | int ticks = 0; 34 | while (GetFileAttributesA(source_file) == INVALID_FILE_ATTRIBUTES) { 35 | SleepEx(1, FALSE); 36 | 37 | if (ticks++ > 100) { 38 | printf("live-compiler error: file locked (tried multiple times)\n"); 39 | return false; 40 | } 41 | } 42 | 43 | return true; 44 | } 45 | #else 46 | static uint64_t get_last_write_time(const char* filepath) { 47 | return 0; 48 | } 49 | 50 | static bool live_compile_watch(LiveCompiler* l, const char* source_file) { 51 | l->og_last_write = get_last_write_time(source_file); 52 | return false; 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /mat.c: -------------------------------------------------------------------------------- 1 | 2 | /* int foo(float a, float b) { 3 | return a > b; 4 | } 5 | 6 | int bar(float a) { 7 | return foo(a, 2.0f); 8 | } */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #if 1 15 | int main() { 16 | printf("H %d\n", count(0b100010010001)); 17 | printf("F %d\n", foo(16, 3)); 18 | } 19 | 20 | int foo(int a, int b) { 21 | return a / b; 22 | } 23 | #endif 24 | 25 | 26 | uint32_t count(uint64_t bits) { 27 | uint32_t used = 0; 28 | for (uint32_t i = 0; i < 64; i++) { 29 | if (bits & (1ull << i)) { 30 | used += 1; 31 | } 32 | } 33 | 34 | return used; 35 | } 36 | 37 | void matmul(float* dst, float* a, float* b) { 38 | if (dst == NULL) { 39 | return; 40 | } 41 | 42 | for (size_t i = 0; i < 16; i++) { 43 | dst[i*16 + 0] = -0.0f; 44 | dst[i*16 + 1] = -0.0f; 45 | dst[i*16 + 2] = -0.0f; 46 | dst[i*16 + 3] = -0.0f; 47 | dst[i*16 + 4] = -0.0f; 48 | dst[i*16 + 5] = -0.0f; 49 | dst[i*16 + 6] = -0.0f; 50 | dst[i*16 + 7] = -0.0f; 51 | dst[i*16 + 8] = -0.0f; 52 | dst[i*16 + 9] = -0.0f; 53 | dst[i*16 + 10] = -0.0f; 54 | dst[i*16 + 11] = -0.0f; 55 | dst[i*16 + 12] = -0.0f; 56 | dst[i*16 + 13] = -0.0f; 57 | dst[i*16 + 14] = -0.0f; 58 | dst[i*16 + 15] = -0.0f; 59 | } 60 | 61 | for (size_t kk = 0; kk < 16; kk += 4) { 62 | for (size_t jj = 0; jj < 16; jj += 4) { 63 | for (size_t i = 0; i < 16; i++) { 64 | float a0 = a[i*16 + (kk+0)]; 65 | float a1 = a[i*16 + (kk+1)]; 66 | float a2 = a[i*16 + (kk+2)]; 67 | float a3 = a[i*16 + (kk+3)]; 68 | float b0 = b[(kk+0)*16 + (jj+0)]; 69 | float b1 = b[(kk+1)*16 + (jj+0)]; 70 | float b2 = b[(kk+2)*16 + (jj+0)]; 71 | float b3 = b[(kk+3)*16 + (jj+0)]; 72 | float b4 = b[(kk+0)*16 + (jj+1)]; 73 | float b5 = b[(kk+1)*16 + (jj+1)]; 74 | float b6 = b[(kk+2)*16 + (jj+1)]; 75 | float b7 = b[(kk+3)*16 + (jj+1)]; 76 | float b8 = b[(kk+0)*16 + (jj+2)]; 77 | float b9 = b[(kk+1)*16 + (jj+2)]; 78 | float b10 = b[(kk+2)*16 + (jj+2)]; 79 | float b11 = b[(kk+3)*16 + (jj+2)]; 80 | float b12 = b[(kk+0)*16 + (jj+3)]; 81 | float b13 = b[(kk+1)*16 + (jj+3)]; 82 | float b14 = b[(kk+2)*16 + (jj+3)]; 83 | float b15 = b[(kk+3)*16 + (jj+3)]; 84 | float sum0 = dst[i*16 + (jj+0)]; 85 | float sum1 = dst[i*16 + (jj+1)]; 86 | float sum2 = dst[i*16 + (jj+2)]; 87 | float sum3 = dst[i*16 + (jj+3)]; 88 | sum0 += a0 * b0; 89 | sum0 += a1 * b1; 90 | sum0 += a2 * b2; 91 | sum0 += a3 * b3; 92 | dst[i*16 + (jj+0)] = sum0; 93 | sum1 += a0 * b4; 94 | sum1 += a1 * b5; 95 | sum1 += a2 * b6; 96 | sum1 += a3 * b7; 97 | dst[i*16 + (jj+1)] = sum1; 98 | sum2 += a0 * b8; 99 | sum2 += a1 * b9; 100 | sum2 += a2 * b10; 101 | sum2 += a3 * b11; 102 | dst[i*16 + (jj+2)] = sum2; 103 | sum3 += a0 * b12; 104 | sum3 += a1 * b13; 105 | sum3 += a2 * b14; 106 | sum3 += a3 * b15; 107 | dst[i*16 + (jj+3)] = sum3; 108 | } 109 | } 110 | } 111 | } 112 | 113 | -------------------------------------------------------------------------------- /mat.lua: -------------------------------------------------------------------------------- 1 | 2 | local N = tonumber(arg[1]) 3 | 4 | 5 | print(src:tostring()) 6 | -------------------------------------------------------------------------------- /meta/hexembed.c: -------------------------------------------------------------------------------- 1 | /* hexembed.c - copyright Lewis Van Winkle */ 2 | /* zlib license */ 3 | /* negate modded it, because it was cool */ 4 | #define _CRT_SECURE_NO_WARNINGS 5 | #include 6 | #include 7 | #include 8 | 9 | #define OUTPUT_PATH "bin/objs/freestanding.c" 10 | 11 | int main(int argc, char *argv[]) { 12 | if (argc < 2) { 13 | printf("Usage:\n\thexembed \n"); 14 | return 1; 15 | } 16 | 17 | FILE* out = fopen(OUTPUT_PATH, "wb"); 18 | fprintf(out, "#include \n\n"); 19 | fprintf(out, "typedef struct InternalFile InternalFile;\n"); 20 | fprintf(out, "struct InternalFile {\n"); 21 | fprintf(out, " InternalFile* next;\n"); 22 | fprintf(out, " const char* name;\n"); 23 | fprintf(out, " size_t size;\n"); 24 | fprintf(out, " char data[];\n"); 25 | fprintf(out, "};\n"); 26 | 27 | for (int i = 1; i < argc; i++) { 28 | const char *fname = argv[i]; 29 | FILE *fp = fopen(fname, "rb"); 30 | if (!fp) { 31 | fprintf(stderr, "Error opening file: %s.\n", fname); 32 | return 1; 33 | } 34 | 35 | fseek(fp, 0, SEEK_END); 36 | const int fsize = ftell(fp); 37 | int padded_size = fsize + 16; 38 | 39 | fseek(fp, 0, SEEK_SET); 40 | unsigned char *b = malloc(padded_size); 41 | fread(b, fsize, 1, fp); 42 | memset(b+fsize, 0, 16); 43 | fclose(fp); 44 | 45 | int file_index = i - 1; 46 | fprintf(out, "InternalFile cuik__ifiles%d = {\n", file_index); 47 | if (file_index) { 48 | fprintf(out, " .next = &cuik__ifiles%d,\n", file_index - 1); 49 | } 50 | fprintf(out, " .name = \"%s\",\n", strrchr(fname, '/')+1); 51 | fprintf(out, " .size = %d,\n", fsize); 52 | fprintf(out, " .data = {\n "); 53 | for (int j = 0; j < padded_size; ++j) { 54 | fprintf(out, "0x%02x%s", b[j] == '\t' ? ' ' : b[j], j == padded_size-1 ? "" : ((j+1) % 16 == 0 ? ",\n " : ",")); 55 | } 56 | fprintf(out, "\n }\n};\n\n"); 57 | free(b); 58 | } 59 | fprintf(out, "InternalFile* cuik__ifiles_root = &cuik__ifiles%d;\n", argc-2); 60 | fclose(out); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /meta/uops.lua: -------------------------------------------------------------------------------- 1 | local xmlparser = require "meta/xmlparser" 2 | local inspect = require "meta/inspect" 3 | 4 | doc, err = xmlparser.parseFile("Y:/Workspace/instructions.xml", false) 5 | 6 | local insts = {} 7 | 8 | for k,v in pairs(doc.children[1].children) do 9 | -- find extensions we care about 10 | print(v.attrs.name) 11 | if v.attrs.name == "BASE" then 12 | for k2,v2 in pairs(v.children) do 13 | local arch = nil 14 | for k3,v3 in pairs(v2.children) do 15 | if v3.tag == "architecture" and v3.attrs.name == "SKL" then 16 | arch = v3 17 | break 18 | end 19 | end 20 | 21 | if arch then 22 | local a, b = v2.attrs.string:match"^(%S+)%s+%((.+)%)" 23 | if a and b then 24 | -- some instruction names have weird shit like the exact opcode 25 | -- written after it, trim them off 26 | local c = a:match"(.+)_.*" 27 | if c then 28 | a = c 29 | end 30 | 31 | if not insts[a] then 32 | insts[a] = {} 33 | end 34 | 35 | local max_lat = 1 36 | for k3,v3 in pairs(arch.children[1].children) do 37 | for k4,v4 in pairs(v3.attrs) do 38 | if k4 ~= "start_op" and k4 ~= "target_op" then 39 | local lat = tonumber(v4) 40 | if lat and lat > max_lat then 41 | max_lat = lat 42 | end 43 | end 44 | end 45 | end 46 | 47 | if insts[a][b] then 48 | print("Duplicate info on ", a, b) 49 | if max_lat > insts[a][b].lat then 50 | insts[a][b].lat = max_lat 51 | end 52 | else 53 | insts[a][b] = { tp = arch.children[1].attrs.TP_ports, ports = arch.children[1].attrs.ports, lat = max_lat } 54 | end 55 | 56 | -- print(">", a, "|", "("..b..")", arch.attrs.name, arch.children[1].attrs.TP_ports, arch.children[1].attrs.ports) 57 | else 58 | -- print(">", v2.attrs.string, arch.attrs.name, arch.children[1].attrs.TP_ports, arch.children[1].attrs.ports) 59 | end 60 | end 61 | end 62 | end 63 | end 64 | 65 | function compile_inst(inst) 66 | 67 | end 68 | 69 | compile_inst(insts["ADD"]) 70 | 71 | print(inspect(insts.ADD)) 72 | 73 | -------------------------------------------------------------------------------- /tb/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = crlf 9 | insert_final_newline = true 10 | indent_style = tab 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /tb/.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | .tup/ 3 | bin/ 4 | negate/ 5 | 6 | tildebackend.lib 7 | tildebackend.a 8 | 9 | *.cache 10 | *.swp 11 | *.out 12 | *.o 13 | *.obj 14 | *.exe 15 | *.pdb 16 | 17 | tup.config 18 | debug.bat 19 | build.bat 20 | run.bat 21 | run4coder.bat 22 | project.4coder 23 | tb.rdbg 24 | build.ninja 25 | .ninja_deps 26 | .ninja_log 27 | -------------------------------------------------------------------------------- /tb/NOTES.txt: -------------------------------------------------------------------------------- 1 | # Optimizer crap 2 | 3 | some of the optimizations i should probably worry about are proving when numbers can't 4 | overflow, like induction vars: 5 | 6 | ``` 7 | for { 8 | i = phi(0, j) 9 | // even if n is TOP, i must be at least TOP and 10 | // after all additions on the PHI... which means 11 | // no overflow 12 | if i >= n break 13 | ... 14 | // next 15 | j = i + 1 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /tb/aarch64/a64.machine: -------------------------------------------------------------------------------- 1 | extra (A64Op) 2 | 3 | // Hello 4 | pat (TB_F32CONST $root value=$value) => (TB_ICONST dt=TB_TYPE_I32 $root value="*(uint32_t*) &$value") 5 | pat (TB_F64CONST $root value=$value) => (TB_ICONST dt=TB_TYPE_I32 $root value="*(uint64_t*) &$value") 6 | 7 | node (add_imm12) 8 | pat (TB_ADD dt=$dt ___ $a (TB_ICONST ... value=$b)) where "($b == ($b & 07777)) || ($b == ($b & 077770000))" => (a64_add_imm12 dt=$dt ___ $a imm=$b) 9 | 10 | -------------------------------------------------------------------------------- /tb/aarch64/a64_gen.h: -------------------------------------------------------------------------------- 1 | typedef enum A64NodeType { 2 | a64_add_imm12 = TB_MACH_A64 + 0, 3 | } A64NodeType; 4 | static const char* node_name(int n_type) { 5 | switch (n_type) { 6 | case a64_add_imm12: return "a64_add_imm12"; 7 | default: return NULL; 8 | } 9 | } 10 | 11 | static TB_Node* mach_dfa_accept(Ctx* ctx, TB_Function* f, TB_Node* n, int state) { 12 | switch (state) { 13 | case 2: { 14 | do { 15 | TB_Node* $root = 0 < n->input_count ? n->inputs[0] : NULL; 16 | float $value = TB_NODE_GET_EXTRA_T(n, TB_NodeFloat32)->value; 17 | 18 | size_t k0_i = 0; 19 | TB_Node* k0 = tb_alloc_node(f, TB_ICONST, TB_TYPE_I32, 1, sizeof(TB_NodeInt)); 20 | set_input(f, k0, $root, k0_i++); 21 | TB_NodeInt* k0_extra = TB_NODE_GET_EXTRA(k0); 22 | k0_extra->value = *(uint32_t*) &$value; 23 | 24 | return k0; 25 | } while (0); 26 | } return NULL; 27 | case 4: { 28 | do { 29 | TB_Node* $root = 0 < n->input_count ? n->inputs[0] : NULL; 30 | double $value = TB_NODE_GET_EXTRA_T(n, TB_NodeFloat64)->value; 31 | 32 | size_t k0_i = 0; 33 | TB_Node* k0 = tb_alloc_node(f, TB_ICONST, TB_TYPE_I32, 1, sizeof(TB_NodeInt)); 34 | set_input(f, k0, $root, k0_i++); 35 | TB_NodeInt* k0_extra = TB_NODE_GET_EXTRA(k0); 36 | k0_extra->value = *(uint64_t*) &$value; 37 | 38 | return k0; 39 | } while (0); 40 | } return NULL; 41 | case 9: { 42 | do { 43 | uint64_t $b = TB_NODE_GET_EXTRA_T(n->inputs[2], TB_NodeInt)->value; 44 | TB_Node* $a = 1 < n->input_count ? n->inputs[1] : NULL; 45 | TB_DataType $dt = n->dt; 46 | if (!(($b == ($b & 07777)) || ($b == ($b & 077770000)))) { 47 | break; 48 | } 49 | 50 | size_t k0_i = 0; 51 | TB_Node* k0 = tb_alloc_node(f, a64_add_imm12, $dt, 2, sizeof(A64Op)); 52 | k0_i++; 53 | set_input(f, k0, $a, k0_i++); 54 | A64Op* k0_extra = TB_NODE_GET_EXTRA(k0); 55 | k0_extra->imm = $b; 56 | 57 | return k0; 58 | } while (0); 59 | } return NULL; 60 | // no match? 61 | default: return NULL; 62 | } 63 | } 64 | 65 | static bool mach_is_operand[512] = { 66 | }; 67 | static bool mach_is_subpat[512] = { 68 | }; 69 | #define R_PUSH(next) ((1u << 16u) | (next)) 70 | #define R_POP(n, next) (((n) << 16u) | (next)) 71 | static void global_init(void) { 72 | static const uint32_t edges[] = { 73 | (0)<<16 | (TB_ADD+1), R_PUSH(5), 74 | (0)<<16 | (TB_F32CONST+1), R_PUSH(1), 75 | (0)<<16 | (TB_F64CONST+1), R_PUSH(3), 76 | (1)<<16 | (0), 2, 77 | (2)<<16 | (TB_NULL+1), R_POP(2, 2), 78 | (3)<<16 | (0), 4, 79 | (4)<<16 | (TB_NULL+1), R_POP(2, 4), 80 | (5)<<16 | (TB_NULL+1), 6, 81 | (6)<<16 | (0), 7, 82 | (7)<<16 | (TB_ICONST+1), R_PUSH(8), 83 | (8)<<16 | (0), 8, 84 | (8)<<16 | (TB_NULL+1), R_POP(2, 9), 85 | (9)<<16 | (TB_NULL+1), R_POP(2, 9), 86 | }; 87 | // transitions: 13 88 | size_t count = sizeof(edges) / (2*sizeof(uint32_t)); 89 | node_grammar_alloc(count); 90 | FOR_N(i, 0, count) { 91 | node_grammar_put(edges[i*2], edges[i*2 + 1]); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tb/builtins.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | typedef struct TB_MultiplyResult { 5 | uint64_t lo; 6 | uint64_t hi; 7 | } TB_MultiplyResult; 8 | 9 | #if defined(_MSC_VER) && !defined(__clang__) 10 | static int tb_clz64(uint64_t x) { 11 | return _lzcnt_u64(x); 12 | } 13 | 14 | static int tb_ffs(uint32_t x) { 15 | unsigned long index; 16 | return _BitScanForward(&index, x) ? 1 + index : 0; 17 | } 18 | 19 | static int tb_ffs64(uint64_t x) { 20 | unsigned long index; 21 | return _BitScanForward64(&index, x) ? 1 + index : 0; 22 | } 23 | 24 | static int tb_popcount(uint32_t x) { 25 | return __popcnt(x); 26 | } 27 | 28 | static int tb_popcount64(uint64_t x) { 29 | return __popcnt64(x); 30 | } 31 | 32 | static uint64_t tb_next_pow2(uint64_t x) { 33 | return x == 1 ? 1 : 1 << (64 - _lzcnt_u64(x - 1)); 34 | } 35 | 36 | static bool tb_add_overflow(uint64_t a, uint64_t b, uint64_t* result) { 37 | uint64_t c = a + b; 38 | *result = c; 39 | return c < a; 40 | } 41 | 42 | static bool tb_sub_overflow(uint64_t a, uint64_t b, uint64_t* result) { 43 | uint64_t c = a - b; 44 | *result = c; 45 | return c > a; 46 | } 47 | 48 | #pragma intrinsic(_udiv128) 49 | static uint64_t tb_div128(uint64_t ahi, uint64_t alo, uint64_t b) { 50 | uint64_t rem; 51 | return _udiv128(ahi, alo, b, &rem); 52 | } 53 | 54 | #pragma intrinsic(_umul128) 55 | static TB_MultiplyResult tb_mul64x128(uint64_t a, uint64_t b) { 56 | uint64_t hi; 57 | uint64_t lo = _umul128(a, b, &hi); 58 | return (TB_MultiplyResult) { lo, hi }; 59 | } 60 | #else 61 | static int tb_clz64(uint64_t x) { 62 | return __builtin_clzll(x); 63 | } 64 | 65 | static int tb_ffs(uint32_t x) { 66 | return __builtin_ffs(x); 67 | } 68 | 69 | static int tb_ffs64(uint64_t x) { 70 | return __builtin_ffsll(x); 71 | } 72 | 73 | static int tb_popcount(uint32_t x) { 74 | return __builtin_popcount(x); 75 | } 76 | 77 | static int tb_popcount64(uint64_t x) { 78 | return __builtin_popcountll(x); 79 | } 80 | 81 | static uint64_t tb_next_pow2(uint64_t x) { 82 | return x == 1 ? 1 : 1 << (64 - __builtin_clzll(x - 1)); 83 | } 84 | 85 | static bool tb_add_overflow(uint64_t a, uint64_t b, uint64_t* result) { 86 | return __builtin_add_overflow(a, b, result); 87 | } 88 | 89 | static bool tb_sub_overflow(uint64_t a, uint64_t b, uint64_t* result) { 90 | return __builtin_sub_overflow(a, b, result); 91 | } 92 | 93 | static TB_MultiplyResult tb_mul64x128(uint64_t a, uint64_t b) { 94 | __uint128_t product = (__uint128_t)a * (__uint128_t)b; 95 | 96 | return (TB_MultiplyResult) { (uint64_t)(product & 0xFFFFFFFFFFFFFFFF), (uint64_t)(product >> 64) }; 97 | } 98 | 99 | static uint64_t tb_div128(uint64_t ahi, uint64_t alo, uint64_t b) { 100 | // We don't want 128 bit software division 101 | uint64_t d, e; 102 | __asm__("divq %[b]" 103 | : "=a"(d), "=d"(e) 104 | : [b] "r"(b), "a"(alo), "d"(ahi) 105 | ); 106 | return d; 107 | } 108 | #endif 109 | -------------------------------------------------------------------------------- /tb/debug/cv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../objects/coff.h" 3 | 4 | typedef uint32_t CV_TypeIndex; 5 | 6 | typedef struct { 7 | // Type table 8 | CV_TypeIndex type_entry_count; 9 | TB_Emitter type_section; 10 | 11 | // Type table cache 12 | bool needs_to_free_lookup; 13 | size_t lookup_table_size; // must be a power of two 14 | CV_TypeEntry* lookup_table; 15 | 16 | // Symbol table 17 | // TODO 18 | } CV_Builder; 19 | 20 | typedef struct { 21 | CV_TypeIndex type; 22 | const char* name; 23 | TB_CharUnits offset; 24 | } CV_Field; 25 | 26 | // map to their Codeview record types because it's simpler 27 | typedef enum { 28 | CV_CLASS = 0x1504, CV_STRUCT = 0x1505, CV_UNION = 0x1506, 29 | } CV_RecordType; 30 | 31 | // if cache is NULL and cache_size is not 0, then it'll allocate and zeroed it for you 32 | // which will also be freed on tb_codeview_builder_done 33 | // 34 | // cache_size must be a power of two 35 | // if cache is non-NULL it has cache_size elements and zeroed 36 | CV_Builder tb_codeview_builder_create(size_t cache_size, CV_TypeEntry* cache); 37 | 38 | // doesn't free the section memory 39 | void tb_codeview_builder_done(CV_Builder* builder); 40 | 41 | // Type builder API 42 | CV_TypeIndex tb_codeview_builder_add_array(CV_Builder* builder, CV_TypeIndex base, size_t count); 43 | CV_TypeIndex tb_codeview_builder_add_pointer(CV_Builder* builder, CV_TypeIndex ptr_to); 44 | CV_TypeIndex tb_codeview_builder_add_arg_list(CV_Builder* builder, size_t count, const CV_TypeIndex* args, bool has_varargs); 45 | CV_TypeIndex tb_codeview_builder_add_procedure(CV_Builder* builder, CV_TypeIndex return_type, CV_TypeIndex arg_list, size_t param_count); 46 | 47 | // function ID is just a procedure with name, it's used by the symbol table 48 | CV_TypeIndex tb_codeview_builder_add_function_id(CV_Builder* builder, CV_TypeIndex proc_type, const char* name); 49 | 50 | // When refering to records from other types you must use this forward declaration 51 | CV_TypeIndex tb_codeview_builder_add_incomplete_record(CV_Builder* builder, CV_RecordType rec_type, const char* name); 52 | CV_TypeIndex tb_codeview_builder_add_field_list(CV_Builder* builder, size_t count, const CV_Field* fields); 53 | CV_TypeIndex tb_codeview_builder_add_record(CV_Builder* builder, CV_RecordType rec_type, size_t field_count, CV_TypeIndex field_list, TB_CharUnits size, const char* name); 54 | -------------------------------------------------------------------------------- /tb/disasm.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | size_t tb_x86_print_inst(TB_Disasm* disasm, TB_X86_Inst* inst, bool has_relocs); 4 | 5 | bool tb_disasm_outf(TB_Disasm* disasm, const char* fmt, ...) { 6 | size_t cap = disasm->out_len - disasm->out_curr; 7 | 8 | va_list ap; 9 | va_start(ap, fmt); 10 | int len = vsnprintf(&disasm->out[disasm->out_curr], cap, fmt, ap); 11 | va_end(ap); 12 | 13 | if (len < 0 || len > cap) { 14 | return false; 15 | } else { 16 | disasm->out_curr += len; 17 | return true; 18 | } 19 | } 20 | 21 | ptrdiff_t tb_disasm_print(TB_Arch arch, TB_Disasm* disasm, bool has_relocs) { 22 | switch (arch) { 23 | #ifdef TB_HAS_X64 24 | case TB_ARCH_X86_64: { 25 | TB_X86_Inst inst; 26 | if (!tb_x86_disasm(&inst, disasm->in_len - disasm->in_curr, &disasm->in[disasm->in_curr])) { 27 | return -1; 28 | } 29 | 30 | size_t s = tb_x86_print_inst(disasm, &inst, has_relocs); 31 | TB_ASSERT(s != 0); 32 | 33 | disasm->in_curr += inst.length; 34 | return inst.length; 35 | } 36 | #endif 37 | 38 | default: 39 | tb_todo(); 40 | } 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /tb/host.h: -------------------------------------------------------------------------------- 1 | // architecture 2 | #if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) 3 | #define TB_HOST_X86_64 1 4 | #elif defined(i386) || defined(__i386) || defined(__i386__) 5 | #define TB_HOST_X86 2 6 | #elif defined(__aarch64__) 7 | #define TB_HOST_ARM64 3 8 | #elif defined(__arm__) 9 | #define TB_HOST_ARM32 4 10 | #endif 11 | 12 | // system 13 | #if defined(__APPLE__) && defined(__MACH__) 14 | #define TB_HOST_OSX 1 15 | #elif defined(__gnu_linux__) || defined(__linux__) 16 | #define TB_HOST_LINUX 2 17 | #elif defined(_WIN32) 18 | #define TB_HOST_WINDOWS 3 19 | #endif 20 | -------------------------------------------------------------------------------- /tb/libtb.c: -------------------------------------------------------------------------------- 1 | // This is the TB unity build 2 | void* tb_jit_stack_create(void); 3 | 4 | #include "tb.c" 5 | #include "hash.c" 6 | #include "abi.c" 7 | #include "tb_builder.c" 8 | #include "debug_builder.c" 9 | #include "exporter.c" 10 | #include "disasm.c" 11 | 12 | // JIT 13 | #include "jit.c" 14 | 15 | // Optimizer 16 | #include "opt/optimizer.c" 17 | #include "new_builder.c" 18 | 19 | // Regalloc 20 | #include "codegen.c" 21 | #include "rogers_ra.c" 22 | #include "briggs_ra.c" 23 | 24 | // Parsers 25 | #define TB_COFF_IMPL 26 | #include 27 | 28 | // Debug 29 | #include "debug/cv.c" 30 | #include "debug/sdg.c" 31 | 32 | // Objects 33 | #include "objects/coff_parse.c" 34 | #include "objects/coff.c" 35 | #include "objects/elf64.c" 36 | #include "objects/macho.c" 37 | #include "objects/wasm_obj.c" 38 | 39 | int uf_find(int* uf, int uf_len, int a) { 40 | if (a >= uf_len) { 41 | return a; 42 | } 43 | 44 | // leader 45 | int l = a; 46 | while (uf[l] != l) { 47 | l = uf[l]; 48 | } 49 | 50 | // path compaction 51 | while (uf[a] != a) { 52 | int p = uf[a]; 53 | uf[a] = l, a = p; 54 | } 55 | 56 | return l; 57 | } 58 | 59 | void uf_union(int* uf, int x, int y) { 60 | x = uf_find(uf, INT_MAX, x); 61 | y = uf_find(uf, INT_MAX, y); 62 | 63 | // parent should be the smaller number 64 | if (x > y) { 65 | SWAP(int, x, y); 66 | } 67 | 68 | if (x != y) { 69 | uf[y] = x; 70 | } 71 | } 72 | 73 | // Platform layer 74 | #if defined(_WIN32) 75 | #pragma comment(lib, "onecore.lib") 76 | 77 | void* tb_platform_valloc(size_t size) { 78 | return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 79 | } 80 | 81 | void tb_platform_vfree(void* ptr, size_t size) { 82 | VirtualFree(ptr, 0, MEM_RELEASE); 83 | } 84 | 85 | bool tb_platform_vprotect(void* ptr, size_t size, TB_MemProtect prot) { 86 | DWORD protect; 87 | switch (prot) { 88 | case TB_PAGE_NONE:protect = PAGE_NOACCESS; break; 89 | case TB_PAGE_RO: protect = PAGE_READONLY; break; 90 | case TB_PAGE_RW: protect = PAGE_READWRITE; break; 91 | case TB_PAGE_RX: protect = PAGE_EXECUTE_READ; break; 92 | case TB_PAGE_RXW: protect = PAGE_EXECUTE_READWRITE; break; 93 | default: return false; 94 | } 95 | 96 | DWORD old_protect; 97 | return VirtualProtect(ptr, size, protect, &old_protect); 98 | } 99 | 100 | #if NTDDI_VERSION >= NTDDI_WIN10_RS4 101 | void* tb_jit_stack_create(void) { 102 | size_t size = 2*1024*1024; 103 | 104 | // natural alignment stack because it makes it easy to always find 105 | // the base. 106 | MEM_EXTENDED_PARAMETER param = { 107 | .Type = MemExtendedParameterAddressRequirements, 108 | .Pointer = &(MEM_ADDRESS_REQUIREMENTS){ .Alignment = size } 109 | }; 110 | 111 | return VirtualAlloc2(GetCurrentProcess(), NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, ¶m, 1); 112 | } 113 | #endif /* NTDDI_VERSION >= NTDDI_WIN10_RS4 */ 114 | #elif defined(_POSIX_C_SOURCE) 115 | void* tb_platform_valloc(size_t size) { 116 | return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 117 | } 118 | 119 | void tb_platform_vfree(void* ptr, size_t size) { 120 | munmap(ptr, size); 121 | } 122 | 123 | bool tb_platform_vprotect(void* ptr, size_t size, TB_MemProtect prot) { 124 | uint32_t protect; 125 | switch (prot) { 126 | case TB_PAGE_NONE:protect = 0; break; 127 | case TB_PAGE_RO: protect = PROT_READ; break; 128 | case TB_PAGE_RW: protect = PROT_READ | PROT_WRITE; break; 129 | case TB_PAGE_RX: protect = PROT_READ | PROT_EXEC; break; 130 | case TB_PAGE_RXW: protect = PROT_READ | PROT_WRITE | PROT_EXEC; break; 131 | default: return false; 132 | } 133 | 134 | return mprotect(ptr, size, protect) == 0; 135 | } 136 | #endif 137 | -------------------------------------------------------------------------------- /tb/mips/mips_nodes.inc: -------------------------------------------------------------------------------- 1 | // special 2 | R(sll, 0b000000, 0b000000, MIPSImm) 3 | R(srl, 0b000000, 0b000010, MIPSImm) 4 | R(dsll, 0b000000, 0b111000, MIPSImm) 5 | R(dsrl, 0b000000, 0b111010, MIPSImm) 6 | R(sllv, 0b000000, 0b000100, void) 7 | R(srlv, 0b000000, 0b000110, void) 8 | R(srav, 0b000000, 0b000111, void) 9 | R(sra, 0b000000, 0b000011, void) 10 | R(add, 0b000000, 0b100000, void) 11 | R(addu, 0b000000, 0b100001, void) 12 | R(dadd, 0b000000, 0b101100, void) 13 | R(daddu, 0b000000, 0b101101, void) 14 | R(sub, 0b000000, 0b100010, void) 15 | R(subu, 0b000000, 0b100011, void) 16 | R(dsub, 0b000000, 0b101110, void) 17 | R(dsubu, 0b000000, 0b101111, void) 18 | R(and, 0b000000, 0b100100, void) 19 | R(or, 0b000000, 0b100101, void) 20 | R(xor, 0b000000, 0b100110, void) 21 | R(nor, 0b000000, 0b100111, void) 22 | R(jr, 0b000000, 0b001000, void) 23 | // special2 24 | R(mul, 0b011100, 0b000010, void) 25 | R(adds, 0b010001, 0b000000, void) 26 | // i-types 27 | I(addi, 0b001000, MIPSImm) 28 | I(daddi, 0b011000, MIPSImm) 29 | I(andi, 0b001100, MIPSImm) 30 | I(addiu, 0b001001, MIPSImm) 31 | I(daddiu, 0b011001, MIPSImm) 32 | I(ori, 0b001101, MIPSImm) 33 | I(xori, 0b001110, MIPSImm) 34 | I(lui, 0b001111, MIPSImm) 35 | // loads 36 | I(lb, 0b100000, MIPSImm) 37 | I(lw, 0b100011, MIPSImm) 38 | I(ld, 0b110111, MIPSImm) 39 | I(ldc1, 0b110101, MIPSImm) 40 | // stores 41 | I(sb, 0b101000, MIPSImm) 42 | I(sh, 0b101001, MIPSImm) 43 | I(sw, 0b101011, MIPSImm) 44 | I(sdc1, 0b111101, MIPSImm) 45 | // floats 46 | R(madds, 0b010011, 0b100000, void) 47 | R(maddd, 0b010011, 0b100001, void) 48 | // branches 49 | I(beq, 0b000100, void) 50 | I(bne, 0b000101, void) 51 | I(blez, 0b000110, void) 52 | I(bgtz, 0b000111, void) 53 | J(jal, 0b000011, void) 54 | #undef R 55 | #undef I 56 | #undef J 57 | -------------------------------------------------------------------------------- /tb/mips/targets.txt: -------------------------------------------------------------------------------- 1 | This is *sorta* the tutorial on writing codegen targets. 2 | 3 | Section 1: stubbing out the necessary functions. 4 | codegen_impl.h acts as the glue code for a target, copy them 5 | into your code to get started. 6 | 7 | ``` 8 | // in your cpu_target.c 9 | #include "../tb_internal.h" 10 | 11 | #include "../codegen_impl.h" 12 | 13 | // stubbed out 14 | static void init_ctx(Ctx* restrict ctx, TB_ABI abi) { tb_todo(); } 15 | static RegMask isel_node(Ctx* restrict ctx, Tile* dst, TB_Node* n) { tb_todo(); } 16 | static void emit_tile(Ctx* restrict ctx, TB_CGEmitter* e, Tile* t) { tb_todo(); } 17 | static void pre_emit(Ctx* restrict ctx, TB_CGEmitter* e, TB_Node* n) { tb_todo(); } 18 | static void post_emit(Ctx* restrict ctx, TB_CGEmitter* e) { tb_todo(); } 19 | static void disassemble(TB_CGEmitter* e, Disasm* restrict d, int bb, size_t pos, size_t end) { tb_todo(); } 20 | ``` 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tb/objects/macho.c: -------------------------------------------------------------------------------- 1 | #include "macho.h" 2 | 3 | #define WRITE(data, size) (memcpy(&output[write_pos], data, size), write_pos += (size)) 4 | TB_ExportBuffer tb_macho_write_output(TB_Module* m, TB_Arena* dst_arena, const IDebugFormat* dbg) { 5 | const ICodeGen* code_gen = tb_codegen_info(m); 6 | 7 | TB_Emitter string_table = { 0 }; 8 | 9 | CUIK_TIMED_BLOCK("layout section") { 10 | tb_module_layout_sections(m); 11 | } 12 | 13 | // segments 14 | size_t text_size = 0; 15 | MO_Section64 sections[] = { 16 | { 17 | .sectname = { "__text" }, 18 | .segname = { "__TEXT" }, 19 | .align = 2, 20 | .size = text_size, 21 | .flags = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS 22 | } 23 | }; 24 | enum { NUMBER_OF_SECTIONS = COUNTOF(sections) }; 25 | 26 | size_t output_size = sizeof(MO_Header64) 27 | + sizeof(MO_SymtabCmd) 28 | + sizeof(MO_SegmentCmd64) 29 | + (sizeof(MO_Section64) * NUMBER_OF_SECTIONS); 30 | 31 | MO_SegmentCmd64 segment_cmd = { 32 | .header = { 33 | .cmd = LC_SEGMENT_64, 34 | .cmdsize = sizeof(MO_SegmentCmd64) + sizeof(MO_Section64)*NUMBER_OF_SECTIONS, 35 | }, 36 | .segname = { "__TEXT" }, 37 | .nsects = NUMBER_OF_SECTIONS, 38 | .vmsize = text_size, 39 | .fileoff = output_size, 40 | .filesize = text_size 41 | }; 42 | 43 | // layout section data 44 | sections[0].offset = output_size; 45 | output_size += text_size; 46 | 47 | // generate symbol table 48 | MO_SymtabCmd symtab_cmd = { 49 | .header = { 50 | .cmd = LC_SYMTAB, 51 | .cmdsize = sizeof(MO_SymtabCmd) 52 | } 53 | }; 54 | 55 | // count symbols 56 | { 57 | /*symtab_cmd.nsyms += m->compiled_function_count; 58 | 59 | FOR_N(i, 0, m->max_threads) { 60 | size_t external_len = pool_popcount(m->thread_info[i].externals); 61 | symtab_cmd.nsyms += external_len ? external_len - 1 : 0; 62 | } 63 | 64 | FOR_N(i, 0, m->max_threads) { 65 | symtab_cmd.nsyms += pool_popcount(m->thread_info[i].globals); 66 | }*/ 67 | } 68 | 69 | MO_Header64 header = { 70 | .magic = MH_MAGIC_64, 71 | .filetype = MH_OBJECT, 72 | .ncmds = 2, 73 | .sizeofcmds = symtab_cmd.header.cmdsize + segment_cmd.header.cmdsize, 74 | .flags = 0x2000 75 | }; 76 | 77 | // fill in CPU type and subtype based on target 78 | switch (m->target_arch) { 79 | case TB_ARCH_X86_64: header.cputype = CPU_TYPE_X86_64; header.cpusubtype = 3; break; 80 | case TB_ARCH_AARCH64: header.cputype = CPU_TYPE_AARCH64; header.cpusubtype = 0; break; 81 | default: tb_todo(); 82 | } 83 | 84 | //size_t load_cmds_start = sizeof(MO_Header64); 85 | // fprintf(stderr, "TB warning: Mach-O output isn't ready yet :p sorry\n"); 86 | 87 | // Allocate memory now 88 | size_t write_pos = 0; 89 | TB_ExportChunk* chunk = tb_export_make_chunk(dst_arena, output_size); 90 | uint8_t* restrict output = chunk->data; 91 | 92 | // General layout is: 93 | // HEADER 94 | // LOAD COMMAND 95 | // LOAD COMMAND 96 | // ... 97 | // SEGMENT 98 | // SEGMENT 99 | // ... 100 | WRITE(&header, sizeof(header)); 101 | WRITE(&symtab_cmd, sizeof(symtab_cmd)); 102 | WRITE(&segment_cmd, sizeof(segment_cmd)); 103 | WRITE(§ions, sizeof(MO_Section64) * NUMBER_OF_SECTIONS); 104 | 105 | // write_pos = tb_helper_write_section(m, write_pos, &m->text, output, sections[0].offset); 106 | 107 | // emit section contents 108 | FOR_N(i, 0, NUMBER_OF_SECTIONS) { 109 | 110 | } 111 | 112 | // fwrite(string_table.data, string_table.count, 1, f); 113 | 114 | cuik_free(string_table.data); 115 | return (TB_ExportBuffer){ .total = output_size, .head = chunk, .tail = chunk }; 116 | } 117 | -------------------------------------------------------------------------------- /tb/objects/macho.h: -------------------------------------------------------------------------------- 1 | // imma shorten Mach-O into MO 2 | #include "../tb_internal.h" 3 | 4 | typedef struct { 5 | uint32_t magic; 6 | uint32_t cputype; 7 | uint32_t cpusubtype; 8 | uint32_t filetype; 9 | uint32_t ncmds; 10 | uint32_t sizeofcmds; 11 | uint32_t flags; 12 | uint32_t reserved; 13 | } MO_Header64; 14 | 15 | typedef struct { 16 | uint32_t cmd; 17 | uint32_t cmdsize; 18 | } MO_LoadCmd; 19 | 20 | typedef struct { 21 | MO_LoadCmd header; 22 | uint32_t symoff; 23 | uint32_t nsyms; 24 | uint32_t stroff; 25 | uint32_t strsize; 26 | } MO_SymtabCmd; 27 | 28 | typedef struct { 29 | MO_LoadCmd header; 30 | char segname[16]; 31 | uint64_t vmaddr; 32 | uint64_t vmsize; 33 | uint64_t fileoff; 34 | uint64_t filesize; 35 | uint32_t maxprot; 36 | uint32_t initprot; 37 | uint32_t nsects; 38 | uint32_t flags; 39 | } MO_SegmentCmd64; 40 | 41 | typedef struct { 42 | char sectname[16]; 43 | char segname[16]; 44 | uint64_t addr; 45 | uint64_t size; 46 | uint32_t offset; // file offset 47 | uint32_t align; 48 | uint32_t reloff; 49 | uint32_t nreloc; 50 | uint32_t flags; 51 | uint32_t reserved1; 52 | uint32_t reserved2; 53 | uint32_t reserved3; 54 | } MO_Section64; 55 | 56 | // extracted from the header 57 | #define MH_MAGIC_64 0xFEEDFACF 58 | #define MH_CIGAM_64 0xCFFAEDFE 59 | 60 | #define CPU_TYPE_X86_64 0x1000007 61 | #define CPU_TYPE_AARCH64 0x100000C 62 | 63 | #define MH_OBJECT 1 64 | #define MH_EXECUTE 2 65 | #define MH_FVMLIB 3 66 | #define MH_CORE 4 67 | #define MH_PRELOAD 5 68 | #define MH_DYLIB 6 69 | #define MH_DYLINKER 7 70 | #define MH_BUNDLE 8 71 | 72 | /* Constants for the cmd field of all load commands, the type */ 73 | #define LC_SEGMENT 0x1 74 | #define LC_SYMTAB 0x2 75 | #define LC_SYMSEG 0x3 76 | #define LC_THREAD 0x4 77 | #define LC_UNIXTHREAD 0x5 78 | #define LC_LOADFVMLIB 0x6 79 | #define LC_IDFVMLIB 0x7 80 | #define LC_IDENT 0x8 81 | #define LC_FVMFILE 0x9 82 | #define LC_PREPAGE 0xa 83 | #define LC_DYSYMTAB 0xb 84 | #define LC_LOAD_DYLIB 0xc 85 | #define LC_ID_DYLIB 0xd 86 | #define LC_LOAD_DYLINKER 0xe 87 | #define LC_ID_DYLINKER 0xf 88 | #define LC_PREBOUND_DYLIB 0x10 89 | #define LC_SEGMENT_64 0x19 90 | 91 | #define N_EXT 0x01 92 | #define N_SECT 0x0E 93 | 94 | #define NO_SECT 0 95 | 96 | #define S_ATTR_PURE_INSTRUCTIONS 0x80000000 97 | #define S_ATTR_SOME_INSTRUCTIONS 0x00000400 98 | -------------------------------------------------------------------------------- /tb/objects/parse_prelude.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static int find_char(TB_Slice name, char ch) { 5 | // 8 byte chunks 6 | uint64_t* name_u64 = (uint64_t*) name.data; 7 | 8 | uint64_t char_mask = (~(uint64_t)0) / 255 * (uint64_t)(ch); 9 | size_t j = 0; 10 | for (; j < (name.length/8); j++) { 11 | uint64_t x = name_u64[j] ^ char_mask; 12 | x = ((x - 0x0101010101010101ull) & ~x & 0x8080808080808080ull); 13 | 14 | if (x) { 15 | return j*8 + ((__builtin_ffsll(x) / 8) - 1); 16 | } 17 | } 18 | 19 | // final chunk 20 | if (name.length % 8) { 21 | uint64_t mask = UINT64_MAX >> (64 - ((name.length % 8)*8)); 22 | uint64_t x = (name_u64[j] & mask) ^ char_mask; 23 | x = ((x - 0x0101010101010101ull) & ~x & 0x8080808080808080ull); 24 | 25 | if (x) { 26 | return j*8 + ((__builtin_ffsll(x) / 8) - 1); 27 | } 28 | } 29 | 30 | return name.length; 31 | } 32 | 33 | static int ideally_fast_strlen(const char* name) { 34 | // 8 byte chunks 35 | uint64_t* name_u64 = (uint64_t*) name; 36 | uint64_t char_mask = (~(uint64_t)0) / 255 * (uint64_t)(0); 37 | for (size_t j = 0;; j++) { 38 | uint64_t x = name_u64[j] ^ char_mask; 39 | x = ((x - 0x0101010101010101ull) & ~x & 0x8080808080808080ull); 40 | 41 | if (x) { 42 | size_t len = j*8 + ((__builtin_ffsll(x) / 8) - 1); 43 | assert(len == strlen(name)); 44 | return len; 45 | } 46 | } 47 | } 48 | 49 | static long long parse_decimal_int(size_t n, const char* str) { 50 | const char* end = &str[n]; 51 | 52 | int result = 0; 53 | while (str != end) { 54 | if (*str < '0' || *str > '9') break; 55 | 56 | result *= 10; 57 | result += *str - '0'; 58 | str++; 59 | } 60 | 61 | return result; 62 | } 63 | -------------------------------------------------------------------------------- /tb/opt/dbg.h: -------------------------------------------------------------------------------- 1 | // Integrated IR debugger 2 | void tb_integrated_dbg(TB_Function* f, TB_Node* initial_n) { 3 | printf("Entering integrated debugger...\n"); 4 | 5 | TB_Node* cursor = initial_n; 6 | char line[255]; 7 | for (;;) { 8 | // read 9 | printf("> "); 10 | while (fgets(line, 255, stdin) == NULL) {} 11 | 12 | char* nl = strchr(line, '\n'); 13 | if (nl) *nl = 0; 14 | 15 | if (strncmp(line, "in ", sizeof("in ")-1) == 0) { 16 | // move cursor to input 17 | int i = atoi(line + sizeof("in ") - 1); 18 | if (i < 0 || i >= cursor->input_count) { 19 | printf("error: index OoB (index=%d, limit=%d)\n", i, cursor->input_count); 20 | continue; 21 | } 22 | 23 | if (cursor->inputs[i] == NULL) { 24 | printf("error: no input edge at %%%u[%d]\n", cursor->gvn, i); 25 | continue; 26 | } 27 | 28 | printf("moving cursor %%%u to in[%d] -> %%%u\n", cursor->gvn, i, cursor->inputs[i]->gvn); 29 | cursor = cursor->inputs[i]; 30 | } else if (strncmp(line, "out ", sizeof("out ")-1) == 0) { 31 | // move cursor to user 32 | int i = atoi(line + sizeof("out ") - 1); 33 | if (i < 0 || i >= cursor->user_count) { 34 | printf("error: index OoB (index=%d, limit=%d)\n", i, cursor->user_count); 35 | continue; 36 | } 37 | 38 | TB_User* u = &cursor->users[i]; 39 | printf("moving cursor %%%u to out[%d] -> %%%u\n", cursor->gvn, i, USERN(u)->gvn); 40 | cursor = USERN(u); 41 | } else if (strcmp(line, "p") == 0) { 42 | // print cursor 43 | tb_print_dumb_node(NULL, cursor); 44 | printf("\n"); 45 | } else if (strcmp(line, "users") == 0) { 46 | printf("printing users of %%%u:\n", cursor->gvn); 47 | int i = 0; 48 | FOR_USERS(u, cursor) { 49 | printf(" [%d] used by %-4d ", i, USERI(u)); 50 | tb_print_dumb_node(NULL, USERN(u)); 51 | printf("\n"); 52 | i++; 53 | } 54 | } else if (strcmp(line, "debugger") == 0) { 55 | __builtin_debugtrap(); 56 | break; 57 | } else if (strcmp(line, "pf") == 0) { 58 | tb_print_dumb(f); 59 | } else if (strcmp(line, "exit") == 0) { 60 | exit(1); 61 | } else { 62 | printf("error: unknown command: %s\n", line); 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /tb/opt/libcalls.h: -------------------------------------------------------------------------------- 1 | 2 | // this is a peephole lmao 3 | static TB_Node* ideal_libcall(TB_Function* f, TB_Node* n) { 4 | if (n->inputs[2]->type != TB_SYMBOL) { 5 | return NULL; 6 | } 7 | 8 | const TB_Symbol* sym = TB_NODE_GET_EXTRA_T(n->inputs[2], TB_NodeSymbol)->sym; 9 | 10 | bool is_memcpy = strcmp(sym->name, "memcpy") == 0; 11 | bool is_memset = strcmp(sym->name, "memset") == 0; 12 | if (is_memcpy || is_memset) { 13 | // remove from callgraph 14 | FOR_USERS(u, n) { 15 | TB_Node* un = USERN(u); 16 | if (un->type == TB_CALLGRAPH) { 17 | TB_Node* last = un->inputs[un->input_count - 1]; 18 | set_input(f, un, NULL, un->input_count - 1); 19 | set_input(f, un, last, USERI(u)); 20 | un->input_count--; 21 | break; 22 | } 23 | } 24 | 25 | TB_Node* n2 = tb_alloc_node(f, is_memset ? TB_MEMSET : TB_MEMCPY, TB_TYPE_MEMORY, 5, sizeof(TB_NodeMemAccess)); 26 | set_input(f, n2, n->inputs[0], 0); // ctrl 27 | set_input(f, n2, n->inputs[1], 1); // mem 28 | set_input(f, n2, n->inputs[3], 2); // dst 29 | set_input(f, n2, n->inputs[4], 3); // val 30 | set_input(f, n2, n->inputs[5], 4); // size 31 | TB_NODE_SET_EXTRA(n2, TB_NodeMemAccess, .align = 1); 32 | 33 | TB_Node* dst_ptr = n->inputs[2]; 34 | TB_Node* ctrl = n->inputs[0]; 35 | 36 | // returns the destination pointer, convert any users of that to dst 37 | // and CALL has a projection we wanna get rid of 38 | TB_NodeCall* c = TB_NODE_GET_EXTRA_T(n, TB_NodeCall); 39 | 40 | TB_Node* proj0 = USERN(proj_with_index(n, 0)); 41 | subsume_node(f, proj0, ctrl); 42 | TB_Node* proj1 = USERN(proj_with_index(n, 1)); 43 | subsume_node(f, proj1, n2); 44 | TB_Node* proj2 = USERN(proj_with_index(n, 2)); 45 | subsume_node(f, proj2, dst_ptr); 46 | 47 | return dst_ptr; 48 | } 49 | 50 | return NULL; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /tb/opt/mem_opt.h: -------------------------------------------------------------------------------- 1 | // Certain aliasing optimizations technically count as peepholes lmao, these can get fancy 2 | // so the sliding window notion starts to break down but there's no global analysis and 3 | // i can make them incremental technically so we'll go wit it. 4 | static TB_Node* identity_load(TB_Function* f, TB_Node* n) { 5 | // god i need a pattern matcher 6 | // (load (store X A Y) A) => Y 7 | TB_Node *mem = n->inputs[1], *addr = n->inputs[2]; 8 | if (mem->type == TB_STORE && mem->inputs[2] == addr && 9 | n->dt.raw == mem->inputs[3]->dt.raw) { 10 | return mem->inputs[3]; 11 | } 12 | 13 | return n; 14 | } 15 | 16 | static TB_Node* ideal_return(TB_Function* f, TB_Node* n) { 17 | return NULL; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /tb/opt/peep_float.h: -------------------------------------------------------------------------------- 1 | 2 | static TB_Node* ideal_farith(TB_Function* f, TB_Node* n) { 3 | TB_NodeTypeEnum type = n->type; 4 | TB_ASSERT(type == TB_FADD || type == TB_FMUL); 5 | 6 | TB_Node* a = n->inputs[1]; 7 | TB_Node* b = n->inputs[2]; 8 | 9 | // commutativity opts (we want a canonical form). 10 | int ap = node_pos(a); 11 | int bp = node_pos(b); 12 | if (ap < bp || (ap == bp && a->gvn < b->gvn)) { 13 | set_input(f, n, b, 1); 14 | set_input(f, n, a, 2); 15 | return n; 16 | } 17 | 18 | return NULL; 19 | } 20 | 21 | static TB_Node* identity_flt_binop(TB_Function* f, TB_Node* n) { 22 | Lattice* b = latuni_get(f, n->inputs[2]); 23 | Lattice* add_id = lattice_f32_const(f, -0.0f); 24 | if (n->type == TB_FADD && lattice_meet(f, b, add_id) == add_id) { 25 | return n->inputs[1]; 26 | } 27 | 28 | return n; 29 | } 30 | 31 | static Lattice* value_fpneg(TB_Function* f, TB_Node* n) { 32 | Lattice* a = latuni_get(f, n->inputs[1]); 33 | if (a->tag == LATTICE_FLTCON32) { 34 | union { float f; uint32_t i; } x; 35 | x.f = a->_f32; 36 | x.i ^= 0x80000000; 37 | return lattice_f32_const(f, x.f); 38 | } 39 | 40 | return NULL; 41 | } 42 | 43 | static Lattice* value_fpext(TB_Function* f, TB_Node* n) { 44 | Lattice* a = latuni_get(f, n->inputs[1]); 45 | if (a == &TOP_IN_THE_SKY) { return &TOP_IN_THE_SKY; } 46 | if (a->tag == LATTICE_FLTCON32) { 47 | TB_ASSERT(n->dt.type == TB_TAG_F64); 48 | return lattice_f64_const(f, (double) a->_f32); 49 | } 50 | 51 | return NULL; 52 | } 53 | 54 | static Lattice* value_int2float(TB_Function* f, TB_Node* n) { 55 | Lattice* a = latuni_get(f, n->inputs[1]); 56 | if (a == &TOP_IN_THE_SKY) { return &TOP_IN_THE_SKY; } 57 | if (a->tag != LATTICE_INT || a->_int.min != a->_int.max) { return NULL; } 58 | 59 | switch (n->dt.type) { 60 | case TB_TAG_F32: return lattice_f32_const(f, (int64_t) a->_int.min); 61 | case TB_TAG_F64: return lattice_f64_const(f, (int64_t) a->_int.min); 62 | default: tb_todo(); 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /tb/opt/properties.h: -------------------------------------------------------------------------------- 1 | // Keeping track of all kinds of TB node properties 2 | static bool is_associative(TB_NodeTypeEnum type) { 3 | switch (type) { 4 | case TB_ADD: case TB_MUL: 5 | case TB_AND: case TB_XOR: case TB_OR: 6 | return true; 7 | 8 | default: 9 | return false; 10 | } 11 | } 12 | 13 | static bool is_commutative(TB_NodeTypeEnum type) { 14 | switch (type) { 15 | case TB_ADD: case TB_MUL: 16 | case TB_AND: case TB_XOR: case TB_OR: 17 | case TB_CMP_NE: case TB_CMP_EQ: 18 | case TB_FADD: case TB_FMUL: 19 | return true; 20 | 21 | default: 22 | return false; 23 | } 24 | } 25 | 26 | static bool is_mem_access(TB_Node* n) { 27 | switch (n->type) { 28 | case TB_LOAD: 29 | case TB_STORE: 30 | case TB_MEMCPY: 31 | case TB_MEMSET: 32 | return true; 33 | 34 | default: 35 | return false; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tb/opt/verify.h: -------------------------------------------------------------------------------- 1 | 2 | static void b_set(uint32_t* arr, size_t i) { arr[i / 32] |= 1u << (i % 32); } 3 | static void b_reset(uint32_t* arr, size_t i) { arr[i / 32] &= ~(1u << (i % 32)); } 4 | static bool b_get(uint32_t* arr, size_t i) { return arr[i / 32] & (1u << (i % 32)); } 5 | 6 | static void cycle_check(TB_Function* f, TB_Node* n, uint32_t* visited, uint32_t* progress) { 7 | assert(n->gvn < f->node_count); 8 | if (b_get(visited, n->gvn)) { 9 | if (b_get(progress, n->gvn)) { 10 | tb_print(f); 11 | abort(); 12 | } 13 | } else { 14 | b_set(visited, n->gvn); 15 | b_set(progress, n->gvn); 16 | FOR_N(i, 0, n->input_count) if (n->inputs[i]) { 17 | TB_Node* in = n->inputs[i]; 18 | if (in->type != TB_ROOT && in->type != TB_PHI && !cfg_is_region(in)) { 19 | cycle_check(f, in, visited, progress); 20 | } 21 | } 22 | b_reset(progress, n->gvn); 23 | } 24 | } 25 | 26 | void tb_verify(TB_Function* f, TB_Arena* tmp) { 27 | TB_ArenaSavepoint sp = tb_arena_save(tmp); 28 | 29 | TB_Worklist ws = { 0 }; 30 | worklist_alloc(&ws, f->node_count); 31 | 32 | uint32_t* progress = tb_arena_alloc(tmp, ((f->node_count + 31) / 32) * sizeof(uint32_t)); 33 | uint32_t* visited = tb_arena_alloc(tmp, ((f->node_count + 31) / 32) * sizeof(uint32_t)); 34 | memset(progress, 0, ((f->node_count + 31) / 32) * sizeof(uint32_t)); 35 | 36 | // find all nodes 37 | worklist_push(&ws, f->root_node); 38 | for (size_t i = 0; i < dyn_array_length(ws.items); i++) { 39 | TB_Node* n = ws.items[i]; 40 | FOR_USERS(u, n) { worklist_push(&ws, USERN(u)); } 41 | } 42 | 43 | // cycles are allowed for root, phis and regions 44 | for (size_t i = 1; i < dyn_array_length(ws.items); i++) { 45 | TB_Node* n = ws.items[i]; 46 | if (n->type != TB_PHI && !cfg_is_region(n)) { 47 | memset(visited, 0, ((f->node_count + 31) / 32) * sizeof(uint32_t)); 48 | cycle_check(f, n, visited, progress); 49 | } else if (n->type == TB_PHI) { 50 | assert(cfg_is_region(n->inputs[0])); 51 | assert(n->input_count == 1 + n->inputs[0]->input_count); 52 | } 53 | } 54 | 55 | worklist_free(&ws); 56 | tb_arena_restore(tmp, sp); 57 | } 58 | -------------------------------------------------------------------------------- /tb/opt/worklist.h: -------------------------------------------------------------------------------- 1 | void worklist_alloc(TB_Worklist* restrict ws, size_t initial_cap) { 2 | ws->visited_cap = (initial_cap + 63) / 64; 3 | ws->visited = cuik_malloc(ws->visited_cap * sizeof(uint64_t)); 4 | ws->items = dyn_array_create(uint64_t, ws->visited_cap * 64); 5 | FOR_N(i, 0, ws->visited_cap) { 6 | ws->visited[i] = 0; 7 | } 8 | } 9 | 10 | void worklist_free(TB_Worklist* restrict ws) { 11 | cuik_free(ws->visited); 12 | dyn_array_destroy(ws->items); 13 | } 14 | 15 | void worklist_clear_visited(TB_Worklist* restrict ws) { 16 | CUIK_TIMED_BLOCK("clear visited") { 17 | memset(ws->visited, 0, ws->visited_cap * sizeof(uint64_t)); 18 | } 19 | } 20 | 21 | void worklist_clear(TB_Worklist* restrict ws) { 22 | CUIK_TIMED_BLOCK("clear worklist") { 23 | memset(ws->visited, 0, ws->visited_cap * sizeof(uint64_t)); 24 | dyn_array_clear(ws->items); 25 | } 26 | } 27 | 28 | void worklist_remove(TB_Worklist* restrict ws, TB_Node* n) { 29 | uint64_t gvn_word = n->gvn / 64; // which word this ID is at 30 | if (gvn_word >= ws->visited_cap) return; 31 | 32 | uint64_t gvn_mask = 1ull << (n->gvn % 64); 33 | ws->visited[gvn_word] &= ~gvn_mask; 34 | } 35 | 36 | // checks if node is visited but doesn't push item 37 | bool worklist_test(TB_Worklist* restrict ws, TB_Node* n) { 38 | uint64_t gvn_word = n->gvn / 64; // which word this ID is at 39 | if (gvn_word >= ws->visited_cap) return false; 40 | 41 | uint64_t gvn_mask = 1ull << (n->gvn % 64); 42 | return ws->visited[gvn_word] & gvn_mask; 43 | } 44 | 45 | bool worklist_test_n_set(TB_Worklist* restrict ws, TB_Node* n) { 46 | uint64_t gvn_word = n->gvn / 64; // which word this ID is at 47 | 48 | // resize? 49 | if (gvn_word >= ws->visited_cap) { 50 | size_t new_cap = gvn_word + 16; 51 | ws->visited = cuik_realloc(ws->visited, new_cap * sizeof(uint64_t)); 52 | 53 | // clear new space 54 | FOR_N(i, ws->visited_cap, new_cap) { 55 | ws->visited[i] = 0; 56 | } 57 | 58 | ws->visited_cap = new_cap; 59 | } 60 | 61 | uint64_t gvn_mask = 1ull << (n->gvn % 64); 62 | if (ws->visited[gvn_word] & gvn_mask) { 63 | return true; 64 | } else { 65 | ws->visited[gvn_word] |= gvn_mask; 66 | return false; 67 | } 68 | } 69 | 70 | void worklist_push(TB_Worklist* restrict ws, TB_Node* restrict n) { 71 | if (!worklist_test_n_set(ws, n)) { dyn_array_put(ws->items, n); } 72 | } 73 | 74 | TB_Node* worklist_pop(TB_Worklist* ws) { 75 | if (dyn_array_length(ws->items)) { 76 | TB_Node* n = dyn_array_pop(ws->items); 77 | uint64_t gvn_word = n->gvn / 64; 78 | uint64_t gvn_mask = 1ull << (n->gvn % 64); 79 | 80 | ws->visited[gvn_word] &= ~gvn_mask; 81 | return n; 82 | } else { 83 | return NULL; 84 | } 85 | } 86 | 87 | int worklist_count(TB_Worklist* ws) { 88 | return dyn_array_length(ws->items); 89 | } 90 | -------------------------------------------------------------------------------- /tb/tb_platform.h: -------------------------------------------------------------------------------- 1 | // If you're trying to port TB on to a new platform you'll need to fill in these 2 | // functions with their correct behavior. 3 | #pragma once 4 | #include 5 | 6 | //////////////////////////////// 7 | // Virtual memory management 8 | //////////////////////////////// 9 | typedef enum { 10 | TB_PAGE_NONE, 11 | TB_PAGE_RO, 12 | TB_PAGE_RW, 13 | TB_PAGE_RX, 14 | TB_PAGE_RXW, 15 | } TB_MemProtect; 16 | 17 | // This is used for JIT compiler pages or any large scale memory allocations. 18 | void* tb_platform_valloc(size_t size); 19 | void tb_platform_vfree(void* ptr, size_t size); 20 | bool tb_platform_vprotect(void* ptr, size_t size, TB_MemProtect prot); 21 | -------------------------------------------------------------------------------- /tb/x64/x64_nodes.inc: -------------------------------------------------------------------------------- 1 | X(int3, void) 2 | // general integer binops (GPR ins, GPR outs) 3 | X(add, X86MemOp) 4 | X(or, X86MemOp) 5 | X(and, X86MemOp) 6 | X(sub, X86MemOp) 7 | X(xor, X86MemOp) 8 | X(cmp, X86MemOp) 9 | X(mov, X86MemOp) 10 | X(test, X86MemOp) 11 | X(imul, X86MemOp) 12 | // shifts 13 | X(shl, X86MemOp) 14 | X(shr, X86MemOp) 15 | X(sar, X86MemOp) 16 | X(rol, X86MemOp) 17 | X(ror, X86MemOp) 18 | // misc 19 | // sxt_a2d is CDQ and CQO since they sign extend the (e|r)Ax reg to (e|r)Dx 20 | X(lea, X86MemOp) 21 | X(cmovcc, X86MemOp) 22 | X(div, X86MemOp) 23 | X(idiv, X86MemOp) 24 | X(sxt_a2d, X86MemOp) 25 | X(neg, X86MemOp) 26 | // casts 27 | X(movsx8, X86MemOp) 28 | X(movzx8, X86MemOp) 29 | X(movsx16, X86MemOp) 30 | X(movzx16, X86MemOp) 31 | X(movsx32, X86MemOp) 32 | // jump macro-op 33 | X(cmpjcc, X86MemOp) 34 | X(testjcc, X86MemOp) 35 | X(ucomijcc,X86MemOp) 36 | X(AAAHHHH, X86MemOp) 37 | // float/vector ops 38 | X(vmov, X86MemOp) 39 | X(vadd, X86MemOp) 40 | X(vmul, X86MemOp) 41 | X(vsub, X86MemOp) 42 | X(vdiv, X86MemOp) 43 | X(vmin, X86MemOp) 44 | X(vmax, X86MemOp) 45 | X(vxor, X86MemOp) 46 | X(vzero, X86MemOp) 47 | // scalar only float ops 48 | X(ucomi, X86MemOp) 49 | // simple-high level ops 50 | X(static_call, X86Call) 51 | X(call, X86Call) 52 | #undef X 53 | -------------------------------------------------------------------------------- /tests.lua: -------------------------------------------------------------------------------- 1 | local tally = 0 2 | local succ = 0 3 | 4 | function test_single(path, args, flags) 5 | tally = tally + 1 6 | 7 | if not os.execute(string.format("cuik tests/collection/%s %s -o tests/collection/foo.exe", path, flags)) then 8 | print(" NAY COMPILE "..flags) 9 | return 10 | end 11 | 12 | os.execute("tests\\collection\\foo.exe "..args.." > tests/collection/foo.txt") 13 | local exit = os.execute(string.format("git diff -b --no-index tests/collection/foo.txt tests/collection/%s.gold", path)) 14 | if exit ~= 0 then 15 | print(" NAY "..flags) 16 | else 17 | print(" Yay "..flags) 18 | succ = succ + 1 19 | end 20 | end 21 | 22 | function test(path, args, flags) 23 | print("testing "..path.."...") 24 | test_single(path, args, "") 25 | test_single(path, args, "-g") 26 | test_single(path, args, "-O1") 27 | test_single(path, args, "-O1 -g") 28 | end 29 | 30 | test("crc32.c", "") 31 | test("mur.c", "tests/collection/mur.c") 32 | 33 | print(string.format("run %d / %d", succ, tally)) 34 | -------------------------------------------------------------------------------- /tests/addr.c: -------------------------------------------------------------------------------- 1 | /* 2 | #include 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | /*int foo() { 10 | int x = 5 + 5; 11 | int y = 2 * 5 + 5; 12 | assert(x && "foo"); 13 | 14 | return x * y; 15 | } 16 | 17 | 18 | int ptr(int k) { 19 | static int a, b; 20 | return !!(k ? &a : &b); 21 | } 22 | 23 | int vol(volatile int* x) { 24 | *x = 1; 25 | return *x; 26 | }*/ 27 | 28 | #if 0 29 | /*void mem(int* a, int i) { 30 | while (i--) { 31 | a[i] = 0; 32 | } 33 | }*/ 34 | #else 35 | uint32_t murmur3_32(const void* key, size_t len) { 36 | uint32_t h = 0; 37 | 38 | // main body, work on 32-bit blocks at a time 39 | for (size_t i=0;i> 17))*0x1b873593; 45 | h = (((h^k) << 13) | ((h^k) >> 19))*5 + 0xe6546b64; 46 | } 47 | 48 | // load/mix up to 3 remaining tail bytes into a tail block 49 | uint32_t t = 0; 50 | const uint8_t *tail = ((const uint8_t*) key) + 4*(len/4); 51 | switch(len & 3) { 52 | case 3: t ^= tail[2] << 16; 53 | case 2: t ^= tail[1] << 8; 54 | case 1: { 55 | t ^= tail[0] << 0; 56 | h ^= ((0xcc9e2d51*t << 15) | (0xcc9e2d51*t >> 17))*0x1b873593; 57 | } 58 | } 59 | 60 | // finalization mix, including key length 61 | h = ((h^len) ^ ((h^len) >> 16))*0x85ebca6b; 62 | h = (h ^ (h >> 13))*0xc2b2ae35; 63 | return (h ^ (h >> 16)); 64 | } 65 | #endif 66 | 67 | #if 0 68 | float floats(float* x, int i, int j) { 69 | return x[i*4]*2.0f + 1.0f; 70 | } 71 | 72 | uint64_t baz() { return __rdtsc(); } 73 | #endif 74 | 75 | extern int bar(int, int, int, int, int); 76 | int foo3(int x, int y) { 77 | return bar(x, 1, 2, 3, 4) + y; 78 | } 79 | 80 | extern int bar2(int x); 81 | int foo2(int x) { return bar2(x) + 16; } 82 | 83 | void mur(uint32_t* ptr, uint32_t h, uint32_t k) { 84 | k *= 0xcc9e2d51; 85 | k = ((k << 15) | (k >> 17))*0x1b873593; 86 | *ptr = (((h^k) << 13) | ((h^k) >> 19))*5 + 0xe6546b64; 87 | } 88 | 89 | uint64_t foo() { 90 | return 93487194713ull; 91 | } 92 | 93 | uint64_t aaa(uint64_t mask) { 94 | mask = mask ^ (mask << 1); 95 | mask = mask ^ (mask << 1); 96 | mask = mask ^ (mask << 1); 97 | mask = mask ^ (mask << 1); 98 | mask = mask ^ (mask << 1); 99 | mask = mask ^ (mask << 1); 100 | mask = mask ^ (mask << 1); 101 | mask = mask ^ (mask << 1); 102 | // for (int i=0;i<64;i++){mask = mask ^ (mask << 1);} 103 | return mask; 104 | } 105 | -------------------------------------------------------------------------------- /tests/csmith/platform.info: -------------------------------------------------------------------------------- 1 | integer size = 4 2 | pointer size = 8 3 | -------------------------------------------------------------------------------- /tests/csmith/print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | long long foo(long long* x, long long y, long long z) { 6 | return x[0] + x[y] + x[2] + x[1+y]; 7 | } 8 | 9 | #if 0 10 | uint32_t murmur3_32(const void* key, size_t len) { 11 | const uint32_t* key32 = key; 12 | uint32_t h = 0; 13 | 14 | // main body, work on 32-bit blocks at a time 15 | for (size_t i=0;i> 17))*0x1b873593; 19 | h = (((h^k) << 13) | ((h^k) >> 19))*5 + 0xe6546b64; 20 | } 21 | 22 | // load/mix up to 3 remaining tail bytes into a tail block 23 | /* uint32_t t = 0; 24 | const uint8_t *tail = ((const uint8_t*) key) + 4*(len/4); 25 | switch(len & 3) { 26 | case 3: t ^= tail[2] << 16; 27 | case 2: t ^= tail[1] << 8; 28 | case 1: { 29 | t ^= tail[0] << 0; 30 | h ^= ((0xcc9e2d51*t << 15) | (0xcc9e2d51*t >> 17))*0x1b873593; 31 | } 32 | } */ 33 | 34 | // finalization mix, including key length 35 | h = ((h^len) ^ ((h^len) >> 16))*0x85ebca6b; 36 | h = (h ^ (h >> 13))*0xc2b2ae35; 37 | return (h ^ (h >> 16)); 38 | } 39 | 40 | // void matmul(int* dst, int* a, int* b) {} 41 | 42 | uint8_t cmp(uint32_t* key, uint32_t h) { 43 | if (key[1] >= 16) { 44 | return 0; 45 | } 46 | return 1; 47 | } 48 | #endif 49 | 50 | /*int foo(int arg) { 51 | int x = arg/3; 52 | return x*4+arg; 53 | }*/ 54 | 55 | 56 | -------------------------------------------------------------------------------- /tests/donut/donut.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void memset(void* dst, int val, unsigned long long n); 4 | extern int putchar(int ch); 5 | extern double cos(double x); 6 | extern double sin(double x); 7 | 8 | int k;int main(){float 9 | A=0,B=0,i,j,z[1760];char b[ 10 | 1760];printf("\x1b[2J");for(;; 11 | ){memset(b,32,1760);memset(z,0,7040) 12 | ;for(j=0;6.28>j;j+=0.07)for(i=0;6.28 13 | >i;i+=0.02){float c=sin(i),d=cos(j),e= 14 | sin(A),f=sin(j),g=cos(A),h=d+2,D=1/(c* 15 | h*e+f*g+5),l=cos (i),m=cos(B),n=s\ 16 | in(B),t=c*h*g-f* e;int x=40+30*D* 17 | (l*h*m-t*n),y= 12+15*D*(l*h*n 18 | +t*m),o=x+80*y, N=8*((f*e-c*d*g 19 | )*m-c*d*e-f*g-l *d*n);if(22>y&& 20 | y>0&&x>0&&80>x&&D>z[o]){z[o]=D;;;b[o]= 21 | ".,-~:;=!*#$@"[N>0?N:0];}}/*#****!!-*/ 22 | printf("\x1b[H");for(k=0;1761>k;k++) 23 | putchar(k%80?b[k]:10);A+=0.04;B+= 24 | 0.02;}}/*****####*******!!=;:~ 25 | ~::==!!!**********!!!==::- 26 | .,~~;;;========;;;:~-. 27 | ..,--------,*/ 28 | -------------------------------------------------------------------------------- /tests/donut/donut2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | const char string[] = ".,-~:;=!*#$@"; 6 | 7 | short foo(short* a, int i) { 8 | return a[i]; 9 | } 10 | 11 | int k; 12 | int main() { 13 | short ggg = 0; 14 | 15 | float A = 0.0f, B = 0, i, j, z[1760]; 16 | char b[1760]; 17 | 18 | printf("\x1b[2J"); 19 | for (;;) { 20 | memset(b, 32, 1760); 21 | memset(z, 0, 7040); 22 | for (j = 0; 6.28 > j; j += 0.07) { 23 | for (i = 0; 6.28 > i; i += 0.02) { 24 | float c = sin(i); 25 | float d = cos(j); 26 | float e = sin(A), f = sin(j), g = cos(A); 27 | float h = d + 2; 28 | float D = 1 / (c * h * e + f * g + 5); 29 | float l = cos(i), m = cos(B), n = sin (B); 30 | float t = c * h * g - f * e; 31 | 32 | int x = 40 + 30 * D * (l * h * m - t * n); 33 | int y = 12 + 15 * D * (l * h * n + t * m); 34 | int o = x + 80 * y; 35 | 36 | int N = 8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n); 37 | 38 | if (22 > y && y > 0 && x > 0 && 80 > x && D > z[o]) { 39 | z[o] = D; 40 | b[o] = string[N > 0 ? N : 0]; 41 | } 42 | } 43 | } 44 | 45 | printf("\x1b[H"); 46 | for (k = 0; 1761 > k; k++) { 47 | putchar(k % 80 ? b[k] : 10); 48 | } 49 | 50 | A += 0.04; 51 | B += 0.02; 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /tests/doq/example.doq: -------------------------------------------------------------------------------- 1 | This is a normie paragraph 2 | 3 | ``` 4 | // This is a code block 5 | int main() { 6 | 7 | } 8 | ``` 9 | 10 | # Links 11 | 12 | [link text](http://www.google.com) 13 | 14 | ## Woah 15 | 16 | * Lorem ipsum dolor sit amet 17 | * Consectetur adipiscing elit 18 | * Integer molestie lorem at massa 19 | -------------------------------------------------------------------------------- /tests/doq/golden.html: -------------------------------------------------------------------------------- 1 | This is a normie paragraph 2 |

// This is a code block
 3 | int main() {
 4 | 
 5 | }
 6 | 

Links 7 |

link text 8 |

Woah 9 |

  • Lorem ipsum dolor sit amet 10 |
  • Consectetur adipiscing elit 11 |
  • Integer molestie lorem at massa 12 | 13 | -------------------------------------------------------------------------------- /tests/doq/readme.md: -------------------------------------------------------------------------------- 1 | # doq 2 | A small tool for creating single-file html documents 3 | 4 | ## Overview 5 | doq takes an input file resembling markdown with an optional style sheet and 6 | outputs html. Images and text can be embedded through use of the `@filename` 7 | specifier 8 | 9 | ## Usage 10 | ```bash 11 | doq readme.md style.css > readme.html 12 | ``` 13 | 14 | ## License 15 | ``` 16 | Copyright (c) 2020 rxi 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | this software and associated documentation files (the "Software"), to deal in 20 | the Software without restriction, including without limitation the rights to 21 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 22 | of the Software, and to permit persons to whom the Software is furnished to do 23 | so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all 26 | copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | ``` 36 | -------------------------------------------------------------------------------- /tests/gold/bar.txt: -------------------------------------------------------------------------------- 1 | Hello, World! 20 2 | 00000000 f26b8303 e13b70f7 1350f3f4 3 | c79a971f 35f1141c 26a1e7e8 d4ca64eb 4 | 8ad958cf 78b2dbcc 6be22838 9989ab3b 5 | 4d43cfd0 bf284cd3 ac78bf27 5e133c24 6 | 105ec76f e235446c f165b798 030e349b 7 | d7c45070 25afd373 36ff2087 c494a384 8 | 9a879fa0 68ec1ca3 7bbcef57 89d76c54 9 | 5d1d08bf af768bbc bc267848 4e4dfb4b 10 | 20bd8ede d2d60ddd c186fe29 33ed7d2a 11 | e72719c1 154c9ac2 061c6936 f477ea35 12 | aa64d611 580f5512 4b5fa6e6 b93425e5 13 | 6dfe410e 9f95c20d 8cc531f9 7eaeb2fa 14 | 30e349b1 c288cab2 d1d83946 23b3ba45 15 | f779deae 05125dad 1642ae59 e4292d5a 16 | ba3a117e 4851927d 5b016189 a96ae28a 17 | 7da08661 8fcb0562 9c9bf696 6ef07595 18 | 417b1dbc b3109ebf a0406d4b 522bee48 19 | 86e18aa3 748a09a0 67dafa54 95b17957 20 | cba24573 39c9c670 2a993584 d8f2b687 21 | 0c38d26c fe53516f ed03a29b 1f682198 22 | 5125dad3 a34e59d0 b01eaa24 42752927 23 | 96bf4dcc 64d4cecf 77843d3b 85efbe38 24 | dbfc821c 2997011f 3ac7f2eb c8ac71e8 25 | 1c661503 ee0d9600 fd5d65f4 0f36e6f7 26 | 61c69362 93ad1061 80fde395 72966096 27 | a65c047d 5437877e 4767748a b50cf789 28 | eb1fcbad 197448ae 0a24bb5a f84f3859 29 | 2c855cb2 deeedfb1 cdbe2c45 3fd5af46 30 | 7198540d 83f3d70e 90a324fa 62c8a7f9 31 | b602c312 44694011 5739b3e5 a55230e6 32 | fb410cc2 092a8fc1 1a7a7c35 e811ff36 33 | 3cdb9bdd ceb018de dde0eb2a 2f8b6829 34 | 82f63b78 709db87b 63cd4b8f 91a6c88c 35 | 456cac67 b7072f64 a457dc90 563c5f93 36 | 082f63b7 fa44e0b4 e9141340 1b7f9043 37 | cfb5f4a8 3dde77ab 2e8e845f dce5075c 38 | 92a8fc17 60c37f14 73938ce0 81f80fe3 39 | 55326b08 a759e80b b4091bff 466298fc 40 | 1871a4d8 ea1a27db f94ad42f 0b21572c 41 | dfeb33c7 2d80b0c4 3ed04330 ccbbc033 42 | a24bb5a6 502036a5 4370c551 b11b4652 43 | 65d122b9 97baa1ba 84ea524e 7681d14d 44 | 2892ed69 daf96e6a c9a99d9e 3bc21e9d 45 | ef087a76 1d63f975 0e330a81 fc588982 46 | b21572c9 407ef1ca 532e023e a145813d 47 | 758fe5d6 87e466d5 94b49521 66df1622 48 | 38cc2a06 caa7a905 d9f75af1 2b9cd9f2 49 | ff56bd19 0d3d3e1a 1e6dcdee ec064eed 50 | c38d26c4 31e6a5c7 22b65633 d0ddd530 51 | 0417b1db f67c32d8 e52cc12c 1747422f 52 | 49547e0b bb3ffd08 a86f0efc 5a048dff 53 | 8ecee914 7ca56a17 6ff599e3 9d9e1ae0 54 | d3d3e1ab 21b862a8 32e8915c c083125f 55 | 144976b4 e622f5b7 f5720643 07198540 56 | 590ab964 ab613a67 b831c993 4a5a4a90 57 | 9e902e7b 6cfbad78 7fab5e8c 8dc0dd8f 58 | e330a81a 115b2b19 020bd8ed f0605bee 59 | 24aa3f05 d6c1bc06 c5914ff2 37faccf1 60 | 69e9f0d5 9b8273d6 88d28022 7ab90321 61 | ae7367ca 5c18e4c9 4f48173d bd23943e 62 | f36e6f75 0105ec76 12551f82 e03e9c81 63 | 34f4f86a c69f7b69 d5cf889d 27a40b9e 64 | 79b737ba 8bdcb4b9 988c474d 6ae7c44e 65 | be2da0a5 4c4623a6 5f16d052 ad7d5351 66 | 691daa2f -------------------------------------------------------------------------------- /tests/gold/bar.txxt: -------------------------------------------------------------------------------- 1 | Hello, World! 20 2 | 00000000 f26b8303 e13b70f7 1350f3f4 3 | c79a971f 35f1141c 26a1e7e8 d4ca64eb 4 | 8ad958cf 78b2dbcc 6be22838 9989ab3b 5 | 4d43cfd0 bf284cd3 ac78bf27 5e133c24 6 | 105ec76f e235446c f165b798 030e349b 7 | d7c45070 25afd373 36ff2087 c494a384 8 | 9a879fa0 68ec1ca3 7bbcef57 89d76c54 9 | 5d1d08bf af768bbc bc267848 4e4dfb4b 10 | 20bd8ede d2d60ddd c186fe29 33ed7d2a 11 | e72719c1 154c9ac2 061c6936 f477ea35 12 | aa64d611 580f5512 4b5fa6e6 b93425e5 13 | 6dfe410e 9f95c20d 8cc531f9 7eaeb2fa 14 | 30e349b1 c288cab2 d1d83946 23b3ba45 15 | f779deae 05125dad 1642ae59 e4292d5a 16 | ba3a117e 4851927d 5b016189 a96ae28a 17 | 7da08661 8fcb0562 9c9bf696 6ef07595 18 | 417b1dbc b3109ebf a0406d4b 522bee48 19 | 86e18aa3 748a09a0 67dafa54 95b17957 20 | cba24573 39c9c670 2a993584 d8f2b687 21 | 0c38d26c fe53516f ed03a29b 1f682198 22 | 5125dad3 a34e59d0 b01eaa24 42752927 23 | 96bf4dcc 64d4cecf 77843d3b 85efbe38 24 | dbfc821c 2997011f 3ac7f2eb c8ac71e8 25 | 1c661503 ee0d9600 fd5d65f4 0f36e6f7 26 | 61c69362 93ad1061 80fde395 72966096 27 | a65c047d 5437877e 4767748a b50cf789 28 | eb1fcbad 197448ae 0a24bb5a f84f3859 29 | 2c855cb2 deeedfb1 cdbe2c45 3fd5af46 30 | 7198540d 83f3d70e 90a324fa 62c8a7f9 31 | b602c312 44694011 5739b3e5 a55230e6 32 | fb410cc2 092a8fc1 1a7a7c35 e811ff36 33 | 3cdb9bdd ceb018de dde0eb2a 2f8b6829 34 | 82f63b78 709db87b 63cd4b8f 91a6c88c 35 | 456cac67 b7072f64 a457dc90 563c5f93 36 | 082f63b7 fa44e0b4 e9141340 1b7f9043 37 | cfb5f4a8 3dde77ab 2e8e845f dce5075c 38 | 92a8fc17 60c37f14 73938ce0 81f80fe3 39 | 55326b08 a759e80b b4091bff 466298fc 40 | 1871a4d8 ea1a27db f94ad42f 0b21572c 41 | dfeb33c7 2d80b0c4 3ed04330 ccbbc033 42 | a24bb5a6 502036a5 4370c551 b11b4652 43 | 65d122b9 97baa1ba 84ea524e 7681d14d 44 | 2892ed69 daf96e6a c9a99d9e 3bc21e9d 45 | ef087a76 1d63f975 0e330a81 fc588982 46 | b21572c9 407ef1ca 532e023e a145813d 47 | 758fe5d6 87e466d5 94b49521 66df1622 48 | 38cc2a06 caa7a905 d9f75af1 2b9cd9f2 49 | ff56bd19 0d3d3e1a 1e6dcdee ec064eed 50 | c38d26c4 31e6a5c7 22b65633 d0ddd530 51 | 0417b1db f67c32d8 e52cc12c 1747422f 52 | 49547e0b bb3ffd08 a86f0efc 5a048dff 53 | 8ecee914 7ca56a17 6ff599e3 9d9e1ae0 54 | d3d3e1ab 21b862a8 32e8915c c083125f 55 | 144976b4 e622f5b7 f5720643 07198540 56 | 590ab964 ab613a67 b831c993 4a5a4a90 57 | 9e902e7b 6cfbad78 7fab5e8c 8dc0dd8f 58 | e330a81a 115b2b19 020bd8ed f0605bee 59 | 24aa3f05 d6c1bc06 c5914ff2 37faccf1 60 | 69e9f0d5 9b8273d6 88d28022 7ab90321 61 | ae7367ca 5c18e4c9 4f48173d bd23943e 62 | f36e6f75 0105ec76 12551f82 e03e9c81 63 | 34f4f86a c69f7b69 d5cf889d 27a40b9e 64 | 79b737ba 8bdcb4b9 988c474d 6ae7c44e 65 | be2da0a5 4c4623a6 5f16d052 ad7d5351 66 | a2e7e3cf -------------------------------------------------------------------------------- /tests/gold/ex0.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | printf("Hello, World!\n"); 5 | return 0; 6 | } 7 | 8 | -------------------------------------------------------------------------------- /tests/gold/ex1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | main() 5 | { 6 | int arr[2]; 7 | int *p; 8 | 9 | p = &arr[1]; 10 | *p = 0; 11 | printf("%d\n", arr[1]); 12 | } 13 | -------------------------------------------------------------------------------- /tests/gold/ex2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct { int x; int y; } s; 4 | 5 | s v; 6 | 7 | int 8 | main() 9 | { 10 | v.x = 1; 11 | v.y = 2; 12 | printf("%d\n", 3 - v.x - v.y); 13 | } 14 | -------------------------------------------------------------------------------- /tests/gold/ex4.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #if 1 9 | static const char str[] = "Paused my existence sesh to be here"; 10 | int main() { 11 | uint64_t h = xxh_64(str, sizeof(str)-1, 0); 12 | printf("hash = %"PRIu64"\n", h); 13 | return 0; 14 | } 15 | #endif 16 | 17 | uint64_t xxh_64 (const void *key, int len, uint64_t h) { 18 | // primes used in mul-rot updates 19 | uint64_t p1 = 0x9e3779b185ebca87, p2 = 0xc2b2ae3d27d4eb4f, 20 | p3 = 0x165667b19e3779f9, p4 =0x85ebca77c2b2ae63, p5 = 0x27d4eb2f165667c5; 21 | 22 | // inital 32-byte (4x8) wide hash state 23 | uint64_t s[4] = {h+p1+p2, h+p2, h, h-p1}; 24 | 25 | // bulk work: process all 32 byte blocks 26 | uint64_t *k32 = (uint64_t*) key; 27 | for (int i=0; i < (len/32); i+=4) { 28 | uint64_t b[4] = {k32[i+0], k32[i+1], k32[i+2], k32[i+3]}; 29 | for (int j=0;j<4;j++) b[j] = b[j]*p2+s[j]; 30 | for (int j=0;j<4;j++) s[j] = ((b[j] << 31) | (b[j] >> 33))*p1; 31 | } 32 | 33 | // mix 32-byte state down to 8-byte state, initalize to value for short keys 34 | uint64_t s64 = (s[2] + p5); 35 | if (len > 32) { 36 | s64 = ((s[0] << 1) | (s[0] >> 63)) + ((s[1] << 7) | (s[1] >> 57)) + 37 | ((s[2] << 12) | (s[2] >> 52)) + ((s[3] << 18) | (s[3] >> 46)); 38 | for (int i=0; i<4;i++) { 39 | uint64_t ps = (((s[i]*p2) << 31) | ((s[i]*p2) >> 33))*p1; 40 | s64 = (s64 ^ ps)*p1 + p4; 41 | } 42 | } 43 | s64 += len; 44 | 45 | // up to 31 bytes remain, process 0-3 8 byte blocks 46 | uint8_t *tail = ((uint8_t *) key) + ((len/32)*32); 47 | for (int i=0;i < (len & 31) / 8; i++,tail+=8) { 48 | uint64_t b = (*((uint64_t*) tail))*p2; 49 | b = (((b << 31)| (b >> 33))*p1) ^ s64; 50 | s64 = ((b << 27) | (b >> 37))*p1 + p4; 51 | } 52 | 53 | // up to 7 bytes remain, process 0-1 4 byte block 54 | for (int i=0;i< (len & 7) / 4; i++, tail +=4) { 55 | uint64_t b = s64 ^ (*(uint32_t*)tail)*p1; 56 | s64 = ((b << 23) | (b >> 41))*p2 + p3; 57 | } 58 | 59 | // up to 3 bytes remain, process 0-3 1 byte blocks 60 | for (int i=0;i<(len & 3); i++,tail++) { 61 | uint64_t b = s64 ^ (*tail)*p5; 62 | s64 = ((b << 11) | (b >> 53))*p1; 63 | } 64 | 65 | // finalization mix 66 | s64 = (s64 ^ (s64 >> 33))*p2; 67 | s64 = (s64 ^ (s64 >> 29))*p3; 68 | return (s64 ^ (s64 >> 32)); 69 | } 70 | -------------------------------------------------------------------------------- /tests/gold/ex5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const unsigned char * 5 | utf8_simple(const unsigned char *s, long *c) 6 | { 7 | const unsigned char *next; 8 | if (s[0] < 0x80) { 9 | *c = s[0]; 10 | next = s + 1; 11 | } else if ((s[0] & 0xe0) == 0xc0) { 12 | *c = ((long)(s[0] & 0x1f) << 6) | 13 | ((long)(s[1] & 0x3f) << 0); 14 | next = s + 2; 15 | } else if ((s[0] & 0xf0) == 0xe0) { 16 | *c = ((long)(s[0] & 0x0f) << 12) | 17 | ((long)(s[1] & 0x3f) << 6) | 18 | ((long)(s[2] & 0x3f) << 0); 19 | next = s + 3; 20 | } else if ((s[0] & 0xf8) == 0xf0 && (s[0] <= 0xf4)) { 21 | *c = ((long)(s[0] & 0x07) << 18) | 22 | ((long)(s[1] & 0x3f) << 12) | 23 | ((long)(s[2] & 0x3f) << 6) | 24 | ((long)(s[3] & 0x3f) << 0); 25 | next = s + 4; 26 | } else { 27 | *c = -1; // invalid 28 | next = s + 1; // skip this byte 29 | } 30 | if (*c >= 0xd800 && *c <= 0xdfff) 31 | *c = -1; // surrogate half 32 | return next; 33 | } 34 | 35 | int main() { 36 | const unsigned char* src = "零番隊 第三官"; 37 | const unsigned char* start = src; 38 | const unsigned char* end = src + strlen(src); 39 | 40 | while (src != end) { 41 | long c; 42 | src = utf8_simple(src, &c); 43 | printf("[%zu] %ld\n", src - start, c); 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /tests/gold/ex6.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #if 0 6 | int main() { 7 | printf("Hash: %#x\n", murmur3_32("Hello!!!", 8)); 8 | return 0; 9 | } 10 | #endif 11 | 12 | #if 0 13 | uint32_t murmur3_32(const void* key, size_t len); 14 | #else 15 | uint32_t murmur3_32(const void* key, size_t len) { 16 | const uint32_t* key32 = key; 17 | uint32_t h = 0; 18 | 19 | // main body, work on 32-bit blocks at a time 20 | for (size_t i=0;i> 17))*0x1b873593; 26 | h = (((h^k) << 13) | ((h^k) >> 19))*5 + 0xe6546b64; 27 | } 28 | 29 | // load/mix up to 3 remaining tail bytes into a tail block 30 | uint32_t t = 0; 31 | const uint8_t *tail = ((const uint8_t*) key) + 4*(len/4); 32 | switch(len & 3) { 33 | case 3: t ^= tail[2] << 16; 34 | case 2: t ^= tail[1] << 8; 35 | case 1: { 36 | t ^= tail[0] << 0; 37 | h ^= ((0xcc9e2d51*t << 15) | (0xcc9e2d51*t >> 17))*0x1b873593; 38 | } 39 | } 40 | 41 | // finalization mix, including key length 42 | h = ((h^len) ^ ((h^len) >> 16))*0x85ebca6b; 43 | h = (h ^ (h >> 13))*0xc2b2ae35; 44 | return (h ^ (h >> 16)); 45 | } 46 | #endif 47 | -------------------------------------------------------------------------------- /tests/gold/ex7.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | // rpc: LR 4 | 5 | int foo(int x, int y) { 6 | int a = x & y + 1; 7 | int b = x | y + 2; 8 | int c = x ^ y + 3; 9 | int d = x + y + 4; 10 | int e = x - y + 5; 11 | int f = x * y + 6; 12 | int g = x / y + 7; 13 | // int h = x % y; 14 | int z = a + b + c + d + e + f + g; 15 | return z + 8; 16 | } 17 | 18 | float bar(float a) { 19 | a = a + 135.0f; 20 | a = a - 246.0f; 21 | a = a * 69.0f; 22 | a = a / 42.0f; 23 | return -a; 24 | } 25 | -------------------------------------------------------------------------------- /tests/gold/foo.txt: -------------------------------------------------------------------------------- 1 | Hello, World! 20 2 | 00000000 f26b8303 e13b70f7 1350f3f4 3 | c79a971f 35f1141c 26a1e7e8 d4ca64eb 4 | 8ad958cf 78b2dbcc 6be22838 9989ab3b 5 | 4d43cfd0 bf284cd3 ac78bf27 5e133c24 6 | 105ec76f e235446c f165b798 030e349b 7 | d7c45070 25afd373 36ff2087 c494a384 8 | 9a879fa0 68ec1ca3 7bbcef57 89d76c54 9 | 5d1d08bf af768bbc bc267848 4e4dfb4b 10 | 20bd8ede d2d60ddd c186fe29 33ed7d2a 11 | e72719c1 154c9ac2 061c6936 f477ea35 12 | aa64d611 580f5512 4b5fa6e6 b93425e5 13 | 6dfe410e 9f95c20d 8cc531f9 7eaeb2fa 14 | 30e349b1 c288cab2 d1d83946 23b3ba45 15 | f779deae 05125dad 1642ae59 e4292d5a 16 | ba3a117e 4851927d 5b016189 a96ae28a 17 | 7da08661 8fcb0562 9c9bf696 6ef07595 18 | 417b1dbc b3109ebf a0406d4b 522bee48 19 | 86e18aa3 748a09a0 67dafa54 95b17957 20 | cba24573 39c9c670 2a993584 d8f2b687 21 | 0c38d26c fe53516f ed03a29b 1f682198 22 | 5125dad3 a34e59d0 b01eaa24 42752927 23 | 96bf4dcc 64d4cecf 77843d3b 85efbe38 24 | dbfc821c 2997011f 3ac7f2eb c8ac71e8 25 | 1c661503 ee0d9600 fd5d65f4 0f36e6f7 26 | 61c69362 93ad1061 80fde395 72966096 27 | a65c047d 5437877e 4767748a b50cf789 28 | eb1fcbad 197448ae 0a24bb5a f84f3859 29 | 2c855cb2 deeedfb1 cdbe2c45 3fd5af46 30 | 7198540d 83f3d70e 90a324fa 62c8a7f9 31 | b602c312 44694011 5739b3e5 a55230e6 32 | fb410cc2 092a8fc1 1a7a7c35 e811ff36 33 | 3cdb9bdd ceb018de dde0eb2a 2f8b6829 34 | 82f63b78 709db87b 63cd4b8f 91a6c88c 35 | 456cac67 b7072f64 a457dc90 563c5f93 36 | 082f63b7 fa44e0b4 e9141340 1b7f9043 37 | cfb5f4a8 3dde77ab 2e8e845f dce5075c 38 | 92a8fc17 60c37f14 73938ce0 81f80fe3 39 | 55326b08 a759e80b b4091bff 466298fc 40 | 1871a4d8 ea1a27db f94ad42f 0b21572c 41 | dfeb33c7 2d80b0c4 3ed04330 ccbbc033 42 | a24bb5a6 502036a5 4370c551 b11b4652 43 | 65d122b9 97baa1ba 84ea524e 7681d14d 44 | 2892ed69 daf96e6a c9a99d9e 3bc21e9d 45 | ef087a76 1d63f975 0e330a81 fc588982 46 | b21572c9 407ef1ca 532e023e a145813d 47 | 758fe5d6 87e466d5 94b49521 66df1622 48 | 38cc2a06 caa7a905 d9f75af1 2b9cd9f2 49 | ff56bd19 0d3d3e1a 1e6dcdee ec064eed 50 | c38d26c4 31e6a5c7 22b65633 d0ddd530 51 | 0417b1db f67c32d8 e52cc12c 1747422f 52 | 49547e0b bb3ffd08 a86f0efc 5a048dff 53 | 8ecee914 7ca56a17 6ff599e3 9d9e1ae0 54 | d3d3e1ab 21b862a8 32e8915c c083125f 55 | 144976b4 e622f5b7 f5720643 07198540 56 | 590ab964 ab613a67 b831c993 4a5a4a90 57 | 9e902e7b 6cfbad78 7fab5e8c 8dc0dd8f 58 | e330a81a 115b2b19 020bd8ed f0605bee 59 | 24aa3f05 d6c1bc06 c5914ff2 37faccf1 60 | 69e9f0d5 9b8273d6 88d28022 7ab90321 61 | ae7367ca 5c18e4c9 4f48173d bd23943e 62 | f36e6f75 0105ec76 12551f82 e03e9c81 63 | 34f4f86a c69f7b69 d5cf889d 27a40b9e 64 | 79b737ba 8bdcb4b9 988c474d 6ae7c44e 65 | be2da0a5 4c4623a6 5f16d052 ad7d5351 66 | 691daa2f -------------------------------------------------------------------------------- /tests/gold/test.lua: -------------------------------------------------------------------------------- 1 | -- silly little OS detection 2 | local is_windows = package.config:sub(1,1) == "\\" 3 | 4 | local x = {} 5 | if is_windows then 6 | local cmd = io.popen("dir *.c /B") 7 | for c in cmd:lines() do 8 | x[#x + 1] = c 9 | end 10 | cmd:close() 11 | else 12 | local cmd = io.popen("find *.c -maxdepth 1") 13 | for c in cmd:lines() do 14 | x[#x + 1] = c 15 | end 16 | cmd:close() 17 | end 18 | 19 | local exe_name = "a" 20 | if not is_windows then 21 | exe_name = "./a.out" 22 | end 23 | 24 | for i=1,#x do 25 | print("Testing... "..x[i]) 26 | print("* Cuik") 27 | os.execute(string.format("cuik %s && %s > cuik.txt", x[i], exe_name)) 28 | print("* Clang") 29 | os.execute(string.format("clang %s && %s > clang.txt", x[i], exe_name)) 30 | print("* Diff") 31 | os.execute("git diff --no-index cuik.txt clang.txt") 32 | end 33 | 34 | for i=1,#x do 35 | print("Testing optimized... "..x[i]) 36 | print("* Cuik") 37 | os.execute(string.format("cuik %s -O && %s > cuik.txt", x[i], exe_name)) 38 | print("* Clang") 39 | os.execute(string.format("clang %s && %s > clang.txt", x[i], exe_name)) 40 | print("* Diff") 41 | os.execute("git diff --no-index cuik.txt clang.txt") 42 | end 43 | --------------------------------------------------------------------------------