├── LICENSE.md ├── Makefile ├── README.md ├── documentation ├── API.md ├── C Embed Tutorial.md ├── Compiler Specifications.md ├── ISA.md ├── Tagha ASM Reference.md ├── Tagha Profiling Results Clang-LLVM June 29 2020.txt ├── Tagha Profiling Results Clang-LLVM Sept 3 2020.txt ├── Tagha Profiling Results GCC June 29 2020.txt ├── Tagha Profiling Results GCC Sept 3 2020.txt ├── Tagha Testing and Results Doc.txt ├── Tagha Toolchain API.md ├── dynamic_linking_in_tagha.png └── taghalogo.png ├── tagha ├── Makefile ├── allocators │ ├── bistack │ │ ├── Makefile │ │ ├── bistack.c │ │ ├── bistack.h │ │ ├── test_bistack.c │ │ └── valgrind.sh │ ├── cache │ │ ├── Makefile │ │ ├── cache.c │ │ └── cache.h │ ├── mempool │ │ ├── Makefile │ │ ├── mempool.c │ │ ├── mempool.h │ │ ├── test_mempool.c │ │ └── valgrind.sh │ ├── objpool │ │ ├── Makefile │ │ ├── objpool.c │ │ ├── objpool.h │ │ ├── test_objpool.c │ │ └── valgrind.sh │ └── region │ │ ├── Makefile │ │ ├── region.c │ │ ├── region.h │ │ ├── test_region.c │ │ └── valgrind.sh ├── harbol_common_defines.h ├── harbol_common_includes.h ├── tagha.c └── tagha.h ├── tagha_libc ├── Makefile ├── tagha_ctype.c ├── tagha_libc.tasm ├── tagha_stdio.c ├── tagha_stdlib.c ├── tagha_string.c └── tagha_time.c ├── tagha_toolchain ├── assembler │ ├── Makefile │ ├── assembler.c │ ├── test.tasm │ └── valgrind.sh ├── disassembler │ ├── Makefile │ ├── disassembler.c │ └── valgrind.sh ├── instr_gen.h ├── libharbol.zip ├── module_gen.h └── module_info.h ├── test_asm ├── test_3d_vecs.tasm ├── test_3d_vecs.tbc ├── test_dynamiclinking.tasm ├── test_dynamiclinking.tbc ├── test_dynamicloading.tasm ├── test_dynamicloading.tbc ├── test_factorial.tasm ├── test_factorial.tbc ├── test_fib34.tasm ├── test_fib34.tbc ├── test_fib40.tasm ├── test_fib40.tbc ├── test_funcptr.tasm ├── test_funcptr.tbc ├── test_global.tasm ├── test_global.tbc ├── test_int_cmp.tasm ├── test_int_cmp.tbc ├── test_invalid_memory.tasm ├── test_invalid_memory.tbc ├── test_loop.tasm ├── test_loop.tbc ├── test_native_number.tasm ├── test_native_number.tbc ├── test_ptr.tasm ├── test_ptr.tbc ├── test_selfcall.tasm ├── test_selfcall.tbc ├── test_simd.tasm ├── test_simd.tbc ├── test_str_cmp.tasm └── test_str_cmp.tbc └── test_driver.c /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2018-2020] Assyrianic aka Nergal aka Nirgal aka 'Khanno Hanna' 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | #CC = clang-9 3 | CFLAGS = -Wextra -Wall -std=c99 -s -O2 -mtune=native -march=native 4 | TFLAGS = -Wextra -Wall -std=c99 -g -O2 -mtune=native -march=native 5 | PFLAGS = -Wextra -Wall -std=c99 -pg -O2 -mtune=native -march=native 6 | # -static 7 | 8 | taghatest: 9 | $(CC) $(CFLAGS) test_driver.c -L. -ltagha -o taghatest 10 | 11 | debug: 12 | $(CC) $(TFLAGS) test_driver.c -L. -ltagha -o taghatest 13 | 14 | clean: 15 | $(RM) *.o 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tagha 2 | 3 | ![Tagha Logo by Noxabellus and modified by Khanno](https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/intermediary/f/efc8ece3-f4a3-4477-8ebb-cb9595fb9e58/dcx0vh7-7c8a2027-14e9-48a9-a0e9-638260f44433.png/v1/fill/w_400,h_462,strp/tagha_virtual_machine_logo_by_assyrianic_dcx0vh7-fullview.png) 4 | 5 | 6 | [![star this repo](http://githubbadges.com/star.svg?user=assyrianic&repo=tagha&style=plastic)](https://github.com/assyrianic/tagha) 7 | [![forks repo](http://githubbadges.com/fork.svg?user=assyrianic&repo=tagha&style=plastic)](https://github.com/assyrianic/tagha) 8 | 9 | ## Introduction 10 | 11 | **Tagha** is a minimal, fast, memory-safe, self-contained, register-based virtual machine runtime environment designed to execute C code that's compiled to bytecode scripts. 12 | 13 | ### Rationale: 14 | 15 | + 1. give C binary portability. 16 | + 2. be fast. 17 | + 3. be open-source. 18 | + 4. have a runtime environment without dependencies (beyond libc of course) 19 | + 5. be portable and embeddable for any program and platform. 20 | + 6. be small and minimal. ![file size](https://img.shields.io/github/repo-size/assyrianic/tagha.svg?style=plastic) 21 | + 7. be memory safe. 22 | + 8. be easy for compilers to target. 23 | 24 | 25 | ### Features 26 | 27 | * Tagha is 64-bit as registers & memory addresses are 64-bit. (will run significantly slower (~25%) on 32-bit OS's and drastically slower (~4x-5x) on 32-bit systems.) 28 | * Self-contained, everything the codebase needs is packaged together and there's no dependencies except for some C standard library API. 29 | * Tagha codebase has its own, open source implementation of libc for scripts to use (INCOMPLETE). 30 | * Register-based virtual machine that handles immediate values, register, and memory operations. 31 | * Supports 1, 2, 4, and 8 byte operations. 32 | * Utilize up to 256 **general purpose registers** _per_ function!. 33 | * Floats and doubles are supported (can be compiled without and also can be compiled with only one or the other). 34 | * Uses computed gotos (ones that use a `void*`) which is 20%-25% faster than using a switch+loop construct {[citation](http://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables)}. 35 | * Embeddable and easy to embed. 36 | * Tagha allocates only what is needed and performs no garbage collection during runtime. 37 | * Scripts can call host-defined functions (Native Interface). 38 | * Host Applications can call script functions and retrieve return values from the script function invocation. 39 | * Scripts (by exporting Tagha's own API) can manually load other scripts as dynamic libraries. 40 | * VM runs as little-endian format (only). 41 | * Small. The entire runtime as a static library is less than 50kb. 42 | * Tagha's entire VM code is **about 1k lines of code**! 43 | * Speed, Tagha is very fast for a virtual machine that does not use a JIT -- Check out the documentation for profiling results. 44 | * Memory safe, Tagha sandboxes scripts by blocking memory operations that were not allocated by the script's own memory allocator. 45 | * Tagha Assembler - transforms human readable bytecode into binary bytecode. 46 | * Tagha Bytecode Builder - header-only encoder functions to help with lower level bytecode creation. 47 | * Tagha Module Builder - header-only library that helps create a full-fledged Tagha Module script. 48 | 49 | 50 | ## Usage 51 | 52 | ```c 53 | #include "tagha.h" 54 | 55 | int main(int argc, char *argv[]) 56 | { 57 | /// make our script instance. 58 | struct TaghaModule *script = tagha_module_new_from_file("my_tbc_script.tbc"); 59 | 60 | /// call 'main' with no command-line arguments. 61 | const int32_t result = tagha_module_run(script, 0, NULL); 62 | 63 | /// do something with 'result'. 64 | 65 | /// clean up script. sets 'script' to NULL. 66 | tagha_module_free(&script); 67 | } 68 | ``` 69 | 70 | ## Contributing 71 | 72 | To submit a patch, file an issue and/or hit up a pull request. 73 | 74 | ## Help 75 | 76 | If you need help or have any question, simply file an issue with **\[HELP\]** in the title. 77 | 78 | ## Installation 79 | 80 | ### Requirements 81 | 82 | C99 compliant compiler and libc implementation with the following headers available: 83 | * stdlib.h 84 | * stdio.h 85 | * stdbool.h 86 | * inttypes.h 87 | * string.h 88 | * stdarg.h 89 | * limits.h 90 | * float.h 91 | It is preferable that you compile using GCC or Clang/LLVM. 92 | 93 | ### Installation 94 | 95 | To embed Tagha into your application, you must build Tagha as either a static or shared library. 96 | 97 | Create a directory with the repo, change directory to the `tagha` folder and run `make` which will create a static library of Tagha. To create a shared library version, run `make shared`. To clean up the object files, run `make clean`. 98 | 99 | If for any reason, you need to create a debug version, run `make debug` to create a debug version of the static library and `make debug_shared` for a debug version of the shared library. 100 | 101 | Once you've built Tagha as a library, include "tagha.h" into your C or C++ application. 102 | 103 | If you need help in embedding, check out the C tutorial on embedding in the documentation. 104 | 105 | 106 | To compile `.tasm` scripts to `.tbc` executables, you'll need to build the Tagha Assembler! Change directory into the `assembler` directory and run `make` which will build the Tagha assembler executable (named `tagha_assembler`). 107 | 108 | ### How to create TBC Scripts with Tagha ASM. 109 | 110 | Scripts can be created by supplying a `.tasm` file as a command-line argument to the tagha assembler. Here's an example: 111 | 112 | ```sh 113 | ./tagha_assembler 'script.tasm' 114 | ``` 115 | 116 | If there are no errors reported, a `.tbc` binary, with the same filename as the script, will be produced. Now that you have a usable tbc script, you can run it from your C or C++ application. 117 | 118 | To execute tbc scripts, embed Tagha into your C or C++ application (or build the example host application) and direct your application to the file directly or a special directory just for tbc scripts. 119 | 120 | 121 | ### Using the Tagha Disassembler. 122 | Also as part of the Tagha Toolchain, there includes a disassembler which functions exactly the same as the Tagha Assembler. The only difference between the assembler and the disassembler is you feed a `.tbc` script to the disassembler and it spits out a `.tasm` file. 123 | 124 | The `.tasm` file can be re-assembled by the assembler but requires fixing jump labels if any. 125 | 126 | 127 | ### Configuration 128 | 129 | Tagha can be configured in consideration with floating point support. 130 | If you wish to completely remove floating point support, go into "tagha.h" and comment out these two macros: 131 | ```c 132 | #define TAGHA_FLOAT32_DEFINED /// allow tagha to use 32-bit floats 133 | #define TAGHA_FLOAT64_DEFINED /// allow tagha to use 64-bit floats 134 | ``` 135 | 136 | If you require one of these specific float types, you can comment out the other. 137 | If you need to re-enable floating point support for all types, simply uncomment the defines. 138 | 139 | Note: Changing the header file requires that you recompile the Tagha library for the changes to take effect on the runtime. 140 | 141 | ### Testing 142 | If you wish to build and test the Tagha code base, compile `test_driver.c` with either the shared or static Tagha library, link with Tagha's libc implementation, compile the Tagha assembler and compile the testing .tasm scripts in the `test_asm` folder, and run the generated .tbc scripts. 143 | 144 | ## Credits 145 | 146 | * Khanno Hanna - main developer of Tagha. 147 | * Id Software - developers of Quake 3 Virtual Machine, which inspired Tagha's creation. 148 | * Noxabellus - helping out with development, design, logo designer, & accidentally giving me ideas. 149 | * Animu/Megumazing - helping out with development & design. 150 | 151 | ## License 152 | [![License](https://img.shields.io/github/license/assyrianic/tagha.svg?label=License&style=plastic)](https://github.com/assyrianic/tagha) 153 | 154 | This project is licensed under MIT License. 155 | 156 | 157 | ## FAQ 158 | * Q: _**Why not just pick a scripting language?**_ 159 | * A: You're right. Any developer could simply choose an existing scripting language and its implementation, but not all developers want to use a scripting language and they could have various reasons like performance, syntax, maybe the runtime is too bloated. Secondly, not all developers might know the language, are comfortable with it, or don't preffer it. Perhaps for the sake of consistency with the code base, they want the entire code to be in one language. After all, to be able to utilize the scripting language, you'd need to learn it as well as learning the exposed API of the host app. My point is, there's a myriad of reasons to choose (or not to choose) a scripting language. 160 | 161 | * Q: _**Ok, then why not use a dynamic linking/shared library module/plugin system?**_ 162 | * A: Same answer as before, any developer could choose such a system over a scripting language as well. The benefits of this is having the native speed of the application's implementation language while still being extendable/modifiable. However the drawbacks to a shared library plugin system is that you need to build the plugins for every architecture and OS for the shared plugins to run properly. On Windows OS' this isn't as big a deal but Linux ABIs also use the OS as a factor. Thus, having portable programs isn't easy to implement with using a plugin system without taking ABI in account. 163 | 164 | * Q: _**Then why use Tagha at all?**_ 165 | * A: You should use Tagha if you want a bytecode runtime environment that is fast, minimal, very small memory footprint, completely self-contained within a single (static or shared) library, open source, and permissive in licensing with absolutely no strings attached. 166 | 167 | * Q: _**Why implement Tagha in C and not C++?**_ 168 | * A: The design choices for Tagha was to be minimal, fast, and with little-to-no dependencies except for a few C standard library functions. To achieve this, I needed to use C which allowed me to manipulate memory as fast and seamless as possible. I'm aware C++ allows me to manipulate memory but it's not without trouble. 169 | 170 | * Q: _**Can Tagha be used to implement any language?**_ 171 | * A: In theory yes; in practice, yes but not perfectly. If we take Lua's example, Lua values are entirely pointers to a tagged union type in which the types are either a float value, string, or table/hashmap. Tagha is designed as a runtime environment for C code that is compiled to bytecode, not Lua. Lua as a language can be supported but features like tables have to be translated into more lower level operations or implemented as natives. Since Tagha has the bare minimum features to be a C runtime, that can be adapted to other languages although it would require more effort. 172 | 173 | * Q: _**Will you implement a JIT in the future?**_ 174 | * A: Maybe. I will likely not implement a JIT but I could make a compromise by adding JIT compiling support. If I were to seriously consider implementing a JIT, I'd likely use the MIR JIT Compiler since it's also in C and is planned as a standalone library, easy to use JIT compilation library. 175 | -------------------------------------------------------------------------------- /documentation/Compiler Specifications.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | The Tagha Runtime Environment is not meant to be tied down to a single compiler. Similar to how C and C++ have many compilers for them. Tagha is only meant to be a minimal runtime that has the bare bones environment necessary to run bytecode compiled from C code. 3 | 4 | 5 | # Tagha Required C Specifications 6 | 7 | * `double` and `long double` should always be 8 bytes in data size. 8 | 9 | * `float` and `double` are as defined by the IEEE. 10 | 11 | * `long`, `size_t`, and `long long` should be 8 bytes in size. 12 | 13 | * all binary math operations assume both operands are the same size. 14 | 15 | * `argc` and `argv` are implementation-defined for Tagha, so `main` could have any type of parameters as necessary to script devs. 16 | 17 | * if `argv` contains pointers that are not owned by the script's memory allocator, then **the VM _will_ throw a runtime exception if the bytecode dereferences them**. Keep this in mind when passing objects to the scripts. A workaround for this is to allocate from the script's own runtime heap, copy data to it, and pass that to `main` (check example in API.md). 18 | -------------------------------------------------------------------------------- /documentation/Tagha ASM Reference.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | This article teaches about the Tagha Assembly language that's compiled by the Tagha Assembler into a working .tbc script 3 | 4 | # Comments 5 | In Tagha Assembly, there are single and multi-line comments. 6 | `;` is for single line comments and C style `/* */` multi-line comments. 7 | 8 | # Directives 9 | In Tagha Assembly, the assembly directives start with a dollar sign `$`. Here's a break down of all the Tagha Assembly directives. 10 | 11 | #### opstack_size 12 | TBC scripts, by default, are alloted an operand stack size of 4kb. 13 | the `$opstack_size` directive allows a programmer (or generating compiler) to change the default operand stack size given to scripts. 14 | The directive only takes one argument which is either a decimal, `0x` hexadecimal, `0b` binary, or `0#` octal argument. 15 | 16 | Example Tagha Assembly code usage: 17 | ```asm 18 | $opstack_size 10 ;; sets the stack size to 10 * 8 bytes (decimal) 19 | $opstack_size 0x10 ;; sets the stack size to 16 * 8 bytes (hexadecimal) 20 | $opstack_size 010 ;; sets the stack size to 8 * 8 bytes (octal) 21 | $opstack_size 0b10 ;; sets the stack size to 2 * 8 bytes (binary) 22 | ``` 23 | 24 | #### callstack_size 25 | Used the same way as `$opstack_size` but for a module's call stack size. 26 | ```asm 27 | $callstack_size 10 ;; sets the stack size to 10 * 8 bytes (decimal) 28 | $callstack_size 0x10 ;; sets the stack size to 16 * 8 bytes (hexadecimal) 29 | $callstack_size 010 ;; sets the stack size to 8 * 8 bytes (octal) 30 | $callstack_size 0b10 ;; sets the stack size to 2 * 8 bytes (binary) 31 | ``` 32 | 33 | #### heap_size 34 | TBC scripts utilize a memory allocator which stores the stack and data tables. After allocating the stack and data tables, what's left of the allocator can be used internally as heap data. 35 | 36 | The `$heap_size` directive allows a programmer (or generating compiler) to leave extra heap memory for scripts to use. 37 | The directive only takes one argument which is either a decimal, `0x` hexadecimal, `0b` binary, or `0#` octal argument. 38 | 39 | Example Tagha Assembly code usage: 40 | ```asm 41 | $heap_size 10 ;; sets the heap size to 10 bytes (decimal) 42 | $heap_size 0x10 ;; sets the heap size to 16 bytes (hexadecimal) 43 | $heap_size 010 ;; sets the heap size to 8 bytes (octal) 44 | $heap_size 0b10 ;; sets the heap size to 2 bytes (binary) 45 | ``` 46 | 47 | #### global 48 | Since global variables in a tbc script are designed to be accessible by the host application, global variable require to be named and defined through Tagha Assembly code. 49 | 50 | the `$global` directive has different arguments depending on the type of global variable. 51 | 52 | For strings (anonymous or named), the arguments are... 53 | * arg 1 - the name of the string. 54 | * arg 2 - the actual string data. 55 | 56 | Example Tagha Assembly code usage of defining a string. 57 | ```asm 58 | $global str1, "hello world\n" ;; escape chars are handled. 59 | ``` 60 | 61 | For every other variable, there's 2 kinds of possible arguments: 62 | Like the string version, you must have a name for the variable as well as the byte size of the variable. 63 | for the 3rd argument (the actual data). You have two (limited) options. 64 | 65 | * the data for a global var must have each data count listed by size and the data and the count must be equal to the bytesize of the global var. 66 | 67 | * if you're making a large global variable (struct or array or something) and you only want its data to be zero, you simply put a `0` as the 3rd argument without requiring to give a size. 68 | 69 | ```asm 70 | $global i, 12, 0 71 | $global n, 4, byte 0, byte 0, byte 0, byte 0x40 72 | $global m, 4, half 0, half 0x4000 ;; same as what n does, including data 73 | $global c, 4, long 0x40000000 ;; same as n does, including data 74 | $global ptr, 8, word 0 ;; 'word' is redundant since we're only zeroing. 75 | ``` 76 | 77 | #### native 78 | the `$native` directive exists to expose the C or C++ native to the script. The directive only takes one argument which is the name of the native you want to expose. 79 | 80 | Here's an example Tagha Assembly code that exposes `malloc` from `stdlib.h` to a script: 81 | ```asm 82 | $native malloc 83 | ``` 84 | 85 | #### extern 86 | the `$extern` directive exists to do dynamic linking between scripts. The directive only takes one argument which is the name of the external function to link. 87 | 88 | Here's an example of how it's used: 89 | ```asm 90 | $extern function_name 91 | ``` 92 | 93 | # Code Syntax 94 | ### Functions 95 | In Tagha Assembly, operations can _only_ exist within function blocks. After defining the function name, you must follow the name with a beginning curly brace `{`. Once a function's code is finished, you must end the block with, you guessed it, an ending curly brace `}`. 96 | 97 | Let's create an example by defining a function and call it `main`! Since `main` is a function, we must give it a `ret` opcode so that the virtual machine can destroy the execution frame after `main` is done calling! 98 | 99 | ```asm 100 | ;; create "main" and its code block! 101 | main: { ;; : colon is optional here! 102 | ret 103 | } 104 | ``` 105 | 106 | It's necessary at this point to bring up that the rest of this article assumes that you've read the [Tagha Instruction Set article](https://github.com/assyrianic/Tagha-Virtual-Machine/wiki/Tagha's-Instruction-Set-Opcodes)! 107 | 108 | Alright, we've created our `main` function but it doesn't do jack. Now let's make it do jack. 109 | Before we start writing code, let me introduce you to registers in Tagha. 110 | 111 | ### Registers 112 | Registers are special locations in a CPU that store data. All registers in Tagha are 64-bit and can be used for both integer, floating point, & memory based operations. Registers are vital is much of Tagha's operations as they're useful in many optimization cases. 113 | 114 | **Note**: all the register names start with an `r` to denote that it's a register! 115 | 116 | Tagha allows you to use up to 256 registers in a given execution frame. All that is necessary to save register space is by allocating and reducing the amount of registers available in each function execution context. 117 | 118 | ### Immediate/Constant Values 119 | One of the most common ways to input data is through constants or immediate values. Immediate values are always encoded as 8 bytes for the sake of convenience. Just like the `$opstack_size` or `$heap_size` directive, an immediate value can be numbered as a decimal, hexadecimal, binary, or octal value! 120 | 121 | ### Register Indirection / Register-Memory 122 | As mentioned in the registers portion, registers data can be used as memory addresses. 123 | Dereferencing a register as a memory address has the following format: 124 | ```asm 125 | [register +/- constant_offset] 126 | ``` 127 | 128 | Register-Memory operations also gives you the ability to add (or subtract) an offset to the address you're dereferencing. The offset is calculated in terms of bytes (again Tagha is byte-addressable). 129 | 130 | Here's an example where we assume `r0` contains a memory address and we want to dereference it as an int16: 131 | ```asm 132 | st2 [r0 + 0] ;; store 2 bytes 133 | ``` 134 | 135 | Conveniently, you don't need the zero since it will not change the address given 136 | ```asm 137 | st2 [r0] ;; store 2 bytes 138 | ``` 139 | but you do need to do arithmetic for any number larger than 0: 140 | ```asm 141 | st2 [r0+2] 142 | ``` 143 | which is necessary for cases such as arrays or structs. 144 | 145 | ### Putting It All Together 146 | Now that we've gotten the 3 data operations covered, we can go back to our `main` example. Let's create an example where we do something like `1 + 2`! 147 | 148 | ```asm 149 | ;; create "main" and its code block! 150 | main: { 151 | alloc 2 ;; allocate 2 registers 152 | movi r0, 2 ;; set r0's value to 2 153 | movi r1, 1 ;; set r1's value to 1 154 | add r0, r1 ;; add r1 to r0, modifying r0. r0 is now "3" 155 | ret 156 | } 157 | ``` 158 | 159 | 160 | ### Jump Labels 161 | So far the only type of label you've been exposed to is the `function` label but that's only for defining functions. 162 | One of the most prized and valuable constructs in any programming language is the control flow controls like `if-else` statements, `switch`, and `while` or `for` loops. 163 | 164 | In order to implement these control flow tools, we need the ability to label areas of flow! 165 | 166 | In Tagha ASM, jump labels are defined using a `.` dot sign! 167 | 168 | Here's an example: 169 | ```asm 170 | main { 171 | .exit 172 | ret 173 | } 174 | ``` 175 | 176 | the jump label so far is pretty useless, all it does is point to where the `ret` opcode is. 177 | Let's take all that we've learned and create real, usable code! 178 | 179 | Let's create a `main` function in our script so the script can have a direct entry point and we'll have a secondary function called factorial: 180 | 181 | ```asm 182 | main: { 183 | ret 184 | } 185 | 186 | factorial: { 187 | ret 188 | } 189 | ``` 190 | 191 | So far both functions do nothing. We'll implement factorial as a function that takes a single int32 parameter and returns an int32 as well. 192 | 193 | the implementation of the factorial looks like this in C, Golang, and Python respectively: 194 | ```c 195 | uint32_t factorial(const uint32_t i) { 196 | return (i<=1)? 1 : i * factorial(i-1); 197 | } 198 | ``` 199 | 200 | ```go 201 | func factorial(i int) int { 202 | if i <= 1 { 203 | return i 204 | } 205 | return i * factorial(i-1) 206 | } 207 | ``` 208 | 209 | ```python 210 | def factorial(i: int) -> int: 211 | if i<=1: 212 | return i 213 | return i * factorial(i-1) 214 | ``` 215 | 216 | So now let's implement that in our Tagha ASM language! 217 | 218 | Now the important part here is that since `factorial` calls another function (itself), we must save the link register before calling another function and then restore it once the execution frame goes back to the original one by using `pushlr` and `poplr`. 219 | 220 | For further details on Calling Convention, please refer to the Instruction Set Arch documentation. 221 | 222 | 223 | ```asm 224 | factorial { 225 | pushlr ;; preserve link register. 226 | alloc 3 ;; allot 3 registers for this function frame! 227 | mov r0, r3 ;; r3 is previous call's r0! 228 | 229 | ;; if( i<=1 ) 230 | movi r1, 1 231 | ule r0, r1 232 | jz .L1 233 | 234 | ;; return 1; 235 | mov r0, r1 236 | jmp .L2 237 | 238 | .L1 239 | ;; return i * factorial(i-1); 240 | mov r2, r0 ;; int temp = i; 241 | sub r2, r1 ;; temp -= 1; 242 | mov r0, r2 243 | call factorial ;; int res = factorial(temp); 244 | mul r3, r0 ;; i * res; 245 | 246 | .L2 247 | poplr ;; restore link register. 248 | redux 3 ;; put back the 3 registers we allotted. 249 | 250 | ret 251 | } 252 | ``` 253 | 254 | Alright, we have a defined and working function in Tagha Assembly, how can we call it within our script's Tagha ASM code? 255 | 256 | To actually give `factorial` arguments and call it from Tagha Assembly code, we must set the `semkath` register to a numeric value and then call `factorial`, here's an example. 257 | ```asm 258 | main { 259 | pushlr ;; since we're gonna call factorial, preserve link register. 260 | alloc 1 ;; allocate 1 register. 261 | movi r0, 5 ;; set that 1 reg to hold the argument of 5. 262 | call factorial ;; call factorial. 263 | poplr ;; we've returned from 'factorial', restore old link register. 264 | ret 265 | } 266 | ``` 267 | 268 | When `factorial` has finished execution, the final return value will be in `r0`. The factorial of 5 is 120, so register `r0` data will contain the 32-bit integer value of 120. Now concerning the C standard, `main` should be returning 0, so `r0` must be set to 0 after the call to `factorial` is finished. -------------------------------------------------------------------------------- /documentation/Tagha Profiling Results Clang-LLVM June 29 2020.txt: -------------------------------------------------------------------------------- 1 | Clang Version: clang version 9.0.0-2~ubuntu18.04.2 (tags/RELEASE_900/final) 2 | Specs: Intel Core i5 8250U | 8GB RAM 3 | Flags: -Wextra -Wall -Wrestrict -std=c99 -s -O3 4 | Results: 5 | 6 | Test Purpose: Recursive Function Call Overhead. 7 | Performance counter stats for './taghatest test_fib.tbc' (50 runs): 8 | 9 | 523.641518 task-clock (msec) # 1.000 CPUs utilized ( +- 0.53% ) 10 | 3 context-switches # 0.005 K/sec ( +- 15.10% ) 11 | 0 cpu-migrations # 0.000 K/sec ( +- 69.99% ) 12 | 54 page-faults # 0.103 K/sec ( +- 0.40% ) 13 | 1,771,947,003 cycles # 3.384 GHz ( +- 0.52% ) 14 | 4,181,367,102 instructions # 2.36 insn per cycle ( +- 0.00% ) 15 | 433,934,415 branches # 828.686 M/sec ( +- 0.00% ) 16 | 4,171,940 branch-misses # 0.96% of all branches ( +- 0.26% ) 17 | 18 | 0.523894881 seconds time elapsed ( +- 0.53% ) 19 | 20 | 21 | Test Purpose: Dynamically Linked Function Call Overhead (runs test_factorial.tbc in a loop, iterating 10M times). 22 | Performance counter stats for './taghatest test_dynamiclinking.tbc' (50 runs): 23 | 24 | 1392.250730 task-clock (msec) # 1.000 CPUs utilized ( +- 0.54% ) 25 | 35 context-switches # 0.025 K/sec ( +- 73.62% ) 26 | 0 cpu-migrations # 0.000 K/sec ( +- 37.29% ) 27 | 58 page-faults # 0.041 K/sec ( +- 0.33% ) 28 | 4,655,421,239 cycles # 3.344 GHz ( +- 0.14% ) 29 | 11,542,550,472 instructions # 2.48 insn per cycle ( +- 0.00% ) 30 | 1,230,459,442 branches # 883.792 M/sec ( +- 0.00% ) 31 | 196,196 branch-misses # 0.02% of all branches ( +- 11.06% ) 32 | 33 | 1.392784354 seconds time elapsed ( +- 0.55% ) 34 | 35 | 36 | Test Purpose: Dynamically Loaded Function Call Overhead (runs test_factorial.tbc in a loop, iterating 10M times). 37 | Performance counter stats for './taghatest test_dynamicloading.tbc' (50 runs): 38 | 39 | 1398.918557 task-clock (msec) # 1.000 CPUs utilized ( +- 0.50% ) 40 | 4 context-switches # 0.003 K/sec ( +- 19.69% ) 41 | 0 cpu-migrations # 0.000 K/sec ( +- 60.19% ) 42 | 58 page-faults # 0.041 K/sec ( +- 0.38% ) 43 | 4,693,886,538 cycles # 3.355 GHz ( +- 0.15% ) 44 | 11,512,532,891 instructions # 2.45 insn per cycle ( +- 0.00% ) 45 | 1,230,451,682 branches # 879.573 M/sec ( +- 0.00% ) 46 | 224,374 branch-misses # 0.02% of all branches ( +- 25.38% ) 47 | 48 | 1.398736821 seconds time elapsed ( +- 0.49% ) 49 | 50 | 51 | Test Purpose: Recursive Function Call Overhead. 52 | Performance counter stats for './taghatest test_factorial.tbc' (50 runs): 53 | 54 | 1.072002 task-clock (msec) # 0.678 CPUs utilized ( +- 2.96% ) 55 | 0 context-switches # 0.000 K/sec 56 | 0 cpu-migrations # 0.000 K/sec 57 | 54 page-faults # 0.050 M/sec ( +- 0.41% ) 58 | 960,943 cycles # 0.896 GHz ( +- 1.54% ) 59 | 694,091 instructions # 0.72 insn per cycle ( +- 0.38% ) 60 | 134,477 branches # 125.444 M/sec ( +- 0.32% ) 61 | 6,081 branch-misses # 4.52% of all branches ( +- 0.92% ) 62 | 63 | 0.001580391 seconds time elapsed ( +- 3.07% ) 64 | 65 | 66 | Test Purpose: Basic Loop Overhead. (iterates 100M times) 67 | Performance counter stats for './taghatest test_loop.tbc' (50 runs): 68 | 69 | 810.255282 task-clock (msec) # 0.999 CPUs utilized ( +- 0.15% ) 70 | 5 context-switches # 0.006 K/sec ( +- 22.03% ) 71 | 0 cpu-migrations # 0.000 K/sec ( +- 48.45% ) 72 | 54 page-faults # 0.066 K/sec ( +- 0.35% ) 73 | 2,738,603,886 cycles # 3.380 GHz ( +- 0.03% ) 74 | 4,701,699,194 instructions # 1.72 insn per cycle ( +- 0.00% ) 75 | 400,309,509 branches # 494.054 M/sec ( +- 0.00% ) 76 | 13,827 branch-misses # 0.00% of all branches ( +- 2.06% ) 77 | 78 | 0.810811763 seconds time elapsed ( +- 0.15% ) -------------------------------------------------------------------------------- /documentation/Tagha Profiling Results Clang-LLVM Sept 3 2020.txt: -------------------------------------------------------------------------------- 1 | Clang Version: clang version 9.0.0-2~ubuntu18.04.2 (tags/RELEASE_900/final) 2 | Specs: Intel Core i5 8250U | 8GB RAM 3 | Flags: -Wextra -Wall -Wrestrict -std=c99 -s -O3 4 | Results: 5 | 6 | Test Purpose: Recursive Function Call Overhead. 7 | Performance counter stats for './taghatest test_fib.tbc' (50 runs): 8 | 9 | 380.750183 task-clock (msec) # 0.999 CPUs utilized ( +- 0.81% ) 10 | 5 context-switches # 0.014 K/sec ( +- 25.85% ) 11 | 0 cpu-migrations # 0.000 K/sec ( +- 69.99% ) 12 | 54 page-faults # 0.141 K/sec ( +- 0.37% ) 13 | 1,274,129,283 cycles # 3.346 GHz ( +- 0.63% ) 14 | 3,000,181,310 instructions # 2.35 insn per cycle ( +- 0.00% ) 15 | 433,922,727 branches # 1139.652 M/sec ( +- 0.00% ) 16 | 4,397,650 branch-misses # 1.01% of all branches ( +- 1.82% ) 17 | 18 | 0.381043833 seconds time elapsed ( +- 0.81% ) 19 | 20 | 21 | Test Purpose: Dynamically Linked Function Call Overhead (runs test_factorial.tbc in a loop, iterating 10M times). 22 | Performance counter stats for './taghatest test_dynamiclinking.tbc' (50 runs): 23 | 24 | 959.820239 task-clock (msec) # 1.000 CPUs utilized ( +- 0.31% ) 25 | 4 context-switches # 0.004 K/sec ( +- 15.88% ) 26 | 0 cpu-migrations # 0.000 K/sec ( +- 60.19% ) 27 | 57 page-faults # 0.060 K/sec ( +- 0.38% ) 28 | 3,243,508,657 cycles # 3.379 GHz ( +- 0.23% ) 29 | 8,221,875,022 instructions # 2.53 insn per cycle ( +- 0.00% ) 30 | 1,150,338,210 branches # 1198.493 M/sec ( +- 0.00% ) 31 | 13,181,707 branch-misses # 1.15% of all branches ( +- 0.96% ) 32 | 33 | 0.960087962 seconds time elapsed ( +- 0.31% ) 34 | 35 | 36 | Test Purpose: Dynamically Loaded Function Call Overhead (runs test_factorial.tbc in a loop, iterating 10M times). 37 | Performance counter stats for './taghatest test_dynamicloading.tbc' (50 runs): 38 | 39 | 976.490657 task-clock (msec) # 1.000 CPUs utilized ( +- 0.24% ) 40 | 4 context-switches # 0.004 K/sec ( +- 18.18% ) 41 | 0 cpu-migrations # 0.000 K/sec ( +-100.00% ) 42 | 58 page-faults # 0.059 K/sec ( +- 0.37% ) 43 | 3,307,865,566 cycles # 3.388 GHz ( +- 0.23% ) 44 | 8,201,883,228 instructions # 2.48 insn per cycle ( +- 0.00% ) 45 | 1,150,339,425 branches # 1178.034 M/sec ( +- 0.00% ) 46 | 14,579,738 branch-misses # 1.27% of all branches ( +- 1.13% ) 47 | 48 | 0.976764544 seconds time elapsed ( +- 0.24% ) 49 | 50 | 51 | Test Purpose: Recursive Function Call Overhead. 52 | Performance counter stats for './taghatest test_factorial.tbc' (50 runs): 53 | 54 | 1.132467 task-clock (msec) # 0.676 CPUs utilized ( +- 2.65% ) 55 | 0 context-switches # 0.018 K/sec ( +-100.00% ) 56 | 0 cpu-migrations # 0.000 K/sec 57 | 54 page-faults # 0.048 M/sec ( +- 0.39% ) 58 | 964,668 cycles # 0.852 GHz ( +- 1.21% ) 59 | 690,470 instructions # 0.72 insn per cycle ( +- 0.26% ) 60 | 133,913 branches # 118.249 M/sec ( +- 0.25% ) 61 | 6,246 branch-misses # 4.66% of all branches ( +- 1.03% ) 62 | 63 | 0.001674510 seconds time elapsed ( +- 2.75% ) 64 | 65 | 66 | Test Purpose: Basic Loop Overhead. (iterates 100M times) 67 | Performance counter stats for './taghatest test_loop.tbc' (50 runs): 68 | 69 | 383.620041 task-clock (msec) # 0.999 CPUs utilized ( +- 0.42% ) 70 | 2 context-switches # 0.005 K/sec ( +- 18.58% ) 71 | 0 cpu-migrations # 0.000 K/sec ( +- 42.86% ) 72 | 54 page-faults # 0.140 K/sec ( +- 0.33% ) 73 | 1,298,249,148 cycles # 3.384 GHz ( +- 0.39% ) 74 | 3,401,172,100 instructions # 2.62 insn per cycle ( +- 0.00% ) 75 | 400,216,779 branches # 1043.263 M/sec ( +- 0.00% ) 76 | 10,501 branch-misses # 0.00% of all branches ( +- 1.29% ) 77 | 78 | 0.383878753 seconds time elapsed ( +- 0.42% ) 79 | -------------------------------------------------------------------------------- /documentation/Tagha Profiling Results GCC June 29 2020.txt: -------------------------------------------------------------------------------- 1 | GCC Version: gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 2 | Specs: Intel Core i5 8250U | 8GB RAM 3 | Flags: -Wextra -Wall -Wrestrict -std=c99 -s -O2 4 | Results: 5 | 6 | Test Purpose: Recursive Function Call Overhead. 7 | Performance counter stats for './taghatest test_fib.tbc' (50 runs): 8 | 9 | 370.448791 task-clock (msec) # 0.999 CPUs utilized ( +- 0.67% ) 10 | 2 context-switches # 0.004 K/sec ( +- 22.96% ) 11 | 0 cpu-migrations # 0.000 K/sec ( +- 48.45% ) 12 | 55 page-faults # 0.148 K/sec ( +- 0.30% ) 13 | 1,255,537,086 cycles # 3.389 GHz ( +- 0.67% ) 14 | 2,953,939,776 instructions # 2.35 insn per cycle ( +- 0.00% ) 15 | 452,359,203 branches # 1221.111 M/sec ( +- 0.00% ) 16 | 4,403,216 branch-misses # 0.97% of all branches ( +- 0.50% ) 17 | 18 | 0.370689788 seconds time elapsed ( +- 0.67% ) 19 | 20 | 21 | Test Purpose: Dynamically Linked Function Call Overhead (runs test_factorial.tbc in a loop, iterating 10M times). 22 | Performance counter stats for './taghatest test_dynamiclinking.tbc' (50 runs): 23 | 24 | 895.366021 task-clock (msec) # 1.000 CPUs utilized ( +- 0.15% ) 25 | 2 context-switches # 0.002 K/sec ( +- 17.23% ) 26 | 0 cpu-migrations # 0.000 K/sec ( +- 56.54% ) 27 | 58 page-faults # 0.065 K/sec ( +- 0.29% ) 28 | 3,031,378,750 cycles # 3.386 GHz ( +- 0.10% ) 29 | 8,311,783,203 instructions # 2.74 insn per cycle ( +- 0.00% ) 30 | 1,280,322,862 branches # 1429.944 M/sec ( +- 0.00% ) 31 | 169,669 branch-misses # 0.01% of all branches ( +- 15.43% ) 32 | 33 | 0.895620420 seconds time elapsed ( +- 0.15% ) 34 | 35 | 36 | Test Purpose: Dynamically Loaded Function Call Overhead (runs test_factorial.tbc in a loop, iterating 10M times). 37 | Performance counter stats for './taghatest test_dynamicloading.tbc' (50 runs): 38 | 39 | 897.967359 task-clock (msec) # 1.000 CPUs utilized ( +- 0.14% ) 40 | 4 context-switches # 0.004 K/sec ( +- 16.36% ) 41 | 0 cpu-migrations # 0.000 K/sec ( +- 56.54% ) 42 | 58 page-faults # 0.065 K/sec ( +- 0.24% ) 43 | 3,037,699,653 cycles # 3.383 GHz ( +- 0.07% ) 44 | 8,281,799,804 instructions # 2.73 insn per cycle ( +- 0.00% ) 45 | 1,280,326,052 branches # 1425.805 M/sec ( +- 0.00% ) 46 | 173,051 branch-misses # 0.01% of all branches ( +- 20.18% ) 47 | 48 | 0.898233756 seconds time elapsed ( +- 0.14% ) 49 | 50 | 51 | Test Purpose: Recursive Function Call Overhead. 52 | Performance counter stats for './taghatest test_factorial.tbc' (50 runs): 53 | 54 | 0.851934 task-clock (msec) # 0.685 CPUs utilized ( +- 6.41% ) 55 | 0 context-switches # 0.000 K/sec 56 | 0 cpu-migrations # 0.000 K/sec 57 | 55 page-faults # 0.064 M/sec ( +- 0.33% ) 58 | 940,003 cycles # 1.103 GHz ( +- 1.36% ) 59 | 706,720 instructions # 0.75 insn per cycle ( +- 0.55% ) 60 | 136,931 branches # 160.729 M/sec ( +- 0.42% ) 61 | 6,001 branch-misses # 4.38% of all branches ( +- 0.82% ) 62 | 63 | 0.001242905 seconds time elapsed ( +- 6.47% ) 64 | 65 | 66 | Test Purpose: Basic Loop Overhead. (iterates 100M times) 67 | Performance counter stats for './taghatest test_loop.tbc' (50 runs): 68 | 69 | 550.213013 task-clock (msec) # 1.000 CPUs utilized ( +- 0.18% ) 70 | 3 context-switches # 0.005 K/sec ( +- 24.22% ) 71 | 0 cpu-migrations # 0.000 K/sec ( +- 69.99% ) 72 | 54 page-faults # 0.099 K/sec ( +- 0.28% ) 73 | 1,860,047,295 cycles # 3.381 GHz ( +- 0.04% ) 74 | 3,201,369,151 instructions # 1.72 insn per cycle ( +- 0.00% ) 75 | 400,252,271 branches # 727.450 M/sec ( +- 0.00% ) 76 | 11,363 branch-misses # 0.00% of all branches ( +- 1.39% ) 77 | 78 | 0.550469626 seconds time elapsed ( +- 0.18% ) 79 | -------------------------------------------------------------------------------- /documentation/Tagha Profiling Results GCC Sept 3 2020.txt: -------------------------------------------------------------------------------- 1 | GCC Version: gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 2 | Specs: Intel Core i5 8250U | 8GB RAM 3 | Flags: -Wextra -Wall -Wrestrict -std=c99 -s -O2 4 | Results: 5 | 6 | Test Purpose: Recursive Function Call Overhead. 7 | Performance counter stats for './taghatest test_fib.tbc' (50 runs): 8 | 9 | 398.329815 task-clock (msec) # 0.999 CPUs utilized ( +- 0.65% ) 10 | 2 context-switches # 0.005 K/sec ( +- 18.93% ) 11 | 0 cpu-migrations # 0.000 K/sec ( +- 56.54% ) 12 | 55 page-faults # 0.137 K/sec ( +- 0.34% ) 13 | 1,347,381,187 cycles # 3.383 GHz ( +- 0.62% ) 14 | 2,953,998,460 instructions # 2.19 insn per cycle ( +- 0.00% ) 15 | 452,369,349 branches # 1135.665 M/sec ( +- 0.00% ) 16 | 8,114,106 branch-misses # 1.79% of all branches ( +- 0.32% ) 17 | 18 | 0.398587678 seconds time elapsed ( +- 0.65% ) 19 | 20 | 21 | Test Purpose: Dynamically Linked Function Call Overhead (runs test_factorial.tbc in a loop, iterating 10M times). 22 | Performance counter stats for './taghatest test_dynamiclinking.tbc' (50 runs): 23 | 24 | 873.451416 task-clock (msec) # 1.000 CPUs utilized ( +- 0.54% ) 25 | 5 context-switches # 0.005 K/sec ( +- 15.68% ) 26 | 0 cpu-migrations # 0.000 K/sec ( +- 69.99% ) 27 | 59 page-faults # 0.067 K/sec ( +- 0.29% ) 28 | 2,958,759,374 cycles # 3.387 GHz ( +- 0.54% ) 29 | 8,091,812,174 instructions # 2.73 insn per cycle ( +- 0.00% ) 30 | 1,210,327,829 branches # 1385.684 M/sec ( +- 0.00% ) 31 | 1,887,065 branch-misses # 0.16% of all branches ( +- 25.23% ) 32 | 33 | 0.873736506 seconds time elapsed ( +- 0.54% ) 34 | 35 | 36 | Test Purpose: Dynamically Loaded Function Call Overhead (runs test_factorial.tbc in a loop, iterating 10M times). 37 | Performance counter stats for './taghatest test_dynamicloading.tbc' (50 runs): 38 | 39 | 873.342771 task-clock (msec) # 1.000 CPUs utilized ( +- 0.54% ) 40 | 7 context-switches # 0.008 K/sec ( +- 28.26% ) 41 | 0 cpu-migrations # 0.000 K/sec ( +- 69.99% ) 42 | 58 page-faults # 0.067 K/sec ( +- 0.23% ) 43 | 2,953,627,748 cycles # 3.382 GHz ( +- 0.54% ) 44 | 8,061,818,571 instructions # 2.73 insn per cycle ( +- 0.00% ) 45 | 1,200,329,014 branches # 1374.408 M/sec ( +- 0.00% ) 46 | 1,693,387 branch-misses # 0.14% of all branches ( +- 30.07% ) 47 | 48 | 0.873634268 seconds time elapsed ( +- 0.54% ) 49 | 50 | 51 | Test Purpose: Recursive Function Call Overhead. 52 | Performance counter stats for './taghatest test_factorial.tbc' (50 runs): 53 | 54 | 1.125335 task-clock (msec) # 0.673 CPUs utilized ( +- 3.77% ) 55 | 0 context-switches # 0.071 K/sec ( +- 48.45% ) 56 | 0 cpu-migrations # 0.000 K/sec 57 | 54 page-faults # 0.048 M/sec ( +- 0.32% ) 58 | 1,000,283 cycles # 0.889 GHz ( +- 1.27% ) 59 | 706,435 instructions # 0.71 insn per cycle ( +- 0.41% ) 60 | 136,898 branches # 121.650 M/sec ( +- 0.33% ) 61 | 6,420 branch-misses # 4.69% of all branches ( +- 0.91% ) 62 | 63 | 0.001672683 seconds time elapsed ( +- 3.83% ) 64 | 65 | 66 | Test Purpose: Basic Loop Overhead. (iterates 100M times) 67 | Performance counter stats for './taghatest test_loop.tbc' (50 runs): 68 | 69 | 408.765037 task-clock (msec) # 0.999 CPUs utilized ( +- 0.31% ) 70 | 2 context-switches # 0.006 K/sec ( +- 14.89% ) 71 | 0 cpu-migrations # 0.000 K/sec ( +- 56.54% ) 72 | 55 page-faults # 0.134 K/sec ( +- 0.36% ) 73 | 1,380,857,156 cycles # 3.378 GHz ( +- 0.15% ) 74 | 3,201,227,848 instructions # 2.32 insn per cycle ( +- 0.00% ) 75 | 400,227,091 branches # 979.113 M/sec ( +- 0.00% ) 76 | 10,886 branch-misses # 0.00% of all branches ( +- 1.36% ) 77 | 78 | 0.409017545 seconds time elapsed ( +- 0.31% ) 79 | -------------------------------------------------------------------------------- /documentation/Tagha Testing and Results Doc.txt: -------------------------------------------------------------------------------- 1 | test_3d_vecs.tbc - purpose: test float and pointer/array arithmetics. 2 | accurate result: 3 | result => -1065353216 | err? 'None' <-- -9.f as an int32. 4 | op stack : 0 - '0xc022000000000000' <-- -9.f as an uint64. 5 | 6 | test_factorial.tbc - purpose: test stack and recursive call frames. 7 | accurate result: 8 | result => 120 | err? 'None' <-- factorial of 5 9 | 10 | test_fib34.tbc - purpose: test recursive calling speed 11 | accurate result: 12 | result => 5702887 | err? 'None' <-- fibonacci of 34 13 | 14 | test_fib40.tbc - purpose: test recursive calling speed 15 | accurate result: 16 | result => 102334155 | err? 'None' <-- fibonacci of 40 17 | 18 | test_global.tbc - purpose: calling a native and testing a host app-side variable being modified by the script. 19 | accurate result: 20 | op stack : 0 - '0x43960000' <-- 300.f 21 | op stack : 1 - '0x64' <-- 100 22 | op stack : 2 - '0x20' <-- 32 23 | 24 | test_funcptr.tbc - purpose: advanced test combo of calling natives, passing host exported global pointers to natives, and loading string literals from script memory. 25 | accurate result: 26 | Please enter a long string: <-- 27 | *long pause because you're supposed to type stuff and hit enter.* 28 | ... 29 | op stack : 0 - '0x100' <-- size of buffer array. 30 | op stack : 1 - '0x7f777fdaba00' <-- ptr value of 'stdin'. (random number because ptr) 31 | 32 | test_dynamicloading - purpose: tests manually loading a script module and calling factorial from script bytecode! 33 | accurate result: 34 | same result as test_factorial.tbc test module. 35 | 36 | test_dynamiclinking - purpose: tests dynamically linking a script module and calling factorial from script bytecode! 37 | accurate result: 38 | same result as test_factorial.tbc test module. 39 | 40 | test_native_number.tbc - purpose: profile calling a C function 1 billion times with a number and adding it. 41 | 42 | test_selfcall.tbc - purpose: test having the invoking API call a function within itself. 43 | accurate result: 44 | send flowers 45 | send nudes <-- the correct output should print "send nudes". 46 | send flowers 47 | send keks 48 | 49 | test_loop.tbc - purpose: test a simple loop iterating 100M times. 50 | accurate result: 51 | result => 100000000 | err? 'None' 52 | op stack : 0 - '0' 53 | op stack : 1 - '0x1' 54 | op stack : 2 - '0x5f5e100' 55 | 56 | test_simd.tbc - purpose: test vector/SIMD opcodes. 57 | accurate result: 58 | result => 1082130432 | err? 'None' 59 | op stack : 0 - '0x55cdee83185c' 60 | op stack : 1 - '0xc0400000c0000000' <-- float32 packed into 8 bytes. 61 | op stack : 2 - '0xc0800000' <-- remaining float32 value within packed vec of 12 bytes (float32 * 3) 62 | 63 | test_str_cmp.tbc - purpose: test vector (of a string) comparisons. 64 | accurate result: 65 | result => 1 | err? 'None' <-- `result` must be `1`. 66 | 67 | test_int_cmp.tbc - purpose: test vector (of an int) comparisons. 68 | accurate result: 69 | result => 1 | err? 'None' <-- `result` must be `1`. 70 | -------------------------------------------------------------------------------- /documentation/Tagha Toolchain API.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | This article teaches how to utilize the Tagha toolchain API that helps in creating, debugging, & analyzing Tagha modules. 3 | 4 | 5 | # Header-Only Files 6 | 7 | ## instr_gen.h 8 | This header is for streamlining the instruction generation process. 9 | 10 | The instruction generator function has the following signature: 11 | ```c 12 | size_t tagha_instr_gen(struct HarbolByteBuf *tbc, enum TaghaInstrSet op, ...); 13 | ``` 14 | 15 | `tagha_instr_gen` returns a `size_t` integer indicating the amount of bytes written. 16 | 17 | The `tbc` "tagha bytecode" argument can optionally be `NULL` so as to just return the amount of bytes that would've been written. 18 | 19 | Arguments associated with `op` "opcode" will not be read/consumed if `tbc` is `NULL`, thus it's safe to simply pass a `NULL` argument and the opcode like so: 20 | ```c 21 | const size_t call_bytes = tagha_instr_gen(NULL, call); 22 | const size_t redux_bytes = tagha_instr_gen(NULL, redux, 10); /// 10 is passed but won't be processed. 23 | ``` 24 | 25 | If `tbc` is _NOT_ `NULL`, then you MUST supply arguments for the opcode to process. 26 | 27 | 28 | ## module_gen.h 29 | This header is for simplifying the module generation process. 30 | 31 | The module generation process (using this header) starts by creating a module generator object: 32 | ```c 33 | struct TaghaModGen modgen = tagha_mod_gen_create(); 34 | ``` 35 | 36 | There are three API functions that fill in the module generator: 37 | ```c 38 | void tagha_mod_gen_write_header(struct TaghaModGen *mod, uint32_t opstacksize, uint32_t callstacksize, uint32_t heapsize, uint32_t flags); 39 | 40 | void tagha_mod_gen_write_func(struct TaghaModGen *mod, uint32_t flags, const char name[], const struct HarbolByteBuf *bytecode); 41 | 42 | void tagha_mod_gen_write_var(struct TaghaModGen *mod, uint32_t flags, const char name[], const struct HarbolByteBuf *datum); 43 | ``` 44 | 45 | These 3 functions can be used at anytime after creating the module generator but not after finalization (will talk about this later). 46 | 47 | Writing the module header is simple. You simplify fill in how large the two stacks will be, how large the module's heap will be, and the kind of runtime flags you want to give it! 48 | 49 | For writing global variables and functions, you will need to use the byte buffer found in the Harbol library. Functions take a bytebuffer filled with bytecode (typically filled in by the `tagha_instr_gen` function we mentioned at the start) while global variables take a bytebuffer of their data, even if it's all 0. 50 | 51 | Once you've filled the module generator object, you simply need to finalize the module by using one of 3 API functions: 52 | 53 | ```c 54 | bool tagha_mod_gen_create_file(struct TaghaModGen *mod, const char filename[]); 55 | struct HarbolByteBuf tagha_mod_gen_buffer(struct TaghaModGen *mod); 56 | uint8_t *tagha_mod_gen_raw(struct TaghaModGen *mod); 57 | ``` 58 | 59 | NOTE: Finalizing the module WILL clear out the module generator object's data so keep this in mind. 60 | 61 | `tagha_mod_gen_create_file` is good if you're compiling/assembling a Tagha Module to a file, simply supply a name for the module file binary. 62 | `tagha_mod_gen_buffer` is good if you're disassembling or analyzing the generated module binary. 63 | `tagha_mod_gen_raw` is good if you're dynamically creating a Tagha Module during runtime. 64 | 65 | ## module_info.h 66 | This header is for debugging purposes by unwinding the runtime stacks `tagha_module_print_callstack` and `tagha_module_print_opstack` or emitting the module's header information `tagha_module_print_header`. 67 | 68 | The following functions are: 69 | ```c 70 | void tagha_module_print_header(const struct TaghaModule *mod, FILE *stream); 71 | void tagha_module_print_opstack(const struct TaghaModule *mod, FILE *stream); 72 | void tagha_module_print_callstack(const struct TaghaModule *mod, FILE *stream); 73 | ``` -------------------------------------------------------------------------------- /documentation/dynamic_linking_in_tagha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/documentation/dynamic_linking_in_tagha.png -------------------------------------------------------------------------------- /documentation/taghalogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/documentation/taghalogo.png -------------------------------------------------------------------------------- /tagha/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | #CC = clang-9 3 | CFLAGS = -Wextra -Wall -Wrestrict -std=c99 -static -s -O2 -mtune=native -march=native 4 | TFLAGS = -Wextra -Wall -Wrestrict -std=c99 -g -O2 -mtune=native -march=native 5 | PFlAGS = -Wextra -Wall -Wrestrict -std=c99 -pg -O2 -mtune=native -march=native 6 | 7 | # -static 8 | 9 | SRCS = allocators/region/region.c allocators/mempool/mempool.c tagha.c 10 | OBJS = region.o mempool.o tagha.o 11 | 12 | LIBNAME = libtagha 13 | 14 | tagha: 15 | $(CC) $(CFLAGS) -c $(SRCS) 16 | $(AR) cr $(LIBNAME).a $(OBJS) 17 | 18 | shared: 19 | $(CC) $(CFLAGS) -shared $(SRCS) -o $(LIBNAME).so 20 | 21 | debug: 22 | $(CC) $(TFLAGS) -c $(SRCS) 23 | $(AR) cr $(LIBNAME).a $(OBJS) 24 | 25 | debug_shared: 26 | $(CC) $(TFLAGS) -shared $(SRCS) -o $(LIBNAME).so 27 | 28 | profile: 29 | $(CC) $(PFlAGS) -c $(SRCS) 30 | $(AR) cr $(LIBNAME).a $(OBJS) 31 | 32 | disasm: 33 | $(CC) -O2 -masm=intel -S $(SRCS) 34 | # llvm-gcc -emit-llvm -S $(SRCS) 35 | # -fverbose-asm 36 | 37 | clean: 38 | $(RM) *.o 39 | -------------------------------------------------------------------------------- /tagha/allocators/bistack/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Wextra -pedantic -std=c99 -s -O2 3 | TFLAGS = -Wall -Wextra -pedantic -std=c99 -g -O2 4 | 5 | SRCS = bistack.c 6 | OBJS = $(SRCS:.c=.o) 7 | 8 | harbol_bistack: 9 | $(CC) $(CFLAGS) -c $(SRCS) 10 | 11 | debug: 12 | $(CC) $(TFLAGS) -c $(SRCS) 13 | 14 | test: 15 | $(CC) $(TFLAGS) $(SRCS) test_bistack.c -o harbol_bistack_test 16 | 17 | clean: 18 | $(RM) *.o 19 | $(RM) harbol_bistack_test 20 | -------------------------------------------------------------------------------- /tagha/allocators/bistack/bistack.c: -------------------------------------------------------------------------------- 1 | #include "bistack.h" 2 | 3 | #ifdef OS_WINDOWS 4 | # define HARBOL_LIB 5 | #endif 6 | 7 | 8 | HARBOL_EXPORT bool harbol_bistack_init(struct HarbolBiStack *const bistack, const size_t len) { 9 | if( len==0 ) 10 | return false; 11 | 12 | bistack->mem = ( uintptr_t )calloc(len, sizeof(uint8_t)); 13 | if( bistack->mem==NIL ) 14 | return false; 15 | 16 | bistack->size = len; 17 | bistack->front = bistack->mem; 18 | bistack->back = bistack->mem + len; 19 | return true; 20 | } 21 | 22 | HARBOL_EXPORT struct HarbolBiStack harbol_bistack_make(const size_t len, bool *const res) 23 | { 24 | struct HarbolBiStack bistack = {0}; 25 | *res = harbol_bistack_init(&bistack, len); 26 | return bistack; 27 | } 28 | 29 | HARBOL_EXPORT struct HarbolBiStack harbol_bistack_make_from_buffer(void *const restrict buf, const size_t len) 30 | { 31 | struct HarbolBiStack bistack = {0}; 32 | bistack.size = len; 33 | bistack.mem = bistack.front = ( uintptr_t )buf; 34 | bistack.back = bistack.mem + len; 35 | return bistack; 36 | } 37 | 38 | HARBOL_EXPORT void harbol_bistack_clear(struct HarbolBiStack *const bistack) 39 | { 40 | if( bistack->mem==NIL ) 41 | return; 42 | 43 | free(( void* )bistack->mem); 44 | bistack->mem = bistack->front = bistack->back = NIL; 45 | bistack->size = 0; 46 | } 47 | 48 | HARBOL_EXPORT void *harbol_bistack_alloc_front(struct HarbolBiStack *const bistack, const size_t size) 49 | { 50 | if( bistack->mem==NIL ) 51 | return NULL; 52 | 53 | const size_t aligned_size = harbol_align_size(size, sizeof(uintptr_t)); 54 | /// front end arena is too high! 55 | if( bistack->front + aligned_size >= bistack->back ) 56 | return NULL; 57 | 58 | const uintptr_t p = bistack->front; 59 | bistack->front += aligned_size; 60 | return memset(( void* )p, 0, aligned_size); 61 | } 62 | 63 | HARBOL_EXPORT void *harbol_bistack_alloc_back(struct HarbolBiStack *const restrict bistack, const size_t size) 64 | { 65 | if( bistack->mem==NIL ) 66 | return NULL; 67 | 68 | const size_t aligned_size = harbol_align_size(size, sizeof(uintptr_t)); 69 | /// back end arena is too low 70 | if( bistack->back - aligned_size <= bistack->front ) 71 | return NULL; 72 | 73 | bistack->back -= aligned_size; 74 | return memset(( void* )bistack->back, 0, aligned_size); 75 | } 76 | 77 | HARBOL_EXPORT void harbol_bistack_reset_front(struct HarbolBiStack *const bistack) 78 | { 79 | if( bistack->mem==NIL ) 80 | return; 81 | 82 | bistack->front = bistack->mem; 83 | } 84 | 85 | HARBOL_EXPORT void harbol_bistack_reset_back(struct HarbolBiStack *const bistack) 86 | { 87 | if( bistack->mem==NIL ) 88 | return; 89 | 90 | bistack->back = bistack->mem + bistack->size; 91 | } 92 | 93 | HARBOL_EXPORT void harbol_bistack_reset_all(struct HarbolBiStack *const bistack) 94 | { 95 | if( bistack->mem==NIL ) 96 | return; 97 | 98 | bistack->front = bistack->mem; 99 | bistack->back = bistack->mem + bistack->size; 100 | } 101 | 102 | HARBOL_EXPORT intptr_t harbol_bistack_get_margins(const struct HarbolBiStack bistack) 103 | { 104 | return ( intptr_t )bistack.back - ( intptr_t )bistack.front; 105 | } 106 | 107 | 108 | HARBOL_EXPORT bool harbol_bistack_resize(struct HarbolBiStack *const restrict bistack, const size_t new_size) { 109 | uint8_t *const new_buf = harbol_recalloc(( void* )bistack->mem, new_size, sizeof *new_buf, bistack->size); 110 | if( new_buf==NULL ) 111 | return false; 112 | 113 | bistack->mem = bistack->front = ( uintptr_t )new_buf; 114 | bistack->size = new_size; 115 | bistack->back = bistack->mem + new_size; 116 | return true; 117 | } -------------------------------------------------------------------------------- /tagha/allocators/bistack/bistack.h: -------------------------------------------------------------------------------- 1 | #ifndef HARBOL_BISTACK_INCLUDED 2 | # define HARBOL_BISTACK_INCLUDED 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "../../harbol_common_defines.h" 9 | #include "../../harbol_common_includes.h" 10 | 11 | 12 | /// Double-Ended Stack Allocator 13 | struct HarbolBiStack { 14 | uintptr_t mem, front, back; 15 | size_t size; 16 | }; 17 | 18 | 19 | HARBOL_EXPORT struct HarbolBiStack harbol_bistack_make(size_t len, bool *res); 20 | HARBOL_EXPORT NO_NULL bool harbol_bistack_init(struct HarbolBiStack *bistack, size_t len); 21 | HARBOL_EXPORT NO_NULL struct HarbolBiStack harbol_bistack_make_from_buffer(void *buf, size_t len); 22 | HARBOL_EXPORT NO_NULL void harbol_bistack_clear(struct HarbolBiStack *bistack); 23 | 24 | HARBOL_EXPORT NO_NULL void *harbol_bistack_alloc_front(struct HarbolBiStack *bistack, size_t size); 25 | HARBOL_EXPORT NO_NULL void *harbol_bistack_alloc_back(struct HarbolBiStack *bistack, size_t size); 26 | 27 | HARBOL_EXPORT NO_NULL void harbol_bistack_reset_front(struct HarbolBiStack *bistack); 28 | HARBOL_EXPORT NO_NULL void harbol_bistack_reset_back(struct HarbolBiStack *bistack); 29 | HARBOL_EXPORT NO_NULL void harbol_bistack_reset_all(struct HarbolBiStack *bistack); 30 | 31 | HARBOL_EXPORT intptr_t harbol_bistack_get_margins(struct HarbolBiStack bistack); 32 | 33 | /// Warning: Resizing WILL reset the memory margins. 34 | /// So don't resize unless you're absolutely done using the data before resizing. 35 | /// Note: This will resize the bistack using a heap-allocated buffer. 36 | HARBOL_EXPORT NO_NULL bool harbol_bistack_resize(struct HarbolBiStack *bistack, size_t new_size); 37 | /********************************************************************/ 38 | 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif /** HARBOL_BISTACK_INCLUDED */ -------------------------------------------------------------------------------- /tagha/allocators/bistack/test_bistack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bistack.h" 5 | 6 | void test_harbol_bistack(FILE *debug_stream); 7 | 8 | #ifdef HARBOL_USE_MEMPOOL 9 | struct HarbolMemPool *g_pool; 10 | #endif 11 | 12 | union Value { 13 | int64_t int64; 14 | }; 15 | 16 | int main(void) 17 | { 18 | FILE *debug_stream = fopen("harbol_bistack_output.txt", "w"); 19 | if( debug_stream==NULL ) 20 | return -1; 21 | 22 | #ifdef HARBOL_USE_MEMPOOL 23 | struct HarbolMemPool m = harbol_mempool_create(1000000); 24 | g_pool = &m; 25 | #endif 26 | test_harbol_bistack(debug_stream); 27 | 28 | fclose(debug_stream); debug_stream=NULL; 29 | #ifdef HARBOL_USE_MEMPOOL 30 | harbol_mempool_clear(g_pool); 31 | #endif 32 | } 33 | 34 | 35 | void test_harbol_bistack(FILE *const debug_stream) 36 | { 37 | 38 | } -------------------------------------------------------------------------------- /tagha/allocators/bistack/valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes -v ./harbol_bistack_test 4 | -------------------------------------------------------------------------------- /tagha/allocators/cache/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Wextra -pedantic -std=c99 -s -O2 3 | TFLAGS = -Wall -Wextra -pedantic -std=c99 -g -O2 4 | 5 | SRCS = cache.c 6 | OBJS = $(SRCS:.c=.o) 7 | 8 | harbol_cache: 9 | $(CC) $(CFLAGS) -c $(SRCS) 10 | 11 | debug: 12 | $(CC) $(TFLAGS) -c $(SRCS) 13 | 14 | clean: 15 | $(RM) *.o 16 | -------------------------------------------------------------------------------- /tagha/allocators/cache/cache.c: -------------------------------------------------------------------------------- 1 | #include "cache.h" 2 | 3 | #ifdef OS_WINDOWS 4 | # define HARBOL_LIB 5 | #endif 6 | 7 | 8 | HARBOL_EXPORT struct HarbolCache harbol_cache_create(const size_t size) 9 | { 10 | struct HarbolCache cache = {0}; 11 | if( size==0 ) 12 | return cache; 13 | else { 14 | uint8_t *const restrict buf = calloc(size, sizeof *buf); 15 | if( buf==NULL ) { 16 | return cache; 17 | } else { 18 | cache.size = size; 19 | cache.mem = ( uintptr_t )buf; 20 | cache.offs = cache.mem + size; 21 | return cache; 22 | } 23 | } 24 | } 25 | 26 | HARBOL_EXPORT struct HarbolCache harbol_cache_from_buffer(void *const restrict buf, const size_t size) 27 | { 28 | struct HarbolCache cache = {0}; 29 | if( size==0 ) 30 | return cache; 31 | else { 32 | cache.size = size; 33 | cache.mem = ( uintptr_t )buf; 34 | cache.offs = cache.mem + size; 35 | return cache; 36 | } 37 | } 38 | 39 | HARBOL_EXPORT bool harbol_cache_clear(struct HarbolCache *const restrict cache) 40 | { 41 | if( cache->mem==0 ) 42 | return false; 43 | else { 44 | void *restrict ptr = ( void* )cache->mem; 45 | free(ptr); 46 | *cache = (struct HarbolCache){0}; 47 | return true; 48 | } 49 | } 50 | 51 | HARBOL_EXPORT void *harbol_cache_alloc(struct HarbolCache *const restrict cache, const size_t size) 52 | { 53 | if( cache->mem==0 || size==0 || size > cache->size ) 54 | return NULL; 55 | else { 56 | const size_t alloc_size = harbol_align_size(size, sizeof(uintptr_t)); 57 | if( cache->offs - alloc_size < cache->mem ) 58 | return NULL; 59 | else { 60 | cache->offs -= alloc_size; 61 | void *const restrict mem = ( void* )cache->offs; 62 | return memset(mem, 0, alloc_size); 63 | } 64 | } 65 | } 66 | 67 | HARBOL_EXPORT size_t harbol_cache_remaining(const struct HarbolCache *const cache) 68 | { 69 | return cache->offs - cache->mem; 70 | } 71 | -------------------------------------------------------------------------------- /tagha/allocators/cache/cache.h: -------------------------------------------------------------------------------- 1 | #ifndef HARBOL_CACHE_INCLUDED 2 | # define HARBOL_CACHE_INCLUDED 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "../../harbol_common_defines.h" 9 | #include "../../harbol_common_includes.h" 10 | 11 | 12 | struct HarbolCache { 13 | uintptr_t mem, offs; 14 | size_t size; 15 | }; 16 | 17 | HARBOL_EXPORT struct HarbolCache harbol_cache_create(size_t bytes); 18 | HARBOL_EXPORT NO_NULL struct HarbolCache harbol_cache_from_buffer(void *buf, size_t bytes); 19 | HARBOL_EXPORT NO_NULL bool harbol_cache_clear(struct HarbolCache *cache); 20 | 21 | HARBOL_EXPORT NO_NULL void *harbol_cache_alloc(struct HarbolCache *cache, size_t bytes); 22 | HARBOL_EXPORT NO_NULL size_t harbol_cache_remaining(const struct HarbolCache *cache); 23 | /********************************************************************/ 24 | 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif /* HARBOL_CACHE_INCLUDED */ 31 | -------------------------------------------------------------------------------- /tagha/allocators/mempool/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Wextra -pedantic -std=c99 -s -O2 3 | TFLAGS = -Wall -Wextra -pedantic -std=c99 -g -O2 4 | 5 | SRCS = mempool.c 6 | SRCS += ../region/region.c 7 | OBJS = $(SRCS:.c=.o) 8 | 9 | harbol_mempool: 10 | $(CC) $(CFLAGS) -c $(SRCS) 11 | 12 | debug: 13 | $(CC) $(TFLAGS) -c $(SRCS) 14 | 15 | test: 16 | $(CC) $(TFLAGS) $(SRCS) test_mempool.c -o harbol_mempool_test 17 | 18 | clean: 19 | $(RM) *.o 20 | $(RM) harbol_mempool_test 21 | -------------------------------------------------------------------------------- /tagha/allocators/mempool/mempool.c: -------------------------------------------------------------------------------- 1 | #include "mempool.h" 2 | 3 | #ifdef OS_WINDOWS 4 | # define HARBOL_LIB 5 | #endif 6 | 7 | 8 | HARBOL_EXPORT struct HarbolMemNode *harbol_memnode_split(struct HarbolMemNode *const node, const size_t bytes) 9 | { 10 | const uintptr_t n = ( uintptr_t )node; 11 | struct HarbolMemNode *const r = ( struct HarbolMemNode* )(n + (node->size - bytes)); 12 | node->size -= bytes; 13 | r->size = bytes; 14 | return r; 15 | } 16 | 17 | 18 | HARBOL_EXPORT void harbol_memnode_replace(struct HarbolMemNode *const old, struct HarbolMemNode *const replace) 19 | { 20 | /// replace this node. 21 | replace->prev = old->prev; 22 | replace->next = old->next; 23 | if( replace->prev != NULL ) 24 | replace->prev->next = replace; 25 | if( replace->next != NULL ) 26 | replace->next->prev = replace; 27 | } 28 | 29 | 30 | /// makes a current node into the next node. 31 | HARBOL_EXPORT void harbol_freelist_insert_before(struct HarbolFreeList *const list, struct HarbolMemNode *const curr, struct HarbolMemNode *const insert) 32 | { 33 | insert->next = curr; 34 | if( curr->prev==NULL ) { 35 | list->head = insert; 36 | } else { 37 | insert->prev = curr->prev; 38 | curr->prev->next = insert; 39 | } 40 | curr->prev = insert; 41 | } 42 | 43 | static void _harbol_freelist_transfer_node(struct HarbolMemPool *const mempool, struct HarbolMemNode *const node) 44 | { 45 | const size_t slot = (node->size >> HARBOL_BUCKET_BITS) - 1; 46 | struct HarbolFreeList *const l = (slot < HARBOL_BUCKET_SIZE) ? &mempool->buckets[slot] : &mempool->large; 47 | harbol_freelist_insert(mempool, l, node, (slot < HARBOL_BUCKET_SIZE)); 48 | } 49 | 50 | 51 | HARBOL_EXPORT void harbol_freelist_insert(struct HarbolMemPool *const mempool, struct HarbolFreeList *const list, struct HarbolMemNode *const node, const bool is_bucket) 52 | { 53 | if( list->head==NULL ) { 54 | list->head = node; 55 | list->len++; 56 | return; 57 | } 58 | 59 | for( struct HarbolMemNode *iter = list->head; iter != NULL; iter = iter->next ) { 60 | if( ( uintptr_t )iter==mempool->stack.offs ) { 61 | mempool->stack.offs += iter->size; 62 | harbol_freelist_remove(list, iter); 63 | iter = list->head; 64 | continue; 65 | } 66 | const uintptr_t inode = ( uintptr_t )node; 67 | const uintptr_t iiter = ( uintptr_t )iter; 68 | const uintptr_t iter_end = iiter + iter->size; 69 | const uintptr_t node_end = inode + node->size; 70 | 71 | if( iter==node ) { 72 | return; 73 | } else if( iter < node ) { 74 | if( iter_end > inode ) { /// node was coalesced prior. 75 | return; 76 | } else if( iter_end==inode ) { /// if we can coalesce, do so. 77 | iter->size += node->size; 78 | return; 79 | } 80 | } else if( iter > node ) { 81 | /// Address sort, lowest to highest aka ascending order. 82 | if( iter==list->head ) { 83 | if( iter_end==inode ) { 84 | iter->size += node->size; 85 | if( is_bucket ) { 86 | _harbol_freelist_transfer_node(mempool, harbol_freelist_remove(list, iter)); 87 | } 88 | } else if( node_end==iiter ) { 89 | node->size += list->head->size; 90 | node->next = list->head->next; 91 | node->prev = NULL; 92 | list->head = node; 93 | if( is_bucket ) { 94 | _harbol_freelist_transfer_node(mempool, harbol_freelist_remove(list, list->head)); 95 | } 96 | } else { 97 | node->next = iter; 98 | node->prev = NULL; 99 | iter->prev = node; 100 | list->head = node; 101 | list->len++; 102 | } 103 | return; 104 | } else if( iter_end==inode ) { 105 | iter->size += node->size; 106 | if( is_bucket ) { 107 | _harbol_freelist_transfer_node(mempool, harbol_freelist_remove(list, iter)); 108 | } 109 | return; 110 | } else { 111 | harbol_freelist_insert_before(list, iter, node); 112 | list->len++; 113 | return; 114 | } 115 | } 116 | } 117 | } 118 | 119 | HARBOL_EXPORT struct HarbolMemNode *harbol_freelist_remove(struct HarbolFreeList *const list, struct HarbolMemNode *const node) 120 | { 121 | if( node->prev != NULL ) { 122 | node->prev->next = node->next; 123 | } else { 124 | list->head = node->next; 125 | if( list->head != NULL ) 126 | list->head->prev = NULL; 127 | else list->tail = NULL; 128 | } 129 | 130 | if( node->next != NULL ) { 131 | node->next->prev = node->prev; 132 | } else { 133 | list->tail = node->prev; 134 | if( list->tail != NULL ) 135 | list->tail->next = NULL; 136 | else list->head = NULL; 137 | } 138 | list->len--; 139 | return node; 140 | } 141 | 142 | HARBOL_EXPORT struct HarbolMemNode *harbol_freelist_find(struct HarbolFreeList *const list, const size_t bytes) 143 | { 144 | for( struct HarbolMemNode *node = list->head; node != NULL; node = node->next ) { 145 | if( node->size < bytes ) 146 | continue; 147 | /// close in size - reduce fragmentation by not splitting. 148 | else if( node->size <= bytes + MEM_SPLIT_THRESHOLD ) 149 | return harbol_freelist_remove(list, node); 150 | else return harbol_memnode_split(node, bytes); 151 | } 152 | return NULL; 153 | } 154 | 155 | 156 | HARBOL_EXPORT bool harbol_mempool_init(struct HarbolMemPool *const mempool, const size_t size) { 157 | if( size==0 ) 158 | return false; 159 | 160 | mempool->stack = harbol_region_make(size); 161 | return true; 162 | } 163 | 164 | HARBOL_EXPORT struct HarbolMemPool harbol_mempool_make(const size_t size, bool *const res) 165 | { 166 | struct HarbolMemPool mempool = {0}; 167 | *res = harbol_mempool_init(&mempool, size); 168 | return mempool; 169 | } 170 | 171 | HARBOL_EXPORT bool harbol_mempool_init_from_buffer(struct HarbolMemPool *const mempool, void *const restrict buf, const size_t size) { 172 | if( size==0 || size<=sizeof(struct HarbolMemNode) ) 173 | return false; 174 | 175 | mempool->stack = harbol_region_make_from_buffer(buf, size); 176 | return true; 177 | } 178 | 179 | HARBOL_EXPORT struct HarbolMemPool harbol_mempool_make_from_buffer(void *const restrict buf, const size_t size, bool *const restrict res) 180 | { 181 | struct HarbolMemPool mempool = { 0 }; 182 | *res = harbol_mempool_init_from_buffer(&mempool, buf, size); 183 | return mempool; 184 | } 185 | 186 | HARBOL_EXPORT void harbol_mempool_clear(struct HarbolMemPool *const mempool) 187 | { 188 | harbol_region_clear(&mempool->stack); 189 | *mempool = ( struct HarbolMemPool ){0}; 190 | } 191 | 192 | static NO_NULL struct HarbolMemNode *_get_freenode(struct HarbolMemPool *const mempool, const size_t bytes) 193 | { 194 | /// check if we have a good sized node from the buckets. 195 | const size_t slot = (bytes >> HARBOL_BUCKET_BITS) - 1; 196 | struct HarbolFreeList *const l = (slot < HARBOL_BUCKET_SIZE) ? &mempool->buckets[slot] : &mempool->large; 197 | return harbol_freelist_find(l, bytes); 198 | } 199 | 200 | HARBOL_EXPORT void *harbol_mempool_alloc(struct HarbolMemPool *const mempool, const size_t size) 201 | { 202 | if( size==0 || size > mempool->stack.size ) 203 | return NULL; 204 | 205 | /// visual of the allocation block. 206 | /// -------------- 207 | /// | mem size | lowest addr of block 208 | /// | next node | 12 bytes - 32 bit 209 | /// | prev node | 24 bytes - 64 bit 210 | /// |------------| 211 | /// | alloc'd | 212 | /// | memory | 213 | /// | space | highest addr of block 214 | /// -------------- 215 | const size_t alloc_bytes = harbol_align_size(size + sizeof(struct HarbolMemNode), sizeof(intptr_t)); 216 | struct HarbolMemNode *new_mem = _get_freenode(mempool, alloc_bytes); 217 | if( new_mem==NULL ) { 218 | new_mem = harbol_region_alloc(&mempool->stack, alloc_bytes); 219 | if( new_mem==NULL ) 220 | return NULL; 221 | else new_mem->size = alloc_bytes; 222 | } 223 | 224 | new_mem->next = new_mem->prev = NULL; 225 | uint8_t *const final_mem = ( uint8_t* )new_mem + sizeof *new_mem; 226 | return memset(final_mem, 0, new_mem->size - sizeof *new_mem); 227 | } 228 | 229 | HARBOL_EXPORT void *harbol_mempool_realloc(struct HarbolMemPool *const restrict mempool, void *const ptr, const size_t size) 230 | { 231 | if( size > mempool->stack.size ) 232 | return NULL; 233 | /// NULL ptr should make this work like regular alloc. 234 | else if( ptr==NULL ) 235 | return harbol_mempool_alloc(mempool, size); 236 | else if( ( uintptr_t )ptr - sizeof(struct HarbolMemNode) < mempool->stack.mem ) 237 | return NULL; 238 | 239 | struct HarbolMemNode *node = ( struct HarbolMemNode* )(( uint8_t* )ptr - sizeof *node); 240 | uint8_t *resized_block = harbol_mempool_alloc(mempool, size); 241 | if( resized_block==NULL ) 242 | return NULL; 243 | 244 | struct HarbolMemNode *resized = ( struct HarbolMemNode* )(resized_block - sizeof *resized); 245 | memmove(resized_block, ptr, ((node->size > resized->size)? (resized->size) : (node->size)) - sizeof *node); 246 | harbol_mempool_free(mempool, ptr); 247 | return resized_block; 248 | } 249 | 250 | HARBOL_EXPORT bool harbol_mempool_free(struct HarbolMemPool *const restrict mempool, void *const ptr) 251 | { 252 | if( ptr==NULL || ( uintptr_t )ptr - sizeof(struct HarbolMemNode) < mempool->stack.mem ) 253 | return false; 254 | 255 | /// behind the actual pointer data is the allocation info. 256 | struct HarbolMemNode *mem_node = ( struct HarbolMemNode* )(( uint8_t* )ptr - sizeof *mem_node); 257 | const size_t slot = (mem_node->size >> HARBOL_BUCKET_BITS) - 1; 258 | 259 | /// make sure the pointer data is valid. 260 | if( ( uintptr_t )mem_node < mempool->stack.offs 261 | || (( uintptr_t )mem_node - mempool->stack.mem) > mempool->stack.size 262 | || mem_node->size==0 263 | || mem_node->size > mempool->stack.size ) 264 | return false; 265 | 266 | /// if the mem_node is right at the stack base ptr, then add it to the stack. 267 | if( ( uintptr_t )mem_node==mempool->stack.offs ) { 268 | mempool->stack.offs += mem_node->size; 269 | } else { 270 | /// try to place it into bucket or large freelist. 271 | struct HarbolFreeList *const list = ( slot < HARBOL_BUCKET_SIZE ) ? &mempool->buckets[slot] : &mempool->large; 272 | harbol_freelist_insert(mempool, list, mem_node, ( slot < HARBOL_BUCKET_SIZE )); 273 | } 274 | return true; 275 | } 276 | 277 | HARBOL_EXPORT bool harbol_mempool_cleanup(struct HarbolMemPool *const restrict mempool, void **const restrict ptrref) 278 | { 279 | if( *ptrref==NULL ) 280 | return false; 281 | 282 | const bool free_result = harbol_mempool_free(mempool, *ptrref); 283 | *ptrref = NULL; 284 | return free_result; 285 | } 286 | 287 | HARBOL_EXPORT size_t harbol_mempool_mem_remaining(const struct HarbolMemPool *mempool) 288 | { 289 | size_t total_remaining = mempool->stack.offs - mempool->stack.mem; 290 | 291 | for( struct HarbolMemNode *n = mempool->large.head; n != NULL; n = n->next ) 292 | total_remaining += n->size; 293 | 294 | for( size_t i=0; ibuckets[i].head; n != NULL; n = n->next ) 296 | total_remaining += n->size; 297 | 298 | return total_remaining; 299 | } -------------------------------------------------------------------------------- /tagha/allocators/mempool/mempool.h: -------------------------------------------------------------------------------- 1 | #ifndef HARBOL_MEMPOOL_INCLUDED 2 | # define HARBOL_MEMPOOL_INCLUDED 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "../../harbol_common_defines.h" 9 | #include "../../harbol_common_includes.h" 10 | #include "../region/region.h" 11 | 12 | 13 | struct HarbolMemNode { 14 | size_t size; 15 | struct HarbolMemNode *next, *prev; 16 | }; 17 | 18 | HARBOL_EXPORT NO_NULL NONNULL_RET struct HarbolMemNode *harbol_memnode_split(struct HarbolMemNode *const node, const size_t bytes); 19 | HARBOL_EXPORT NO_NULL void harbol_memnode_replace(struct HarbolMemNode *old, struct HarbolMemNode *replace); 20 | 21 | 22 | struct HarbolFreeList { 23 | struct HarbolMemNode *head, *tail; 24 | size_t len; 25 | }; 26 | 27 | struct HarbolMemPool; 28 | 29 | HARBOL_EXPORT NO_NULL void harbol_freelist_insert(struct HarbolMemPool *mempool, struct HarbolFreeList *list, struct HarbolMemNode *node, bool is_bucket); 30 | HARBOL_EXPORT NO_NULL NONNULL_RET struct HarbolMemNode *harbol_freelist_remove(struct HarbolFreeList *list, struct HarbolMemNode *node); 31 | HARBOL_EXPORT NO_NULL void harbol_freelist_insert_before(struct HarbolFreeList *list, struct HarbolMemNode *curr, struct HarbolMemNode *insert); 32 | HARBOL_EXPORT NO_NULL struct HarbolMemNode *harbol_freelist_find(struct HarbolFreeList *list, size_t size); 33 | 34 | 35 | enum { 36 | HARBOL_BUCKET_SIZE = 8, 37 | HARBOL_BUCKET_BITS = 3, 38 | MEM_SPLIT_THRESHOLD = 32 39 | }; 40 | 41 | struct HarbolMemPool { 42 | struct HarbolFreeList 43 | large, /// large free list. 44 | buckets[HARBOL_BUCKET_SIZE] /// bucket free list for smaller allocations. 45 | ; 46 | struct HarbolRegion stack; 47 | }; 48 | 49 | 50 | HARBOL_EXPORT NO_NULL bool harbol_mempool_init(struct HarbolMemPool *mempool, size_t size); 51 | HARBOL_EXPORT struct HarbolMemPool harbol_mempool_make(size_t bytes, bool *res); 52 | 53 | HARBOL_EXPORT NO_NULL bool harbol_mempool_init_from_buffer(struct HarbolMemPool *mempool, void *buf, size_t size); 54 | HARBOL_EXPORT NO_NULL struct HarbolMemPool harbol_mempool_make_from_buffer(void *buf, size_t bytes, bool *res); 55 | 56 | HARBOL_EXPORT NO_NULL void harbol_mempool_clear(struct HarbolMemPool *mempool); 57 | 58 | HARBOL_EXPORT NO_NULL void *harbol_mempool_alloc(struct HarbolMemPool *mempool, size_t bytes); 59 | HARBOL_EXPORT NEVER_NULL(1) void *harbol_mempool_realloc(struct HarbolMemPool *mempool, void *ptr, size_t bytes); 60 | HARBOL_EXPORT NEVER_NULL(1) bool harbol_mempool_free(struct HarbolMemPool *mempool, void *ptr); 61 | HARBOL_EXPORT NO_NULL bool harbol_mempool_cleanup(struct HarbolMemPool *mempool, void **ptrref); 62 | 63 | HARBOL_EXPORT NO_NULL size_t harbol_mempool_mem_remaining(const struct HarbolMemPool *mempool); 64 | /********************************************************************/ 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | 70 | #endif /** HARBOL_MEMPOOL_INCLUDED */ 71 | -------------------------------------------------------------------------------- /tagha/allocators/mempool/test_mempool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mempool.h" 5 | 6 | void test_harbol_mempool(FILE *debug_stream); 7 | 8 | #ifdef HARBOL_USE_MEMPOOL 9 | struct HarbolMemPool *g_pool; 10 | #endif 11 | 12 | union Value { 13 | int64_t int64; 14 | }; 15 | 16 | int main(void) 17 | { 18 | FILE *debug_stream = fopen("harbol_mempool_output.txt", "w"); 19 | if( debug_stream==NULL ) 20 | return -1; 21 | 22 | #ifdef HARBOL_USE_MEMPOOL 23 | struct HarbolMemPool m = harbol_mempool_create(1000000); 24 | g_pool = &m; 25 | #endif 26 | test_harbol_mempool(debug_stream); 27 | 28 | fclose(debug_stream); debug_stream=NULL; 29 | #ifdef HARBOL_USE_MEMPOOL 30 | harbol_mempool_clear(g_pool); 31 | #endif 32 | } 33 | 34 | static void _print_mempool_nodes(const struct HarbolMemPool *const mempool, FILE *const debug_stream) 35 | { 36 | fputs("\nmempool :: printing mempool free bucket.\n", debug_stream); 37 | for( size_t i=0; ibuckets[i].len); 39 | for( struct HarbolMemNode *n=mempool->buckets[i].head; n != NULL; n = n->next ) 40 | fprintf(debug_stream, "mempool bucket[%zu] node :: n (%" PRIuPTR ") size == %zu.\n", i, ( uintptr_t )n, n->size); 41 | } 42 | 43 | fputs("\nmempool :: printing mempool free list.\n", debug_stream); 44 | for( struct HarbolMemNode *n = mempool->large.head; n != NULL; n = n->next ) 45 | fprintf(debug_stream, "mempool list node :: n (%" PRIuPTR ") size == %zu.\n", ( uintptr_t )n, n->size); 46 | 47 | fprintf(debug_stream, "mempool memory remaining :: %zu | freenodes: %zu.\n", harbol_mempool_mem_remaining(mempool), mempool->large.len); 48 | } 49 | 50 | 51 | void test_harbol_mempool(FILE *const debug_stream) 52 | { 53 | /// Test allocation and initializations 54 | fputs("mempool :: test allocation/initialization.\n", debug_stream); 55 | 56 | struct HarbolMemPool i = harbol_mempool_make(1000, &( bool ){false}); 57 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 58 | 59 | const clock_t start = clock(); 60 | /// test giving memory 61 | fputs("mempool :: test giving memory.\n", debug_stream); 62 | fputs("\nmempool :: allocating int ptr.\n", debug_stream); 63 | int *p = harbol_mempool_alloc(&i, sizeof *p); 64 | fprintf(debug_stream, "p is null? '%s'\n", p ? "no" : "yes"); 65 | if( p ) { 66 | *p = 500; 67 | fprintf(debug_stream, "p's value: %i\n", *p); 68 | } 69 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 70 | 71 | fputs("\nmempool :: allocating float ptr.\n", debug_stream); 72 | float *f = harbol_mempool_alloc(&i, sizeof *f); 73 | fprintf(debug_stream, "f is null? '%s'\n", f ? "no" : "yes"); 74 | if( f ) { 75 | *f = 500.5f; 76 | fprintf(debug_stream, "f's value: %f\n", *f); 77 | } 78 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 79 | 80 | /// test releasing memory 81 | fputs("mempool :: test releasing memory.\n", debug_stream); 82 | harbol_mempool_free(&i, f), f=NULL; 83 | harbol_mempool_free(&i, p), p=NULL; 84 | 85 | /// test re-giving memory 86 | fputs("mempool :: test regiving memory.\n", debug_stream); 87 | p = harbol_mempool_alloc(&i, sizeof *p); 88 | fprintf(debug_stream, "p is null? '%s'\n", p ? "no" : "yes"); 89 | if( p ) { 90 | *p = 532; 91 | fprintf(debug_stream, "p's value: %i\n", *p); 92 | } 93 | f = harbol_mempool_alloc(&i, sizeof *f); 94 | fprintf(debug_stream, "f is null? '%s'\n", f ? "no" : "yes"); 95 | if( f ) { 96 | *f = 466.5f; 97 | fprintf(debug_stream, "f's value: %f\n", *f); 98 | } 99 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 100 | harbol_mempool_free(&i, p), p=NULL; /// release memory that's from different region. 101 | harbol_mempool_free(&i, f), f=NULL; 102 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 103 | 104 | /// test giving array memory 105 | fputs("\nmempool :: test giving array memory.\n", debug_stream); 106 | const size_t arrsize = 100; 107 | p = harbol_mempool_alloc(&i, sizeof *p * arrsize); 108 | fprintf(debug_stream, "p is null? '%s'\n", p ? "no" : "yes"); 109 | if( p != NULL ) { 110 | for( size_t i=0; isize, sizeof *list, ( uintptr_t )b, ( uintptr_t )b - i.stack.mem, i.stack.mem); 142 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 143 | 144 | struct UniNode *node1 = harbol_mempool_alloc(&i, sizeof *node1); 145 | assert( node1 ); 146 | 147 | b = ( struct HarbolMemNode* )(( uint8_t* )node1 - sizeof *b); 148 | fprintf(debug_stream, "mempool :: node1 (%" PRIuPTR ") alloc node size: %zu, sizeof *node1: %zu, b node: (%" PRIuPTR "), offset: %" PRIiPTR "\n", ( uintptr_t )node1, b->size, sizeof *node1, ( uintptr_t )b, ( uintptr_t )b - i.stack.mem); 149 | 150 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 151 | node1->data = ( uint8_t* ) &( union Value ){.int64 = 1}; 152 | { list->head = list->tail = node1; } 153 | 154 | struct UniNode *node2 = harbol_mempool_alloc(&i, sizeof *node2); 155 | assert( node2 ); 156 | b = ( struct HarbolMemNode* )(( uint8_t* )node2 - sizeof *b); 157 | fprintf(debug_stream, "mempool :: node2 (%" PRIuPTR ") alloc node size: %zu, sizeof *node2: %zu, b node: (%" PRIuPTR "), offset: %" PRIiPTR "\n", ( uintptr_t )node2, b->size, sizeof *node2, ( uintptr_t )b, ( uintptr_t )b - i.stack.mem); 158 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 159 | node2->data = ( uint8_t* ) &( union Value ){.int64 = 2}; 160 | { list->tail = node2; list->head->next = node2; } 161 | 162 | struct UniNode *node3 = harbol_mempool_alloc(&i, sizeof *node3); 163 | assert( node3 ); 164 | b = ( struct HarbolMemNode* )(( uint8_t* )node3 - sizeof *b); 165 | fprintf(debug_stream, "mempool :: node3 (%" PRIuPTR ") alloc node size: %zu, sizeof *node3: %zu, b node: (%" PRIuPTR "), offset: %" PRIiPTR "\n", ( uintptr_t )node3, b->size, sizeof *node3, ( uintptr_t )b, ( uintptr_t )b - i.stack.mem); 166 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 167 | node3->data = ( uint8_t* ) &( union Value ){.int64 = 3}; 168 | { list->tail->next = node3; list->tail = node3; } 169 | 170 | struct UniNode *node4 = harbol_mempool_alloc(&i, sizeof *node4); 171 | assert( node4 ); 172 | b = ( struct HarbolMemNode* )(( uint8_t* )node4 - sizeof *b); 173 | fprintf(debug_stream, "mempool :: node4 (%" PRIuPTR ") alloc node size: %zu, sizeof *node4: %zu, b node: (%" PRIuPTR "), offset: %" PRIiPTR "\n", ( uintptr_t )node4, b->size, sizeof *node4, ( uintptr_t )b, ( uintptr_t )b - i.stack.mem); 174 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 175 | node4->data = ( uint8_t* ) &( union Value ){.int64 = 4}; 176 | { list->tail->next = node4; list->tail = node4; } 177 | 178 | struct UniNode *node5 = harbol_mempool_alloc(&i, sizeof *node5); 179 | assert( node5 ); 180 | b = ( struct HarbolMemNode* )(( uint8_t* )node5 - sizeof *b); 181 | fprintf(debug_stream, "mempool :: node5 (%" PRIuPTR ") alloc node size: %zu, sizeof *node5: %zu, b node: (%" PRIuPTR "), offset: %" PRIiPTR "\n", ( uintptr_t )node5, b->size, sizeof *node5, ( uintptr_t )b, ( uintptr_t )b - i.stack.mem); 182 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 183 | node5->data = ( uint8_t* ) &( union Value ){.int64 = 5}; 184 | { list->tail->next = node5; list->tail = node5; } 185 | 186 | struct UniNode *node6 = harbol_mempool_alloc(&i, sizeof *node6); 187 | assert( node6 ); 188 | b = ( struct HarbolMemNode* )(( uint8_t* )node6 - sizeof *b); 189 | fprintf(debug_stream, "mempool :: node6 (%" PRIuPTR ") alloc node size: %zu, sizeof *node6: %zu, b node: (%" PRIuPTR "), offset: %" PRIiPTR "\n", ( uintptr_t )node6, b->size, sizeof *node6, ( uintptr_t )b, ( uintptr_t )b - i.stack.mem); 190 | fprintf(debug_stream, "remaining heap mem: '%zu'\n", harbol_mempool_mem_remaining(&i)); 191 | node6->data = ( uint8_t* ) &( union Value ){.int64 = 6}; 192 | { list->tail->next = node6; list->tail = node6; } 193 | 194 | _print_mempool_nodes(&i, debug_stream); 195 | 196 | for( struct UniNode *n=list->head; n != NULL; n = n->next ) 197 | fprintf(debug_stream, "uninode value : %" PRIi64 "\n", (( const union Value* )n->data)->int64); 198 | 199 | harbol_mempool_free(&i, list), list =NULL; 200 | harbol_mempool_free(&i, node1), node1=NULL; 201 | harbol_mempool_free(&i, node2), node2=NULL; 202 | harbol_mempool_free(&i, node3), node3=NULL; 203 | harbol_mempool_free(&i, node4), node4=NULL; 204 | harbol_mempool_free(&i, node5), node5=NULL; 205 | harbol_mempool_free(&i, node6), node6=NULL; 206 | _print_mempool_nodes(&i, debug_stream); 207 | 208 | /// test "double freeing" 209 | fputs("\nmempool :: test double freeing.\n", debug_stream); 210 | p = harbol_mempool_alloc(&i, sizeof *p); 211 | fprintf(debug_stream, "p is null? '%s'\n", p ? "no" : "yes"); 212 | if( p ) { 213 | *p = 500; 214 | fprintf(debug_stream, "p's value: %i\n", *p); 215 | } 216 | harbol_mempool_free(&i, p); 217 | harbol_mempool_free(&i, p); 218 | harbol_mempool_cleanup(&i, (void**)&p); 219 | 220 | fprintf(debug_stream, "\nmempool :: pool size == %zu.\n", harbol_mempool_mem_remaining(&i)); 221 | _print_mempool_nodes(&i, debug_stream); 222 | 223 | float *hk = harbol_mempool_alloc(&i, sizeof *hk * 99); 224 | double *fg = harbol_mempool_alloc(&i, sizeof *fg * 10); 225 | char *fff = harbol_mempool_alloc(&i, sizeof *fff * 50); 226 | float *f32 = harbol_mempool_alloc(&i, sizeof *f32 * 23); 227 | char *jj = harbol_mempool_alloc(&i, sizeof *jj * 100); 228 | struct HarbolMemNode *ac = harbol_mempool_alloc(&i, sizeof *ac * 31); 229 | harbol_mempool_free(&i, fff); 230 | harbol_mempool_free(&i, fg); 231 | harbol_mempool_free(&i, ac); 232 | harbol_mempool_free(&i, f32); 233 | fprintf(debug_stream, "\nmempool :: pool size == %zu.\n", harbol_mempool_mem_remaining(&i)); 234 | _print_mempool_nodes(&i, debug_stream); 235 | fprintf(debug_stream, "mempool :: heap bottom (%zu).\n", i.stack.offs); 236 | 237 | harbol_mempool_free(&i, hk); 238 | fprintf(debug_stream, "\ncrazy mempool :: pool size == %zu.\n", harbol_mempool_mem_remaining(&i)); 239 | _print_mempool_nodes(&i, debug_stream); 240 | 241 | harbol_mempool_free(&i, jj); 242 | 243 | fprintf(debug_stream, "\nlast mempool :: pool size == %zu.\n", harbol_mempool_mem_remaining(&i)); 244 | _print_mempool_nodes(&i, debug_stream); 245 | 246 | 247 | fprintf(debug_stream, "\nmempool :: pool size == %zu.\n", harbol_mempool_mem_remaining(&i)); 248 | fputs("\n", debug_stream); 249 | _print_mempool_nodes(&i, debug_stream); 250 | 251 | fputs("\nmempool :: test reallocating jj to a single value.\n", debug_stream); 252 | jj = harbol_mempool_alloc(&i, sizeof *jj); 253 | *jj = 50; 254 | fprintf(debug_stream, "mempool :: jj == %i.\n", *jj); 255 | 256 | int *newer = harbol_mempool_realloc(&i, jj, sizeof *newer); 257 | fputs("\nmempool :: test reallocating jj to int ptr 'newer'.\n", debug_stream); 258 | fprintf(debug_stream, "mempool :: newer == %i.\n", *newer); 259 | 260 | jj = harbol_mempool_realloc(&i, newer, sizeof *jj); 261 | fputs("\nmempool :: test reallocating newer back to jj.\n", debug_stream); 262 | fprintf(debug_stream, "mempool :: jj == %i.\n", *jj); 263 | 264 | newer = harbol_mempool_realloc(&i, jj, sizeof *newer * 10); 265 | fputs("\nmempool :: test reallocating jj back to newer as an array of int[10].\n", debug_stream); 266 | for( size_t i=0; i<10; i++ ) { 267 | newer[i] = i+1; 268 | fprintf(debug_stream, "mempool :: newer[%zu] == %i.\n", i, newer[i]); 269 | } 270 | fputs("\n", debug_stream); 271 | newer = harbol_mempool_realloc(&i, newer, sizeof *newer * 5); 272 | for( size_t i=0; i<5; i++ ) 273 | fprintf(debug_stream, "mempool :: reallocated newer[%zu] == %i.\n", i, newer[i]); 274 | harbol_mempool_free(&i, newer); 275 | 276 | const clock_t end = clock(); 277 | printf("memory pool run time: %f\n", (end-start)/( double )CLOCKS_PER_SEC); 278 | /// free data 279 | fputs("\nmempool :: test destruction.\n", debug_stream); 280 | harbol_mempool_clear(&i); 281 | fprintf(debug_stream, "i's heap is null? '%s'\n", i.stack.mem != NIL ? "no" : "yes"); 282 | fprintf(debug_stream, "i's freelist is null? '%s'\n", i.large.head != NULL ? "no" : "yes"); 283 | } -------------------------------------------------------------------------------- /tagha/allocators/mempool/valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes -v ./harbol_mempool_test 4 | -------------------------------------------------------------------------------- /tagha/allocators/objpool/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Wextra -pedantic -std=c99 -s -O2 3 | TFLAGS = -Wall -Wextra -pedantic -std=c99 -g -O2 4 | 5 | SRCS = objpool.c 6 | OBJS = $(SRCS:.c=.o) 7 | 8 | harbol_objpool: 9 | $(CC) $(CFLAGS) -c $(SRCS) 10 | 11 | debug: 12 | $(CC) $(TFLAGS) -c $(SRCS) 13 | 14 | test: 15 | $(CC) $(TFLAGS) $(SRCS) test_objpool.c -o harbol_objpool_test 16 | 17 | clean: 18 | $(RM) *.o 19 | $(RM) harbol_objpool_test 20 | -------------------------------------------------------------------------------- /tagha/allocators/objpool/objpool.c: -------------------------------------------------------------------------------- 1 | #include "objpool.h" 2 | 3 | #ifdef OS_WINDOWS 4 | # define HARBOL_LIB 5 | #endif 6 | 7 | 8 | HARBOL_EXPORT NO_NULL bool harbol_objpool_init(struct HarbolObjPool *const objpool, const size_t objsize, const size_t len) { 9 | if( len==0 || objsize==0 ) { 10 | return false; 11 | } 12 | 13 | const size_t aligned_objsize = harbol_align_size(objsize, sizeof(size_t)); 14 | objpool->mem = ( uintptr_t )calloc(len, aligned_objsize); 15 | if( objpool->mem==NIL ) { 16 | return false; 17 | } 18 | 19 | objpool->size = objpool->free_blocks = len; 20 | objpool->objsize = aligned_objsize; 21 | for( size_t i=0; ifree_blocks; i++ ) { 22 | size_t *const restrict index = ( size_t* )(objpool->mem + (i * objpool->objsize)); 23 | *index = i + 1; 24 | } 25 | objpool->next = objpool->mem; 26 | return true; 27 | } 28 | 29 | HARBOL_EXPORT struct HarbolObjPool harbol_objpool_make(const size_t objsize, const size_t len, bool *const res) 30 | { 31 | struct HarbolObjPool objpool = {0}; 32 | *res = harbol_objpool_init(&objpool, objsize, len); 33 | return objpool; 34 | } 35 | 36 | HARBOL_EXPORT NO_NULL bool harbol_objpool_init_from_buffer(struct HarbolObjPool *const restrict objpool, void *const restrict buf, const size_t objsize, const size_t len) { 37 | /// If the object index isn't large enough to align to a size_t, then we can't use it. 38 | if( objsize < sizeof(size_t) || (objsize * len) < (harbol_align_size(objsize, sizeof(size_t)) * len) ) { 39 | return false; 40 | } 41 | 42 | objpool->objsize = harbol_align_size(objsize, sizeof(size_t)); 43 | objpool->size = objpool->free_blocks = len; 44 | objpool->mem = ( uintptr_t )buf; 45 | for( size_t i=0; ifree_blocks; i++ ) { 46 | size_t *const restrict index = ( size_t* )(objpool->mem + (i * objpool->objsize)); 47 | *index = i + 1; 48 | } 49 | objpool->next = objpool->mem; 50 | return true; 51 | } 52 | 53 | HARBOL_EXPORT struct HarbolObjPool harbol_objpool_from_buffer(void *const restrict buf, const size_t objsize, const size_t len, bool *const restrict res) 54 | { 55 | struct HarbolObjPool objpool = {0}; 56 | *res = harbol_objpool_init_from_buffer(&objpool, buf, objsize, len); 57 | return objpool; 58 | } 59 | 60 | HARBOL_EXPORT void harbol_objpool_clear(struct HarbolObjPool *const objpool) 61 | { 62 | if( objpool->mem==NIL ) 63 | return; 64 | 65 | free(( void* )objpool->mem); 66 | *objpool = (struct HarbolObjPool){0}; 67 | } 68 | 69 | HARBOL_EXPORT void *harbol_objpool_alloc(struct HarbolObjPool *const objpool) 70 | { 71 | if( objpool->free_blocks > 0 ) { 72 | /// for first allocation, head points to the very first index. 73 | /// next = &pool[0]; 74 | /// ret = next == ret = &pool[0]; 75 | size_t *const index = ( size_t* )objpool->next; 76 | objpool->free_blocks--; 77 | 78 | /// after allocating, we set head to the address of the index that *next holds. 79 | /// next = &pool[*next * pool.objsize]; 80 | objpool->next = ( objpool->free_blocks != 0 ) ? objpool->mem + (*index * objpool->objsize) : 0; 81 | return memset(index, 0, objpool->objsize); 82 | } 83 | return NULL; 84 | } 85 | 86 | HARBOL_EXPORT void harbol_objpool_free(struct HarbolObjPool *const restrict objpool, void *const restrict ptr) 87 | { 88 | const uintptr_t p = ( uintptr_t )ptr; 89 | if( ptr==NULL || p < objpool->mem || p > objpool->mem + (objpool->size * objpool->objsize) ) { 90 | return; 91 | } 92 | /// when we free our pointer, we recycle the pointer space to store the previous index 93 | /// and then we push it as our new head. 94 | 95 | /// *p = index of next in relation to the buffer; 96 | /// next = p; 97 | { 98 | size_t *const restrict index = ptr; 99 | *index = ( objpool->next != 0 ) ? (objpool->next - objpool->mem) / objpool->objsize : objpool->size; 100 | } 101 | objpool->next = p; 102 | ++objpool->free_blocks; 103 | } 104 | 105 | HARBOL_EXPORT void harbol_objpool_cleanup(struct HarbolObjPool *const restrict objpool, void **const restrict ptrref) 106 | { 107 | if( *ptrref==NULL ) 108 | return; 109 | 110 | harbol_objpool_free(objpool, *ptrref); *ptrref = NULL; 111 | } -------------------------------------------------------------------------------- /tagha/allocators/objpool/objpool.h: -------------------------------------------------------------------------------- 1 | #ifndef HARBOL_OBJPOOL_INCLUDED 2 | # define HARBOL_OBJPOOL_INCLUDED 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "../../harbol_common_defines.h" 9 | #include "../../harbol_common_includes.h" 10 | 11 | 12 | struct HarbolObjPool { 13 | uintptr_t 14 | mem, /// Beginning of memory pool 15 | next /// Num of next free block 16 | ; 17 | size_t 18 | size, /// Num of blocks. 19 | objsize, /// size of each block 20 | free_blocks /// Num of remaining blocks 21 | ; 22 | }; 23 | 24 | HARBOL_EXPORT struct HarbolObjPool harbol_objpool_make(size_t objsize, size_t len, bool *res); 25 | HARBOL_EXPORT NO_NULL bool harbol_objpool_init(struct HarbolObjPool *objpool, size_t objsize, size_t len); 26 | HARBOL_EXPORT NO_NULL struct HarbolObjPool harbol_objpool_from_buffer(void *buf, size_t objsize, size_t len, bool *res); 27 | HARBOL_EXPORT NO_NULL bool harbol_objpool_init_from_buffer(struct HarbolObjPool *objpool, void *buf, size_t objsize, size_t len); 28 | HARBOL_EXPORT NO_NULL void harbol_objpool_clear(struct HarbolObjPool *objpool); 29 | 30 | HARBOL_EXPORT NO_NULL void *harbol_objpool_alloc(struct HarbolObjPool *objpool); 31 | HARBOL_EXPORT NEVER_NULL(1) void harbol_objpool_free(struct HarbolObjPool *objpool, void *ptr); 32 | HARBOL_EXPORT NO_NULL void harbol_objpool_cleanup(struct HarbolObjPool *objpool, void **ptrref); 33 | /********************************************************************/ 34 | 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif /** HARBOL_OBJPOOL_INCLUDED */ -------------------------------------------------------------------------------- /tagha/allocators/objpool/test_objpool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "objpool.h" 5 | 6 | void test_harbol_objpool(FILE *debug_stream); 7 | 8 | #ifdef HARBOL_USE_MEMPOOL 9 | struct HarbolMemPool *g_pool; 10 | #endif 11 | 12 | union Value { 13 | int64_t int64; 14 | }; 15 | 16 | int main(void) 17 | { 18 | FILE *debug_stream = fopen("harbol_objpool_output.txt", "w"); 19 | if( debug_stream==NULL ) 20 | return -1; 21 | 22 | #ifdef HARBOL_USE_MEMPOOL 23 | struct HarbolMemPool m = harbol_mempool_create(1000000); 24 | g_pool = &m; 25 | #endif 26 | test_harbol_objpool(debug_stream); 27 | 28 | fclose(debug_stream); debug_stream=NULL; 29 | #ifdef HARBOL_USE_MEMPOOL 30 | harbol_mempool_clear(g_pool); 31 | #endif 32 | } 33 | 34 | 35 | void test_harbol_objpool(FILE *const debug_stream) 36 | { 37 | /// Test allocation and initializations 38 | fputs("objpool :: test allocation/initialization.\n", debug_stream); 39 | 40 | struct HarbolObjPool i = harbol_objpool_make(sizeof(union Value), 5, &( bool ){false}); 41 | fprintf(debug_stream, "remaining object pool mem: '%zu'\n", i.free_blocks); 42 | 43 | fputs("\nobjpool :: test allocing values.\n", debug_stream); 44 | union Value *valtable[5] = {NULL}; 45 | for( size_t n=0; n<(1[&valtable] - valtable); n++ ) { 46 | valtable[n] = harbol_objpool_alloc(&i); 47 | valtable[n]->int64 = n + 1; 48 | fprintf(debug_stream, "valtable[%zu]: %" PRIi64 "\n", n, valtable[n]->int64); 49 | fprintf(debug_stream, "post-allocation remaining object pool mem: '%zu'\n", i.free_blocks); 50 | } 51 | 52 | fputs("\nobjpool :: test freeing values.\n", debug_stream); 53 | for( size_t n=0; n<(1[&valtable] - valtable); n++ ) { 54 | harbol_objpool_free(&i, valtable[n]); 55 | fprintf(debug_stream, "post-free remaining object pool mem: '%zu'\n", i.free_blocks); 56 | } 57 | for( size_t n=0; n<(1[&valtable] - valtable); n++ ) { 58 | valtable[n] = harbol_objpool_alloc(&i); 59 | valtable[n]->int64 = n + 1; 60 | fprintf(debug_stream, "valtable[%zu]: %" PRIi64 "\n", n, valtable[n]->int64); 61 | fprintf(debug_stream, "post-allocation remaining object pool mem: '%zu'\n", i.free_blocks); 62 | } 63 | 64 | /// free data 65 | fputs("\nobjpool :: test destruction.\n", debug_stream); 66 | harbol_objpool_clear(&i); 67 | fprintf(debug_stream, "i's heap is null? '%s'\n", i.mem != NIL ? "no" : "yes"); 68 | fprintf(debug_stream, "i's next is null? '%s'\n", i.next != NIL ? "no" : "yes"); 69 | } -------------------------------------------------------------------------------- /tagha/allocators/objpool/valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes -v ./harbol_objpool_test 4 | -------------------------------------------------------------------------------- /tagha/allocators/region/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Wextra -pedantic -std=c99 -s -O2 3 | TFLAGS = -Wall -Wextra -pedantic -std=c99 -g -O2 4 | 5 | SRCS = region.c 6 | OBJS = $(SRCS:.c=.o) 7 | 8 | harbol_region: 9 | $(CC) $(CFLAGS) -c $(SRCS) 10 | 11 | debug: 12 | $(CC) $(TFLAGS) -c $(SRCS) 13 | 14 | test: 15 | $(CC) $(TFLAGS) $(SRCS) test_region.c -o harbol_region_test 16 | 17 | clean: 18 | $(RM) *.o 19 | $(RM) harbol_region_test 20 | -------------------------------------------------------------------------------- /tagha/allocators/region/region.c: -------------------------------------------------------------------------------- 1 | #include "region.h" 2 | 3 | #ifdef OS_WINDOWS 4 | # define HARBOL_LIB 5 | #endif 6 | 7 | 8 | HARBOL_EXPORT struct HarbolRegion harbol_region_make(const size_t size) 9 | { 10 | struct HarbolRegion region = {0}; 11 | if( size==0 ) 12 | return region; 13 | 14 | region.mem = ( uintptr_t )calloc(size, sizeof(uint8_t)); 15 | if( region.mem==NIL ) { 16 | return region; 17 | } 18 | region.size = size; 19 | region.offs = region.mem + size; 20 | return region; 21 | } 22 | 23 | HARBOL_EXPORT struct HarbolRegion harbol_region_make_from_buffer(void *const restrict buf, const size_t size) 24 | { 25 | struct HarbolRegion region = {0}; 26 | if( size==0 ) 27 | return region; 28 | 29 | region.size = size; 30 | region.mem = ( uintptr_t )buf; 31 | region.offs = region.mem + size; 32 | return region; 33 | } 34 | 35 | HARBOL_EXPORT void harbol_region_clear(struct HarbolRegion *const region) 36 | { 37 | if( region->mem==NIL ) 38 | return; 39 | 40 | free(( void* )region->mem); 41 | *region = (struct HarbolRegion){0}; 42 | } 43 | 44 | HARBOL_EXPORT void *harbol_region_alloc(struct HarbolRegion *const region, const size_t size) 45 | { 46 | if( region->mem==0 || size==0 || size > region->size ) 47 | return NULL; 48 | 49 | const size_t alloc_size = harbol_align_size(size, sizeof(uintptr_t)); 50 | if( region->offs - alloc_size < region->mem ) 51 | return NULL; 52 | 53 | region->offs -= alloc_size; 54 | return memset(( void* )region->offs, 0, alloc_size); 55 | } 56 | 57 | HARBOL_EXPORT size_t harbol_region_remaining(const struct HarbolRegion *const region) 58 | { 59 | return region->offs - region->mem; 60 | } -------------------------------------------------------------------------------- /tagha/allocators/region/region.h: -------------------------------------------------------------------------------- 1 | #ifndef HARBOL_REGION_INCLUDED 2 | # define HARBOL_REGION_INCLUDED 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "../../harbol_common_defines.h" 9 | #include "../../harbol_common_includes.h" 10 | 11 | 12 | struct HarbolRegion { 13 | uintptr_t mem, offs; 14 | size_t size; 15 | }; 16 | 17 | HARBOL_EXPORT struct HarbolRegion harbol_region_make(size_t bytes); 18 | HARBOL_EXPORT NO_NULL struct HarbolRegion harbol_region_make_from_buffer(void *buf, size_t bytes); 19 | HARBOL_EXPORT NO_NULL void harbol_region_clear(struct HarbolRegion *cache); 20 | 21 | HARBOL_EXPORT NO_NULL void *harbol_region_alloc(struct HarbolRegion *cache, size_t bytes); 22 | HARBOL_EXPORT NO_NULL size_t harbol_region_remaining(const struct HarbolRegion *cache); 23 | /********************************************************************/ 24 | 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif /** HARBOL_REGION_INCLUDED */ -------------------------------------------------------------------------------- /tagha/allocators/region/test_region.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "region.h" 5 | 6 | void test_harbol_region(FILE *debug_stream); 7 | 8 | #ifdef HARBOL_USE_MEMPOOL 9 | struct HarbolMemPool *g_pool; 10 | #endif 11 | 12 | union Value { 13 | int64_t int64; 14 | }; 15 | 16 | int main(void) 17 | { 18 | FILE *debug_stream = fopen("harbol_region_output.txt", "w"); 19 | if( debug_stream==NULL ) 20 | return -1; 21 | 22 | #ifdef HARBOL_USE_MEMPOOL 23 | struct HarbolMemPool m = harbol_mempool_create(1000000); 24 | g_pool = &m; 25 | #endif 26 | test_harbol_region(debug_stream); 27 | 28 | fclose(debug_stream); debug_stream=NULL; 29 | #ifdef HARBOL_USE_MEMPOOL 30 | harbol_mempool_clear(g_pool); 31 | #endif 32 | } 33 | 34 | 35 | void test_harbol_region(FILE *const debug_stream) 36 | { 37 | /// Test allocation and initializations 38 | fputs("region :: test allocation/initialization.\n", debug_stream); 39 | 40 | struct HarbolRegion i = harbol_region_make(sizeof(union Value) * 10); 41 | fprintf(debug_stream, "remaining region mem: '%zu'\n", harbol_region_remaining(&i)); 42 | 43 | fputs("\nregion :: test allocing values.\n", debug_stream); 44 | union Value *valtable[10] = {NULL}; 45 | for( size_t n=0; n<(1[&valtable] - valtable); n++ ) { 46 | valtable[n] = harbol_region_alloc(&i, sizeof *valtable[0]); 47 | valtable[n]->int64 = n + 1; 48 | fprintf(debug_stream, "valtable[%zu]: %" PRIi64 "\nremaining region mem: '%zu'\n", n, valtable[n]->int64, harbol_region_remaining(&i)); 49 | } 50 | 51 | fputs("\nregion :: test allocing differently sized values.\n", debug_stream); 52 | harbol_region_clear(&i); 53 | i = harbol_region_make(1000); 54 | 55 | /// on creation, the offset ptr for the region points to invalid memory. 56 | fprintf(debug_stream, "pre-alloc offset ptr: '%" PRIuPTR "'\n", i.offs); 57 | 58 | float32_t *f = harbol_region_alloc(&i, sizeof *f); 59 | *f = 32.f; 60 | fprintf(debug_stream, "post-alloc offset ptr: '%" PRIuPTR "'\n", i.offs); 61 | 62 | 63 | float64_t (*v)[3] = harbol_region_alloc(&i, sizeof *v); 64 | 65 | (*v)[0] = 3.; 66 | (*v)[1] = 5.; 67 | (*v)[2] = 10.; 68 | fprintf(debug_stream, "remaining region mem: '%zu'\nf value: %" PRIf32 "\nvec values: { %" PRIf64 ", %" PRIf64 ", %" PRIf64 " } | is aligned? %u\n", harbol_region_remaining(&i), *f, (*v)[0], (*v)[1], (*v)[2], is_ptr_aligned(v, sizeof(uintptr_t))); 69 | 70 | /// free data 71 | fputs("\nregion :: test destruction.\n", debug_stream); 72 | harbol_region_clear(&i); 73 | fprintf(debug_stream, "i's base is null? '%s'\n", i.mem != NIL ? "no" : "yes"); 74 | } -------------------------------------------------------------------------------- /tagha/allocators/region/valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes -v ./harbol_region_test 4 | -------------------------------------------------------------------------------- /tagha/harbol_common_defines.h: -------------------------------------------------------------------------------- 1 | #ifndef HARBOL_COMMON_DEFINES_INCLUDED 2 | # define HARBOL_COMMON_DEFINES_INCLUDED 3 | 4 | /** Check if Windows */ 5 | #if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) 6 | # ifndef OS_WINDOWS 7 | # define OS_WINDOWS 1 8 | # endif 9 | 10 | /** Check if Linux/UNIX & FreeBSD */ 11 | #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__linux__) || defined(linux) || defined(__linux) || defined(__freeBSD__) 12 | # ifndef OS_LINUX_UNIX 13 | # define OS_LINUX_UNIX 1 14 | # endif 15 | 16 | /** Check if Android */ 17 | #elif defined(__ANDROID__) 18 | # ifndef OS_ANDROID 19 | # define OS_ANDROID 1 20 | # endif 21 | 22 | /** Check if Solaris/SunOS */ 23 | #elif defined(sun) || defined(__sun) 24 | # if defined(__SVR4) || defined(__svr4__) 25 | # ifndef OS_SOLARIS 26 | # define OS_SOLARIS 1 27 | # endif 28 | # else 29 | # ifndef OS_SUNOS 30 | # define OS_SUNOS 1 31 | # endif 32 | # endif 33 | 34 | /** Check if Macintosh/MacOS/iOS */ 35 | #elif defined(macintosh) || defined(Macintosh) || defined(__APPLE__) 36 | # include "TargetConditionals.h" 37 | # if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR 38 | # ifndef OS_IPHONE_SIM 39 | # define OS_IPHONE_SIM 1 40 | # endif 41 | # ifndef OS_IPHONE 42 | # define OS_IPHONE 1 43 | # endif 44 | # elif TARGET_OS_IPHONE 45 | # ifndef OS_IPHONE 46 | # define OS_IPHONE 1 47 | # endif 48 | # else 49 | # ifndef OS_MAC 50 | # define OS_MAC 1 51 | # endif 52 | # endif 53 | 54 | #endif /** end OS checks */ 55 | 56 | /** check what compiler we got */ 57 | #if defined(__clang__) 58 | # ifndef COMPILER_CLANG 59 | # define COMPILER_CLANG 60 | # endif 61 | #elif defined(__GNUC__) || defined(__GNUG__) 62 | # ifndef COMPILER_GCC 63 | # define COMPILER_GCC 64 | # endif 65 | #elif defined(_MSC_VER) 66 | # ifndef COMPILER_MSVC 67 | # define COMPILER_MSVC 68 | # endif 69 | #elif defined(__INTEL_COMPILER) 70 | # ifndef COMPILER_INTEL 71 | # define COMPILER_INTEL 72 | # endif 73 | #endif /** end compiler check macros */ 74 | 75 | /** check arch platform. */ 76 | #if defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) || defined(_M_AMD64) 77 | # ifndef PLATFORM_AMD64 78 | # define PLATFORM_AMD64 79 | # endif 80 | # ifndef PLATFORM_x64 81 | # define PLATFORM_x64 82 | # endif 83 | #elif defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__IA32__) || defined(_M_IX86) || defined(__X86__) || defined(_X86_) || defined(__I86__) || defined(__386) 84 | # ifndef PLATFORM_IA32 85 | # define PLATFORM_IA32 86 | # endif 87 | # ifndef PLATFORM_x86 88 | # define PLATFORM_x86 89 | # endif 90 | #elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || defined(__ia64) || defined(_M_IA64) || defined(__itanium__) 91 | # ifndef PLATFORM_IA64 92 | # define PLATFORM_IA64 93 | # endif 94 | # ifndef PLATFORM_ITANIUM 95 | # define PLATFORM_ITANIUM 96 | # endif 97 | #elif defined(__arm__) || defined(_ARM) || defined(_M_ARM) || defined(__arm) 98 | # ifndef PLATFORM_ARM32 99 | # define PLATFORM_ARM32 100 | # endif 101 | #elif defined(__aarch64__) 102 | # ifndef PLATFORM_ARM64 103 | # define PLATFORM_ARM64 104 | # endif 105 | #elif defined(__riscv) || defined(_riscv) || defined(__riscv__) 106 | # ifndef PLATFORM_RISCV 107 | # define PLATFORM_RISCV 108 | # endif 109 | # if defined(__riscv_xlen) && __riscv_xlen==32 110 | # ifndef PLATFORM_RISCV32 111 | # define PLATFORM_RISCV32 112 | # endif 113 | # elif defined(__riscv_xlen) && __riscv_xlen==64 114 | # ifndef PLATFORM_RISCV64 115 | # define PLATFORM_RISCV64 116 | # endif 117 | # endif 118 | #endif /** end platform arch defines. */ 119 | 120 | /** set up the C standard macros! */ 121 | #ifdef __STDC__ 122 | # ifndef C89 123 | # define C89 124 | # endif 125 | # ifdef __STDC_VERSION__ 126 | # ifndef C90 127 | # define C90 128 | # endif 129 | # if (__STDC_VERSION__ >= 199409L) 130 | # ifndef C94 131 | # define C94 132 | # endif 133 | # endif 134 | # if (__STDC_VERSION__ >= 199901L) 135 | # ifndef C99 136 | # define C99 137 | # endif 138 | # endif 139 | # if (__STDC_VERSION__ >= 201112L) 140 | # ifndef C11 141 | # define C11 142 | # endif 143 | # endif 144 | # if (__STDC_VERSION__ >= 201710L) 145 | # ifndef C18 146 | # define C18 147 | # endif 148 | # endif 149 | # endif 150 | #endif 151 | 152 | #ifdef __cplusplus 153 | # if( __cplusplus >= 199711L ) 154 | # ifndef CPP03 155 | # define CPP03 156 | # endif 157 | # endif 158 | # if( __cplusplus >= 201103L ) 159 | # ifndef CPP11 160 | # define CPP11 161 | # endif 162 | # endif 163 | # if( __cplusplus >= 201402L ) 164 | # ifndef CPP14 165 | # define CPP14 166 | # endif 167 | # endif 168 | # if( __cplusplus >= 201703L ) 169 | # ifndef CPP17 170 | # define CPP17 171 | # endif 172 | # endif 173 | # if( __cplusplus >= 202002L ) 174 | # ifndef CPP20 175 | # define CPP20 176 | # endif 177 | # endif 178 | #endif 179 | 180 | 181 | /** setup RAII destructor macro if possibru to mark functions as cleaner-uppers. */ 182 | #ifndef RAII_DTOR 183 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 184 | # define RAII_DTOR(func) __attribute__ ((cleanup((func)))) 185 | # else 186 | # define RAII_DTOR(func) 187 | # endif 188 | #endif 189 | 190 | /** setup macro to mark a param as "cannot or can never be NULL". */ 191 | #ifndef NEVER_NULL 192 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 193 | # define NEVER_NULL(...) __attribute__( (nonnull(__VA_ARGS__)) ) 194 | # else 195 | # define NEVER_NULL(...) 196 | # endif 197 | #endif 198 | 199 | /** setup macro that declares all params to never be NULL. */ 200 | #ifndef NO_NULL 201 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 202 | # define NO_NULL __attribute__((nonnull)) 203 | # else 204 | # define NO_NULL 205 | # endif 206 | #endif 207 | 208 | /** setup macro to mark a function as never returning a null pointer. */ 209 | #ifndef NONNULL_RET 210 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 211 | # define NONNULL_RET __attribute__((returns_nonnull)) 212 | # else 213 | # define NONNULL_RET 214 | # endif 215 | #endif 216 | 217 | /** setup macro that marks a function as deprecated. */ 218 | #ifndef DEPRECATED 219 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 220 | # define DEPRECATED(func) func __attribute__ ((deprecated)) 221 | # elif defined(COMPILER_MSVC) 222 | # define DEPRECATED(func) __declspec(deprecated) func 223 | # else 224 | # define DEPRECATED(func) 225 | # endif 226 | #endif 227 | 228 | /** setup macro that defines a var that it may be aliased by other data. */ 229 | #ifndef PTR_ALIAS 230 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 231 | # define PTR_ALIAS __attribute__ ((__may_alias__)) 232 | # else 233 | # define PTR_ALIAS 234 | # endif 235 | #endif 236 | 237 | /** setup macro that marks a function as having hidden visibility. */ 238 | #ifndef VIS_HIDDEN 239 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 240 | # define VIS_HIDDEN __attribute__ ((visibility ("hidden"))) 241 | # else 242 | # define VIS_HIDDEN 243 | # endif 244 | #endif 245 | 246 | /** setup macro that marks a function as having internal visibility. */ 247 | #ifndef VIS_INTERNAL 248 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 249 | # define VIS_INTERNAL __attribute__ ((visibility ("internal"))) 250 | # else 251 | # define VIS_INTERNAL 252 | # endif 253 | #endif 254 | 255 | /** setup macro that marks a function as having protected visibility. */ 256 | #ifndef VIS_PROTECTED 257 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 258 | # define VIS_PROTECTED __attribute__ ((visibility ("protected"))) 259 | # else 260 | # define VIS_PROTECTED 261 | # endif 262 | #endif 263 | 264 | /** setup macro that gives a warning if a function's result is unused. */ 265 | #ifndef WARN_UNUSED_RESULT 266 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 267 | # define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) 268 | # else 269 | # define WARN_UNUSED_RESULT 270 | # endif 271 | #endif 272 | 273 | /** setup macro to mark a function or variable as unused and ignore unused warnings. */ 274 | #ifndef UNUSED 275 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 276 | # define UNUSED __attribute__ ((unused)) 277 | # else 278 | # define UNUSED 279 | # endif 280 | #endif 281 | 282 | /** setup macro to mark a function as a hot spot, thus requiring aggressive optimizations. */ 283 | #ifndef HOT 284 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 285 | # define HOT __attribute__ ((hot)) 286 | # else 287 | # define HOT 288 | # endif 289 | #endif 290 | 291 | /** setup macro to make vector types. Argument must be power of 2. 292 | * Example: 293 | typedef __attribute__ ((vector_size (32))) int int_vec32_t; which makes int_vec32_t as 32-bytes. 294 | * 295 | * All the basic integer types can be used as base types, both as signed and as unsigned: char, short, int, long, long long. In addition, float and double can be used to build floating-point vector types. 296 | * +, -, *, /, unary minus, ^, |, &, ~, %. are the only operators allowed. 297 | * 298 | * Bigger Example: 299 | typedef int v4si SIMD_VEC(16); 300 | v4si a = {1,2,3,4}; 301 | v4si b = {3,2,1,4}; 302 | v4si c; 303 | 304 | c = a > b; /// The result would be {0, 0,-1, 0} 305 | c = a == b; /// The result would be {0,-1, 0,-1} 306 | */ 307 | #ifndef SIMD_VEC 308 | # if defined(COMPILER_CLANG) || defined(COMPILER_GCC) 309 | # define SIMD_VEC(bytes) __attribute__ ((vector_size ((bytes)))) 310 | # else 311 | # define SIMD_VEC(bytes) 312 | # endif 313 | #endif 314 | 315 | 316 | #ifdef HARBOL_DLL 317 | # ifndef HARBOL_LIB 318 | # define HARBOL_EXPORT __declspec(dllimport) 319 | # else 320 | # define HARBOL_EXPORT __declspec(dllexport) 321 | # endif 322 | #else 323 | # define HARBOL_EXPORT 324 | #endif 325 | 326 | #ifdef __cplusplus 327 | # ifdef OS_WINDOWS 328 | # ifndef restrict 329 | # define restrict __restrict 330 | # endif 331 | # else 332 | # ifndef restrict 333 | # define restrict __restrict__ 334 | # endif 335 | # endif 336 | #endif 337 | 338 | 339 | #ifdef COMPILER_MSVC 340 | # ifndef inline 341 | # define inline __inline 342 | # endif 343 | #elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L 344 | # ifndef inline 345 | # define inline __inline__ 346 | # endif 347 | #endif 348 | 349 | 350 | #ifdef C11 351 | # ifndef IS_VAR_OF_TYPE 352 | # define IS_VAR_OF_TYPE(n, T) _Generic((n), T:true, default:false) 353 | # endif 354 | #endif 355 | 356 | #ifndef IN_BOUNDS 357 | # ifdef __cplusplus 358 | template< typename T > static inline T _in_bounds(T val, T max, T min) { 359 | return (val - min) <= (max - min); 360 | } 361 | # define IN_BOUNDS _in_bounds 362 | # else 363 | # define IN_BOUNDS(val, max, min) ( ((val) - (min)) <= ((max) - (min)) ) 364 | # endif 365 | #endif 366 | 367 | #endif /** HARBOL_COMMON_DEFINES_INCLUDED */ -------------------------------------------------------------------------------- /tagha/harbol_common_includes.h: -------------------------------------------------------------------------------- 1 | #ifndef HARBOL_COMMON_INCLUDES_INCLUDED 2 | # define HARBOL_COMMON_INCLUDES_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | /** placing this here so we can get this after including inttypes.h */ 15 | #if defined(INTPTR_MAX) 16 | # if defined(INT32_MAX) && INTPTR_MAX==INT32_MAX 17 | # ifndef HARBOL32 18 | # define HARBOL32 19 | # endif 20 | # endif 21 | # if defined(INT64_MAX) && INTPTR_MAX==INT64_MAX 22 | # ifndef HARBOL64 23 | # define HARBOL64 24 | # endif 25 | # endif 26 | #endif 27 | 28 | 29 | #ifndef NIL 30 | # define NIL ( uintptr_t )NULL 31 | #endif 32 | 33 | 34 | /** types as defined by Harbol. */ 35 | #ifndef __ssize_t_defined 36 | typedef long ssize_t; 37 | # define __ssize_t_defined 38 | #endif 39 | 40 | 41 | /** According to C99 standards. 42 | * there are three floating point types: float, double, and long double. 43 | * 44 | * The type double provides at least as much precision as float, and the type long double provides at least as much precision as double. 45 | * 46 | * so in summary: float <= double <= long double 47 | */ 48 | #ifndef __float32_t_defined 49 | # if FLT_MANT_DIG==24 50 | # define __float32_t_defined 51 | # define PRIf32 "f" 52 | # define SCNf32 "f" 53 | # define SCNxf32 "a" 54 | # define strtof32 strtof 55 | typedef float float32_t; 56 | # elif DBL_MANT_DIG==24 57 | # define __float32_t_defined 58 | # define PRIf32 "f" 59 | # define SCNf32 "lf" 60 | # define SCNxf32 "la" 61 | # define strtof32 strtod 62 | typedef double float32_t; 63 | # else 64 | # error "no appropriate float32_t implementation" 65 | # endif 66 | #endif 67 | 68 | #ifdef C11 69 | _Static_assert(sizeof(float32_t) * CHAR_BIT == 32, "Unexpected `float32_t` size"); 70 | #endif 71 | 72 | 73 | #ifndef __float64_t_defined 74 | # if DBL_MANT_DIG==53 75 | # define __float64_t_defined 76 | # define PRIf64 "f" 77 | # define SCNf64 "lf" 78 | # define SCNxf64 "la" 79 | # define strtof64 strtod 80 | typedef double float64_t; 81 | # elif LDBL_MANT_DIG==53 82 | # define __float64_t_defined 83 | # define PRIf64 "Lf" 84 | # define SCNf64 "Lf" 85 | # define SCNxf64 "La" 86 | # define strtof64 strtold 87 | typedef long double float64_t; 88 | /// This is unlikely but check just in case. 89 | # elif FLT_MANT_DIG==53 90 | # define __float64_t_defined 91 | # define PRIf64 "f" 92 | # define SCNf64 "f" 93 | # define SCNxf64 "a" 94 | # define strtof64 strtof 95 | typedef float float64_t; 96 | # else 97 | # error "no appropriate float64_t implementation" 98 | # endif 99 | #endif 100 | 101 | #ifdef C11 102 | _Static_assert(sizeof(float64_t) * CHAR_BIT == 64, "Unexpected `float64_t` size"); 103 | #endif 104 | 105 | 106 | #ifndef __floatptr_t_defined 107 | # if defined(HARBOL64) 108 | # define __floatptr_t_defined 109 | # define PRIfPTR PRIf64 110 | # define strtofptr strtof64 111 | # define SCNfPTR SCNf64 112 | # define SCNxfPTR SCNxf64 113 | typedef float64_t floatptr_t; 114 | # elif defined(HARBOL32) 115 | # define __floatptr_t_defined 116 | # define PRIfPTR PRIf32 117 | # define strtofptr strtof32 118 | # define SCNfPTR SCNf32 119 | # define SCNxfPTR SCNxf32 120 | typedef float32_t floatptr_t; 121 | # else 122 | # error "no appropriate floatptr_t implementation" 123 | # endif 124 | #endif 125 | 126 | #ifdef C11 127 | _Static_assert(sizeof(floatptr_t)==sizeof(intptr_t), "Unexpected `floatptr_t` size"); 128 | #endif 129 | 130 | 131 | #ifndef __floatmax_t_defined 132 | # if LDBL_MANT_DIG > DBL_MANT_DIG 133 | # define __floatmax_t_defined 134 | # define PRIfMAX "Lf" 135 | # define SCNfMAX "Lf" 136 | # define SCNxfMAX "La" 137 | # define strtofmax strtold 138 | typedef long double floatmax_t; 139 | # elif DBL_MANT_DIG==LDBL_MANT_DIG && DBL_MANT_DIG > FLT_MANT_DIG 140 | # define __floatmax_t_defined 141 | # define PRIfMAX "f" 142 | # define SCNfMAX "lf" 143 | # define SCNxfMAX "la" 144 | # define strtofmax strtod 145 | typedef double floatmax_t; 146 | # elif DBL_MANT_DIG==FLT_MANT_DIG 147 | # define __floatmax_t_defined 148 | # define PRIfMAX "f" 149 | # define SCNfMAX "f" 150 | # define SCNxfMAX "a" 151 | # define strtofmax strtof 152 | typedef float floatmax_t; 153 | # else 154 | # error "no appropriate floatmax_t implementation" 155 | # endif 156 | #endif 157 | 158 | 159 | static inline void *harbol_recalloc(void *const arr, const size_t new_size, const size_t element_size, const size_t old_size) { 160 | if( arr==NULL || old_size==0 ) 161 | return calloc(new_size, element_size); 162 | 163 | #ifdef __cplusplus 164 | uint8_t *const restrict new_block = reinterpret_cast< decltype(new_block) >(realloc(arr, new_size * element_size)); 165 | #else 166 | uint8_t *const restrict new_block = realloc(arr, new_size * element_size); 167 | #endif 168 | if( new_block==NULL ) 169 | return NULL; 170 | 171 | if( old_size < new_size ) 172 | memset(&new_block[old_size * element_size], 0, (new_size - old_size) * element_size); 173 | 174 | return new_block; 175 | } 176 | 177 | static inline void harbol_cleanup(void *const ptr_ref) { 178 | #ifdef __cplusplus 179 | void **const p = reinterpret_cast< decltype(p) >(ptr_ref); 180 | #else 181 | void **const p = ptr_ref; 182 | #endif 183 | free(*p); *p = NULL; 184 | } 185 | 186 | static inline NO_NULL void *harbol_mempcpy(void *const dest, const void *const src, const size_t bytes) { 187 | #ifdef __cplusplus 188 | uint8_t *const r = reinterpret_cast< decltype(r) >(memcpy(dest, src, bytes)); 189 | return r + bytes; 190 | #else 191 | return (( uint8_t* )memcpy(dest, src, bytes) + bytes); 192 | #endif 193 | } 194 | 195 | static inline NO_NULL void *harbol_memccpy(void *const restrict dest, const void *const src, const int c, const size_t bytes) { 196 | #ifdef __cplusplus 197 | const uint8_t *const p = reinterpret_cast< decltype(p) >(memchr(src, c, bytes)); 198 | if( p != nullptr ) { 199 | const uint8_t *const s = reinterpret_cast< decltype(s) >(src); 200 | return harbol_mempcpy(dest, src, (p - s + 1)); 201 | } 202 | memcpy(dest, src, bytes); 203 | return nullptr; 204 | #else 205 | const uint8_t *const p = memchr(src, c, bytes); 206 | if( p != NULL ) { 207 | return harbol_mempcpy(dest, src, (p - ( const uint8_t* )src + 1)); 208 | } 209 | memcpy(dest, src, bytes); 210 | return NULL; 211 | #endif 212 | } 213 | 214 | 215 | static inline size_t harbol_align_size(const size_t size, const size_t align) { 216 | return (size + (align - 1)) & ~(align - 1); 217 | } 218 | 219 | static inline size_t harbol_pad_size(const size_t size, const size_t align) { 220 | return (align - (size & (align - 1))) & (align - 1); 221 | } 222 | 223 | 224 | /// these are NOT cryptographic hashes. 225 | /// use ONLY FOR HASH TABLE IMPLEMENTATIONS. 226 | #ifdef __cplusplus 227 | static inline NO_NULL size_t string_hash(const char *const key) 228 | #else 229 | static inline size_t string_hash(const char key[static 1]) 230 | #endif 231 | { 232 | size_t h = 0; 233 | for( size_t i=0; key[i] != 0; i++ ) { 234 | h = ( size_t )key[i] + (h << 6) + (h << 16) - h; 235 | } 236 | return h; 237 | } 238 | 239 | 240 | #ifdef __cplusplus 241 | static inline NO_NULL size_t array_hash(const uint8_t *const key, const size_t len) 242 | #else 243 | static inline size_t array_hash(const uint8_t key[static 1], const size_t len) 244 | #endif 245 | { 246 | size_t h = 0; 247 | for( size_t i=0; i> n) & 0xFF) + (h << 6) + (h << 16) - h; 257 | } 258 | return h; 259 | } 260 | 261 | static inline size_t float_hash(const floatptr_t a) { 262 | union { 263 | const floatptr_t f; 264 | const size_t s; 265 | } c = {a}; 266 | return int_hash(c.s); 267 | } 268 | 269 | static inline NO_NULL size_t ptr_hash(const void *const p) { 270 | union { 271 | const void *const p; 272 | const size_t y; 273 | } c = {p}; 274 | return (c.y >> 4u) | (c.y << (8u * sizeof(void*) - 4u)); 275 | } 276 | 277 | #ifdef C11 278 | # define harbol_hash(h) _Generic((h)+0, \ 279 | int : int_hash, \ 280 | size_t : int_hash, \ 281 | int64_t : int_hash, \ 282 | uint64_t : int_hash, \ 283 | float32_t : float_hash, \ 284 | float64_t : float_hash, \ 285 | floatptr_t : float_hash, \ 286 | char* : string_hash, \ 287 | const char* : string_hash, \ 288 | default : ptr_hash) \ 289 | ((h)) 290 | #endif 291 | 292 | 293 | static inline NO_NULL ssize_t get_file_size(FILE *const file) { 294 | fseek(file, 0, SEEK_END); 295 | const ssize_t filesize = ftell(file); 296 | rewind(file); 297 | return filesize; 298 | } 299 | 300 | /// Harbol Iterator. 301 | /// for "struct/union" types, cast from the 'ptr' alias. 302 | union HarbolIter { 303 | const bool *b00l; 304 | const uint8_t *uint8; const int8_t *int8; 305 | const uint16_t *uint16; const int16_t *int16; 306 | const uint32_t *uint32; const int32_t *int32; 307 | const uint64_t *uint64; const int64_t *int64; 308 | const size_t *size; const ssize_t *ssize; 309 | const uintptr_t *uintptr; const intptr_t *intptr; 310 | 311 | const float32_t *float32; 312 | const float64_t *float64; 313 | const floatptr_t *floatptr; 314 | const floatmax_t *floatmax; 315 | 316 | const char *string; 317 | const void *ptr; 318 | const union HarbolIter *self; 319 | }; 320 | 321 | #ifdef __cplusplus 322 | static inline NO_NULL uint8_t *make_buffer_from_binary(const char *const file_name, size_t *const restrict bytes) 323 | #else 324 | static inline NO_NULL uint8_t *make_buffer_from_binary(const char file_name[static 1], size_t *const restrict bytes) 325 | #endif 326 | { 327 | FILE *restrict file = fopen(file_name, "rb"); 328 | if( file==NULL ) { 329 | return NULL; 330 | } 331 | 332 | const ssize_t filesize = get_file_size(file); 333 | if( filesize<=0 ) { 334 | fclose(file); 335 | return NULL; 336 | } 337 | #ifdef __cplusplus 338 | uint8_t *restrict stream = reinterpret_cast< decltype(stream) >(calloc(filesize, sizeof *stream)); 339 | #else 340 | uint8_t *restrict stream = calloc(filesize, sizeof *stream); 341 | #endif 342 | *bytes = fread(stream, sizeof *stream, filesize, file); 343 | fclose(file); file = NULL; 344 | return stream; 345 | } 346 | 347 | #ifdef __cplusplus 348 | static inline NO_NULL char *make_buffer_from_text(const char *const file_name, size_t *const restrict len) 349 | #else 350 | static inline NO_NULL char *make_buffer_from_text(const char file_name[static 1], size_t *const restrict len) 351 | #endif 352 | { 353 | FILE *restrict file = fopen(file_name, "r"); 354 | if( file==NULL ) { 355 | return NULL; 356 | } 357 | 358 | const ssize_t filesize = get_file_size(file); 359 | if( filesize<=0 ) { 360 | fclose(file); 361 | return NULL; 362 | } 363 | 364 | #ifdef __cplusplus 365 | char *restrict stream = reinterpret_cast< decltype(stream) >(calloc(filesize + 1, sizeof *stream)); 366 | #else 367 | char *restrict stream = calloc(filesize + 1, sizeof *stream); 368 | #endif 369 | *len = fread(stream, sizeof *stream, filesize, file); 370 | fclose(file); file = NULL; 371 | return stream; 372 | } 373 | 374 | static inline bool is_ptr_aligned(const void *const ptr, const size_t bytes) { 375 | return (( uintptr_t )ptr & (bytes-1))==0; 376 | } 377 | 378 | static inline NO_NULL void *dup_data(const void *const data, const size_t bytes) 379 | { 380 | #ifdef __cplusplus 381 | uint8_t *restrict cpy = reinterpret_cast< decltype(cpy) >(calloc(bytes, sizeof *cpy)); 382 | #else 383 | uint8_t *restrict cpy = calloc(bytes, sizeof *cpy); 384 | #endif 385 | return( cpy==NULL ) ? NULL : memcpy(cpy, data, bytes); 386 | } 387 | 388 | #ifdef __cplusplus 389 | static inline NO_NULL char *dup_str(const char *cstr) 390 | #else 391 | static inline char *dup_str(const char cstr[static 1]) 392 | #endif 393 | { 394 | const size_t len = strlen(cstr); 395 | #ifdef __cplusplus 396 | char *restrict cpy = reinterpret_cast< decltype(cpy) >(calloc(len + 1, sizeof *cpy)); 397 | #else 398 | char *restrict cpy = calloc(len + 1, sizeof *cpy); 399 | #endif 400 | return( cpy==NULL ) ? NULL : strcpy(cpy, cstr); 401 | } 402 | 403 | 404 | #ifdef __cplusplus 405 | static inline NO_NULL char *sprintf_alloc(const char *restrict fmt, ...) 406 | #else 407 | static inline char *sprintf_alloc(const char fmt[static 1], ...) 408 | #endif 409 | { 410 | va_list ap, st; 411 | va_start(ap, fmt); 412 | va_copy(st, ap); 413 | 414 | char c = 0; 415 | const int32_t size = vsnprintf(&c, 1, fmt, ap); 416 | va_end(ap); 417 | 418 | #ifdef __cplusplus 419 | char *restrict text = reinterpret_cast< decltype(cpy) >(calloc(size + 2, sizeof *text)); 420 | #else 421 | char *restrict text = calloc(size + 2, sizeof *text); 422 | #endif 423 | if( text != NULL ) { 424 | vsnprintf(text, size + 1, fmt, st); 425 | } 426 | va_end(st); 427 | return text; 428 | } 429 | 430 | static inline NO_NULL void harbol_print_tree_tabs(const size_t tabs, FILE *const f) { 431 | const size_t amount = tabs * 2; 432 | char str_branches[256] = {0}; 433 | if( amount > 0 ) { 434 | char *end = &str_branches[0] + sizeof str_branches; 435 | char *p = harbol_memccpy(&str_branches[0], " ", 0, sizeof str_branches); 436 | for( size_t i=1; iint conversion ops. */ \ 120 | X(f32tof64) X(f64tof32) X(itof64) X(itof32) X(f64toi) X(f32toi) \ 121 | \ 122 | /** control flow ops. */ \ 123 | X(jmp) X(jz) X(jnz) \ 124 | \ 125 | /** function ops. */ \ 126 | X(pushlr) X(poplr) X(call) X(callr) X(ret) \ 127 | \ 128 | /** vector extension. */ \ 129 | X(setvlen) X(setelen) \ 130 | X(vmov) \ 131 | X(vadd) X(vsub) X(vmul) X(vdiv) X(vmod) X(vneg) \ 132 | X(vfadd) X(vfsub) X(vfmul) X(vfdiv) X(vfneg) \ 133 | X(vand) X(vor) X(vxor) X(vsll) X(vsrl) X(vsra) X(vnot) \ 134 | X(vcmp) X(vilt) X(vile) X(vult) X(vule) X(vflt) X(vfle) \ 135 | \ 136 | /** extra, super-instrs */ \ 137 | X(restore) /** poplr + ret */ \ 138 | X(leave) /** poplr + redux + ret */ \ 139 | X(remit) /** redux + ret */ \ 140 | X(enter) /** alloc + pushlr */ 141 | 142 | #define X(x) x, 143 | enum TaghaInstrSet { TAGHA_INSTR_SET MaxOps }; 144 | #undef X 145 | 146 | 147 | enum { 148 | TAGHA_MAGIC_VERIFIER = 0x7A6AC0DE /// "tagha code" 149 | }; 150 | struct TaghaModuleHeader { 151 | uint32_t 152 | magic, 153 | opstacksize, 154 | callstacksize, 155 | stacksize, 156 | heapsize, 157 | memsize, 158 | funcs_offset, 159 | func_count, 160 | vars_offset, 161 | var_count, 162 | mem_offset, 163 | flags 164 | ; 165 | }; 166 | 167 | struct TaghaItemEntry { 168 | uint32_t 169 | size, 170 | flags, 171 | name_len, 172 | data_len 173 | ; 174 | }; 175 | 176 | /** Tagha Script File/Binary Format Structure 177 | * ------------------------------ start of header ------------------------------ 178 | * 4 bytes: magic verifier ==> TAGHA_MAGIC_VERIFIER 179 | * 4 bytes: operand stack size. 180 | * 4 bytes: call stack size. 181 | * 4 bytes: total stack size. 182 | * 4 bytes: heap size. 183 | * 4 bytes: total mem size. 184 | * 4 bytes: func table offset (from base). 185 | * 4 bytes: amount of funcs. 186 | * 4 bytes: var table offset (from base). 187 | * 4 bytes: amount of vars. 188 | * 4 bytes: mem region offset (from base). 189 | * 4 bytes: flags. 190 | * ------------------------------ end of header -------------------------------- 191 | * .funcs table. 192 | * n bytes: func table. 193 | * 4 bytes: entry size. 194 | * 4 bytes: flags: if bytecode func, a native, or extern. 195 | * 4 bytes: string size + '\0' of func string. 196 | * 4 bytes: instr len, 8 if native. 197 | * m bytes: func string. 198 | * if bytecode func: 199 | * x bytes - instructions. 200 | * else if native func: 201 | * 8 bytes - function pointer to native. 202 | * else if extern func: 203 | * 8 bytes - pointer to owner module. 204 | * 205 | * .vars table. 206 | * n bytes: global vars table. == low segment 207 | * 4 bytes: entry size. 208 | * 4 bytes: flags. 209 | * 4 bytes: string size + '\0' of global var string. 210 | * 4 bytes: byte size, 8 if ptr. 211 | * n bytes: global var string. 212 | * n bytes: data. All 0 if not initialized in script code. 213 | * 214 | * .mem region - taken control by the memory pool as both a stack and heap. 215 | * | portion marshalled by the bifurcated stack: 216 | * | <- operand stack start 217 | * | ... 218 | * | <- operand stack end => highest memory safety segment. 219 | * | <- call stack end 220 | * | ... 221 | * | <- call stack start, unaccessable from bytecode. 222 | * | remaining memory region portion marshalled by pool allocator as the heap. 223 | */ 224 | 225 | 226 | struct TaghaModule; 227 | typedef union TaghaVal TaghaCFunc(struct TaghaModule *ctxt, const union TaghaVal params[]); 228 | 229 | struct TaghaNative { 230 | const char *name; 231 | TaghaCFunc *cfunc; 232 | }; 233 | 234 | 235 | enum { 236 | TAGHA_FLAG_NATIVE = 1, /// if is a native C or JIT compiled function. 237 | TAGHA_FLAG_EXTERN = 2, /// function is from different module. 238 | TAGHA_FLAG_LINKED = 4, /// ptr has been linked. 239 | }; 240 | 241 | struct TaghaItem { 242 | uintptr_t 243 | item, /// data, as uint8_t*, cast as needed. 244 | owner /// Add an owner so we can do dynamic linking & loading. 245 | ; 246 | uint_fast32_t flags; 247 | }; /// 12 (32-bit) ~ 24 (64-bit) bytes 248 | typedef const struct TaghaItem *TaghaFunc; 249 | 250 | enum { TAGHA_SYM_BUCKETS = 32 }; 251 | struct TaghaSymTable { 252 | const char **keys; /// array of string names of each item. 253 | struct TaghaItem *table; 254 | size_t 255 | len, /// table's len. 256 | buckets[TAGHA_SYM_BUCKETS], /// hash index bucket for each index. SIZE_MAX if invalid. 257 | *hashes, /// hash value for each item index. 258 | *chain, /// index chain to resolve collisions. SIZE_MAX if invalid. 259 | *bytes /// byte of item data. 260 | ; 261 | }; /// 148 (32-bit) ~ 296 (64-bit) bytes 262 | 263 | 264 | enum TaghaErrCode { 265 | TaghaErrNone, /// a-okay! 266 | TaghaErrBadNative, /// missing native function. (native wasn't linked.) 267 | TaghaErrBadExtern, /// missing extern function. (extern wasn't linked.) 268 | TaghaErrOpStackOF, /// op stack overflow! 269 | TaghaErrOpcodeOOB, /// opcode out of bounds! 270 | TaghaErrBadPtr, /// nil/invalid pointer. 271 | TaghaErrBadFunc, /// nil function. 272 | }; 273 | 274 | 275 | /// Script/Module Structure. 276 | struct TaghaModule { 277 | struct HarbolMemPool heap; /// holds ALL memory in a script. 278 | const struct TaghaSymTable *funcs, *vars; 279 | uintptr_t 280 | script, /// ptr to base address of script (uint8_t*) 281 | low_seg, /// lower memory segment (uint8_t*) 282 | high_seg, /// higher memory segment (uint8_t*) 283 | opstack, /// ptr to base of operand stack (union TaghaVal*) 284 | callstack, /// ptr to base of call stack (uintptr_t*) 285 | osp, /// operand stack ptr (union TaghaVal*) 286 | ofp, /// operand frame ptr (union TaghaVal*) 287 | csp, /// call stack ptr (uintptr_t*) 288 | lr /// link register. 289 | ; 290 | size_t opstack_size, callstack_size; 291 | uint_fast16_t vec_len, elem_len; 292 | uint32_t flags; 293 | int err, cond; 294 | }; 295 | 296 | /// Module Constructors. 297 | TAGHA_EXPORT NO_NULL struct TaghaModule *tagha_module_new_from_file(const char filename[]); 298 | TAGHA_EXPORT NO_NULL struct TaghaModule *tagha_module_new_from_buffer(uint8_t buffer[]); 299 | 300 | /// Module Destructors. 301 | TAGHA_EXPORT bool tagha_module_clear(struct TaghaModule *module); 302 | TAGHA_EXPORT bool tagha_module_free(struct TaghaModule **modref); 303 | 304 | /// Calling/Execution API. 305 | TAGHA_EXPORT NEVER_NULL(1,2) bool tagha_module_call(struct TaghaModule *module, const char name[], size_t args, const union TaghaVal params[], union TaghaVal *retval); 306 | 307 | TAGHA_EXPORT NEVER_NULL(1,2) bool tagha_module_invoke(struct TaghaModule *module, TaghaFunc func, size_t args, const union TaghaVal params[], union TaghaVal *retval); 308 | 309 | TAGHA_EXPORT NEVER_NULL(1) int tagha_module_run(struct TaghaModule *module, size_t argc, const union TaghaVal argv[]); 310 | 311 | /// Runtime Data API. 312 | TAGHA_EXPORT NO_NULL void *tagha_module_get_var(const struct TaghaModule *module, const char name[]); 313 | TAGHA_EXPORT NO_NULL TaghaFunc tagha_module_get_func(const struct TaghaModule *module, const char name[]); 314 | TAGHA_EXPORT NO_NULL uint32_t tagha_module_get_flags(const struct TaghaModule *module); 315 | 316 | TAGHA_EXPORT NO_NULL uintptr_t tagha_module_heap_alloc(struct TaghaModule *module, size_t size); 317 | TAGHA_EXPORT NO_NULL bool tagha_module_heap_free(struct TaghaModule *module, uintptr_t ptr); 318 | 319 | /// Error API. 320 | TAGHA_EXPORT NO_NULL NONNULL_RET const char *tagha_module_get_err(const struct TaghaModule *module); 321 | TAGHA_EXPORT NO_NULL void tagha_module_throw_err(struct TaghaModule *module, enum TaghaErrCode err); 322 | 323 | /// Inter-Module/Process Linking API. 324 | TAGHA_EXPORT NO_NULL void tagha_module_link_natives(struct TaghaModule *module, const struct TaghaNative natives[]); 325 | TAGHA_EXPORT NO_NULL bool tagha_module_link_ptr(struct TaghaModule *module, const char name[], uintptr_t ptr); 326 | TAGHA_EXPORT NO_NULL void tagha_module_link_module(struct TaghaModule *module, const struct TaghaModule *lib); 327 | 328 | /** I like Golang. 329 | type TaghaSys struct { 330 | modules map[string]*TaghaModule // map[string]TaghaFunc 331 | natives map[string]TaghaCFunc 332 | } 333 | */ 334 | 335 | #ifdef __cplusplus 336 | } /// extern "C" 337 | #endif 338 | 339 | #endif /** TAGHA_INCLUDED */ -------------------------------------------------------------------------------- /tagha_libc/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wextra -Wall -std=c99 -s -O2 3 | TESTFLAGS = -Wextra -Wall -std=c99 -g -O2 4 | 5 | SRCS = tagha_ctype.c 6 | SRCS += tagha_stdio.c 7 | SRCS += tagha_stdlib.c 8 | SRCS += tagha_string.c 9 | SRCS += tagha_time.c 10 | SRCS += tagha_module.c 11 | SRCS += tagha_simd.c 12 | OBJS = $(SRCS:.c=.o) 13 | 14 | LIBNAME = libtaghaclib 15 | 16 | tagha_libc: 17 | $(CC) $(CFLAGS) -c $(SRCS) 18 | $(AR) cr $(LIBNAME).a $(OBJS) 19 | 20 | shared: 21 | $(CC) $(CFLAGS) -shared $(SRCS) -o $(LIBNAME).so 22 | 23 | debug: 24 | $(CC) $(TESTFLAGS) -c $(SRCS) 25 | $(AR) cr $(LIBNAME).a $(OBJS) 26 | 27 | debug_shared: 28 | $(CC) $(TESTFLAGS) -shared $(SRCS) -o $(LIBNAME).so 29 | 30 | clean: 31 | $(RM) *.o 32 | -------------------------------------------------------------------------------- /tagha_libc/tagha_ctype.c: -------------------------------------------------------------------------------- 1 | #include "../tagha/tagha.h" 2 | #include 3 | 4 | /** int isalnum(int c); */ 5 | static union TaghaVal native_isalnum(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 6 | { 7 | ( void )module; 8 | return ( union TaghaVal ){ .int32 = isalnum(params[0].int32) }; 9 | } 10 | 11 | /** int isalpha(int c); */ 12 | static union TaghaVal native_isalpha(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 13 | { 14 | ( void )module; 15 | return ( union TaghaVal ){ .int32 = isalpha(params[0].int32) }; 16 | } 17 | 18 | /** int isblank(int c); */ 19 | static union TaghaVal native_isblank(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 20 | { 21 | ( void )module; 22 | return ( union TaghaVal ){ .int32 = isblank(params[0].int32) }; 23 | } 24 | 25 | /** int iscntrl(int c); */ 26 | static union TaghaVal native_iscntrl(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 27 | { 28 | ( void )module; 29 | return ( union TaghaVal ){ .int32 = iscntrl(params[0].int32) }; 30 | } 31 | 32 | /** int isdigit(int c); */ 33 | static union TaghaVal native_isdigit(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 34 | { 35 | ( void )module; 36 | return ( union TaghaVal ){ .int32 = isdigit(params[0].int32) }; 37 | } 38 | 39 | /** int isgraph(int c); */ 40 | static union TaghaVal native_isgraph(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 41 | { 42 | ( void )module; 43 | return ( union TaghaVal ){ .int32 = isgraph(params[0].int32) }; 44 | } 45 | 46 | /** int islower(int c); */ 47 | static union TaghaVal native_islower(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 48 | { 49 | ( void )module; 50 | return ( union TaghaVal ){ .int32 = islower(params[0].int32) }; 51 | } 52 | 53 | /** int isprint(int c); */ 54 | static union TaghaVal native_isprint(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 55 | { 56 | ( void )module; 57 | return ( union TaghaVal ){ .int32 = isprint(params[0].int32) }; 58 | } 59 | 60 | /** int ispunct(int c); */ 61 | static union TaghaVal native_ispunct(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 62 | { 63 | ( void )module; 64 | return ( union TaghaVal ){ .int32 = ispunct(params[0].int32) }; 65 | } 66 | 67 | /** int isspace(int c); */ 68 | static union TaghaVal native_isspace(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 69 | { 70 | ( void )module; 71 | return ( union TaghaVal ){ .int32 = isspace(params[0].int32) }; 72 | } 73 | 74 | /** int isupper(int c); */ 75 | static union TaghaVal native_isupper(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 76 | { 77 | ( void )module; 78 | return ( union TaghaVal ){ .int32 = isupper(params[0].int32) }; 79 | } 80 | 81 | /** int toupper(int c); */ 82 | static union TaghaVal native_toupper(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 83 | { 84 | ( void )module; 85 | return ( union TaghaVal ){ .int32 = toupper(params[0].int32) }; 86 | } 87 | 88 | /** int tolower(int c); */ 89 | static union TaghaVal native_tolower(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 90 | { 91 | ( void )module; 92 | return ( union TaghaVal ){ .int32 = tolower(params[0].int32) }; 93 | } 94 | 95 | /** int isxdigit(int c); */ 96 | static union TaghaVal native_isxdigit(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 97 | { 98 | ( void )module; 99 | return ( union TaghaVal ){ .int32 = isxdigit(params[0].int32) }; 100 | } 101 | 102 | 103 | bool tagha_module_load_ctype_natives(struct TaghaModule *const module) 104 | { 105 | return module ? tagha_module_register_natives(module, ( const struct TaghaNative[] ){ 106 | {"__tagha_isalnum", &native_isalnum}, 107 | {"__tagha_isalpha", &native_isalpha}, 108 | {"__tagha_isblank", &native_isblank}, 109 | {"__tagha_iscntrl", &native_iscntrl}, 110 | {"__tagha_isdigit", &native_isdigit}, 111 | {"__tagha_isgraph", &native_isgraph}, 112 | {"__tagha_islower", &native_islower}, 113 | {"__tagha_isprint", &native_isprint}, 114 | {"__tagha_ispunct", &native_ispunct}, 115 | {"__tagha_isspace", &native_isspace}, 116 | {"__tagha_isupper", &native_isupper}, 117 | {"__tagha_toupper", &native_toupper}, 118 | {"__tagha_tolower", &native_tolower}, 119 | {"__tagha_isxdigit", &native_isxdigit}, 120 | {NULL, NULL} 121 | }) : false; 122 | } -------------------------------------------------------------------------------- /tagha_libc/tagha_libc.tasm: -------------------------------------------------------------------------------- 1 | $native puts ;; int puts(const char *str); 2 | 3 | -------------------------------------------------------------------------------- /tagha_libc/tagha_string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../tagha/tagha.h" 3 | 4 | /** void *memcpy(void *dest, const void *src, size_t num); */ 5 | static union TaghaVal native_memcpy(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 6 | { 7 | ( void )module; 8 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )memcpy(( void* )params[0].uintptr, ( const void* )params[1].uintptr, params[2].uint64) }; 9 | } 10 | 11 | /** void *memmove(void *dest, const void *src, size_t num); */ 12 | static union TaghaVal native_memmove(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 13 | { 14 | ( void )module; 15 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )memmove(( void* )params[0].uintptr, ( const void* )params[1].uintptr, params[2].uint64) }; 16 | } 17 | 18 | /** char *strcpy(char *dest, const char *src); */ 19 | static union TaghaVal native_strcpy(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 20 | { 21 | ( void )module; 22 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strcpy(( char* )params[0].uintptr, ( const char* )params[1].uintptr) }; 23 | } 24 | 25 | /** char *strncpy(char *dest, const char *src, size_t num); */ 26 | static union TaghaVal native_strncpy(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 27 | { 28 | ( void )module; 29 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strncpy(( char* )params[0].uintptr, ( const char* )params[1].uintptr, params[2].uint64) }; 30 | } 31 | 32 | /** char *strcat(char *dest, const char *src); */ 33 | static union TaghaVal native_strcat(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 34 | { 35 | ( void )module; 36 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strcat(( char* )params[0].uintptr, ( const char* )params[1].uintptr) }; 37 | } 38 | 39 | /** char *strncat(char *dest, const char *src, size_t num); */ 40 | static union TaghaVal native_strncat(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 41 | { 42 | ( void )module; 43 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strncat(( char* )params[0].uintptr, ( const char* )params[1].uintptr, params[2].uint64) }; 44 | } 45 | 46 | /** int memcmp(const void *ptr1, const void *ptr2, size_t num); */ 47 | static union TaghaVal native_memcmp(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 48 | { 49 | ( void )module; 50 | return ( union TaghaVal ){ .int32 = memcmp(( const void* )params[0].uintptr, ( const void* )params[1].uintptr, params[2].uint64) }; 51 | } 52 | 53 | /** int strcmp(const char *str1, const char *str2); */ 54 | static union TaghaVal native_strcmp(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 55 | { 56 | ( void )module; 57 | return ( union TaghaVal ){ .int32 = strcmp(( const char* )params[0].uintptr, ( const char* )params[1].uintptr) }; 58 | } 59 | 60 | /** int strcoll(const char *str1, const char *str2); */ 61 | static union TaghaVal native_strcoll(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 62 | { 63 | ( void )module; 64 | return ( union TaghaVal ){ .int32 = strcoll(( const char* )params[0].uintptr, ( const char* )params[1].uintptr) }; 65 | } 66 | 67 | /** int strncmp(const char *str1, const char *str2, size_t num); */ 68 | static union TaghaVal native_strncmp(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 69 | { 70 | ( void )module; 71 | return ( union TaghaVal ){ .int32 = strncmp(( const char* )params[0].uintptr, ( const char* )params[1].uintptr, params[2].uint64) }; 72 | } 73 | 74 | /** size_t strxfrm(char *dest, const char *src, size_t num); */ 75 | static union TaghaVal native_strxfrm(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 76 | { 77 | ( void )module; 78 | return ( union TaghaVal ){ .uint64 = strxfrm(( char* )params[0].uintptr, ( const char* )params[1].uintptr, params[2].uint64) }; 79 | } 80 | 81 | /** void *memchr(const void *ptr, int value, size_t num); */ 82 | static union TaghaVal native_memchr(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 83 | { 84 | ( void )module; 85 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )memchr(( const void* )params[0].uintptr, params[1].int32, params[2].uint64) }; 86 | } 87 | 88 | /** char *strchr(const char *str, int character); */ 89 | static union TaghaVal native_strchr(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 90 | { 91 | ( void )module; 92 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strchr(( const char* )params[0].uintptr, params[1].int32) }; 93 | } 94 | 95 | /** size_t strcspn(const char *str1, const char *str2); */ 96 | static union TaghaVal native_strcspn(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 97 | { 98 | ( void )module; 99 | return ( union TaghaVal ){ .uint64 = strcspn(( const char* )params[0].uintptr, ( const char* )params[1].uintptr) }; 100 | } 101 | 102 | /** char *strpbrk(const char *str1, const char *str2); */ 103 | static union TaghaVal native_strpbrk(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 104 | { 105 | ( void )module; 106 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strpbrk(( const char* )params[0].uintptr, ( const char* )params[1].uintptr) }; 107 | } 108 | 109 | /** char *strrchr(const char *str, int character); */ 110 | static union TaghaVal native_strrchr(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 111 | { 112 | ( void )module; 113 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strrchr(( const char* )params[0].uintptr, params[1].int32) }; 114 | } 115 | 116 | /** size_t strspn(const char *str1, const char *str2); */ 117 | static union TaghaVal native_strspn(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 118 | { 119 | ( void )module; 120 | return ( union TaghaVal ){ .uint64 = strspn(( const char* )params[0].uintptr, ( const char* )params[1].uintptr) }; 121 | } 122 | 123 | /** char *strstr(const char *str1, const char *str2); */ 124 | static union TaghaVal native_strstr(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 125 | { 126 | ( void )module; 127 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strstr(( const char* )params[0].uintptr, ( const char* )params[1].uintptr) }; 128 | } 129 | 130 | /** char *strtok(char *str, const char *delimiters); */ 131 | static union TaghaVal native_strtok(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 132 | { 133 | ( void )module; 134 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strtok(( char* )params[0].uintptr, ( const char* )params[1].uintptr) }; 135 | } 136 | 137 | /** void *memset(void *ptr, int value, size_t num); */ 138 | static union TaghaVal native_memset(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 139 | { 140 | ( void )module; 141 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )memset(( void* )params[0].uintptr, params[1].int32, params[1].uint64) }; 142 | } 143 | 144 | /** char *strerror(int errnum); */ 145 | static union TaghaVal native_strerror(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 146 | { 147 | ( void )module; 148 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )strerror(params[0].int32) }; 149 | } 150 | 151 | /** size_t strlen(const char *str); */ 152 | static union TaghaVal native_strlen(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 153 | { 154 | ( void )module; 155 | return ( union TaghaVal ){ .uint64 = strlen(( const char* )params[0].uintptr) }; 156 | } 157 | 158 | 159 | bool tagha_module_load_string_natives(struct TaghaModule *const module) 160 | { 161 | return module ? tagha_module_register_natives(module, ( const struct TaghaNative[] ){ 162 | {"__tagha_memcpy", &native_memcpy}, 163 | {"__tagha_memmove", &native_memmove}, 164 | {"__tagha_strcpy", &native_strcpy}, 165 | {"__tagha_strncpy", &native_strncpy}, 166 | {"__tagha_strcat", &native_strcat}, 167 | {"__tagha_strncat", &native_strncat}, 168 | {"__tagha_memcmp", &native_memcmp}, 169 | {"__tagha_strcmp", &native_strcmp}, 170 | {"__tagha_strcoll", &native_strcoll}, 171 | {"__tagha_strncmp", &native_strncmp}, 172 | {"__tagha_strxfrm", &native_strxfrm}, 173 | {"__tagha_memchr", &native_memchr}, 174 | {"__tagha_strchr", &native_strchr}, 175 | {"__tagha_strcspn", &native_strcspn}, 176 | {"__tagha_strpbrk", &native_strpbrk}, 177 | {"__tagha_strrchr", &native_strrchr}, 178 | {"__tagha_strspn", &native_strspn}, 179 | {"__tagha_strstr", &native_strstr}, 180 | {"__tagha_strtok", &native_strtok}, 181 | {"__tagha_memset", &native_memset}, 182 | {"__tagha_strerror", &native_strerror}, 183 | {"__tagha_strlen", &native_strlen}, 184 | {NULL, NULL} 185 | }) : false; 186 | } -------------------------------------------------------------------------------- /tagha_libc/tagha_time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../tagha/tagha.h" 4 | 5 | /** clock_t clock(void); */ 6 | static union TaghaVal native_clock(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 7 | { 8 | ( void )module; ( void )params; 9 | union { 10 | const clock_t cl; 11 | const union TaghaVal v; 12 | } conv = { clock() }; 13 | return conv.v; 14 | } 15 | 16 | /** time_t time(time_t *timer); */ 17 | static union TaghaVal native_time(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 18 | { 19 | ( void )module; 20 | union { 21 | const time_t t; 22 | const union TaghaVal v; 23 | } conv = { time(( time_t* )params[0].uintptr) }; 24 | return conv.v; 25 | } 26 | 27 | /** float64_t difftime(time_t end, time_t beginning); */ 28 | static union TaghaVal native_difftime(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 29 | { 30 | ( void )module; 31 | const union { 32 | const union TaghaVal v; 33 | const time_t t; 34 | } end = { params[0] }, begin = { params[1] }; 35 | return ( union TaghaVal ){ .float64 = difftime(end.t, begin.t) }; 36 | } 37 | 38 | /** time_t mktime(struct tm *timeptr); */ 39 | static union TaghaVal native_mktime(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 40 | { 41 | ( void )module; 42 | union { 43 | const time_t t; 44 | const union TaghaVal v; 45 | } conv = { mktime(( struct tm* )params[0].uintptr) }; 46 | return conv.v; 47 | } 48 | 49 | /** char *asctime(const struct tm *timeptr); */ 50 | static union TaghaVal native_asctime(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 51 | { 52 | ( void )module; 53 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )asctime(( const struct tm* )params[0].uintptr) }; 54 | } 55 | 56 | /** char *ctime(const time_t *timer); */ 57 | static union TaghaVal native_ctime(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 58 | { 59 | ( void )module; 60 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )ctime(( const time_t* )params[0].uintptr)}; 61 | } 62 | 63 | /** struct tm *gmtime(const time_t *timer); */ 64 | static union TaghaVal native_gmtime(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 65 | { 66 | ( void )module; 67 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )gmtime(( const time_t* )params[0].uintptr) }; 68 | } 69 | 70 | /** struct tm *localtime(const time_t *timer); */ 71 | static union TaghaVal native_localtime(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 72 | { 73 | ( void )module; 74 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )localtime(( const time_t* )params[0].uintptr) }; 75 | } 76 | 77 | /** size_t strftime(char *ptr, size_t maxsize, const char *format, const struct tm *timeptr); */ 78 | static union TaghaVal native_strftime(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 79 | { 80 | ( void )module; 81 | return ( union TaghaVal ){ .uint64 = strftime(( char* )params[0].uintptr, params[1].uint64, ( const char* )params[2].uintptr, ( const struct tm* )params[3].uintptr) }; 82 | } 83 | 84 | 85 | bool tagha_module_load_time_natives(struct TaghaModule *const module) 86 | { 87 | return module ? tagha_module_register_natives(module, ( const struct TaghaNative[] ){ 88 | {"__tagha_clock", &native_clock}, 89 | {"__tagha_time", &native_time}, 90 | {"__tagha_difftime", &native_difftime}, 91 | {"__tagha_mktime", &native_mktime}, 92 | {"__tagha_asctime", &native_asctime}, 93 | {"__tagha_ctime", &native_ctime}, 94 | {"__tagha_gmtime", &native_gmtime}, 95 | {"__tagha_localtime", &native_localtime}, 96 | {"__tagha_strftime", &native_strftime}, 97 | {NULL, NULL} 98 | }) : false; 99 | } -------------------------------------------------------------------------------- /tagha_toolchain/assembler/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wextra -Wall -Wrestrict -std=c99 -s -O2 -flto 3 | TFLAGS = -Wextra -Wall -Wrestrict -std=c99 -g -O2 -flto 4 | # -static 5 | SRCS = ../libharbol/stringobj/stringobj.c 6 | SRCS += ../libharbol/bytebuffer/bytebuffer.c 7 | SRCS += ../libharbol/vector/vector.c 8 | SRCS += ../libharbol/map/map.c 9 | SRCS += ../libharbol/linkmap/linkmap.c 10 | SRCS += ../libharbol/lex/lex.c 11 | SRCS += assembler.c 12 | 13 | tagha_assembler: 14 | $(CC) $(CFLAGS) $(SRCS) -o tagha_assembler 15 | 16 | debug: 17 | $(CC) $(TFLAGS) $(SRCS) -o tagha_assembler 18 | 19 | clean: 20 | $(RM) *.o 21 | -------------------------------------------------------------------------------- /tagha_toolchain/assembler/test.tasm: -------------------------------------------------------------------------------- 1 | ;; single line comment! 2 | 3 | /** 4 | multi-line comment! */ 5 | 6 | $opstack_size 6000.0 7 | $callstack_size 6000 8 | $heap_size 0x1000 9 | 10 | 11 | $global g_player, 12, 0 12 | $global str0, "lolololololol" 13 | $global raw, `''''raw string!\n` 14 | $global n, 4, long 0x40000000 15 | $global x, 4, byte 0, byte 0, byte 0, byte 0x40 16 | 17 | $native puts 18 | $extern _libtagha@load_module 19 | 20 | main { 21 | .label alloc 2 ;; 0 22 | ldvar r1, str0 ;; 2 23 | lea r0, [r1-1] ;; 6 24 | lea r0, r1-1 ;; 11 25 | st8 [r0], r1 ;; 11 26 | .l: call puts ;; 16 27 | movi r10, 1000000 28 | jmp .l ;; 21 29 | } -------------------------------------------------------------------------------- /tagha_toolchain/assembler/valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | 4 | #valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes -v ./tagha_assembler `*.tasm` 5 | 6 | for i in $(find . -name '*.tasm'); do 7 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes -v ./tagha_assembler "$i" 8 | done 9 | 10 | # for SERIOUS debugging! 11 | #--vgdb-error=0 12 | -------------------------------------------------------------------------------- /tagha_toolchain/disassembler/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wextra -Wall -Wrestrict -std=c99 -s -O2 -flto 3 | TFLAGS = -Wextra -Wall -Wrestrict -std=c99 -g -O2 -flto 4 | # -static 5 | SRCS = ../libharbol/stringobj/stringobj.c 6 | SRCS += ../libharbol/bytebuffer/bytebuffer.c 7 | SRCS += ../libharbol/vector/vector.c 8 | SRCS += ../libharbol/map/map.c 9 | SRCS += ../libharbol/linkmap/linkmap.c 10 | SRCS += disassembler.c 11 | 12 | tagha_disassembler: 13 | $(CC) $(CFLAGS) $(SRCS) -o tagha_disassembler 14 | 15 | debug: 16 | $(CC) $(TFLAGS) $(SRCS) -o tagha_disassembler 17 | 18 | clean: 19 | $(RM) *.o 20 | -------------------------------------------------------------------------------- /tagha_toolchain/disassembler/disassembler.c: -------------------------------------------------------------------------------- 1 | #include "../libharbol/harbol.h" 2 | #include "../../tagha/tagha.h" 3 | 4 | 5 | bool tagha_disasm_module(const char filename[restrict static 1]) 6 | { 7 | uint8_t *filedata = make_buffer_from_binary(filename); 8 | if( filedata==NULL ) { 9 | fprintf(stderr, "Tagha Disassembler Error: **** Couldn't load Tagha Module file: '%s' ****\n", filename); 10 | return false; 11 | } 12 | 13 | const struct TaghaModuleHeader *const hdr = ( const struct TaghaModuleHeader* )(filedata); 14 | if( hdr->magic != TAGHA_MAGIC_VERIFIER ) { 15 | free(filedata); filedata=NULL; 16 | fprintf(stderr, "Tagha Disassembler Error: **** Invalid Tagha Module file: '%s' ****\n", filename); 17 | return false; 18 | } 19 | 20 | struct HarbolString 21 | header = harbol_string_create(NULL), 22 | nbc_funcs = harbol_string_create(NULL), /// NBC "Non-ByteCode" functions. 23 | vars = harbol_string_create(NULL), 24 | bc_funcs = harbol_string_create(NULL) 25 | ; 26 | struct HarbolVector 27 | func_names = harbol_vector_create(sizeof(struct HarbolString), 0), 28 | var_names = harbol_vector_create(sizeof(struct HarbolString), 0) 29 | ; 30 | 31 | #define X(x) #x , 32 | const char *restrict opcode_strs[] = { TAGHA_INSTR_SET }; 33 | #undef X 34 | 35 | /// collect header info. 36 | harbol_string_add_format(&header, ";; '%s' disassembled by the official tagha disassembler.\n", filename); 37 | harbol_string_add_format(&header, "$opstack_size %d\n", hdr->opstacksize / sizeof(union TaghaVal)); 38 | harbol_string_add_format(&header, "$callstack_size %d\n", hdr->callstacksize / sizeof(uintptr_t)); 39 | harbol_string_add_format(&header, ";; total stacks size '%d'\n\n", hdr->stacksize); 40 | harbol_string_add_format(&header, "$heap_size %d\n", hdr->heapsize); 41 | harbol_string_add_format(&header, ";; total memory usage: '%d' bytes\n\n", hdr->memsize); 42 | 43 | const uint32_t func_table_size = hdr->func_count; 44 | const uint32_t var_table_size = hdr->var_count; 45 | harbol_string_add_format(&header, ";; function count: %u\n", func_table_size); 46 | harbol_string_add_format(&header, ";; global var count: %u\n\n", var_table_size); 47 | union HarbolBinIter iter = { .uint8 = filedata + hdr->funcs_offset }; 48 | { 49 | union HarbolBinIter first_run = iter; 50 | for( uint32_t i=0; iname_len; 56 | if( !entry->flags ) 57 | first_run.uint8 += entry->data_len; 58 | } 59 | 60 | for( uint32_t i=0; iname_len + entry->data_len; 66 | } 67 | } 68 | 69 | for( uint32_t i=0; iflags==0; 73 | if( is_bytecode ) { 74 | harbol_string_add_format(&bc_funcs, ";; bytecode len: %u\n%s: {\n", entry->data_len, iter.string); 75 | iter.uint8 += entry->name_len; 76 | union HarbolBinIter pc = iter; 77 | iter.uint8 += entry->data_len; 78 | const uintptr_t offs = ( uintptr_t )(pc.uint8) + 1; 79 | while( *pc.uint8 != 0 && pc.uint8cstr, addr, addr2); 141 | break; 142 | } 143 | 144 | /// u16 imm 145 | case setvlen: { 146 | const uintptr_t addr = ( uintptr_t )(pc.uint8) - offs; 147 | const uint32_t width = *pc.uint16++; 148 | const uintptr_t addr2 = ( uintptr_t )(pc.uint8) - offs; 149 | harbol_string_add_format(&bc_funcs, " %-10s %u ;; offset: %" PRIuPTR " - %" PRIuPTR "\n", opcode_strs[opcode], width, addr, addr2); 150 | break; 151 | } 152 | 153 | /// u8 reg + u16 offset. 154 | case lra: { 155 | const uintptr_t addr = ( uintptr_t )(pc.uint8) - offs; 156 | const uint32_t reg = *pc.uint8++; 157 | const uint32_t offset = *pc.uint16++; 158 | const uintptr_t addr2 = ( uintptr_t )(pc.uint8) - offs; 159 | harbol_string_add_format(&bc_funcs, " %-10s r%u, %u ;; offset: %" PRIuPTR " - %" PRIuPTR "\n", opcode_strs[opcode], reg, offset, addr, addr2); 160 | break; 161 | } 162 | 163 | /// u8 reg + named u16 offset. 164 | case ldvar: case ldfn: { 165 | const uintptr_t addr = ( uintptr_t )(pc.uint8) - offs; 166 | const uint32_t regid = *pc.uint8++; 167 | const uint32_t label = *pc.uint16++; 168 | const uintptr_t addr2 = ( uintptr_t )(pc.uint8) - offs; 169 | const struct HarbolString *const label_name = harbol_vector_get((opcode==ldvar) ? &var_names : &func_names, (opcode==ldvar) ? label : label - 1); 170 | 171 | harbol_string_add_format(&bc_funcs, " %-10s r%u, %s ;; offset: %" PRIuPTR " - %" PRIuPTR "\n", opcode_strs[opcode], regid, label_name->cstr, addr, addr2); 172 | break; 173 | } 174 | 175 | case lea: 176 | case ld1: case ld2: case ld4: case ld8: case ldu1: case ldu2: case ldu4: { 177 | const uintptr_t addr = ( uintptr_t )(pc.uint8) - offs; 178 | const uint32_t dst = *pc.uint8++; 179 | const uint32_t src = *pc.uint8++; 180 | const int32_t offset = *pc.int16++; 181 | const uintptr_t addr2 = ( uintptr_t )(pc.uint8) - offs; 182 | const bool neg = offset < 0; 183 | 184 | harbol_string_add_format(&bc_funcs, " %-10s r%u, [r%u%s%d] ;; offset: %" PRIuPTR " - %" PRIuPTR "\n", opcode_strs[opcode], dst, src, !neg ? "+" : "", offset, addr, addr2); 185 | break; 186 | } 187 | 188 | case st1: case st2: case st4: case st8: { 189 | const uintptr_t addr = ( uintptr_t )(pc.uint8) - offs; 190 | const uint32_t dst = *pc.uint8++; 191 | const uint32_t src = *pc.uint8++; 192 | const int32_t offset = *pc.int16++; 193 | const uintptr_t addr2 = ( uintptr_t )(pc.uint8) - offs; 194 | const bool neg = offset < 0; 195 | 196 | harbol_string_add_format(&bc_funcs, " %-10s [r%u%s%d], r%u ;; offset: %" PRIuPTR " - %" PRIuPTR "\n", opcode_strs[opcode], dst, !neg ? "+" : "", offset, src, addr, addr2); 197 | break; 198 | } 199 | case jmp: case jz: case jnz: { 200 | const uintptr_t addr = ( uintptr_t )(pc.uint8) - offs; 201 | const int32_t label = *pc.int32++; 202 | const uintptr_t addr2 = ( uintptr_t )(pc.uint8) - offs; 203 | 204 | const uintptr_t label_addr = ( uintptr_t )(( int32_t )addr2 + label + 1); 205 | harbol_string_add_format(&bc_funcs, " %-10s %d ;; label addr: %" PRIuPTR " | offset: %" PRIuPTR " - %" PRIuPTR "\n", opcode_strs[opcode], label, label_addr, addr, addr2); 206 | break; 207 | } 208 | 209 | case movi: { 210 | const uintptr_t addr = ( uintptr_t )(pc.uint8) - offs; 211 | const uint32_t reg = *pc.uint8++; 212 | const int64_t imm = *pc.int64++; 213 | const uintptr_t addr2 = ( uintptr_t )(pc.uint8) - offs; 214 | 215 | harbol_string_add_format(&bc_funcs, " %-10s r%u, %#" PRIx64 " ;; offset: %" PRIuPTR " - %" PRIuPTR "\n", opcode_strs[opcode], reg, imm, addr, addr2); 216 | break; 217 | } 218 | default: break; 219 | } 220 | } 221 | harbol_string_add_format(&bc_funcs, "}\n\n"); 222 | } else { 223 | if( entry->flags & TAGHA_FLAG_NATIVE ) { 224 | harbol_string_add_format(&nbc_funcs, "$native %s\n", iter.string); 225 | } else if( entry->flags & TAGHA_FLAG_EXTERN ) { 226 | harbol_string_add_format(&nbc_funcs, "$extern %s\n", iter.string); 227 | } 228 | iter.uint8 += entry->name_len; 229 | } 230 | } 231 | harbol_string_add_cstr(&nbc_funcs, "\n"); 232 | 233 | /// iterate var table and get bytecode sizes. 234 | for( uint32_t i=0; idata_len; 238 | harbol_string_add_format(&vars, "$global %-25s, %u", iter.string, entry->data_len); 239 | iter.uint8 += entry->name_len; 240 | union HarbolBinIter data = iter; 241 | iter.uint8 += entry->data_len; 242 | for( ; bytes / sizeof(union TaghaVal) > 0; bytes -= sizeof(union TaghaVal) ) { 243 | harbol_string_add_format(&vars, ", word %#" PRIx64 "", *data.uint64++); 244 | } 245 | switch( bytes ) { 246 | case 4: 247 | harbol_string_add_format(&vars, ", long %#x", *data.uint32++); 248 | bytes -= 4; 249 | break; 250 | case 2: 251 | harbol_string_add_format(&vars, ", half %#x", *data.uint16++); 252 | bytes -= 2; 253 | break; 254 | case 1: 255 | harbol_string_add_format(&vars, ", byte %#x", *data.uint8++); 256 | bytes -= 1; 257 | break; 258 | } 259 | harbol_string_add_format(&vars, "\n"); 260 | } 261 | harbol_string_add_cstr(&vars, "\n"); 262 | free(filedata); filedata=NULL; 263 | 264 | for( size_t i=0; i 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | 13 | static inline size_t tagha_instr_gen(struct HarbolByteBuf *const tbc, const enum TaghaInstrSet op, ...) 14 | { 15 | if( op >= MaxOps ) 16 | return 0; 17 | 18 | size_t bytes = 1; 19 | if( tbc != NULL ) 20 | harbol_bytebuffer_insert_byte(tbc, op); 21 | 22 | va_list ap; va_start(ap, op); 23 | switch( op ) { 24 | /// one byte operand. 25 | case alloc: case redux: case setelen: case enter: 26 | case neg: case fneg: 27 | case _not: case setc: 28 | case callr: 29 | case f32tof64: case f64tof32: 30 | case itof64: case itof32: 31 | case f64toi: case f32toi: 32 | case vneg: case vfneg: 33 | case vnot: 34 | case leave: case remit: { 35 | if( tbc != NULL ) { 36 | const int oper1 = va_arg(ap, int); 37 | harbol_bytebuffer_insert_byte(tbc, oper1); 38 | } 39 | bytes += 1; 40 | break; 41 | } 42 | 43 | /// two byte operands. 44 | case mov: 45 | case add: case sub: case mul: case idiv: case mod: 46 | case fadd: case fsub: case fmul: case fdiv: 47 | case _and: case _or: case _xor: case sll: case srl: case sra: 48 | case ilt: case ile: case ult: case ule: case cmp: case flt: case fle: 49 | case vmov: 50 | case vadd: case vsub: case vmul: case vdiv: case vmod: 51 | case vfadd: case vfsub: case vfmul: case vfdiv: 52 | case vand: case vor: case vxor: case vsll: case vsrl: case vsra: 53 | case vcmp: case vilt: case vile: case vult: case vule: case vflt: case vfle: { 54 | if( tbc != NULL ) { 55 | const int oper1 = va_arg(ap, int); 56 | const int oper2 = va_arg(ap, int); 57 | harbol_bytebuffer_insert_byte(tbc, oper1); 58 | harbol_bytebuffer_insert_byte(tbc, oper2); 59 | } 60 | bytes += 2; 61 | break; 62 | } 63 | 64 | case call: case setvlen: { 65 | if( tbc != NULL ) { 66 | const int oper1 = va_arg(ap, int); 67 | harbol_bytebuffer_insert_int16(tbc, ( uint16_t )oper1); 68 | } 69 | bytes += 2; 70 | break; 71 | } 72 | 73 | /// one byte + unsigned 2-byte int. 74 | case lra: case ldvar: case ldfn: { 75 | if( tbc != NULL ) { 76 | const int oper1 = va_arg(ap, int); 77 | const int oper2 = va_arg(ap, int); 78 | harbol_bytebuffer_insert_byte(tbc, oper1); 79 | harbol_bytebuffer_insert_int16(tbc, oper2); 80 | } 81 | bytes += 3; 82 | break; 83 | } 84 | 85 | /// two bytes + signed 2-byte int. 86 | case lea: 87 | case ld1: case ld2: case ld4: case ld8: case ldu1: case ldu2: case ldu4: 88 | case st1: case st2: case st4: case st8: { 89 | if( tbc != NULL ) { 90 | const int oper1 = va_arg(ap, int); 91 | const int oper2 = va_arg(ap, int); 92 | const int _oper3 = va_arg(ap, int); 93 | const int16_t oper3 = ( int16_t )_oper3; 94 | harbol_bytebuffer_insert_byte(tbc, oper1); 95 | harbol_bytebuffer_insert_byte(tbc, oper2); 96 | harbol_bytebuffer_insert_int16(tbc, ( uint16_t )oper3); 97 | } 98 | bytes += 4; 99 | break; 100 | } 101 | 102 | /// signed 4-byte operand. 103 | case jmp: case jz: case jnz: { 104 | if( tbc != NULL ) { 105 | const int32_t oper1 = va_arg(ap, int32_t); 106 | harbol_bytebuffer_insert_int32(tbc, ( uint32_t )oper1); 107 | } 108 | bytes += 4; 109 | break; 110 | } 111 | 112 | /// byte + 8-byte int operand. 113 | case movi: { 114 | if( tbc != NULL ) { 115 | const int oper1 = va_arg(ap, int); 116 | const union TaghaVal oper2 = va_arg(ap, union TaghaVal); 117 | harbol_bytebuffer_insert_byte(tbc, oper1); 118 | harbol_bytebuffer_insert_int64(tbc, oper2.uint64); 119 | } 120 | bytes += 9; 121 | break; 122 | } 123 | 124 | /// no operands. 125 | case ret: case halt: case nop: case pushlr: case poplr: case restore: case MaxOps: { 126 | break; 127 | } 128 | } 129 | va_end(ap); 130 | return bytes; 131 | } 132 | 133 | 134 | 135 | 136 | #ifdef __cplusplus 137 | } /** extern "C" */ 138 | #endif 139 | 140 | #endif /** TAGHA_INSTR_GEN */ -------------------------------------------------------------------------------- /tagha_toolchain/libharbol.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/tagha_toolchain/libharbol.zip -------------------------------------------------------------------------------- /tagha_toolchain/module_gen.h: -------------------------------------------------------------------------------- 1 | #ifndef TAGHA_MODULE_GEN 2 | # define TAGHA_MODULE_GEN 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "../tagha/tagha.h" 9 | #include "libharbol/harbol.h" 10 | 11 | 12 | struct TaghaModGen { 13 | struct TaghaModuleHeader hdr; 14 | struct HarbolByteBuf var_data, func_data; 15 | }; 16 | 17 | 18 | static inline struct TaghaModGen tagha_mod_gen_create(void) 19 | { 20 | struct TaghaModGen module = {0}; 21 | module.func_data = harbol_bytebuffer_create(); 22 | module.var_data = harbol_bytebuffer_create(); 23 | module.hdr.magic = TAGHA_MAGIC_VERIFIER; 24 | return module; 25 | } 26 | 27 | static inline NO_NULL void tagha_mod_gen_write_header(struct TaghaModGen *const mod, const uint32_t opstacksize, const uint32_t callstacksize, const uint32_t heapsize, const uint32_t flags) 28 | { 29 | mod->hdr.opstacksize = opstacksize; 30 | mod->hdr.callstacksize = callstacksize; 31 | mod->hdr.stacksize = opstacksize + callstacksize; 32 | mod->hdr.heapsize = heapsize; 33 | mod->hdr.memsize = opstacksize + callstacksize + heapsize; 34 | mod->hdr.flags = flags; 35 | } 36 | 37 | #ifdef __cplusplus 38 | static inline NEVER_NULL(1,3) void tagha_mod_gen_write_func(struct TaghaModGen *const restrict mod, const uint32_t flags, const char *name, const struct HarbolByteBuf *const bytecode) 39 | #else 40 | static inline NEVER_NULL(1,3) void tagha_mod_gen_write_func(struct TaghaModGen *const restrict mod, const uint32_t flags, const char name[static 1], const struct HarbolByteBuf *const bytecode) 41 | #endif 42 | { 43 | uint32_t entry_size = sizeof(struct TaghaItemEntry); 44 | const uint32_t name_len = ( uint32_t )(strlen(name)) + 1; 45 | const uint32_t name_len_diff = ( uint32_t )(harbol_align_size(name_len, 4)) - name_len; 46 | const uint32_t data_len = ( uint32_t )(bytecode->count); 47 | const uint32_t data_len_diff = ( uint32_t )(harbol_align_size(data_len, 4)) - data_len; 48 | entry_size += name_len + name_len_diff; 49 | entry_size += ( flags != 0 ) ? 8 : data_len + data_len_diff; 50 | 51 | /// entry size 52 | harbol_bytebuffer_insert_int32(&mod->func_data, entry_size); 53 | 54 | /// write flag 55 | harbol_bytebuffer_insert_int32(&mod->func_data, flags); 56 | 57 | /// write strlen 58 | harbol_bytebuffer_insert_int32(&mod->func_data, name_len + name_len_diff); 59 | 60 | /// write instrlen 61 | ( flags != 0 ) ? 62 | harbol_bytebuffer_insert_int32(&mod->func_data, 8) : 63 | harbol_bytebuffer_insert_int32(&mod->func_data, data_len + data_len_diff); 64 | 65 | /// write string of func. 66 | harbol_bytebuffer_insert_cstr(&mod->func_data, name); 67 | harbol_bytebuffer_insert_zeros(&mod->func_data, name_len_diff); 68 | 69 | /// write bytecode. 70 | if( !flags ) { 71 | harbol_bytebuffer_append(&mod->func_data, bytecode); 72 | harbol_bytebuffer_insert_zeros(&mod->func_data, data_len_diff); 73 | } 74 | mod->hdr.func_count++; 75 | } 76 | 77 | #ifdef __cplusplus 78 | static inline NEVER_NULL(1,3,4) void tagha_mod_gen_write_var(struct TaghaModGen *const restrict mod, const uint32_t flags, const char *name, const struct HarbolByteBuf *const datum) 79 | #else 80 | static inline NEVER_NULL(1,3,4) void tagha_mod_gen_write_var(struct TaghaModGen *const restrict mod, const uint32_t flags, const char name[static 1], const struct HarbolByteBuf *const datum) 81 | #endif 82 | { 83 | uint32_t entry_size = sizeof(struct TaghaItemEntry); 84 | const uint32_t name_len = ( uint32_t )(strlen(name)) + 1; 85 | const uint32_t data_len = ( uint32_t )(datum->count); 86 | const uint32_t name_len_diff = ( uint32_t )(harbol_align_size(name_len, 4)) - name_len; 87 | const uint32_t data_len_diff = ( uint32_t )(harbol_align_size(data_len, 4)) - data_len; 88 | entry_size += name_len + data_len + name_len_diff + data_len_diff; 89 | 90 | /// entry size 91 | harbol_bytebuffer_insert_int32(&mod->var_data, entry_size); 92 | 93 | /// write flag 94 | harbol_bytebuffer_insert_int32(&mod->var_data, flags); 95 | 96 | /// write strlen 97 | harbol_bytebuffer_insert_int32(&mod->var_data, name_len + name_len_diff); 98 | 99 | /// write var data size 100 | harbol_bytebuffer_insert_int32(&mod->var_data, data_len + data_len_diff); 101 | 102 | /// write string of var name. 103 | harbol_bytebuffer_insert_cstr(&mod->var_data, name); 104 | harbol_bytebuffer_insert_zeros(&mod->var_data, name_len_diff); 105 | 106 | /// write var data. 107 | harbol_bytebuffer_append(&mod->var_data, datum); 108 | harbol_bytebuffer_insert_zeros(&mod->var_data, data_len_diff); 109 | 110 | mod->hdr.var_count++; 111 | } 112 | 113 | static inline NO_NULL struct HarbolByteBuf __tagha_mod_gen_finalize(struct TaghaModGen *const mod) 114 | { 115 | struct HarbolByteBuf final_tbc = harbol_bytebuffer_create(); 116 | 117 | /// calculate offsets. 118 | mod->hdr.funcs_offset = sizeof mod->hdr; 119 | mod->hdr.vars_offset = mod->hdr.funcs_offset + mod->func_data.count; 120 | mod->hdr.mem_offset = mod->hdr.vars_offset + mod->var_data.count; 121 | 122 | /// add the header. 123 | harbol_bytebuffer_insert_obj(&final_tbc, &mod->hdr, sizeof mod->hdr); 124 | 125 | /// build func table & var table. 126 | harbol_bytebuffer_append(&final_tbc, &mod->func_data); 127 | harbol_bytebuffer_append(&final_tbc, &mod->var_data); 128 | 129 | /// build memory region. 130 | harbol_bytebuffer_insert_zeros(&final_tbc, mod->hdr.memsize); 131 | 132 | /// free data. 133 | harbol_bytebuffer_clear(&mod->func_data); 134 | harbol_bytebuffer_clear(&mod->var_data); 135 | 136 | return final_tbc; 137 | } 138 | 139 | #ifdef __cplusplus 140 | static inline NO_NULL bool tagha_mod_gen_create_file(struct TaghaModGen *const restrict mod, const char *filename) 141 | #else 142 | static inline NO_NULL bool tagha_mod_gen_create_file(struct TaghaModGen *const restrict mod, const char filename[static 1]) 143 | #endif 144 | { 145 | bool result = false; 146 | FILE *restrict tbcfile = fopen(filename, "w"); 147 | if( tbcfile==NULL ) 148 | goto null_file_err; 149 | 150 | struct HarbolByteBuf final_tbc = __tagha_mod_gen_finalize(mod); 151 | result = harbol_bytebuffer_to_file(&final_tbc, tbcfile); 152 | fclose(tbcfile); tbcfile = NULL; 153 | harbol_bytebuffer_clear(&final_tbc); 154 | 155 | null_file_err: 156 | return result; 157 | } 158 | 159 | static inline NO_NULL struct HarbolByteBuf tagha_mod_gen_buffer(struct TaghaModGen *const mod) 160 | { 161 | struct HarbolByteBuf final_tbc = __tagha_mod_gen_finalize(mod); 162 | return final_tbc; 163 | } 164 | 165 | static inline NO_NULL uint8_t *tagha_mod_gen_raw(struct TaghaModGen *const mod) 166 | { 167 | return tagha_mod_gen_buffer(mod).table; 168 | } 169 | 170 | #ifdef __cplusplus 171 | } /// extern "C" 172 | #endif 173 | 174 | #endif /** TAGHA_MODULE_GEN */ -------------------------------------------------------------------------------- /tagha_toolchain/module_info.h: -------------------------------------------------------------------------------- 1 | #ifndef TAGHA_MODULE_INFO 2 | # define TAGHA_MODULE_INFO 3 | 4 | 5 | #include "../tagha/tagha.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | static inline NO_NULL void tagha_module_print_header(const struct TaghaModule *const mod, FILE *const stream) 12 | { 13 | #ifdef __cplusplus 14 | const struct TaghaModuleHeader *const hdr = reinterpret_cast< decltype(hdr) >(mod->script); 15 | #else 16 | const struct TaghaModuleHeader *const hdr = ( const struct TaghaModuleHeader* )(mod->script); 17 | #endif 18 | fprintf(stream, "Tagha Module::\nOperand Stack Size: %u bytes\nCall Stack Size: %u bytes\nTotal Stack Size: %u bytes\nHeap Size: %u bytes\nTotal Memory: %u bytes\nFunction Count: %u\nGlobal Var Count: %u\nScript Flags: %u\n", hdr->opstacksize, hdr->callstacksize, hdr->stacksize, hdr->heapsize, hdr->memsize, hdr->func_count, hdr->var_count, hdr->flags); 19 | } 20 | 21 | static inline NO_NULL void tagha_module_print_opstack(const struct TaghaModule *const mod, FILE *const stream) 22 | { 23 | size_t i = 0; 24 | #ifdef __cplusplus 25 | const union TaghaVal *const top = reinterpret_cast< decltype(top) >(mod->opstack + mod->opstack_size); 26 | for( const union TaghaVal *head = reinterpret_cast< decltype(opstk) >(mod->osp); head < top; head++ ) { 27 | #else 28 | const union TaghaVal *const top = ( const union TaghaVal* )(mod->opstack + mod->opstack_size); 29 | for( const union TaghaVal *head = ( const union TaghaVal* )(mod->osp); head < top; head++ ) { 30 | #endif 31 | fprintf(stream, "op stack : %-12zu - '%#" PRIx64 "'\n", i, head->uint64); 32 | i++; 33 | } 34 | } 35 | 36 | static inline NO_NULL void tagha_module_print_callstack(const struct TaghaModule *const mod, FILE *const stream) 37 | { 38 | size_t i = 0; 39 | #ifdef __cplusplus 40 | const uintptr_t *const base = reinterpret_cast< decltype(top) >(mod->callstack); 41 | for( const uintptr_t *head = reinterpret_cast< decltype(head) >(mod->csp); head >= base; head-- ) { 42 | #else 43 | const uintptr_t *const base = ( const uintptr_t* )(mod->callstack); 44 | for( const uintptr_t *head = ( const uintptr_t* )(mod->csp); head >= base; head-- ) { 45 | #endif 46 | fprintf(stream, "call stack : %-10zu - '%" PRIuPTR "'\n", i, *head); 47 | i++; 48 | } 49 | } 50 | 51 | #ifdef __cplusplus 52 | } /** extern "C" */ 53 | #endif 54 | 55 | #endif /** TAGHA_MODULE_INFO */ -------------------------------------------------------------------------------- /test_asm/test_3d_vecs.tasm: -------------------------------------------------------------------------------- 1 | $opstack_size 10 ; 10 stack cells so 10 registers 2 | 3 | main { 4 | pushlr ;; push link register 5 | ;; think of 'alloc' as allocating the number of registers. 6 | alloc 4 ;; reduce stack pointer by 32 (8 * 4) bytes | rsp -= 32; 7 | lra r1, 2 ;; float v[3]; | rsp[1] = &rsp[2]; 8 | movi r0, 0x40000000 ;; rsp[0] = 2.f; 9 | st4 [r1], r0 ;; v[0] = 2.f; | (( int* )rsp[1])[0] = rsp[0] 10 | movi r0, 0x40400000 ;; rsp[0] = 3.f; 11 | st4 [r1+4], r0 ;; v[1] = 2.f; | (( int* )rsp[1])[1] = rsp[0]; 12 | movi r0, 0x40800000 ;; rsp[0] = 4.f; 13 | st4 [r1+8], r0 ;; v[2] = 4.f; | (( int* )rsp[1])[2] = rsp[0]; 14 | call Vec3D_Invert ;; Vec3D_Invert(rsp); 15 | poplr ;; pop back link register 16 | ret 17 | } 18 | 19 | /** 20 | void Vec3D_Invert(float v[const static 3]) 21 | { 22 | v[0] = -v[0]; 23 | v[1] = -v[1]; 24 | v[2] = -v[2]; 25 | } 26 | */ 27 | 28 | Vec3D_Invert { 29 | alloc 1 ;; rsp -= 8; 30 | ;; REMEMBER that our array is stored in r1 so allocating another cell means it's in r2 now!!! 31 | ;; rsp[0] == 0; 32 | ;; rsp[1] == 0; 33 | ;; rsp[2] == v[3]; 34 | 35 | ;; v[0] = -v[0]; 36 | ld4 r0, [r2] ;; rsp[0] = (( int* )rsp[2])[0]; 37 | f32tof64 r0 ;; rsp[0] = ( double )rsp[0]; 38 | fneg r0 ;; rsp[0] = -rsp[1]; 39 | fadd r1, r0 ;; rsp[1] += rsp[0]; 40 | f64tof32 r0 ;; rsp[0] = ( float )rsp[0]; 41 | st4 [r2], r0 ;; (( int* )rsp[2])[0] = rsp[0]; 42 | 43 | ;; v[1] = -v[1]; 44 | ld4 r0, [r2+4] ;; rsp[0] = (( int* )rsp[2])[1]; 45 | f32tof64 r0 ;; rsp[0] = ( double )rsp[0]; 46 | fneg r0 ;; rsp[0] = -rsp[1]; 47 | fadd r1, r0 ;; rsp[1] += rsp[0]; 48 | f64tof32 r0 ;; rsp[0] = ( float )rsp[0]; 49 | st4 [r2+4], r0 ;; (( int* )rsp[2])[1] = rsp[0]; 50 | 51 | ;; v[2] = -v[2]; 52 | ld4 r0, [r2+8] ;; rsp[0] = (( int* )rsp[2])[2]; 53 | f32tof64 r0 ;; rsp[0] = ( double )rsp[0]; 54 | fneg r0 ;; rsp[0] = -rsp[1]; 55 | fadd r1, r0 ;; rsp[0] += rsp[1]; 56 | f64tof32 r0 ;; rsp[0] = ( float )rsp[0]; 57 | st4 [r2+8], r0 ;; (( int* )rsp[2])[2] = rsp[0]; 58 | ret 59 | } -------------------------------------------------------------------------------- /test_asm/test_3d_vecs.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_3d_vecs.tbc -------------------------------------------------------------------------------- /test_asm/test_dynamiclinking.tasm: -------------------------------------------------------------------------------- 1 | $global module_str "test_factorial.tbc" 2 | 3 | $native tagha_module_new_from_file 4 | $native tagha_module_link_module 5 | $native tagha_module_free 6 | 7 | $extern factorial 8 | 9 | 10 | main { 11 | alloc 10 12 | movi r9, 10000000 ;; 1000000000 / 1000000 / 10000000 13 | 14 | ldvar r1, module_str 15 | movi r0, 1 16 | call tagha_module_new_from_file 17 | mov r8, r0 18 | 19 | mov r2, r8 20 | movi r1, 0 21 | call tagha_module_link_module ;; a system using Tagha would typically link modules to one another during loading. 22 | 23 | ;; Manually linking from scripts is not really recommended but to show it's possible to do so. 24 | 25 | ;; test dynamic linking function invocation overhead 26 | movi r7, 1 27 | movi r6, 0 28 | 29 | .loop 30 | movi r0, 5 ;; rsp[0] = 5; 31 | call factorial ;; factorial(5); 32 | sub r9, r7 33 | cmp r9, r6 34 | jz .loop 35 | mov r4, r0 36 | 37 | mov r3, r8 ;; rsp[3] = rsp[8]; 38 | lra r1, 3 ;; rsp[1] = &rsp[3]; 39 | call tagha_module_free ;; tagha_module_free(&module); 40 | mov r9, r4 41 | remit 9 42 | } -------------------------------------------------------------------------------- /test_asm/test_dynamiclinking.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_dynamiclinking.tbc -------------------------------------------------------------------------------- /test_asm/test_dynamicloading.tasm: -------------------------------------------------------------------------------- 1 | $global module_str, "test_factorial.tbc" 2 | $global factorial_str, "factorial" 3 | 4 | ;; struct TaghaModule *tagha_module_new_from_file(const char filename[]); 5 | $native tagha_module_new_from_file 6 | 7 | ;; TaghaFunc tagha_module_get_func(const struct TaghaModule *module, const char name[]); 8 | $native tagha_module_get_func 9 | 10 | ;; bool tagha_module_free(struct TaghaModule **modref); 11 | $native tagha_module_free 12 | 13 | main { 14 | alloc 10 15 | movi r9, 10000000 ;; 1000000000 / 1000000 / 10000000 16 | 17 | ;; struct TaghaModule *fact_module = tagha_module_new_from_file("test_factorial.tbc"); 18 | ldvar r1, module_str 19 | call tagha_module_new_from_file 20 | 21 | mov r8, r0 ;; module ptr is in r0, copy the ptr to stack for later freeing. 22 | ldvar r2, factorial_str 23 | 24 | ;; int (*const factorial)(int) = dlsym(fact_module, "factorial"); 25 | mov r1, r8 26 | call tagha_module_get_func 27 | mov r7, r0 ;; copy function pointer 28 | 29 | movi r6, 0 30 | movi r5, 1 31 | 32 | ;; test dynamic loading function invocation overhead 33 | .loop 34 | movi r0, 5 ;; rsp[0] = 5; 35 | callr r7 ;; (*factorial)(rsp); | result in r0. 36 | sub r9, r5 ;; rsp[9] -= rsp[5]; 37 | cmp r9, r6 ;; while( rsp[9] != rsp[6] ) 38 | jz .loop 39 | mov r4, r0 40 | 41 | mov r3, r8 ;; rsp[3] = rsp[8]; 42 | lra r1, 3 ;; rsp[1] = &rsp[3]; 43 | call tagha_module_free ;; tagha_module_free(&module); 44 | mov r9, r4 45 | redux 9 46 | ret 47 | } -------------------------------------------------------------------------------- /test_asm/test_dynamicloading.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_dynamicloading.tbc -------------------------------------------------------------------------------- /test_asm/test_factorial.tasm: -------------------------------------------------------------------------------- 1 | /** 2 | uint32_t factorial(const uint32_t i) { 3 | if( i<=1 ) 4 | return 1; 5 | else return i * factorial(i-1); 6 | } 7 | */ 8 | 9 | main { 10 | enter 1 11 | movi r0, 5 12 | call factorial 13 | restore 14 | } 15 | 16 | factorial { 17 | enter 3 18 | mov r0, r3 19 | 20 | ;; if( i<=1 ) 21 | movi r1, 1 22 | ule r0, r1 23 | jz .L1 24 | 25 | ;; return 1; 26 | mov r0, r1 27 | jmp .L2 28 | 29 | .L1 30 | ;; return i * factorial(i-1); 31 | mov r2, r0 ;; int temp = i; 32 | sub r2, r1 ;; temp -= 1; 33 | mov r0, r2 34 | call factorial ;; int res = factorial(temp); 35 | mul r3, r0 ;; i * res; 36 | 37 | .L2 38 | leave 3 39 | } -------------------------------------------------------------------------------- /test_asm/test_factorial.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_factorial.tbc -------------------------------------------------------------------------------- /test_asm/test_fib34.tasm: -------------------------------------------------------------------------------- 1 | main { 2 | enter 1 3 | movi r0, 34 4 | call fib 5 | restore 6 | } 7 | 8 | /** O(2^n) algorithm 9 | int fib(const int n) { 10 | return (n<2) ? n : fib(n-1)+fib(n-2); 11 | } 12 | */ 13 | 14 | fib { 15 | enter 6 16 | movi r3, 1 17 | movi r1, 2 18 | ilt r6, r1 ;; if( n<2 ) 19 | jz .calc 20 | 21 | ;; return 1; 22 | movi r0, 1 23 | leave 6 24 | 25 | .calc 26 | ;; temp1 = fib(n - 1) 27 | mov r2, r6 ;; _temp1 = n; 28 | sub r2, r3 ;; _temp1 -= 1; 29 | mov r0, r2 ;; temp = _temp1; 30 | call fib ;; res1 = factorial(temp); 31 | mov r4, r0 ;; __temp1 = res1; 32 | 33 | ;; temp2 = fib(n - 2) 34 | mov r2, r6 ;; _temp2 = n; 35 | sub r2, r1 ;; _temp2 -= 2; 36 | mov r0, r2 ;; temp = _temp2; 37 | call fib ;; res2 = factorial(temp); 38 | mov r5, r0 ;; __temp2 = res2; 39 | 40 | ;; return temp1 + temp2 41 | add r4, r5 ;; __temp1 += __temp2; 42 | mov r6, r4 ;; n = __temp1; 43 | 44 | leave 6 45 | } 46 | 47 | /* 48 | fib { 49 | pushlr 50 | alloc 6 51 | movi r3, 1 52 | movi r1, 2 53 | ilt r6, r1 ;; if( n<2 ) 54 | jz .calc 55 | 56 | ;; return 1; 57 | movi r0, 1 58 | poplr 59 | redux 6 60 | ret 61 | 62 | .calc 63 | ;; temp1 = fib(n - 1) 64 | mov r2, r6 ;; _temp1 = n; 65 | sub r2, r3 ;; _temp1 -= 1; 66 | mov r0, r2 ;; temp = _temp1; 67 | call fib ;; res1 = factorial(temp); 68 | mov r4, r0 ;; __temp1 = res1; 69 | 70 | ;; temp2 = fib(n - 2) 71 | mov r2, r6 ;; _temp2 = n; 72 | sub r2, r1 ;; _temp2 -= 2; 73 | mov r0, r2 ;; temp = _temp2; 74 | call fib ;; res2 = factorial(temp); 75 | mov r5, r0 ;; __temp2 = res2; 76 | 77 | ;; return temp1 + temp2 78 | add r4, r5 ;; __temp1 += __temp2; 79 | mov r6, r4 ;; n = __temp1; 80 | 81 | poplr 82 | redux 6 83 | ret 84 | } 85 | */ 86 | 87 | /** Another Implementation 88 | fib { 89 | pushlr 90 | alloc 6 91 | movi r3, 1 92 | movi r1, 2 93 | ilt r6, r1 ;; if( n<2 ) 94 | jz .calc 95 | 96 | ;; return 1; 97 | movi r0, 1 98 | jmp .exit 99 | 100 | .calc 101 | ;; temp1 = fib(n - 1) 102 | mov r2, r6 ;; _temp1 = n; 103 | sub r2, r3 ;; _temp1 -= 1; 104 | mov r0, r2 ;; temp = _temp1; 105 | call fib ;; res1 = factorial(temp); 106 | mov r4, r0 ;; __temp1 = res1; 107 | 108 | ;; temp1 = fib(n - 2) 109 | mov r2, r6 ;; _temp2 = n; 110 | sub r2, r1 ;; _temp2 -= 2; 111 | mov r0, r2 ;; temp = _temp2; 112 | call fib ;; res2 = factorial(temp); 113 | mov r5, r0 ;; __temp2 = res2; 114 | 115 | ;; return temp1 + temp2 116 | add r4, r5 ;; __temp1 += __temp2; 117 | mov r6, r4 ;; n = __temp1; 118 | 119 | .exit 120 | poplr 121 | redux 6 122 | ret 123 | } 124 | */ -------------------------------------------------------------------------------- /test_asm/test_fib34.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_fib34.tbc -------------------------------------------------------------------------------- /test_asm/test_fib40.tasm: -------------------------------------------------------------------------------- 1 | main { 2 | alloc 1 3 | pushlr 4 | movi r0, 40 5 | call fib 6 | poplr 7 | ret 8 | } 9 | 10 | /** 11 | /// O(2^n) algorithm 12 | int fib(const int n) { 13 | return (n<2) ? n : fib(n-1)+fib(n-2); 14 | } 15 | */ 16 | 17 | fib { 18 | pushlr 19 | alloc 6 20 | movi r3, 1 21 | movi r1, 2 22 | ilt r6, r1 ;; if( n<2 ) 23 | jz .calc 24 | 25 | ;; return 1; 26 | movi r0, 1 27 | poplr 28 | redux 6 29 | ret 30 | 31 | .calc 32 | ;; temp1 = fib(n - 1) 33 | mov r2, r6 ;; _temp1 = n; 34 | sub r2, r3 ;; _temp1 -= 1; 35 | mov r0, r2 ;; temp = _temp1; 36 | call fib ;; res1 = factorial(temp); 37 | mov r4, r0 ;; __temp1 = res1; 38 | 39 | ;; temp2 = fib(n - 2) 40 | mov r2, r6 ;; _temp2 = n; 41 | sub r2, r1 ;; _temp2 -= 2; 42 | mov r0, r2 ;; temp = _temp2; 43 | call fib ;; res2 = factorial(temp); 44 | mov r5, r0 ;; __temp2 = res2; 45 | 46 | ;; return temp1 + temp2 47 | add r4, r5 ;; __temp1 += __temp2; 48 | mov r6, r4 ;; n = __temp1; 49 | 50 | poplr 51 | redux 6 52 | ret 53 | } -------------------------------------------------------------------------------- /test_asm/test_fib40.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_fib40.tbc -------------------------------------------------------------------------------- /test_asm/test_funcptr.tasm: -------------------------------------------------------------------------------- 1 | $global stdin, 8, word 0 2 | $global string, "Please enter a long string: " 3 | 4 | $native puts ;; int puts(const char *str); 5 | $native fgets ;; char *fgets(char *buf, int size, FILE *stream); 6 | 7 | main { 8 | ;; char string[256]; 9 | alloc 37 ;; 8 * 37 = 288 bytes of stack space. 10 | 11 | ;; puts("Please enter a long string: "); 12 | ldvar r1, string 13 | call puts 14 | 15 | ;; fgets(string, 256, stdin); 16 | ;; loading a global var only loads its address, we need the actual ptr value of stdin. 17 | ldvar r3, stdin 18 | ld8 r3, [r3] 19 | movi r2, 256 20 | lra r1, 5 21 | ldfn r4, fgets 22 | callr r4 23 | 24 | mov r1, r0 25 | call puts 26 | ret 27 | } -------------------------------------------------------------------------------- /test_asm/test_funcptr.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_funcptr.tbc -------------------------------------------------------------------------------- /test_asm/test_global.tasm: -------------------------------------------------------------------------------- 1 | /** 2 | struct Player { 3 | float32_t speed; 4 | uint32_t health, ammo; 5 | } g_player; 6 | * 7 | * g_player is defined in this script 8 | * but its data will be supplied from the host! 9 | */ 10 | 11 | $global g_player, 12, 0 12 | 13 | main: { 14 | alloc 4 15 | ldvar r0, g_player 16 | 17 | movi r1, 0x43960000 ; 300.f as a 32-bit int hex 18 | st4 [r0], r1 ; g_player.speed = 300.f; 19 | 20 | movi r2, 100 21 | st4 [r0+4], r2 ; g_player.health = 100; 22 | 23 | movi r3, 32 24 | st4 [r0+8], r3 ; g_player.ammo = 32; 25 | 26 | ret 27 | } -------------------------------------------------------------------------------- /test_asm/test_global.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_global.tbc -------------------------------------------------------------------------------- /test_asm/test_int_cmp.tasm: -------------------------------------------------------------------------------- 1 | $opstack_size 16 ;; 16 registers 2 | 3 | /** 4 | int main(void) 5 | { 6 | int v1[] = { 1, 2, 3 }; 7 | int v2[] = { 1, 3, 2 }; 8 | return v1==v2; /// int cmp. 9 | } 10 | */ 11 | 12 | main { 13 | setelen long 14 | setvlen 3 15 | 16 | pushlr 17 | alloc 6 18 | 19 | movi r1, 1 20 | mov r2, r1 21 | mov r4, r1 22 | 23 | movi r1, 2 24 | movi r0, 0x200000000 25 | or r2, r0 26 | movi r3, 3 27 | 28 | movi r0, 0x200000000 ;0x300000000 29 | or r4, r0 30 | movi r5, 3 ;2 31 | 32 | vcmp r2, r4 ;; test if equal. 33 | setc r0 ;; return v1==v2; 34 | 35 | poplr 36 | ret 37 | } -------------------------------------------------------------------------------- /test_asm/test_int_cmp.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_int_cmp.tbc -------------------------------------------------------------------------------- /test_asm/test_invalid_memory.tasm: -------------------------------------------------------------------------------- 1 | $global g_var, 12, 0 2 | 3 | ;; test global variable going out of bounds. 4 | test_globalmembnds { 5 | ldvar r0, g_var 6 | movi r1, 50 7 | st4 [r0+300], r1 8 | ret 9 | } 10 | 11 | ;; test local var going out of bounds. 12 | ;; remember we have a stack size of 128 bytes. 13 | test_stackbnds { 14 | movi r1, 90 15 | ;; this probably overwrites alot of global var data but it won't overwrite func data. 16 | st4 [r0-1024], r1 17 | ret 18 | } 19 | 20 | test_toomuchalloc { 21 | alloc 255 22 | alloc 255 23 | alloc 255 24 | ret 25 | } 26 | 27 | main { 28 | alloc 10 29 | pushlr 30 | call test_globalmembnds 31 | call test_stackbnds 32 | poplr 33 | ret 34 | } -------------------------------------------------------------------------------- /test_asm/test_invalid_memory.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_invalid_memory.tbc -------------------------------------------------------------------------------- /test_asm/test_loop.tasm: -------------------------------------------------------------------------------- 1 | /** 2 | int i = 0 3 | while i < 100000000 { 4 | i += 1 5 | } 6 | return i 7 | */ 8 | 9 | main { 10 | alloc 3 11 | movi r2, 100000000 ;; 100M 12 | movi r1, 1 13 | 14 | ;; test basic loop overhead. 15 | .loop 16 | add r0, r1 17 | cmp r0, r2 18 | jz .loop 19 | 20 | remit 2 21 | } -------------------------------------------------------------------------------- /test_asm/test_loop.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_loop.tbc -------------------------------------------------------------------------------- /test_asm/test_native_number.tasm: -------------------------------------------------------------------------------- 1 | ;; int add_one(const int n); 2 | $native add_one 3 | 4 | /** 5 | int main(void) 6 | { 7 | int count = 0; 8 | for( int i=0; i<1_000_000_000; i++ ) 9 | count = add_one(count); 10 | } 11 | */ 12 | 13 | main { 14 | alloc 3 15 | movi r2, 1000000000 16 | movi r1, 0 17 | movi r0, 0 18 | 19 | .loop 20 | call add_one 21 | mov r1, r0 22 | cmp r2, r1 23 | jz .loop 24 | 25 | remit 2 26 | } -------------------------------------------------------------------------------- /test_asm/test_native_number.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_native_number.tbc -------------------------------------------------------------------------------- /test_asm/test_ptr.tasm: -------------------------------------------------------------------------------- 1 | /** 2 | void incr(int *x) { 3 | *x += 1; 4 | } 5 | 6 | int main() 7 | { 8 | int n = 100; 9 | incr(&n); 10 | } 11 | */ 12 | 13 | incr { 14 | alloc 2 15 | movi r1, 1 16 | ld4 r0, [r2] 17 | add r0, r1 18 | st4 [r2], r1 19 | remit 2 20 | } 21 | 22 | main { 23 | enter 2 24 | movi r1, 100 25 | lra r0, 1 26 | restore 27 | } -------------------------------------------------------------------------------- /test_asm/test_ptr.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_ptr.tbc -------------------------------------------------------------------------------- /test_asm/test_selfcall.tasm: -------------------------------------------------------------------------------- 1 | $global func_str, "func" 2 | $global nude_str, "send nudes" 3 | $global kek_str, "send keks" 4 | $global flwr_str, "send flowers" 5 | 6 | $global self, 8, 0 7 | 8 | 9 | ;; TaghaFunc tagha_module_get_func(struct TaghaModule *module, const char name[]); 10 | $native tagha_module_get_func 11 | 12 | ;; int puts(const char *str); 13 | $native puts 14 | 15 | 16 | prior_func { 17 | alloc 2 18 | ldvar r1, flwr_str 19 | call puts 20 | remit 2 21 | } 22 | 23 | func { 24 | enter 2 25 | ldvar r1, nude_str 26 | call puts 27 | call prior_func 28 | leave 2 29 | } 30 | 31 | another_func { 32 | alloc 1 33 | ldvar r1, kek_str 34 | call puts 35 | remit 1 36 | } 37 | 38 | main { 39 | enter 4 40 | call prior_func 41 | 42 | ldvar r2, func_str 43 | movi r1, 0 44 | call tagha_module_get_func 45 | mov r3, r0 46 | 47 | callr r3 48 | call another_func 49 | leave 3 50 | } -------------------------------------------------------------------------------- /test_asm/test_selfcall.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_selfcall.tbc -------------------------------------------------------------------------------- /test_asm/test_simd.tasm: -------------------------------------------------------------------------------- 1 | $opstack_size 10 ; 10 stack cells so 10 registers 2 | 3 | main { 4 | pushlr ;; push link register 5 | ;; think of 'alloc' as allocating the number of registers. 6 | alloc 4 ;; reduce stack pointer by 32 (8 * 4) bytes | rsp -= 32; 7 | lra r1, 2 ;; float v[3]; | rsp[1] = &rsp[2]; 8 | movi r0, 0x40000000 ;; rsp[0] = 2.f; 9 | st4 [r1], r0 ;; v[0] = 2.f; 10 | movi r0, 0x40400000 ;; rsp[0] = 3.f; 11 | st4 [r1+4], r0 ;; v[1] = 3.f; 12 | movi r0, 0x40800000 ;; rsp[0] = 4.f; 13 | st4 [r1+8], r0 ;; v[2] = 4.f; 14 | call Vec3D_Invert ;; Vec3D_Invert(rsp); 15 | poplr ;; pop back link register 16 | ret 17 | } 18 | 19 | /* 20 | void Vec3D_Invert(float v[const static 3]) 21 | { 22 | v[0] = -v[0]; 23 | v[1] = -v[1]; 24 | v[2] = -v[2]; 25 | } 26 | */ 27 | 28 | Vec3D_Invert { 29 | setvlen 3 ;; vector length of 3. 30 | setelen long ;; element length of 4 bytes. NOT setting the element length will assume 64-bit floats/ints. 31 | 32 | vfneg r2 33 | ret 34 | } -------------------------------------------------------------------------------- /test_asm/test_simd.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_simd.tbc -------------------------------------------------------------------------------- /test_asm/test_str_cmp.tasm: -------------------------------------------------------------------------------- 1 | $opstack_size 128 ;; 128 stack cells so 128 registers 2 | 3 | $global str1 "this is a giant string because I really want to make sure this operation isn't messed up. So I'm writing up a random and rather incoherent type of test string just to fill out the space and get as many characters in this quotation as much as possible. Golly gee I hope the vector comparison is fast for a string of this size because I want people to consider using tagha as a bytecode runtime compared to other bytecode runtimes." 4 | 5 | 6 | $native puts ;; int puts(const char *str); 7 | 8 | /** 9 | int main(void) 10 | { 11 | char n[] = "this is a giant string because I really want to make sure this operation isn't messed up. So I'm writing up a random and rather incoherent type of test string just to fill out the space and get as many characters in this quotation as much as possible. Golly gee I hope the vector comparison is fast for a string of this size because I want people to consider using tagha as a bytecode runtime compared to other bytecode runtimes."; 12 | 13 | char x[] = n; 14 | return x == n; /// string cmp. 15 | } 16 | */ 17 | 18 | main { 19 | setelen byte ;; set our vector data size to bytes. 20 | setvlen 430 ;; set the vector width to fit the entire array. 21 | 22 | pushlr ;; preserve calls. 23 | alloc 110 ;; rsp -= 880; 24 | 25 | ldvar r2, str1 26 | lra r1, 3 ;; char n[430]; 27 | call strcopy ;; strcopy(n, str1); 28 | call puts ;; puts(x); 29 | 30 | vmov r57, r3 ;; copy the entire char array. 31 | vcmp r3, r57 ;; test if copy was correct. 32 | setc r0 ;; return x==n; 33 | 34 | poplr 35 | ret 36 | } 37 | 38 | 39 | 40 | /** This implementation of strcopy is just for testing purposes, preferable that this be implemented as a C native under the name 'strcpy'. 41 | void strcopy(char str1[restrict static 1], const char str2[restrict static 1]) { 42 | while( (*str1++ = *str2++) != 0 ); 43 | } 44 | */ 45 | 46 | strcopy { 47 | alloc 5 ;; r5 is old r0, r6 is dest, r7 is src. 48 | mov r4, r7 ;; copy of ptrs. 49 | mov r3, r6 50 | 51 | movi r0, 1 52 | xor r1, r1 53 | 54 | .L3: 55 | ldu1 r2, [r4] 56 | st1 [r3], r2 57 | add r4, r0 58 | add r3, r0 59 | cmp r2, r1 60 | jz .L3 61 | 62 | redux 5 63 | ret 64 | } -------------------------------------------------------------------------------- /test_asm/test_str_cmp.tbc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/assyrianic/Tagha/f71243bd30454ad7ab2c5681ee50b5463a376cd8/test_asm/test_str_cmp.tbc -------------------------------------------------------------------------------- /test_driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "tagha/tagha.h" 6 | #include "tagha_toolchain/module_info.h" 7 | 8 | /// struct TaghaModule *tagha_module_new_from_file(const char filename[]); 9 | static NO_NULL union TaghaVal native_tagha_module_new_from_file(struct TaghaModule *const restrict module, const union TaghaVal params[const static 1]) 10 | { 11 | ( void )(module); 12 | const char *const filename = ( const char* )(params[0].uintptr); 13 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )(tagha_module_new_from_file(filename)) }; 14 | } 15 | 16 | /// bool tagha_module_free(struct TaghaModule **modref); 17 | static NO_NULL union TaghaVal native_tagha_module_free(struct TaghaModule *const restrict module, const union TaghaVal params[const static 1]) 18 | { 19 | ( void )(module); 20 | struct TaghaModule **const restrict modref = ( struct TaghaModule** )(params[0].uintptr); 21 | return ( union TaghaVal ){ .b00l = tagha_module_free(modref) }; 22 | } 23 | 24 | /// TaghaFunc tagha_module_get_func(struct TaghaModule *module, const char name[]); 25 | static NO_NULL union TaghaVal native_tagha_module_get_func(struct TaghaModule *const module, const union TaghaVal params[const static 2]) 26 | { 27 | const struct TaghaModule *const p = ( const struct TaghaModule* )(params[0].uintptr); 28 | const char *const name = ( const char* )(params[1].uintptr); 29 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )(tagha_module_get_func((p==NULL)? module : p, name)) }; 30 | } 31 | 32 | /// int puts(const char *str); 33 | static NO_NULL union TaghaVal native_puts(struct TaghaModule *const restrict module, const union TaghaVal params[const static 1]) 34 | { 35 | ( void )(module); 36 | const char *const cstr = ( const char* )(params[0].uintptr); 37 | return ( union TaghaVal ){ .int32 = puts(cstr) }; 38 | } 39 | 40 | /// char *fgets(char *str, int num, FILE *stream); 41 | static NO_NULL union TaghaVal native_fgets(struct TaghaModule *const module, const union TaghaVal params[const static 3]) 42 | { 43 | ( void )(module); 44 | char *const restrict buffer = ( char* )(params[0].uintptr); 45 | FILE *const restrict stream = ( FILE* )(params[2].uintptr); 46 | return ( union TaghaVal ){ .uintptr = ( uintptr_t )(fgets(buffer, params[1].int32, stream)) }; 47 | } 48 | 49 | /// int add_one(const int n); 50 | static NO_NULL union TaghaVal native_add_one(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 51 | { 52 | ( void )(module); 53 | return ( union TaghaVal ){ .int32 = params[0].int32 + 1 }; 54 | } 55 | 56 | /* 57 | /// int strcpy(char *str1, const char *str2); 58 | static NO_NULL union TaghaVal native_strcpy(struct TaghaModule *const restrict module, const union TaghaVal params[const restrict static 2]) 59 | { 60 | ( void )(module); 61 | char *restrict str1 = ( char* )(params[0].uintptr); 62 | const char *str2 = ( const char* )(params[1].uintptr); 63 | 64 | int i=0; 65 | while( (str1[i] = str2[i]) != 0 ) { 66 | i++; 67 | } 68 | return ( union TaghaVal ){ .int32 = i }; 69 | } 70 | */ 71 | 72 | /// void tagha_module_link_module(struct TaghaModule *module, struct TaghaModule *lib); 73 | static NO_NULL union TaghaVal native_tagha_module_link_module(struct TaghaModule *const restrict module, const union TaghaVal params[const static 2]) 74 | { 75 | struct TaghaModule *const restrict caller = ( struct TaghaModule* )(params[0].uintptr); 76 | const struct TaghaModule *const lib = ( const struct TaghaModule* )(params[1].uintptr); 77 | tagha_module_link_module(((caller==NULL)? module : caller), lib); 78 | return ( union TaghaVal ){ 0 }; 79 | } 80 | 81 | /** 82 | * POTENTIALLY DANGEROUS though not sure how. 83 | * If a dev uses `alloca`, compiler could possibly allocate its data that is already in use. 84 | * Whole point of `alloca` is to have a form of dynamic allocation without resorting to `malloc` + friends and then having to free. 85 | * This implementation of `alloca` does NOT modify the (operand) stack pointer but can cause issues if `alloca` is called and the compiler then allocates additional registers or if a callee function needs registers itself as well which could corrupt data. 86 | */ 87 | /// void *alloca(size_t len); 88 | static NO_NULL union TaghaVal native_alloca(struct TaghaModule *const module, const union TaghaVal params[const static 1]) 89 | { 90 | const size_t len = params[0].size; 91 | const size_t aligned_len = (len + (sizeof(union TaghaVal)-1)) & -sizeof(union TaghaVal); 92 | const uintptr_t alloc_space = module->osp - aligned_len; 93 | return ( union TaghaVal ){ .uintptr = (alloc_space < module->opstack)? NIL : alloc_space }; 94 | } 95 | 96 | 97 | NO_NULL int main(const int argc, char *argv[restrict static 1]) { 98 | ( void )(argc); 99 | if( argv[1]==NULL ) { 100 | printf("[TaghaVM (v%s) Test Host App Usage]: '%s' '.tbc filepath' \n", TAGHA_VERSION_STRING, argv[0]); 101 | //remove("tagha_test_res.txt"); 102 | return 1; 103 | } else { 104 | struct TaghaModule *module = tagha_module_new_from_file(argv[1]); 105 | if( module != NULL ) { 106 | tagha_module_link_natives(module, ( const struct TaghaNative[] ){ 107 | {"tagha_module_new_from_file", &native_tagha_module_new_from_file}, 108 | {"tagha_module_free", &native_tagha_module_free}, 109 | {"tagha_module_get_func", &native_tagha_module_get_func}, 110 | {"tagha_module_link_module", &native_tagha_module_link_module}, 111 | {"puts", &native_puts}, 112 | {"fgets", &native_fgets}, 113 | //{"strcpy", &native_strcpy}, 114 | {"add_one", &native_add_one}, 115 | {NULL, NULL} 116 | }); 117 | 118 | tagha_module_link_ptr(module, "stdin", ( uintptr_t )(stdin)); 119 | tagha_module_link_ptr(module, "stderr", ( uintptr_t )(stderr)); 120 | tagha_module_link_ptr(module, "stdout", ( uintptr_t )(stdout)); 121 | tagha_module_link_ptr(module, "self", ( uintptr_t )(module)); 122 | 123 | const clock_t start = clock(); 124 | const int r = tagha_module_run(module, 0, NULL); 125 | const clock_t end = clock(); 126 | const float64_t elapsed = ( float64_t )(end - start) / ( float64_t )(CLOCKS_PER_SEC); 127 | FILE *res_file = fopen("tagha_test_res.txt", "a+"); 128 | if( res_file==NULL ) 129 | return -1; 130 | 131 | fprintf(res_file, "result => %i | err? '%s' | elapsed => %" PRIf64 "ms\n", r, tagha_module_get_err(module), elapsed * 1000.); 132 | fclose(res_file); 133 | //tagha_module_print_opstack(module, res_file); 134 | //tagha_module_print_callstack(module, res_file); 135 | 136 | tagha_module_free(&module); 137 | } 138 | } 139 | } --------------------------------------------------------------------------------