├── .gitignore ├── .tm_properties ├── .travis.yml ├── Doxyfile ├── LICENSE ├── Makefile ├── README.md ├── Rakefile ├── VERSION ├── deps └── greatest.h ├── doc └── .gitkeep ├── include └── .gitkeep ├── lib └── rake │ ├── env.rb │ └── util.rb ├── manual ├── instructions.md └── notes.md ├── setup-jemalloc.sh ├── src ├── LICENSE ├── bootstrap.c ├── bootstrap.h ├── chunk.c ├── chunk.h ├── chunk.proto ├── debug-lua.include.c ├── debug.c ├── debug.h ├── exception.c ├── exception.h ├── frame.c ├── frame.h ├── gc1.c ├── gc1.h ├── generator.c ├── generator.h ├── heap.h ├── jit-compiler.c ├── jit-compiler.h ├── jit-tracer.c ├── jit-tracer.h ├── object.c ├── object.h ├── symbol.c ├── symbol.h ├── vm-dispatch.include.c ├── vm.c └── vm.h └── test ├── sandbox ├── Rakefile ├── libc.d ├── run.sh ├── test.c └── test_vm.c ├── sorting ├── Rakefile └── test_sorting.c ├── unit-old ├── Rakefile └── test.c └── unit ├── Rakefile ├── preamble.h ├── test_array.c ├── test_call.c ├── test_exception.c ├── test_int_comparison.c ├── test_invokeaddress.c ├── test_loop.c └── test_structure.c /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.o 3 | *.a 4 | *.so 5 | *.dSYM 6 | 7 | deps/jemalloc* 8 | src/chunk.pb-c.* 9 | doc/* 10 | include/* 11 | 12 | test/sandbox/test 13 | test/sandbox/test_vm 14 | test/sorting/test_sorting 15 | test/unit-old/test 16 | 17 | test/unit/* 18 | # Re-add specific files in unit test directory 19 | !test/unit/Rakefile 20 | !test/unit/test_*.c 21 | 22 | # Editor-related files 23 | dummy/* 24 | hivm.xcodeproj 25 | *.sublime* 26 | -------------------------------------------------------------------------------- /.tm_properties: -------------------------------------------------------------------------------- 1 | excludeInFileChooser = "{$excludeInFileChooser,doc,include,*.a,*.so}" 2 | excludeInFolderSearch = "{$excludeInFolderSearch,doc,include,*.a,*.so}" 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: clang 3 | before_install: 4 | - sudo apt-get update -qq 5 | - sudo apt-get -q install libc6-dev protobuf-c-compiler libprotobuf-c0-dev libprotobuf-c0 lua5.1 liblua5.1-0-dev 6 | - sh setup-jemalloc.sh 7 | - cd deps/jemalloc-3.6.0 && sudo make install_include install_lib 8 | install: gem install rake 9 | script: rake 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -g -Wall -c -std=c99 -I. 3 | 4 | LDFLAGS = -lpthread -L. 5 | 6 | # http://stackoverflow.com/questions/7004702/how-can-i-create-a-makefile-for-c-projects-with-src-obj-and-bin-subdirectories 7 | SRCDIR = src 8 | SOURCES = $(wildcard $(SRCDIR)/*.c) 9 | OBJECTS = $(SOURCES:.c=.o) 10 | 11 | OUT = libhivem.a 12 | 13 | # all: $(SOURCES) $(EXECUTABLE) 14 | all: $(OBJECTS) $(OUT) 15 | 16 | # $(EXECUTABLE): $(OBJECTS) 17 | # $(CC) $(LDFLAGS) $(OBJECTS) -o $@ 18 | # chmod +x $(EXECUTABLE) 19 | # mkdir -p $(EXDIR) 20 | # cp $(EXECUTABLE) $(EXDIR)/$(EXECUTABLE) 21 | 22 | %.o: %.c 23 | $(CC) $(CFLAGS) $< -o $@ 24 | 25 | 26 | $(OUT): $(OBJECTS) 27 | ar rcs $(OUT) $(OBJECTS) 28 | 29 | 30 | TESTDIR = test 31 | TESTSOURCES = test/test.c 32 | TESTOBJECTS = $(TESTSOURCES:.c=.o) 33 | TESTTARGET = test/test 34 | 35 | test: $(TESTTARGET) 36 | 37 | $(TESTTARGET): $(TESTOBJECTS) 38 | $(CC) $(LDFLAGS) $(TESTOBJECTS) -o $@ 39 | 40 | 41 | clean: 42 | rm -rf $(SRCDIR)/*.o 43 | rm -rf $(TESTDIR)/*.o 44 | rm -f libhivem.a 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hivm 2 | 3 | [![Build Status](https://travis-ci.org/dirk/hivm.png?branch=master)](https://travis-ci.org/dirk/hivm) 4 | 5 | For now you'll probably just want to read over the [notes](manual/notes.md) and [instruction set](manual/instructions.md). 6 | 7 | ## Getting started 8 | 9 | Hivm uses the [Rake build tool](https://github.com/ruby/rake) which should be available with most Ruby installations or through Rubygems (via `[sudo] gem install rake`). Hivm also depends on a few other open-source libraries: 10 | 11 | * [LLVM] around version 3.5: Compiler infrastructure used in the JIT compiler 12 | * [GNOME GLib] version 2.*: Common C application library 13 | * [Lua] version 5.1: Used by the debugger 14 | 15 | [LLVM]: http://llvm.org/ 16 | [GNOME GLIB]: https://wiki.gnome.org/Projects/GLib 17 | [Lua]: http://www.lua.org/ 18 | 19 | On Mac OS X these should be easily-installed via Homebrew: 20 | 21 | ```sh 22 | brew install glib llvm lua51 23 | ``` 24 | 25 | You can then build the project by invoking Rake: 26 | 27 | ```sh 28 | git clone https://github.com/dirk/hivm.git 29 | cd hivm 30 | rake # Will build the library, headers, and so forth 31 | rake -T # Will show a list of all available tasks 32 | ``` 33 | 34 | ## The Manifesto 35 | 36 | Virtual machines have become a new layer of abstraction between the programmer and the machine their code runs on. The ecosystem of virtual machines is growing and the machines themselves are becoming increasingly more complex. Furthermore, virtual machines have almost always been closely bound to their "native tongue": the language they were originally designed to execute. Running "non-native" languages on these machines is cumbersome and often incurs a penalty in performance and/or functionality. 37 | 38 | The Hivm project aims to overcome these and provide a new, better virtual machine for the execution of static, dynamic, and hybrid languages: 39 | 40 | 1. **No native tongue**: Hivm instead provides a solid, dependable, performant base for implementing languages. 41 | 2. **Minimize complexity**: bootstrapping a language, interacting with the machine, writing platform-native extensions, and the like should not be hard. 42 | 3. **Maximize usable performance**: the machine will not only perform well but also provide easy-to-use and powerful tools and APIs for understanding and optimizing how it performs. 43 | 44 | ## License 45 | 46 | Licensed under the [Mozilla Public License Version 2.0](http://www.mozilla.org/MPL/2.0/). See [LICENSE](./LICENSE) for details. 47 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | 2 | $LOAD_PATH.push File.expand_path(File.dirname(__FILE__)) 3 | require 'lib/rake/util' 4 | require 'lib/rake/env' 5 | 6 | shared_library = "libhivm-#{VERSION}.so" 7 | 8 | desc "Build" 9 | task "build" => ['libhivm.a', 'libhivm-db.a']#, "libhivm.so"] 10 | task "default" => ["build", "build:include"] 11 | 12 | headers = { 13 | "vm" => "hvm", 14 | "object" => "hvm_object", 15 | "symbol" => "hvm_symbol", 16 | "chunk" => "hvm_chunk", 17 | "generator" => "hvm_generator", 18 | "bootstrap" => "hvm_bootstrap", 19 | "exception" => "hvm_exception", 20 | "debug" => "hvm_debug" 21 | } 22 | headers.each do |src, dst| 23 | file "include/#{dst}.h" => "src/#{src}.h" do |t| 24 | sh "cp -p #{t.prerequisites[0]} #{t.name}" 25 | end 26 | end 27 | 28 | namespace "build" do 29 | desc "Build include directory (header files)" 30 | task "include" => headers.values.map {|dst| "include/#{dst}.h" } 31 | 32 | desc "Build the shared library" 33 | task "shared" => shared_library 34 | end 35 | 36 | objects = [ 37 | # Source 38 | 'src/vm.o', 'src/object.o', 'src/symbol.o', 'src/frame.o', 'src/chunk.o', 39 | 'src/generator.o', 'src/bootstrap.o', 'src/exception.o', 'src/gc1.o', 40 | 'src/jit-tracer.o', 'src/jit-compiler-llvm.o', 41 | # Generated source 42 | 'src/chunk.pb-c.o' 43 | ] 44 | debug_objects = objects.map do |file| 45 | (file == 'src/vm.o') ? 'src/vm-db.o' : file 46 | end 47 | debug_objects << 'src/debug.o' 48 | 49 | static_archiver = lambda do |t| 50 | # sh "cc -o #{t.name} #{t.prerequisites.join ' '} #{LDFLAGS} #{CFLAGS}" 51 | sh "#{AR} rcs #{t.name} #{t.prerequisites.join ' '}" 52 | end 53 | 54 | # Generating the static libraries 55 | file 'libhivm.a' => objects, &static_archiver 56 | file 'libhivm-db.a' => debug_objects, &static_archiver 57 | 58 | 59 | # Don't use the giant LLVM-bundled JIT compiler object in the dynamic library 60 | shared_objects = objects.map do |file| 61 | (file == 'src/jit-compiler-llvm.o') ? 'src/jit-compiler.o' : file 62 | end 63 | 64 | file shared_library => shared_objects do |t| 65 | objects = t.prerequisites.join ' ' 66 | # Static libraries 67 | ldflags = '-liconv -lz -lpthread -ledit -lcurses -lm -lc++ ' 68 | ldflags << `pkg-config --libs glib-2.0 lua5.1 libprotobuf-c`.strip+' ' 69 | # Add the LLVM dynamic library 70 | llvm_version = `#{LLVM_CONFIG} --version`.strip 71 | ldflags << "-L#{LLVM_LIBDIR} -lLLVM-#{llvm_version} " 72 | if `uname -s`.strip == 'Darwin' 73 | ldflags += " -macosx_version_min 10.10" 74 | end 75 | sh "#{LD} #{objects} #{ldflags} -dylib -o #{t.name}" 76 | end 77 | 78 | 79 | file 'src/jit-compiler-llvm.o' => 'src/jit-compiler.o' do |t| 80 | llvm_libs = `#{LLVM_CONFIG} --libs #{LLVM_MODULES}`.gsub("\n", '').strip 81 | llvm_ldflags = "-L#{LLVM_LIBDIR} #{llvm_libs}" 82 | # -r remerges into new file 83 | sh "#{LD} #{t.prerequisites.first} #{llvm_ldflags} -r -o #{t.name}" 84 | end 85 | 86 | # file 'src/object-jemalloc.o' => 'src/object.o' do |t| 87 | # # Remerge to pull in jemalloc 88 | # jemalloc = find_jemalloc 89 | # sh "#{LD} #{t.prerequisites.first} #{jemalloc} -r -o #{t.name}" 90 | # end 91 | 92 | file "src/chunk.pb-c.c" => ["src/chunk.proto"] do |t| 93 | sh "protoc-c --c_out=. #{t.prerequisites.first}" 94 | end 95 | 96 | # Let Rake know that debug.o depends on debug-lua.include.c 97 | file 'src/debug.o' => 'src/debug-lua.include.c' 98 | # Ditto for vm-dispatch 99 | file 'src/vm.o' => ['src/vm.c', 'src/vm-dispatch.include.c'] 100 | 101 | # Generic compilation of object files 102 | rule '.o' => ['.c'] do |t| 103 | sh "#{CC} #{t.source} -c #{cflags_for t.name} -o #{t.name}" 104 | # if File.basename(t.name) == "object.o" 105 | # # -r merges object files into a new object file. 106 | # # TODO: Make this platform-independent (fiddle with pkg-config?) 107 | # libs = [ 108 | # "/usr/local/Cellar/glib/2.38.2/lib/libglib-2.0.a", 109 | # "/usr/local/Cellar/gettext/0.18.3.2/lib/libintl.a" 110 | # ] 111 | # sh "#{ld} -r #{t.name} #{libs.join ' '} -o #{t.name}" 112 | # end 113 | jemalloc = find_jemalloc 114 | if File.basename(t.name) == 'object.o' 115 | # a.o -> a.tmp.o 116 | tmp = t.name.sub /\.o$/, '.tmp.o' 117 | sh "mv #{t.name} #{tmp}" 118 | sh "#{LD} #{tmp} #{jemalloc} -r -o #{t.name}" 119 | end 120 | end 121 | 122 | # Compiling the debug version of vm.c (include the dispatcher as a dependency) 123 | file 'src/vm-db.o' => ['src/vm.c', 'src/vm-dispatch.include.c'] do |t| 124 | sh "#{CC} #{t.prerequisites.first} -c #{cflags_for t.name} -o #{t.name}" 125 | end 126 | 127 | desc "Alias for clean:objects" 128 | task 'clean' => ['clean:objects'] 129 | 130 | namespace 'clean' do 131 | desc 'Clean up objects' 132 | task 'objects' do 133 | sh "rm -f src/*.o" 134 | sh "rm -f lib*.*" 135 | # sh "rm test/*.o" 136 | end 137 | 138 | desc 'Clean up copied headers' 139 | task 'headers' do 140 | sh "rm -f include/*.h" 141 | end 142 | 143 | desc 'Clean up generated source files' 144 | task 'generated' do 145 | sh 'rm -f src/chunk.pb-c.*' 146 | end 147 | 148 | desc 'Clean up everything (objects, docs)' 149 | task 'all' => ['clean:objects', 'clean:headers', 'clean:generated', 'doc:clean'] do 150 | sh 'rm include/*.h' 151 | end 152 | end 153 | 154 | desc "Build documentation" 155 | task "doc" do 156 | sh "doxygen Doxyfile" 157 | end 158 | 159 | namespace "doc" do 160 | desc "Clean up documentation" 161 | task "clean" do 162 | sh "rm -rf doc/*" 163 | end 164 | end 165 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.1 2 | -------------------------------------------------------------------------------- /doc/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dirk/hivm/bdead2e1d6baeadc99bcd03cd7aeaa807b37c4b6/doc/.gitkeep -------------------------------------------------------------------------------- /include/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dirk/hivm/bdead2e1d6baeadc99bcd03cd7aeaa807b37c4b6/include/.gitkeep -------------------------------------------------------------------------------- /lib/rake/env.rb: -------------------------------------------------------------------------------- 1 | 2 | # Setting up global constants 3 | cc = ENV['CC'].nil? ? 'clang' : ENV['CC'] 4 | # cpp = ENV['CPP'].nil? ? 'clang++' : ENV['CPP'] 5 | AR = 'ar' 6 | LD = 'ld' 7 | IS_LINUX = `uname -s`.strip == 'Linux' 8 | VERSION = `cat ./VERSION`.strip 9 | 10 | # $ldflags = "-lpthread -L. #{include_env 'LDFLAGS'}".strip 11 | warnings = '-Wall -Wextra -Wsign-conversion -Wconversion' 12 | cflags = "-g -fPIC -std=c11 -I. #{warnings} #{include_env 'CFLAGS'}".strip 13 | 14 | # Expose cflags and Lua for `cflags_for` 15 | CFLAGS = cflags 16 | LUA = 'lua5.1' 17 | 18 | IS_DARWIN = `uname -s`.strip == 'Darwin' 19 | 20 | # Settings for LLVM 21 | llvm_config = 'llvm-config' 22 | if IS_DARWIN 23 | llvm_config = "#{`brew --prefix llvm`.strip}/bin/llvm-config" 24 | end 25 | LLVM_CONFIG = llvm_config 26 | LLVM_MODULES = 'core analysis mcjit native' 27 | LLVM_LIBDIR = `#{llvm_config} --libdir`.strip 28 | 29 | # Constantify some stuff 30 | CC = cc 31 | -------------------------------------------------------------------------------- /lib/rake/util.rb: -------------------------------------------------------------------------------- 1 | 2 | def include_env v 3 | ENV[v].to_s 4 | end 5 | 6 | def cflags_for file 7 | basename = File.basename file 8 | flags = CFLAGS 9 | if %w{object.o generator.o exception.o debug.o bootstrap.o}.include? basename 10 | flags += " #{`pkg-config --cflags glib-2.0`.strip}" 11 | end 12 | if basename == 'object.o' && IS_LINUX 13 | flags += ' -I/usr/local/include' 14 | end 15 | # TODO: Refactor to make this unnecessary 16 | if basename == "generator.o" || basename == "bootstrap.o" || basename == "debug.o" 17 | flags += ' -Wno-unused-parameter' 18 | end 19 | if basename == "vm-db.o" || basename == "debug.o" 20 | flags += " -DHVM_VM_DEBUG #{`pkg-config --cflags #{LUA}`.strip}" 21 | end 22 | if basename == "jit-compiler.o" 23 | flags += " "+`#{LLVM_CONFIG} --cflags`.strip 24 | end 25 | # Optimization passes on some objects 26 | if basename =~ /^vm/ || basename == "gc1.o" 27 | flags += ' -O2' 28 | end 29 | return flags 30 | end 31 | 32 | # Search in a few default places for the jemalloc static library 33 | def find_jemalloc 34 | paths = [ 35 | '/usr/local/lib/libjemalloc.a', 36 | '/usr/local/libjemalloc.a' 37 | ] 38 | paths.each do |p| 39 | return p if File.exists? p 40 | end 41 | raise 'libjemalloc.a not found' 42 | end 43 | -------------------------------------------------------------------------------- /manual/instructions.md: -------------------------------------------------------------------------------- 1 | ### Instruction set 2 | 3 | ##### Document syntax guide 4 | 5 | - Instruction names are `lowercase`. 6 | - Register names are `UPPERCASE`. 7 | - Values in an instruction are prefixed with a `#HASH`. 8 | - Reserved data in an instruction is in `(parentheses)`. 9 | 10 | ##### Reserved data 11 | 12 | Some instructions provide an area region of data in their fields for the virtual machine to use for internal purposes. This region is required; the bytecode generation toolchain will automatically include these regions for you. 13 | 14 | #### Array instructions 15 | 16 | `arraypush A B` 17 | : Push B onto the end of array A. 18 | 19 | `arrayunshift A B` 20 | : Push B onto the front of array A. 21 | 22 | `arraypop B A` 23 | : Pop B off of the end of array A. 24 | 25 | `arrayshift B A` 26 | : Pop B off the front of array A. 27 | 28 | `arrayset A I V` 29 | : Set value V at index I in array A. 30 | 31 | `arrayget V A I` 32 | : Get value at index I from array A and store it in V. 33 | 34 | `arrayremove V A I` 35 | : Remove value at index I from array A and store it in V. 36 | 37 | `arraynew A L` 38 | : Create a new array in A with length of non-negative integer L ($zero is allowed). 39 | 40 | `arraylen B A` 41 | : Get the length of A and store it in B as an integer. 42 | 43 | #### Structure operations 44 | 45 | `structset S K V` 46 | : Set key symbol (non-negative integer) K in struct S to value V. 47 | 48 | `structget V S K` 49 | : Get value from S by key symbol K and store the value in V. 50 | 51 | `structdelete V S K` 52 | : Same as `structget`, however the key-value will be removed from S. 53 | 54 | `structhas B S K` 55 | : Set boolean in B depending on whether or not struct S has a value for symbol key K. (Boolean will be integer 0 or 1.) 56 | 57 | `structnew S` 58 | : Create a new struct in register S. 59 | 60 | #### Subroutines 61 | 62 | Subroutines may be executed either statically (address or symbolic name known ahead of time) or dynamically (address or name not known until execution). Static execution is referred to as a call, whereas dynamic execution is referred to as an invocation. 63 | 64 | Non-primitive subroutine execution instructions include a reserved data section called the `(tag)`. The tag provides a space for the virtual machine to store information relating to the execution of that subroutine. Tags are currently 3 bytes in size. 65 | 66 | ##### Calls 67 | 68 | Calls operate directly via hardcoded destinations. They are intended to be used within a common compilation block for fast subroutine invocation and tail-call recursion. 69 | 70 | `call (tag) SUB RET` 71 | : Call the subroutine at address SUB (8-byte). RET can be a register for return or $null for no return or ignoring return. 72 | 73 | `callprimitive CONST RET` 74 | : Look up a symbol ID from the constant table (4 bytes constant index), then call the primitive identified by that symbol ID. 75 | 76 | `tailcall (tag) SUB` 77 | : Same as `call` but does not grow the stack. Current subroutine's return will be the return from SUB. 78 | 79 | `callsymbolic (tag) CONST RET` 80 | : Look up a symbol ID from the constant table, then call the subroutine identified by that symbol ID. 81 | 82 | ##### Invocations 83 | 84 | Invocations use symbols and addresses passed via registers to control which subroutine/primitive is invoked. 85 | 86 | `invokesymbolic (tag) SYM RET` 87 | : Invoke subroutine identified by the symbol ID in register SYM. 88 | 89 | `invokeaddress (tag) ADDR RET` 90 | : Invoke subroutine at address in register ADDR. 91 | 92 | `invokeprimitive SYM RET` 93 | : Invoke the primitive with symbol ID in SYM. 94 | 95 | ##### Header 96 | 97 | **Note:** This is currently being (re)considered and is not currently implemented. 98 | 99 | Subroutines may have an optional header. This is a special no-op instruction that the virtual machine uses to store optimization metadata (tracking hot-ness, trampolining to/from the JIT, etc.). Using `hvm_gen_sub` and similar generator functions will automatically insert this header for you. The header may only appear as the first instruction in a subroutine. 100 | 101 | `subheader (data)` 102 | : Header at the beginning of the subroutine. 103 | 104 | #### Control flow 105 | 106 | `return RET` 107 | : Return from the current subroutine to the parent. RET can be a register for returning a value or $null. 108 | 109 | `if COND DEST` 110 | : Jump to DEST if COND is truthy (not null and not a zero integer). 111 | 112 | ##### Jumps and gotos 113 | 114 | `jump DIFF` 115 | : Jump DIFF (integer) instructions forwards (positive) or backwards (negative). 116 | 117 | `goto DEST` 118 | : Go to DEST (non-negative integer). 119 | 120 | `gotoaddress DEST` 121 | : Go to address in register DEST. 122 | 123 | #### Exceptions 124 | 125 | `catch DEST EXC` 126 | : Register an exception handler for the current stack frame at destination DEST. If the handler is invoked then the exception will be placed in register EXC (can be $null). 127 | 128 | `getexceptiondata DATA EXC` 129 | : Get the data object from exception EXC and store it in DATA. 130 | 131 | `clearcatch` 132 | : Clear the current stack frame's exception handler. 133 | 134 | `throw DATA` 135 | : Raise an exception. DATA is an object to be attached to the exception (can be $null). 136 | 137 | `clearexception` 138 | : Clear the current exception and continue execution. 139 | 140 | `continue` 141 | : **Note**: Currently not implemented and likely to be removed. 142 | Can be called from an exception handler to attempt to continue from an exception. (Warning: Dangerous!) 143 | 144 | `rethrow EXC` 145 | : Reraise an exception (will preserve the stack trace from the exception structure's point of origin instead of wherever the current handler is). 146 | 147 | `setexception EXC` 148 | : Set the current exception into register EXC. 149 | 150 | #### Constant and literal assignment 151 | 152 | ##### Constants 153 | 154 | Constants are stored in a constant-substitution section of an `hvm_chunk`. At load time these constants are copied from the chunk into the global constant pool (`vm->const_pool`) and the indexes in the instructions are updated to point the corresponding value in the global pool. 155 | 156 | NOTE: May want to make a `setconstant` instruction available. 157 | 158 | `setstring A #S` 159 | : Set the string referenced by constant index S into register A. 160 | 161 | `setinteger A #I` 162 | : Set the integer referenced by constant index I into register A. 163 | 164 | `setfloat A #F` 165 | : Set the float referenced by constant index F into register A. 166 | 167 | `setstruct A #S` 168 | : Set the structure referenced by constant index S into register A. 169 | 170 | `setsymbol A #S` 171 | : At load time: look up string in constant index S, then get the symbol ID from the VM's symbol table for that string. Upon execution register A will be set to that non-negative integer ID. 172 | 173 | `setnull A` 174 | : Set register A to null. 175 | 176 | ##### Literals 177 | 178 | Literals are directly stored in the instruction. For example `litinteger` takes 10 bytes: byte 1 is the instruction itself, byte 2 is the destination register, and bytes 3-10 are used for the 64-bit integer. 179 | 180 | `litinteger A #I` 181 | : Set A to literal integer I. 182 | 183 | #### Miscellaneous 184 | 185 | `noop` 186 | : Do nothing for a wee bit of time. 187 | 188 | `exit STATUS` 189 | : Exit interpreter with integer status code in STATUS. 190 | 191 | `symbolicate SYM STR` 192 | : Look up the symbol ID for the string in STR and update SYM with that value. 193 | 194 | `move A B` 195 | : A = B. 196 | 197 | #### Local/global variables 198 | 199 | `setlocal N V` 200 | : Sets a local by symbol name N with value V. 201 | 202 | `getlocal V N` 203 | : Gets a local by symbol name N into V. 204 | 205 | `setglobal N V` 206 | : Sets a global by symbol name N with value V. 207 | 208 | `getglobal V N` 209 | : Gets a global by symbol name N into V. 210 | 211 | `findlexical V N` 212 | : **Slow stack search operation.** Climb the stack searching for a local by symbol name N; if found store in V. 213 | 214 | ##### Closures 215 | 216 | `getclosure A` 217 | : Get the current scope as a closure-structure. 218 | **Warning**: These will probably be not-very-performant since it will (in 219 | the unoptimized case) probably end up compacting and copying the stack. 220 | 221 | #### Math 222 | 223 | `add A B C` 224 | : A = B + C 225 | 226 | `sub A B C` 227 | : A = B - C 228 | 229 | `mul A B C` 230 | : A = B * C 231 | 232 | `div A B C` 233 | : A = B / C 234 | 235 | `mod A B C` 236 | : A = B % C 237 | 238 | #### Bitwise 239 | 240 | `and A B C` 241 | : A = B & C 242 | 243 | `or A B C` 244 | : A = B | C 245 | 246 | `not A B` 247 | : A = ~B 248 | 249 | `xor A B C` 250 | : A = B ^ C 251 | 252 | `rshift A B C` 253 | : A = B >> C 254 | 255 | `lshift A B C` 256 | : A = B << C 257 | -------------------------------------------------------------------------------- /manual/notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ## Disclaimer 4 | 5 | These are crazy-ass notes that probably are not very intelligent. You've been warned. 6 | 7 | ## Types 8 | 9 | Hivm has 6+1 data types: 10 | 11 | * Null 12 | * Integer: signed with unlimited precision 13 | * Float: 64-bit/double precision 14 | * String: known-length byte sequences, *use UTF-8 because it's cool and we don't need any more Western-Latin hegemony* 15 | * Structure: composite data type that maps symbols to data values; heavily inspired by Lua's tables and C structs; performance *must* be extremely fast 16 | * Array: dynamic array; can contain any type as values 17 | * (Symbol: just a non-negative integer mapping to a string in the VM's symbol table) 18 | 19 | ### Booleans 20 | 21 | There are two falsy values: null and the zero integer (0x0). Everything else is true. If you want anything fancier than that then you've got to build it yourself at a higher layer. 22 | 23 | (Also if you manage to somehow end up with a sign-negative zero (0x80000000 in big-endian 32-bit), then I will be both impressed and full of pity for you.) 24 | 25 | ### Null 26 | 27 | Null is and will always be nothing. 28 | 29 | ### Simple data types 30 | 31 | Null, integer, float, and string are all "simple" data types. They are immutable. 32 | 33 | ### Complex data types 34 | 35 | Complex data types are mutable. 36 | 37 | Arrays are fast dynamic arrays that grow and shrink as you need them. They also support stack and queue operations (push, pop, shift, unshift). 38 | 39 | Structures are designed to support objects. There will likely be some form of type assignment/hinting so that you can build a relative-high-performance static/dynamic-yet-consistent type system on top of them. The optimizer/JIT compiler will also carefully watch structure interaction (and aforementioned type notations) when generating type-guarded native/optimized code. 40 | 41 | ## Registers 42 | 43 | Hivm provides essentially-infinite registers. Registers are type-aware of the data they contain. 44 | 45 | ### General (temporary) registers: $r0, $r1, ... 46 | 47 | General-purpose registers; local only to the current stack frame and are not saved between calls. 48 | 49 | ### Argument registers: $a0, $a1, ... 50 | 51 | Write-only. Used for arguments when calling subroutines. Contain null by default. 52 | 53 | ### Parameter registers: $pn, $p0, $p1, ... 54 | 55 | Read-only. Used by subroutines to read arguments. $pn is a special integer register that contains the total number of parameters passed (allows varargs). Like argument registers they are null by default. 56 | 57 | ### Virtual registers: $zero, $null 58 | 59 | The $null register will always be null. Writing any value in the $null register will not raise an exception. 60 | 61 | $zero will always be a zero integer. 62 | 63 | ## Calling conventions 64 | 65 | Invoking subroutines should normally follow the following process: 66 | 67 | 1. Set argument registers with values to be passed to the subroutine. 68 | 2. Use the `call SUB RET` instruction to call the subroutine. SUB should be an integer-valued register with the address. RET can be a register for the return value of the subroutine to be placed in. If the subroutine is not expected to return a value or you don't want to use the value returned then use the $null register as RET. 69 | 70 | ## Instruction set 71 | 72 | This is pretty inspired by ARM, MIPS, and various other RISCs. For now it will probably be internally represented in 32-bit words. You should never generate these yourself; always use Hivm's generator API to interact with the code-memory of a VM instance. (However it will make it easy to extract compiled bytecode for a code region (ie. file) and cache that for reuse in the same version of the VM.) As far as any limitations imposed by this design decision goes I'm going to adopt the following philosophy: if it screams bloody murder about something you do now then it may not in the future, if it doesn't scream bloody murder about something right now then it *really shouldn't* in the future. 73 | 74 | See [INSTRUCTIONS](INSTRUCTIONS.md) for detailed documentation of instructions. 75 | 76 | ## Variables 77 | 78 | Variables can either be stored in (temporary/frame-local) registers or scopes. Register variables do not interact with the garbage collection system in any way. Scopes are specialized structures and therefore interact with the GC. This is heavily inspired by old C-style memory management with a somewhat-clear usage divide between stacks (relatively primitive) and heaps (complex). 79 | 80 | ### Closures 81 | 82 | The generator API will provide a handy utility function ("lexicalize"?) which will symbolicate all the local variables for a scope into a compact closure scope structure that can be easily embedded into a function object. 83 | 84 | ## Constants 85 | 86 | Bytecode chunks may include a constant pool for any necessary values. Instructions reference constants locally to their chunks. These chunk-relative references are resolved to VM-relative references when the chunk is loaded. 87 | 88 | ## Examples 89 | 90 | ### Anonymous functions 91 | 92 | The following is pseudo-JS and pseudo-ASM for implementing an anonymous function. 93 | 94 | ```js 95 | var a = function() { ... } 96 | // (stuff) 97 | a() 98 | ``` 99 | 100 | ```ruby 101 | # @_anonymous_123 will be resolved to a relative address by the generator 102 | # and will be marked for absolute address resolution in the chunk created 103 | # by the generator. 104 | SETCONSTANT $r0, @_anonymous_123 105 | CALL _js_new_function, $r1, "(anonymous)", $r0 106 | SETLOCAL :a, $r1 # :a is in the constant pool and resolved ahead-of-time 107 | # (stuff) 108 | GETLOCAL $r2, :a # Get the function struct created by _js_new_function 109 | STRUCTGET $r3, $r2, :_js_function_addr # Get the internal address 110 | CALLDYNAMIC $r3, $null # Invoke the code at that address (_anonymous_123) 111 | 112 | _anonymous_123: 113 | ... 114 | RETURN $null 115 | ``` 116 | -------------------------------------------------------------------------------- /setup-jemalloc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd deps 3 | wget "https://github.com/jemalloc/jemalloc/archive/3.6.0.tar.gz" -q -O jemalloc-3.6.0.tar.gz 4 | tar xzf jemalloc-3.6.0.tar.gz 5 | cd jemalloc-3.6.0 6 | autoconf 7 | ./configure --quiet --with-jemalloc-prefix=je_ 8 | make --quiet 9 | -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | This Source Code Form is subject to the terms of the Mozilla Public 2 | License, v. 2.0. If a copy of the MPL was not distributed with this 3 | file, you can obtain one at http://mozilla.org/MPL/2.0/. 4 | -------------------------------------------------------------------------------- /src/bootstrap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "vm.h" 11 | #include "object.h" 12 | #include "symbol.h" 13 | #include "bootstrap.h" 14 | #include "frame.h" 15 | #include "exception.h" 16 | #include "gc1.h" 17 | #include "chunk.h" 18 | #include "jit-tracer.h" 19 | 20 | #define SYM(V) hvm_symbolicate(vm->symbols, V) 21 | #define PRIM_SET(K, V) hvm_obj_struct_internal_set(vm->primitives, SYM(K), (void*)V); 22 | 23 | void hvm_bootstrap_primitives(hvm_vm *vm) { 24 | // hvm_obj_ref* (*prim)(hvm_vm *vm); 25 | // prim = hvm_prim_print; 26 | PRIM_SET("print", hvm_prim_print); 27 | PRIM_SET("print_char", hvm_prim_print_char); 28 | PRIM_SET("print_exception", hvm_prim_print_exception); 29 | PRIM_SET("int_to_string", hvm_prim_int_to_string); 30 | PRIM_SET("array_clone", hvm_prim_array_clone); 31 | PRIM_SET("exit", hvm_prim_exit); 32 | 33 | PRIM_SET("time_as_int", hvm_prim_time_as_int); 34 | 35 | PRIM_SET("gc_run", hvm_prim_gc_run); 36 | PRIM_SET("rand", hvm_prim_rand); 37 | 38 | PRIM_SET("debug_print_struct", hvm_prim_debug_print_struct); 39 | PRIM_SET("debug_print_current_frame_trace", hvm_prim_debug_print_current_frame_trace); 40 | } 41 | 42 | hvm_obj_ref *hvm_prim_exit(hvm_vm *vm) { 43 | exit(0); 44 | } 45 | 46 | hvm_obj_ref *hvm_prim_print_exception(hvm_vm *vm) { 47 | hvm_obj_ref *excref = vm->param_regs[0]; 48 | assert(excref->type == HVM_STRUCTURE); 49 | 50 | hvm_symbol_id sym = hvm_symbolicate(vm->symbols, "message"); 51 | hvm_obj_ref *messageref = hvm_obj_struct_internal_get(excref->data.v, sym); 52 | assert(messageref->type == HVM_STRING); 53 | 54 | // TODO: Make exceptions be plain structures? 55 | // hvm_obj_ref *excstruct = vm->param_regs[0]; 56 | // assert(excstruct != NULL); 57 | // assert(excstruct->type == HVM_STRUCTURE); 58 | // excref = hvm_obj_struct_internal_get(excstruct->data.v, hvm_symbolicate(vm->symbols, "hvm_exception")); 59 | 60 | // assert(excref != NULL); 61 | // assert(excref->type == HVM_EXCEPTION); 62 | // hvm_exception *exc = excref->data.v; 63 | hvm_exception_print(vm, excref); 64 | return hvm_const_null; 65 | } 66 | 67 | bool hvm_type_check(char *name, hvm_obj_type type, hvm_obj_ref* ref, hvm_vm *vm) { 68 | if(ref->type != type) { 69 | char buff[256]; 70 | buff[0] = '\0'; 71 | strcat(buff, "`"); 72 | strcat(buff, name); 73 | strcat(buff, "` expects "); 74 | strcat(buff, hvm_human_name_for_obj_type(type)); 75 | strcat(buff, ", got "); 76 | strcat(buff, hvm_human_name_for_obj_type(ref->type)); 77 | hvm_obj_ref *message = hvm_new_obj_ref_string_data(hvm_util_strclone(buff)); 78 | hvm_obj_ref *exc = hvm_exception_new(vm, message); 79 | // Push the primitive as the first location 80 | hvm_location *loc = hvm_new_location(); 81 | loc->name = hvm_util_strclone(name); 82 | hvm_exception_push_location(vm, exc, loc); 83 | vm->exception = exc; 84 | return 0; 85 | } else { 86 | return 1; 87 | } 88 | } 89 | 90 | hvm_obj_ref *hvm_prim_print(hvm_vm *vm) { 91 | hvm_obj_ref *strref = vm->param_regs[0]; 92 | if(strref == NULL) { 93 | // Missing parameter 94 | static char *buff = "`print` expects 1 argument"; 95 | hvm_obj_ref *message = hvm_new_obj_ref_string_data(hvm_util_strclone(buff)); 96 | hvm_obj_ref *exc = hvm_exception_new(vm, message); 97 | 98 | hvm_location *loc = hvm_new_location(); 99 | loc->name = hvm_util_strclone("hvm_prim_print"); 100 | hvm_exception_push_location(vm, exc, loc); 101 | 102 | vm->exception = exc; 103 | return NULL; 104 | } 105 | if(!hvm_type_check("print", HVM_STRING, strref, vm)) { return NULL; } 106 | hvm_obj_string *str = strref->data.v; 107 | fputs(str->data, stdout); 108 | return hvm_const_null; 109 | } 110 | hvm_obj_ref *hvm_prim_print_char(hvm_vm *vm) { 111 | hvm_obj_ref *intref = vm->param_regs[0]; 112 | if(!hvm_type_check("print_char", HVM_INTEGER, intref, vm)) { return NULL; } 113 | int64_t i = intref->data.i64; 114 | // fprintf(stderr, "char: %lld\n", i); 115 | char c = (char)i; 116 | fputc(c, stdout); 117 | return hvm_const_null; 118 | } 119 | 120 | hvm_obj_ref *hvm_prim_array_clone(hvm_vm *vm) { 121 | hvm_obj_ref *arrref = vm->param_regs[0]; 122 | if(!hvm_type_check("array_clone", HVM_ARRAY, arrref, vm)) { return NULL; } 123 | hvm_obj_array *arr = arrref->data.v; 124 | guint len = arr->array->len; 125 | // Set up the new array and copy over 126 | hvm_obj_array *newarr = malloc(sizeof(hvm_obj_array)); 127 | newarr->array = g_array_sized_new(TRUE, TRUE, sizeof(hvm_obj_ref*), len); 128 | newarr->array->len = len; 129 | for(guint idx = 0; idx < len; idx++) { 130 | // Copy source pointer from original array into destination in new array 131 | hvm_obj_ref *src = g_array_index(arr->array, hvm_obj_ref*, idx); 132 | hvm_obj_ref **dest = &g_array_index(newarr->array, hvm_obj_ref*, idx); 133 | *dest = src; 134 | } 135 | hvm_obj_ref *newarrref = hvm_new_obj_ref(); 136 | newarrref->type = HVM_ARRAY; 137 | newarrref->data.v = newarr; 138 | return newarrref; 139 | } 140 | 141 | hvm_obj_ref *hvm_prim_int_to_string(hvm_vm *vm) { 142 | hvm_obj_ref *intref = vm->param_regs[0]; 143 | assert(intref != NULL); 144 | assert(intref->type == HVM_INTEGER); 145 | int64_t intval = intref->data.i64; 146 | char buff[24];// Enough to show a 64-bit signed integer in base 10 147 | int err = sprintf(buff, "%lld", intval); 148 | assert(err >= 0); 149 | hvm_obj_ref *str = hvm_new_obj_ref_string_data(hvm_util_strclone(buff)); 150 | hvm_obj_space_add_obj_ref(vm->obj_space, str); 151 | return str; 152 | } 153 | 154 | // Returns microseconds since epoch as 64-bit integer 155 | hvm_obj_ref *hvm_prim_time_as_int(hvm_vm *vm) { 156 | int64_t sec; 157 | hvm_obj_ref * ret; 158 | struct timeval tv; 159 | gettimeofday(&tv, NULL); 160 | sec = (1000000 * tv.tv_sec) + tv.tv_usec; 161 | ret = hvm_new_obj_int(vm); 162 | ret->data.i64 = sec; 163 | return ret; 164 | } 165 | 166 | hvm_obj_ref *hvm_prim_debug_print_struct(hvm_vm *vm) { 167 | hvm_obj_ref *structref = vm->param_regs[0]; 168 | if(!hvm_type_check("debug_print_struct", HVM_STRUCTURE, structref, vm)) { return NULL; } 169 | // Iterating through the structure 170 | hvm_obj_struct *strct = structref->data.v; 171 | fprintf(stdout, "structure(%p)\n", strct); 172 | unsigned int idx; 173 | for(idx = 0; idx < strct->heap_length; idx++) { 174 | hvm_obj_struct_heap_pair *pair = strct->heap[idx]; 175 | char *sym = hvm_desymbolicate(vm->symbols, pair->id); 176 | hvm_obj_ref *ref = pair->obj; 177 | const char *name = hvm_human_name_for_obj_type(ref->type); 178 | fprintf(stdout, " %s = %s(%p)\n", sym, name, ref); 179 | } 180 | return hvm_const_null; 181 | } 182 | 183 | hvm_obj_ref *hvm_prim_debug_print_current_frame_trace(hvm_vm *vm) { 184 | hvm_frame *frame = vm->top; 185 | if(frame->trace == NULL) { 186 | fprintf(stdout, "error: No trace in current frame\n"); 187 | goto end; 188 | } 189 | hvm_call_trace *trace = frame->trace; 190 | printf("frame(%p) = [%u]{\n", frame, trace->sequence_length); 191 | hvm_jit_tracer_dump_trace(vm, trace); 192 | printf("}\n"); 193 | 194 | end: 195 | return hvm_const_null; 196 | } 197 | 198 | hvm_obj_ref *hvm_prim_gc_run(hvm_vm *vm) { 199 | hvm_gc1_run(vm, vm->obj_space); 200 | return hvm_const_null; 201 | } 202 | 203 | hvm_obj_ref *hvm_prim_rand(hvm_vm *vm) { 204 | int ret = rand(); 205 | // Create the full object reference with our random integer 206 | hvm_obj_ref *ref = hvm_new_obj_int(vm); 207 | ref->data.i64 = (int64_t)ret; 208 | // Make sure it's in the object space 209 | hvm_obj_space_add_obj_ref(vm->obj_space, ref); 210 | return ref; 211 | } 212 | -------------------------------------------------------------------------------- /src/bootstrap.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_BOOTSTRAP_H 2 | #define HVM_BOOTSTRAP_H 3 | 4 | void hvm_bootstrap_primitives(hvm_vm *vm); 5 | 6 | hvm_obj_ref *hvm_prim_int_to_string(hvm_vm *vm); 7 | hvm_obj_ref *hvm_prim_exit(hvm_vm *vm); 8 | hvm_obj_ref *hvm_prim_print(hvm_vm *vm); 9 | hvm_obj_ref *hvm_prim_print_exception(hvm_vm *vm); 10 | hvm_obj_ref *hvm_prim_print_char(hvm_vm *vm); 11 | hvm_obj_ref *hvm_prim_gc_run(hvm_vm *vm); 12 | hvm_obj_ref *hvm_prim_rand(hvm_vm *vm); 13 | hvm_obj_ref *hvm_prim_array_clone(hvm_vm *vm); 14 | hvm_obj_ref *hvm_prim_time_as_int(hvm_vm *vm); 15 | 16 | hvm_obj_ref *hvm_prim_debug_print_struct(hvm_vm *vm); 17 | hvm_obj_ref *hvm_prim_debug_print_current_frame_trace(hvm_vm *vm); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/chunk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "vm.h" 6 | #include "object.h" 7 | #include "chunk.h" 8 | #include "symbol.h" 9 | 10 | hvm_chunk *hvm_new_chunk() { 11 | hvm_chunk *chunk = malloc(sizeof(hvm_chunk)); 12 | chunk->data = NULL; 13 | chunk->size = 0; 14 | chunk->capacity = 0; 15 | return chunk; 16 | } 17 | 18 | void hvm_chunk_expand(hvm_chunk *chunk) { 19 | chunk->capacity += 1024;// TODO: Refactor this. 20 | chunk->data = realloc(chunk->data, sizeof(byte) * chunk->capacity); 21 | } 22 | 23 | void hvm_chunk_expand_if_necessary(hvm_chunk *chunk) { 24 | if((chunk->size + 16) >= chunk->capacity) { 25 | hvm_chunk_expand(chunk); 26 | } 27 | } 28 | 29 | hvm_obj_ref *hvm_chunk_get_constant_object(hvm_vm *vm, hvm_chunk_constant *cnst) { 30 | hvm_obj_ref* co = cnst->object; 31 | if(co->type == HVM_STRING) { 32 | hvm_obj_ref *ref = hvm_new_obj_ref(); 33 | hvm_obj_string *str = hvm_new_obj_string(); 34 | ref->type = HVM_STRING; 35 | ref->flags = ref->flags | HVM_OBJ_FLAG_CONSTANT; 36 | ref->data.v = str; 37 | str->data = co->data.v; 38 | return ref; 39 | } else if(co->type == HVM_SYMBOL) { 40 | hvm_obj_ref *ref = hvm_new_obj_ref(); 41 | ref->type = HVM_SYMBOL; 42 | ref->flags = ref->flags | HVM_OBJ_FLAG_CONSTANT; 43 | ref->data.u64 = hvm_symbolicate(vm->symbols, co->data.v); 44 | return ref; 45 | } else { 46 | fprintf(stderr, "Can't yet handle object type %s\n", hvm_human_name_for_obj_type(co->type)); 47 | return hvm_const_null; 48 | } 49 | } 50 | 51 | void print_constant_object(hvm_obj_ref* obj) { 52 | switch(obj->type) { 53 | case HVM_SYMBOL: 54 | printf("%s", (char*)obj->data.v); 55 | break; 56 | // case HVM_STRING: 57 | // printf("%s", (char*)obj->data.v); 58 | // break; 59 | default: 60 | break; 61 | } 62 | } 63 | 64 | void print_relocations(hvm_chunk *chunk) { 65 | hvm_chunk_relocation **relocs = chunk->relocs; 66 | hvm_chunk_relocation *reloc; 67 | printf("relocations:\n"); 68 | while(*relocs != NULL) { 69 | reloc = *relocs; 70 | printf(" 0x%08llX\n", reloc->index); 71 | relocs++; 72 | } 73 | printf("\n"); 74 | } 75 | void print_constants(hvm_chunk *chunk) { 76 | hvm_chunk_constant **consts = chunk->constants; 77 | hvm_chunk_constant *cnst; 78 | printf("consts:\n"); 79 | int i = 0; 80 | while(*consts != NULL) { 81 | cnst = *consts; 82 | printf(" #%-4d 0x%08llX (%p) %-9s", i, cnst->index, cnst->object, hvm_human_name_for_obj_type(cnst->object->type)); 83 | print_constant_object(cnst->object); 84 | printf("\n"); 85 | i++; 86 | consts++; 87 | } 88 | printf("\n"); 89 | } 90 | void print_symbols(hvm_chunk *chunk) { 91 | hvm_chunk_symbol **syms = chunk->symbols; 92 | hvm_chunk_symbol *sym; 93 | printf("symbols:\n"); 94 | int i = 0; 95 | while(*syms != NULL) { 96 | sym = *syms; 97 | printf(" 0x%08llX %s\n", sym->index, sym->name); 98 | i++; 99 | syms++; 100 | } 101 | printf("\n"); 102 | } 103 | 104 | #define READ_U32(V) *(uint32_t*)(V) 105 | #define READ_U64(V) *(uint64_t*)(V) 106 | #define READ_I32(V) *(int32_t*)(V) 107 | #define READ_I64(V) *(int64_t*)(V) 108 | 109 | void hvm_print_data(byte *data, uint64_t size) { 110 | byte op; 111 | byte sym, reg1, reg2, reg3, ret; 112 | uint32_t u32; 113 | uint64_t u64; 114 | int64_t i64; 115 | unsigned long long int i = 0; 116 | 117 | printf("data:\n"); 118 | while(i < size) { 119 | op = data[i]; 120 | printf(" 0x%08llX ", i); 121 | switch(op) { 122 | case HVM_OP_DIE:// 1B OP 123 | printf("die\n"); 124 | break; 125 | case HVM_OP_MOVE:// 1B OP | 1B REG | 1B REG 126 | reg1 = data[i + 1]; 127 | reg2 = data[i + 2]; 128 | i += 2; 129 | printf("$%-3d = $%d\n", reg1, reg2); 130 | break; 131 | case HVM_OP_SETSTRING:// 1B OP | 1B REG | 4B CONST 132 | reg1 = data[i + 1]; 133 | u32 = READ_U32(&data[i + 2]); 134 | i += 5; 135 | printf("$%-3d = setstring #%d\n", reg1, u32); 136 | break; 137 | case HVM_OP_SETSYMBOL:// 1B OP | 1B REG | 4B CONST 138 | reg1 = data[i + 1]; 139 | u32 = READ_U32(&data[i + 2]); 140 | i += 5; 141 | printf("$%-3d = setsymbol #%d\n", reg1, u32); 142 | break; 143 | case HVM_OP_INVOKEPRIMITIVE:// 1B OP | 1B REG | 1B REG 144 | sym = data[i + 1]; 145 | ret = data[i + 2]; 146 | i += 2; 147 | printf("$%-3d = invokeprimitive($%d)\n", ret, sym); 148 | break; 149 | case HVM_OP_INVOKESYMBOLIC: 150 | sym = data[i + 1]; 151 | ret = data[i + 2]; 152 | i += 2; 153 | printf("$%-3d = invokesymbolic($%d)\n", ret, sym); 154 | break; 155 | case HVM_OP_LITINTEGER: // 1B OP | 1B REG | 8B LIT 156 | reg1 = data[i + 1]; 157 | i64 = READ_I64(&data[i + 2]); 158 | i += 9; 159 | printf("$%-3d = litinteger(%lld)\n", reg1, i64); 160 | break; 161 | case HVM_OP_ADD: // 1B OP | 1B REG | 1B REG | 1B REG 162 | reg1 = data[i + 1]; 163 | reg2 = data[i + 2]; 164 | reg3 = data[i + 3]; 165 | i += 3; 166 | printf("$%-3d = $%d + $%d\n", reg1, reg2, reg3); 167 | break; 168 | case HVM_OP_GOTO: // 1B OP | 8B DEST 169 | u64 = READ_U64(&data[i + 1]); 170 | i += 8; 171 | printf("goto(0x%08llX)\n", u64); 172 | break; 173 | case HVM_OP_RETURN: // 1B OP | 1B REG 174 | reg1 = data[i + 1]; 175 | i += 1; 176 | printf("return($%d)\n", reg1); 177 | break; 178 | case HVM_OP_SETGLOBAL: // 1B OP | 1B REG | 1B REG 179 | reg1 = data[i + 1]; 180 | reg2 = data[i + 2]; 181 | i += 2; 182 | printf("setglobal[$%d] = $%d\n", reg1, reg2); 183 | break; 184 | case HVM_OP_SETLOCAL: // 1B OP | 1B REG | 1B REG 185 | reg1 = data[i + 1]; 186 | reg2 = data[i + 2]; 187 | i += 2; 188 | printf("setlocal[$%d] = $%d\n", reg1, reg2); 189 | break; 190 | case HVM_OP_GETLOCAL: // 1B OP | 1B REG | 1B REG 191 | reg1 = data[i + 1]; 192 | reg2 = data[i + 2]; 193 | i += 2; 194 | printf("$%-3d = getlocal[$%d]\n", reg1, reg2); 195 | break; 196 | 197 | case HVM_OP_STRUCTGET: // 1B OP | 3B REGS 198 | reg1 = data[i + 1]; 199 | reg2 = data[i + 2]; 200 | reg3 = data[i + 3]; 201 | i += 3; 202 | printf("$%-3d = $%d.structget[$%d]\n", reg1, reg2, reg3); 203 | break; 204 | case HVM_OP_STRUCTSET: // 1B OP | 3B REGS 205 | reg1 = data[i + 1]; 206 | reg2 = data[i + 2]; 207 | reg3 = data[i + 3]; 208 | i += 3; 209 | printf("$%d.structset[$%d] = $%d\n", reg1, reg2, reg3); 210 | break; 211 | case HVM_OP_STRUCTNEW: // 1B OP | 1B REG 212 | reg1 = data[i + 1]; 213 | i += 1; 214 | printf("$%-3d = structnew\n", reg1); 215 | break; 216 | 217 | case HVM_OP_ARRAYNEW: // 1B OP | 2B REGS 218 | reg1 = data[i + 1]; 219 | reg2 = data[i + 2]; 220 | i += 2; 221 | printf("$%-3d = arraynew[$%d]\n", reg1, reg2); 222 | break; 223 | case HVM_OP_ARRAYGET: 224 | // A = B[C] 225 | reg1 = data[i + 1]; 226 | reg2 = data[i + 2]; 227 | reg3 = data[i + 3]; 228 | i += 3; 229 | printf("$%-3d = $%d.arrayget[$%d]\n", reg1, reg2, reg3); 230 | break; 231 | case HVM_OP_ARRAYSET: // 1B OP | 3B REGS 232 | // A[B] = C 233 | reg1 = data[i + 1]; 234 | reg2 = data[i + 2]; 235 | reg3 = data[i + 3]; 236 | i += 3; 237 | printf("$%d.arrayset[$%d] = $%d\n", reg1, reg2, reg3); 238 | break; 239 | case HVM_OP_ARRAYLEN: // 1B OP | 2B REGS 240 | reg1 = data[i + 1]; 241 | reg2 = data[i + 2]; 242 | i += 2; 243 | printf("$%-3d = $%d.arraylen\n", reg1, reg2); 244 | break; 245 | case HVM_OP_ARRAYPUSH: // 1B OP | 2B REGS 246 | reg1 = data[i + 1]; 247 | reg2 = data[i + 2]; 248 | i += 2; 249 | printf("$%d.arraypush($%d)\n", reg1, reg2); 250 | break; 251 | 252 | case HVM_OP_GOTOADDRESS: // 1B OP | 1B DEST REG 253 | reg1 = data[i + 1]; 254 | printf("goto($%d)\n", reg1); 255 | i += 1; 256 | break; 257 | case HVM_OP_GETCLOSURE: // 1B OP | 1B REG 258 | reg1 = data[i + 1]; 259 | printf("$%-3d = getclosure\n", reg1); 260 | i += 1; 261 | break; 262 | case HVM_OP_LT: // 1B OP | 3B REGs 263 | case HVM_OP_GT: 264 | case HVM_OP_EQ: 265 | case HVM_OP_AND: 266 | reg1 = data[i + 1]; 267 | reg2 = data[i + 2]; 268 | reg3 = data[i + 3]; 269 | char *cmp = "?"; 270 | if(op == HVM_OP_LT) { 271 | cmp = "<"; 272 | } else if(op == HVM_OP_GT) { 273 | cmp = ">"; 274 | } else if(op == HVM_OP_EQ) { 275 | cmp = "=="; 276 | } else if(op == HVM_OP_AND) { 277 | cmp = "&&"; 278 | } 279 | printf("$%-3d = $%-3d %s $%-3d\n", reg1, reg2, cmp, reg3); 280 | i += 3; 281 | break; 282 | // HVM_OP_GT = 45, // 1B OP | 3B REGs 283 | // HVM_OP_LTE = 46, // 1B OP | 3B REGs 284 | // HVM_OP_GTE = 47, // 1B OP | 3B REGs 285 | // HVM_OP_EQ = 48, // 1B OP | 3B REGs 286 | 287 | case HVM_OP_IF: // 1B OP | 1B REG | 8B DEST 288 | reg1 = data[i + 1]; 289 | u64 = READ_U64(&data[i + 2]); 290 | printf("if($%d, 0x%08llX)\n", reg1, u64); 291 | i += 9; 292 | break; 293 | case HVM_OP_CATCH: // 1B OP | 8B DEST | 1B REG 294 | u64 = READ_U64(&data[i + 1]); 295 | reg1 = data[i + 9]; 296 | printf("catch(0x%08llX, $%d)\n", u64, reg1); 297 | i += 9; 298 | break; 299 | case HVM_OP_SETEXCEPTION: // 1B OP | 1B REG 300 | reg1 = data[i + 1]; 301 | printf("$%-3d = setexception\n", reg1); 302 | i += 1; 303 | break; 304 | case HVM_OP_CLEAREXCEPTION: // 1B OP 305 | printf("clearexception\n"); 306 | break; 307 | case HVM_OP_CLEARCATCH: // 1B OP 308 | printf("clearcatch\n"); 309 | break; 310 | case HVM_OP_CALLPRIMITIVE: 311 | case HVM_OP_CALLSYMBOLIC:// 1B OP | 3B TAG | 4B CONST | 1B REG 312 | ret = data[i + 8]; 313 | u32 = READ_U32(&data[i + 4]); 314 | i += 8; 315 | { 316 | char *name = "callsymbolic"; 317 | if(op == HVM_OP_CALLPRIMITIVE) { 318 | name = "callprimitive"; 319 | } 320 | printf("$%-3d = %s #%d\n", ret, name, u32); 321 | } 322 | break; 323 | default: 324 | printf("%02X (%d)\n", op, op); 325 | } 326 | i++; 327 | } 328 | printf("\n"); 329 | } 330 | 331 | void hvm_chunk_disassemble(hvm_chunk *chunk) { 332 | print_relocations(chunk); 333 | print_constants(chunk); 334 | print_symbols(chunk); 335 | hvm_print_data(chunk->data, chunk->size); 336 | } 337 | -------------------------------------------------------------------------------- /src/chunk.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_CHUNK_H 2 | #define HVM_CHUNK_H 3 | 4 | /// Locations where relative addresses have to be updated to absolute ones. 5 | typedef struct hvm_chunk_relocation { 6 | /// Index into the data for the start of the 8-byte code address to be 7 | /// relocated (had the start of the chunk in the VM added to it). 8 | uint64_t index; 9 | } hvm_chunk_relocation; 10 | 11 | /// Marks constants in the chunk to be added to the VM's constant table. 12 | typedef struct hvm_chunk_constant { 13 | /// Index of the symbol ID (4 byte unsigned integer). 14 | uint64_t index; 15 | /// Actual (simplified) object; must be expanded into full object on load 16 | /// by using hvm_chunk_get_constant_object(). 17 | struct hvm_obj_ref* object; 18 | } hvm_chunk_constant; 19 | 20 | /// Marks subroutines in the chunk to be added to the VM's symbol table. 21 | typedef struct hvm_chunk_symbol { 22 | /// Index of the start of the subroutine identified by the symbol. 23 | uint64_t index; 24 | /// Name of that subroutine. 25 | char *name; 26 | } hvm_chunk_symbol; 27 | 28 | typedef struct hvm_chunk_debug_entry { 29 | uint64_t start; 30 | uint64_t end; 31 | uint64_t line; 32 | char *name; 33 | char *file; 34 | unsigned char flags; 35 | } hvm_chunk_debug_entry; 36 | 37 | /// @brief Chunk of instruction code and data (constants, etc.). 38 | typedef struct hvm_chunk { 39 | // This is mostly inspired by the ELF format. There are three main sections 40 | // to a chunk: relocations, substitutions, symbols, and data. 41 | 42 | // RELOCATIONS (relocs) 43 | // Specifies points in the code where relative addresses need to be adjusted 44 | // to absolute addresses. 45 | hvm_chunk_relocation **relocs;// NULL-terminated array of relocs 46 | 47 | // CONSTANT SUBSTITUTIONS (consts) 48 | // Constants: 49 | // Defines constant values and positions in the code where they are used. 50 | // When the chunk is loaded these constants are pulled into the VM's 51 | // constant table and the constant indexes in the code (specified by the 52 | // usage positions) are updated to use the VM's constant indexes. 53 | hvm_chunk_constant **constants;// NULL-terminated array 54 | 55 | // SYMBOLS 56 | // Defines symbols (ie. subroutines) and their locations to be inserted into 57 | // the VM's symbol table. 58 | hvm_chunk_symbol **symbols;// NULL-terminated 59 | 60 | // DEBUG ENTRIES 61 | hvm_chunk_debug_entry **debug_entries;// NULL-terminated 62 | 63 | // DATA 64 | /// Raw instructions. 65 | byte *data; 66 | /// Number of bytes used by the data. 67 | uint64_t size; 68 | /// Total size of the data (includes unused space). 69 | uint64_t capacity; 70 | } hvm_chunk; 71 | 72 | hvm_chunk *hvm_new_chunk(); 73 | void hvm_chunk_expand_if_necessary(hvm_chunk *chunk); 74 | 75 | /// Expands a simplified object representation in a chunk constant into a full 76 | /// constant object reference for use in the VM. 77 | /// @memberof hvm_chunk_constant 78 | hvm_obj_ref *hvm_chunk_get_constant_object(hvm_vm *vm, hvm_chunk_constant *cnst); 79 | 80 | void hvm_chunk_disassemble(hvm_chunk *chunk); 81 | void hvm_print_data(byte *data, uint64_t size); 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /src/chunk.proto: -------------------------------------------------------------------------------- 1 | message Chunk { 2 | message Relocation { 3 | required uint64 index = 1; 4 | } 5 | message Constant { 6 | required uint64 index = 1; 7 | optional string symval = 2; 8 | optional string strval = 3; 9 | optional int64 intval = 4; 10 | } 11 | message Symbol { 12 | required uint64 index = 1; 13 | required string name = 2; 14 | } 15 | message DebugEntry { 16 | required uint64 start = 1; 17 | required uint64 end = 2; 18 | required uint64 line = 3; 19 | required string name = 4; 20 | required string file = 5; 21 | required uint32 flags = 6; 22 | } 23 | 24 | // Header 25 | required int32 chunk_revision = 1; 26 | required int32 vm_version_major = 2; 27 | required int32 vm_version_minor = 3; 28 | // Data 29 | repeated Relocation relocs = 4; 30 | repeated Constant constants = 5; 31 | repeated Symbol symbols = 6; 32 | repeated DebugEntry debug_entries = 7; 33 | required bytes data = 8; 34 | required uint64 size = 9; 35 | } 36 | -------------------------------------------------------------------------------- /src/debug-lua.include.c: -------------------------------------------------------------------------------- 1 | #ifndef HVM_DEBUG_C 2 | #error debug-lua.include.c must only be included in debug.c 3 | #endif 4 | /// @file debug-lua.include.c 5 | /// Included in debug.c to add all the Lua commands for debugging. 6 | 7 | hvm_vm *_hvm_lua_get_vm(lua_State *L) { 8 | lua_pushstring(L, "hvm_vm");// key 9 | lua_gettable(L, LUA_REGISTRYINDEX); 10 | void *vm = lua_touserdata(L, -1); 11 | lua_pop(L, 1); 12 | return (hvm_vm*)vm; 13 | } 14 | 15 | int hvm_lua_continue(lua_State *L) { 16 | //fputs("(db) Exiting\n", stdout); 17 | hvm_debug_continue = false; 18 | return 0;// Pushed zero results onto the stack 19 | } 20 | 21 | /// Print the current debug backtrace 22 | /// @memberof hvm_debugger 23 | int hvm_lua_backtrace(lua_State *L) { 24 | hvm_vm *vm = _hvm_lua_get_vm(L); 25 | // fprintf(stdout, "there! %p\n", vm); 26 | // Hackety hax backtrace building 27 | hvm_obj_ref *exc = hvm_exception_new(vm, NULL); 28 | hvm_exception_build_backtrace(exc, vm); 29 | // Get the backtrace out of the exception 30 | hvm_symbol_id sym = hvm_symbolicate(vm->symbols, "backtrace"); 31 | hvm_obj_ref *backtrace = hvm_obj_struct_internal_get(exc->data.v, sym); 32 | if(backtrace != NULL) { 33 | hvm_print_backtrace_array(backtrace); 34 | } else { 35 | fprintf(stderr, "No backtrace found!\n"); 36 | } 37 | // Then release the exception so we don't leak 38 | hvm_obj_free(exc); 39 | return 0; 40 | } 41 | 42 | /// Sets a breakpoint 43 | /// @memberof hvm_debugger 44 | int hvm_lua_breakpoint(lua_State *L) { 45 | // Argument 1 is the file 46 | const char *file_from_lua = luaL_checkstring(L, 1); 47 | unsigned long length = strlen(file_from_lua); 48 | char *file = malloc(sizeof(char) * (length + 1)); 49 | strcpy(file, file_from_lua); 50 | // Argument 2 is the line number 51 | lua_Integer line_from_lua = luaL_checkinteger(L, 2); 52 | // Explicit conversion to 64-bit 53 | uint64_t line = (uint64_t)line_from_lua; 54 | // Set the breakpoint 55 | hvm_vm *vm = _hvm_lua_get_vm(L); 56 | hvm_debugger_set_breakpoint(vm, file, line); 57 | return 0; 58 | } 59 | 60 | /// Print the registers 61 | /// @memberof hvm_debugger 62 | int hvm_lua_registers(lua_State *L) { 63 | hvm_vm *vm = _hvm_lua_get_vm(L); 64 | for(unsigned int i = 0; i < HVM_GENERAL_REGISTERS; i++) { 65 | hvm_obj_ref *ref = vm->general_regs[i]; 66 | if(ref != hvm_const_null) { 67 | const char *name = hvm_human_name_for_obj_type(ref->type); 68 | fprintf(stderr, "g%-3d = %p (%s)\n", i, ref, name); 69 | } 70 | } 71 | return 0; 72 | } 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "vm.h" 13 | #include "object.h" 14 | #include "symbol.h" 15 | #include "chunk.h" 16 | #include "debug.h" 17 | #include "frame.h" 18 | #include "exception.h" 19 | 20 | #ifndef LUA_OK 21 | #define LUA_OK 0 22 | #endif 23 | 24 | #define true 1 25 | #define false 0 26 | 27 | // Instance of Lua used during debugging 28 | static lua_State *hvm_lua_state; 29 | 30 | // Flag used by `hvm_debug_begin` to check if it should continue the REPL. 31 | static bool hvm_debug_continue; 32 | 33 | // Debugger functions for Lua 34 | int hvm_lua_continue(lua_State*); 35 | int hvm_lua_backtrace(lua_State*); 36 | int hvm_lua_breakpoint(lua_State*); 37 | int hvm_lua_registers(lua_State *L); 38 | 39 | hvm_obj_ref *hvm_prim_debug_begin(hvm_vm *vm) { 40 | hvm_debug_begin(); 41 | return hvm_const_null; 42 | } 43 | 44 | #define ADD_FUNCTION(FUNC, NAME) lua_pushcfunction(L, FUNC); \ 45 | lua_setglobal(L, NAME); 46 | 47 | void hvm_debug_setup_lua(hvm_vm *vm) { 48 | // Setup the Lua instance 49 | hvm_lua_state = lua_open(); 50 | lua_State *L = hvm_lua_state; 51 | luaopen_base(L); 52 | 53 | // Functions for Lua 54 | ADD_FUNCTION(hvm_lua_continue, "continue"); 55 | ADD_FUNCTION(hvm_lua_backtrace, "backtrace"); 56 | ADD_FUNCTION(hvm_lua_breakpoint, "breakpoint"); 57 | ADD_FUNCTION(hvm_lua_registers, "registers"); 58 | 59 | // Add a reference to our VM instance to the Lua C registry 60 | lua_pushstring(L, "hvm_vm");// key 61 | lua_pushlightuserdata(hvm_lua_state, vm);// value 62 | lua_settable(L, LUA_REGISTRYINDEX); 63 | } 64 | 65 | void hvm_debug_setup(hvm_vm *vm) { 66 | // Setup our debugger instance 67 | hvm_debugger *debugger = malloc(sizeof(hvm_debugger)); 68 | vm->debugger = debugger; 69 | // We'll store the whole struct in the GArray (instead of just a pointer) 70 | // for spatial locality. 71 | debugger->breakpoints = g_array_new(TRUE, TRUE, sizeof(hvm_debug_breakpoint)); 72 | 73 | hvm_debug_setup_lua(vm); 74 | 75 | // Add the primitives to the VM 76 | hvm_symbol_id symbol; 77 | symbol = hvm_symbolicate(vm->symbols, "debug_begin"); 78 | hvm_obj_struct_internal_set(vm->primitives, symbol, (void*)hvm_prim_debug_begin); 79 | } 80 | 81 | void hvm_debug_prompt() { 82 | fputs("(db) ", stdout); 83 | } 84 | 85 | #define HVM_DEBUG_C 86 | #include "debug-lua.include.c" 87 | 88 | // Launch the Lua interpreter for debugging. 89 | void hvm_debug_begin() { 90 | // TODO: Use readline or something more friendly 91 | static int BUFFER_SIZE = 256; 92 | char buffer[BUFFER_SIZE]; 93 | 94 | int error = LUA_OK; 95 | hvm_debug_continue = true; 96 | 97 | while(hvm_debug_continue) { 98 | hvm_debug_prompt(); 99 | if (fgets(buffer, BUFFER_SIZE, stdin) == NULL) { 100 | break; 101 | } 102 | // Load our buffer as a function onto the top of the stack 103 | error = luaL_loadbuffer(hvm_lua_state, buffer, strlen(buffer), "line"); 104 | // If it's okay then call the function at the top of the stack 105 | if (error == LUA_OK) { 106 | error = lua_pcall(hvm_lua_state, 0, 0, 0); 107 | } 108 | if(error != LUA_OK) { 109 | // Print and pop the error 110 | fprintf(stderr, "Error: %s\n", lua_tostring(hvm_lua_state, -1)); 111 | lua_pop(hvm_lua_state, 1); 112 | } 113 | } 114 | // lua_close(hvm_lua_state); 115 | } 116 | 117 | bool hvm_debug_before_instruction(hvm_vm *vm) { 118 | // Get the debugger and the instruction pointer out of the VM 119 | hvm_debugger *debugger = (hvm_debugger*)vm->debugger; 120 | uint64_t ip = vm->ip; 121 | hvm_debug_breakpoint *bp; 122 | GArray *breakpoints = debugger->breakpoints; 123 | for(unsigned int i = 0; i < breakpoints->len; i++) { 124 | bp = &g_array_index(breakpoints, hvm_debug_breakpoint, i); 125 | if(bp->start <= ip && ip <= bp->end) { 126 | debugger->current_breakpoint = bp; 127 | hvm_debug_begin(); 128 | return 1; 129 | } 130 | } 131 | return 1; 132 | } 133 | 134 | hvm_chunk_debug_entry *hvm_debug_find_debug_entry_for_breakpoint(hvm_vm *vm, char *file, uint64_t line) { 135 | hvm_chunk_debug_entry *de; 136 | for(uint64_t i = 0; i < vm->debug_entries_size; i++) { 137 | de = &vm->debug_entries[i]; 138 | if(strcmp(de->file, file) == 0 && de->line == line) { 139 | return de; 140 | } 141 | } 142 | return NULL; 143 | } 144 | 145 | void hvm_debugger_set_breakpoint(hvm_vm *vm, char *file, uint64_t line) { 146 | hvm_debugger *debugger = vm->debugger; 147 | hvm_chunk_debug_entry *entry = hvm_debug_find_debug_entry_for_breakpoint(vm, file, line); 148 | if(entry == NULL) { 149 | fprintf(stderr, "Unable to set breakpoint for file '%s' at line %llu\n", file, line); 150 | return; 151 | } 152 | 153 | hvm_debug_breakpoint bp = { 154 | .file = file, 155 | .line = line, 156 | .start = entry->start, 157 | .end = entry->end 158 | }; 159 | g_array_append_val(debugger->breakpoints, bp); 160 | } 161 | -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_DEBUG_H 2 | #define HVM_DEBUG_H 3 | /// @file debug.h 4 | 5 | #ifndef bool 6 | #define bool char 7 | #endif 8 | 9 | typedef struct hvm_debugger { 10 | #ifdef GLIB_MAJOR_VERSION 11 | GArray *breakpoints; 12 | #else 13 | /// @cond 14 | void *breakpoints; 15 | /// @endcond 16 | #endif 17 | struct hvm_debug_breakpoint *current_breakpoint; 18 | } hvm_debugger; 19 | 20 | typedef struct hvm_debug_breakpoint { 21 | char *file; 22 | uint64_t line; 23 | uint64_t start; 24 | uint64_t end; 25 | } hvm_debug_breakpoint; 26 | 27 | /// Called by `hvm_new_vm` to add the debugger primitives to the VM. 28 | void hvm_debug_setup(hvm_vm*); 29 | 30 | /// Hook called by `hvm_vm_run` before it begins executing an instruction. 31 | /// @param vm 32 | /// @param instr Instruction the VM is about to excute. 33 | /// @retval bool Whether the VM should continue execution. 34 | bool hvm_debug_before_instruction(hvm_vm*); 35 | 36 | void hvm_debug_begin(); 37 | 38 | void hvm_debugger_set_breakpoint(hvm_vm *vm, char *file, uint64_t line); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/exception.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "vm.h" 9 | #include "symbol.h" 10 | #include "object.h" 11 | #include "frame.h" 12 | #include "chunk.h" 13 | #include "gc1.h" 14 | #include "exception.h" 15 | 16 | hvm_obj_ref *hvm_exception_new(hvm_vm *vm, hvm_obj_ref *message) { 17 | hvm_obj_ref *exc = hvm_new_obj_ref(); 18 | hvm_obj_struct *excstruct = hvm_new_obj_struct(); 19 | exc->type = HVM_STRUCTURE; 20 | exc->data.v = excstruct; 21 | hvm_obj_space_add_obj_ref(vm->obj_space, exc); 22 | 23 | if(message != NULL) { 24 | hvm_symbol_id sym = hvm_symbolicate(vm->symbols, "message"); 25 | assert(message->type == HVM_STRING); 26 | hvm_obj_struct_internal_set(excstruct, sym, message); 27 | } 28 | return exc; 29 | } 30 | 31 | void hvm_exception_push_location(hvm_vm *vm, hvm_obj_ref *exc, hvm_location *loc) { 32 | hvm_symbol_id sym = hvm_symbolicate(vm->symbols, "backtrace"); 33 | hvm_obj_ref *locations = hvm_obj_struct_internal_get(exc->data.v, sym); 34 | if(locations == NULL) { 35 | // If there's no locations array then we need to make one and add it 36 | // to the exception struct. 37 | locations = hvm_new_obj_ref(); 38 | locations->type = HVM_ARRAY; 39 | locations->data.v = hvm_new_obj_array(); 40 | hvm_obj_struct_internal_set(exc->data.v, sym, locations); 41 | } else { 42 | assert(locations->type == HVM_ARRAY); 43 | } 44 | // Now let's create an internal object ref for the location and push it 45 | // onto the array. 46 | hvm_obj_ref *locref = hvm_new_obj_ref(); 47 | locref->type = HVM_INTERNAL; 48 | locref->data.v = loc; 49 | hvm_obj_array_push(locations, locref); 50 | } 51 | 52 | static inline hvm_chunk_debug_entry *find_debug_entry(hvm_vm *vm, uint64_t ip) { 53 | hvm_chunk_debug_entry *de; 54 | for(uint64_t i = 0; i < vm->debug_entries_size; i++) { 55 | de = &vm->debug_entries[i]; 56 | // printf("ip: %llu, start: %llu; end: %llu\n", ip, de->start, de->end); 57 | if(de->start <= ip && ip <= de->end) { 58 | return de; 59 | } 60 | } 61 | return NULL; 62 | } 63 | 64 | #define FLAG_IS_SET(VAL, FLAG) (VAL & FLAG) == FLAG 65 | 66 | void hvm_exception_build_backtrace(hvm_obj_ref *exc, hvm_vm *vm) { 67 | hvm_chunk_debug_entry *entry; 68 | 69 | // for(uint64_t i = 0; i < vm->debug_entries_size; i++) { 70 | // de = &vm->debug_entries[i]; 71 | // fprintf(stderr, "entry:\n"); 72 | // fprintf(stderr, " start: %llu\n", de->start); 73 | // fprintf(stderr, " end: %llu\n", de->end); 74 | // fprintf(stderr, " line: %llu\n", de->line); 75 | // fprintf(stderr, " name: %s\n", de->name); 76 | // fprintf(stderr, " file: %s\n", de->file); 77 | // fprintf(stderr, " flags: %x\n", de->flags); 78 | // } 79 | 80 | uint32_t i = vm->stack_depth; 81 | while(1) { 82 | hvm_frame* frame = &vm->stack[i]; 83 | uint64_t ip = frame->current_addr; 84 | 85 | entry = find_debug_entry(vm, ip); 86 | if(entry != NULL && FLAG_IS_SET(entry->flags, HVM_DEBUG_FLAG_HIDE_BACKTRACE)) { 87 | goto tail; 88 | } 89 | hvm_location *loc = malloc(sizeof(hvm_location)); 90 | loc->frame = frame; 91 | if(entry != NULL) { 92 | loc->name = entry->name; 93 | loc->file = entry->file; 94 | loc->line = (unsigned int)(entry->line); 95 | } else { 96 | loc->name = "(unknown)"; 97 | loc->file = "(unknown)"; 98 | loc->line = 0; 99 | } 100 | hvm_exception_push_location(vm, exc, loc); 101 | tail: 102 | // Prevent integer overflow wrap-around. 103 | if(i == 0) { break; } 104 | i--; 105 | } 106 | } 107 | 108 | 109 | void hvm_print_backtrace_array(hvm_obj_ref *backtrace) { 110 | assert(backtrace->type = HVM_ARRAY); 111 | hvm_obj_array *arr = backtrace->data.v; 112 | unsigned int i; 113 | uint64_t len = hvm_array_len(arr); 114 | 115 | for(i = 0; i < len; i++) { 116 | hvm_obj_ref *locref = hvm_obj_array_internal_get(arr, i); 117 | assert(locref->type == HVM_INTERNAL); 118 | hvm_location *loc = locref->data.v; 119 | if(loc->name != NULL) { 120 | fprintf(stderr, " %s (%d:%s)\n", loc->name, loc->line, loc->file); 121 | } else { 122 | fprintf(stderr, " unknown (%d:%s)\n", loc->line, loc->file); 123 | } 124 | } 125 | } 126 | 127 | void hvm_exception_print(hvm_vm *vm, hvm_obj_ref *exc) { 128 | hvm_symbol_id messagesym = hvm_symbolicate(vm->symbols, "message"); 129 | hvm_obj_struct *excstruct = exc->data.v; 130 | hvm_obj_ref *message = hvm_obj_struct_internal_get(excstruct, messagesym); 131 | char *msg = "(unknown)"; 132 | if(message != NULL) { 133 | assert(message->type == HVM_STRING); 134 | hvm_obj_string *messagestr = message->data.v; 135 | msg = messagestr->data; 136 | } 137 | fprintf(stderr, "Exception: %s\n", msg); 138 | 139 | // See if there's a backtrace array 140 | hvm_symbol_id backtracesym = hvm_symbolicate(vm->symbols, "backtrace"); 141 | hvm_obj_ref *backtrace = hvm_obj_struct_internal_get(excstruct, backtracesym); 142 | if(backtrace != NULL) { 143 | fprintf(stderr, "Backtrace:\n"); 144 | hvm_print_backtrace_array(backtrace); 145 | } 146 | } 147 | 148 | /* 149 | hvm_obj_ref *hvm_obj_for_exception(hvm_vm *vm, hvm_exception *exc) { 150 | // Create the reference to the internal exception 151 | hvm_obj_ref *excref = hvm_new_obj_ref(); 152 | excref->type = HVM_EXCEPTION; 153 | excref->data.v = exc; 154 | excref->flags |= HVM_OBJ_FLAG_NO_FOLLOW; 155 | hvm_obj_space_add_obj_ref(vm->obj_space, excref); 156 | return excref; 157 | } 158 | */ 159 | -------------------------------------------------------------------------------- /src/exception.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_EXCEPTION_H 2 | #define HVM_EXCEPTION_H 3 | /// @file exception.h 4 | 5 | typedef struct hvm_exception { 6 | hvm_obj_ref *message; 7 | hvm_obj_ref *data; 8 | #ifdef GLIB_MAJOR_VERSION 9 | /// Array of frames. 10 | GArray *backtrace; 11 | #else 12 | /// @cond 13 | void *backtrace; 14 | /// @endcond 15 | #endif 16 | } hvm_exception; 17 | 18 | hvm_obj_ref *hvm_exception_new(hvm_vm*, hvm_obj_ref *message); 19 | 20 | void hvm_exception_push_location(hvm_vm *vm, hvm_obj_ref *exc, hvm_location *loc); 21 | void hvm_exception_build_backtrace(hvm_obj_ref *exc, hvm_vm *vm); 22 | void hvm_exception_print(hvm_vm *vm, hvm_obj_ref *exc); 23 | 24 | void hvm_print_backtrace_array(hvm_obj_ref *backtrace); 25 | 26 | hvm_obj_ref *hvm_obj_for_exception(hvm_vm *vm, hvm_exception *exc); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/frame.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "vm.h" 5 | #include "symbol.h" 6 | #include "object.h" 7 | #include "frame.h" 8 | 9 | hvm_frame *hvm_new_frame() { 10 | hvm_frame *frame = malloc(sizeof(hvm_frame)); 11 | hvm_frame_initialize(frame); 12 | return frame; 13 | } 14 | void hvm_frame_initialize(hvm_frame *frame) { 15 | frame->current_addr = 0; 16 | frame->return_addr = 0; 17 | frame->return_register = 0; 18 | frame->catch_addr = HVM_FRAME_EMPTY_CATCH; 19 | frame->catch_register = hvm_vm_reg_null(); 20 | frame->locals = hvm_new_obj_struct(); 21 | frame->trace = NULL; 22 | } 23 | 24 | hvm_location *hvm_new_location() { 25 | hvm_location *loc = malloc(sizeof(hvm_location)); 26 | loc->frame = NULL; 27 | loc->name = NULL; 28 | loc->file = NULL; 29 | loc->line = 0; 30 | return loc; 31 | } 32 | -------------------------------------------------------------------------------- /src/frame.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_FRAME_H 2 | #define HVM_FRAME_H 3 | /// @file frame.h 4 | 5 | /// Default value for the exception catch destination for a frame. 6 | #define HVM_FRAME_EMPTY_CATCH 0xFFFFFFFFFFFFFFFF 7 | 8 | /// Stack frame. 9 | typedef struct hvm_frame { 10 | /// Current address in this frame. 11 | uint64_t current_addr; 12 | /// Address to return to (set by caller). 13 | uint64_t return_addr; 14 | /// Register to be written to when returning (set by caller). 15 | unsigned char return_register; 16 | /// Exception catch destination 17 | uint64_t catch_addr; 18 | /// Register for exception to be written to 19 | unsigned char catch_register; 20 | /// Local variables of the frame. 21 | hvm_obj_struct *locals; 22 | /// Trace context of the frame (optional) 23 | void *trace; 24 | } hvm_frame; 25 | 26 | typedef struct hvm_location { 27 | /// Frame this location belongs to. 28 | hvm_frame *frame; 29 | 30 | char *name; 31 | char *file; 32 | unsigned int line; 33 | } hvm_location; 34 | 35 | /// @memberof hvm_frame 36 | hvm_frame *hvm_new_frame(); 37 | void hvm_frame_initialize(hvm_frame *frame); 38 | 39 | hvm_location *hvm_new_location(); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/gc1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "vm.h" 8 | #include "symbol.h" 9 | #include "object.h" 10 | #include "frame.h" 11 | #include "gc1.h" 12 | #include "exception.h" 13 | 14 | #define FLAGTRUE(v, f) (v & f) == f 15 | #define FLAGFALSE(v, f) (v & f) == 0 16 | 17 | #define FLAG_GC_MARKED 0x1 18 | 19 | // 0000 0001 20 | #define MARK_ENTRY(E) E->flags = E->flags | 0x1; 21 | // 1111 1110 22 | #define UNMARK_ENTRY(E) E->flags = E->flags & 0xFE; 23 | 24 | // Dereferences an entry pointer and returns the first byte of the struct. 25 | #define FIRST_BYTE_OF_ENTRY(V) *(char*)(V) 26 | 27 | 28 | hvm_gc1_obj_space *hvm_new_obj_space() { 29 | hvm_gc1_obj_space *space = malloc(sizeof(hvm_gc1_obj_space)); 30 | space->heap.size = HVM_GC1_INITIAL_HEAP_SIZE; 31 | space->heap.entries = calloc(HVM_GC1_HEAP_MEMORY_SIZE(space->heap.size), sizeof(hvm_gc1_heap_entry)); 32 | space->heap.length = 0; 33 | return space; 34 | } 35 | 36 | static inline void obj_space_reset_marks(hvm_gc1_obj_space *space) { 37 | unsigned int id = 0; 38 | while(id < space->heap.length) { 39 | hvm_gc1_heap_entry *entry = &space->heap.entries[id]; 40 | // entry->flags = entry->flags & 0xFE; 41 | UNMARK_ENTRY(entry); 42 | id += 1; 43 | } 44 | } 45 | 46 | // Forward declaration 47 | static inline void mark_struct(hvm_obj_struct *strct); 48 | static inline void mark_array(hvm_obj_array *arr); 49 | 50 | static inline void mark_obj_ref(hvm_obj_ref *obj) { 51 | if(FLAGTRUE(obj->flags, HVM_OBJ_FLAG_CONSTANT) || 52 | FLAGFALSE(obj->flags, HVM_OBJ_FLAG_GC_TRACKED) 53 | ) { 54 | return;// Don't process constants or untracked objects 55 | } 56 | assert(obj->entry != NULL); // Make sure there is a GC entry 57 | hvm_gc1_heap_entry *entry = obj->entry; 58 | // Check if already marked 59 | if(FLAGTRUE(entry->flags, FLAG_GC_MARKED)) { return; } 60 | // Mark the GC entry 61 | MARK_ENTRY(entry); 62 | // Handle complex data structures 63 | if(obj->type == HVM_STRUCTURE) { 64 | mark_struct(obj->data.v); 65 | } else if(obj->type == HVM_ARRAY) { 66 | mark_array(obj->data.v); 67 | } else if(obj->type == HVM_EXCEPTION) { 68 | hvm_exception *exc = obj->data.v; 69 | mark_obj_ref(exc->data); 70 | } 71 | } 72 | 73 | void mark_struct(hvm_obj_struct *strct) { 74 | unsigned int idx; 75 | for(idx = 0; idx < strct->heap_length; idx++) { 76 | hvm_obj_struct_heap_pair *pair = strct->heap[idx]; 77 | hvm_obj_ref *obj = pair->obj; 78 | mark_obj_ref(obj); 79 | } 80 | } 81 | void mark_array(hvm_obj_array *arr) { 82 | uint64_t idx, len; 83 | len = hvm_array_len(arr); 84 | for(idx = 0; idx < len; idx++) { 85 | hvm_obj_ref *ptr = hvm_obj_array_internal_get(arr, idx); 86 | mark_obj_ref(ptr); 87 | } 88 | } 89 | 90 | static inline void mark_registers(hvm_vm *vm) { 91 | uint32_t i; 92 | for(i = 0; i < HVM_GENERAL_REGISTERS; i++) { 93 | hvm_obj_ref* obj = vm->general_regs[i]; 94 | if(obj != NULL) { mark_obj_ref(obj); } 95 | } 96 | for(i = 0; i < HVM_ARGUMENT_REGISTERS; i++) { 97 | hvm_obj_ref* obj = vm->arg_regs[i]; 98 | if(obj != NULL) { mark_obj_ref(obj); } 99 | } 100 | for(i = 0; i < HVM_PARAMETER_REGISTERS; i++) { 101 | hvm_obj_ref* obj = vm->param_regs[i]; 102 | if(obj != NULL) { mark_obj_ref(obj); } 103 | } 104 | } 105 | 106 | static inline void mark_stack(hvm_vm *vm) { 107 | uint32_t i; 108 | for(i = 0; i <= vm->stack_depth; i++) { 109 | struct hvm_frame *frame = &vm->stack[i]; 110 | hvm_obj_struct *locals = frame->locals; 111 | mark_struct(locals); 112 | } 113 | } 114 | 115 | void hvm_gc1_free(hvm_gc1_heap_entry *entry) { 116 | // Free the object referenced 117 | hvm_obj_free(entry->obj); 118 | // Then clear out the entry 119 | // memset(entry, 0, sizeof(hvm_gc1_heap_entry)); 120 | // Only setting the first byte since that's what's checked. 121 | FIRST_BYTE_OF_ENTRY(entry) = 0; 122 | } 123 | 124 | static inline void sweep_space(hvm_gc1_obj_space *space) { 125 | for(uint32_t id = 0; id < space->heap.length; id++) { 126 | hvm_gc1_heap_entry *entry = &space->heap.entries[id]; 127 | if(FLAGFALSE(entry->flags, FLAG_GC_MARKED)) { 128 | fprintf(stderr, "freeing:%d\n", id); 129 | hvm_gc1_free(entry); 130 | } 131 | } 132 | } 133 | 134 | static inline bool entry_is_null(hvm_gc1_obj_space *space, uint32_t idx) { 135 | hvm_gc1_heap_entry *entry = &space->heap.entries[idx]; 136 | return FIRST_BYTE_OF_ENTRY(entry) == 0; 137 | } 138 | 139 | static inline void find_next_free_entry(hvm_gc1_obj_space *space, uint32_t *free_entry) { 140 | uint32_t idx = *free_entry; 141 | while(idx < space->heap.length && !entry_is_null(space, idx)) { 142 | idx += 1; 143 | } 144 | *free_entry = idx; 145 | } 146 | static inline bool has_used_entry_after(hvm_gc1_obj_space *space, uint32_t free_entry, uint32_t *used_entry) { 147 | // Start at the index after the free entry index 148 | uint32_t idx = free_entry + 1; 149 | // Make sure we have space left to search 150 | while(idx < space->heap.length) { 151 | if(entry_is_null(space, idx)) { 152 | // Pass over free entry 153 | idx += 1; 154 | continue; 155 | } else { 156 | // Non-null entry, so it's used 157 | *used_entry = idx; 158 | return true; 159 | } 160 | } 161 | return false; // No space left 162 | } 163 | 164 | static inline void relocate_entry(hvm_gc1_obj_space *space, uint32_t free_entry, uint32_t used_entry) { 165 | assert(free_entry < used_entry); 166 | hvm_gc1_heap_entry *dest = &space->heap.entries[free_entry]; 167 | hvm_gc1_heap_entry *source = &space->heap.entries[used_entry]; 168 | // Copy the entries 169 | memcpy(dest, source, sizeof(hvm_gc1_heap_entry)); 170 | // "Delete" the old entry 171 | FIRST_BYTE_OF_ENTRY(source) = 0; 172 | // Update the object reference to point to the right place 173 | hvm_obj_ref *obj = dest->obj; 174 | obj->entry = dest; 175 | } 176 | 177 | static inline void compact_space(hvm_gc1_obj_space *space) { 178 | uint32_t free_entry = 0; 179 | uint32_t used_entry = 0; 180 | find_next_free_entry(space, &free_entry); 181 | while(has_used_entry_after(space, free_entry, &used_entry)) { 182 | // Move the used entry to the free entry 183 | relocate_entry(space, free_entry, used_entry); 184 | fprintf(stderr, "relocating from used:%d to free:%d\n", used_entry, free_entry); 185 | // Find the next free entry after this one 186 | free_entry += 1; 187 | find_next_free_entry(space, &free_entry); 188 | } 189 | fprintf(stderr, "free entry:%d\n", free_entry); 190 | space->heap.length = free_entry; 191 | } 192 | 193 | static inline void obj_space_mark(hvm_vm *vm) { 194 | // Go through the registers 195 | mark_registers(vm); 196 | // Climb through each of the stack frames 197 | mark_stack(vm); 198 | } 199 | 200 | void hvm_gc1_run(hvm_vm *vm, hvm_gc1_obj_space *space) { 201 | // fprintf(stderr, "gc1_run.start\n"); 202 | // Reset all of our markings 203 | obj_space_reset_marks(space); 204 | // Mark objects 205 | obj_space_mark(vm); 206 | // Free unmarked objects 207 | sweep_space(space); 208 | // Compact the object space 209 | compact_space(space); 210 | // fprintf(stderr, "gc1_run.end\n"); 211 | // TODO: Shrink the object space 212 | } 213 | 214 | void hvm_obj_space_grow(hvm_gc1_obj_space *space) { 215 | unsigned int old_size = space->heap.size; 216 | space->heap.size = HVM_GC1_HEAP_GROW_FUNCTION(space->heap.size); 217 | fprintf(stderr, "gc1: growing from %u to %u\n", old_size, space->heap.size); 218 | space->heap.entries = realloc(space->heap.entries, HVM_GC1_HEAP_MEMORY_SIZE(space->heap.size)); 219 | } 220 | 221 | void hvm_obj_space_add_obj_ref(hvm_gc1_obj_space *space, hvm_obj_ref *obj) { 222 | if(FLAGTRUE(obj->flags, HVM_OBJ_FLAG_CONSTANT)) { 223 | return;// Don't track constants 224 | } 225 | if(FLAGTRUE(obj->flags, HVM_OBJ_FLAG_GC_TRACKED)) { 226 | return;// Already in the GC system 227 | } else { 228 | obj->flags |= HVM_OBJ_FLAG_GC_TRACKED; 229 | } 230 | unsigned int next_id = space->heap.length; 231 | space->heap.length += 1; 232 | // Check if we still have space 233 | if(next_id >= space->heap.size) { 234 | hvm_obj_space_grow(space); 235 | } 236 | hvm_gc1_heap_entry *entry = &space->heap.entries[next_id]; 237 | // fprintf(stderr, "obj_space_add: obj_ref = %p (%s)\n", obj, hvm_human_name_for_obj_type(obj->type)); 238 | // Set up the entry to point to the object 239 | entry->obj = obj; 240 | entry->flags = 0x0; 241 | // Set the object to point back to the entry 242 | obj->entry = entry; 243 | } 244 | -------------------------------------------------------------------------------- /src/gc1.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_GC1_H 2 | #define HVM_GC1_H 3 | 4 | // Communicates with the VM, object space, and heap to mark and sweep memory 5 | // in the object space and heap. 6 | 7 | // Ideally this will run in a separate thread and do smart locking with the 8 | // object space and heap to sweep and defrag/compact both with minimal 9 | // locking with the VM. 10 | 11 | #define HVM_GC1_INITIAL_HEAP_SIZE 1024 12 | #define HVM_GC1_HEAP_GROW_FUNCTION(V) (V * 8) 13 | #define HVM_GC1_HEAP_MEMORY_SIZE(S) (S * sizeof(hvm_gc1_heap_entry)) 14 | 15 | typedef struct hvm_gc1_heap { 16 | /// Base of entries area 17 | struct hvm_gc1_heap_entry *entries; 18 | /// Number of entries allocated 19 | unsigned int size; 20 | /// Number of entries in the heap 21 | unsigned int length; 22 | } hvm_gc1_heap; 23 | 24 | typedef struct hvm_gc1_heap_entry { 25 | /// Object this entry is tied to 26 | hvm_obj_ref *obj; 27 | /// Flags pertaining to the entry and its object 28 | byte flags; 29 | } hvm_gc1_heap_entry; 30 | 31 | typedef struct hvm_gc1_obj_space { 32 | hvm_gc1_heap heap; 33 | } hvm_gc1_obj_space; 34 | 35 | hvm_gc1_obj_space *hvm_new_obj_space(); 36 | void hvm_obj_space_add_obj_ref(hvm_gc1_obj_space *space, hvm_obj_ref *obj); 37 | 38 | void hvm_gc1_run(hvm_vm *vm, hvm_gc1_obj_space *space); 39 | /// Traverse the object space and reset the mark bits. 40 | void hvm_gc1_obj_space_mark_reset(hvm_gc1_obj_space *space); 41 | void hvm_gc1_obj_space_mark(hvm_vm*); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/generator.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_GEN_H 2 | #define HVM_GEN_H 3 | 4 | // A-type = 1B OP | 1B REG | 1B REG | 1B REG 5 | // B1-type = 1B OP | 1B REG | 4B SYM 6 | // B2-type = 1B OP | 4B SYM | 1B REG 7 | // C-type = 1B OP | 1B REG | 4B CONST 8 | // D-type = 1B OP | 8B DEST 9 | // E-type = 1B OP | 4B DIFF 10 | // F-type = 1B OP 11 | 12 | typedef enum { 13 | HVM_GEN_BASE, 14 | HVM_GEN_OPA1, // 1B OP | 1B REG 15 | HVM_GEN_OPA2, // 1B OP | 1B REG | 1B REG 16 | HVM_GEN_OPA3, // 1B OP | 1B REG | 1B REG | 1B REG 17 | HVM_GEN_OPB1, // 1B OP | 1B REG | 4B SYM 18 | HVM_GEN_OPB2, // 1B OP | 4B SYM | 1B REG 19 | HVM_GEN_OPC, 20 | HVM_GEN_OPD1, // 1B OP | 8B DEST 21 | HVM_GEN_OPD2, // 1B OP | 8B DEST | 1B REG 22 | HVM_GEN_OPD3, // 1B OP | 1B VAL | 8B DEST 23 | HVM_GEN_OPE, // 1B OP | 4B DIFF 24 | HVM_GEN_OPF, // 1B OP 25 | HVM_GEN_OPG, // 1B OP | 1B REG | 8B LITERAL 26 | HVM_GEN_OPH, // 1B OP | 1B REG | 4B CONST 27 | 28 | // 1B OP | 3B TAG | 8B DEST | 1B REG 29 | HVM_GEN_OP_CALL, 30 | HVM_GEN_OP_CALL_LABEL, 31 | // 1B OP | 3B TAG | 4B CONST | 1B REG 32 | HVM_GEN_OP_CALLSYMBOLIC, 33 | HVM_GEN_OP_CALLPRIMITIVE, 34 | 35 | // 1B OP | 3B TAG | 1B REG | 1B REG 36 | HVM_GEN_OP_INVOKESYMBOLIC, 37 | HVM_GEN_OP_INVOKEADDRESS, 38 | // 1B OP | 1B REG | 1B REG (untagged) 39 | HVM_GEN_OP_INVOKEPRIMITIVE, 40 | 41 | HVM_GEN_OPD1_LABEL, // 1B OP | [8B DEST] 42 | HVM_GEN_OPD2_LABEL, // 1B OP | [8B DEST] | 1B REG 43 | HVM_GEN_OPD3_LABEL, // 1B OP | 1B REG | [8B DEST] 44 | HVM_GEN_OPH_DATA, // 1B OP | 1B REG | [4B CONST] 45 | HVM_GEN_OPG_LABEL, // 1B OP | 1B REG | 8B DEST (i64) 46 | HVM_GEN_OPB2_SYMBOL,// 1B OP | [4B SYM] | 1B REG 47 | 48 | HVM_GEN_LABEL, 49 | HVM_GEN_SUB, 50 | HVM_GEN_BLOCK, 51 | HVM_GEN_DEBUG_ENTRY 52 | } hvm_gen_item_type; 53 | 54 | #define HVM_GEN_ITEM_HEAD hvm_gen_item_type type; 55 | 56 | typedef struct hvm_gen_item_base { 57 | HVM_GEN_ITEM_HEAD; 58 | } hvm_gen_item_base; 59 | 60 | // OPCODES -------------------------------------------------------------------- 61 | typedef struct hvm_gen_item_op_a1 { 62 | HVM_GEN_ITEM_HEAD; 63 | // 1B OP | 1B REG 64 | byte op; 65 | byte reg1; 66 | } hvm_gen_item_op_a1; 67 | typedef struct hvm_gen_item_op_a2 { 68 | HVM_GEN_ITEM_HEAD; 69 | // 1B OP | 1B REG | 1B REG 70 | byte op; 71 | byte reg1; 72 | byte reg2; 73 | } hvm_gen_item_op_a2; 74 | typedef struct hvm_gen_item_op_a3 { 75 | HVM_GEN_ITEM_HEAD; 76 | // 1B OP | 1B REG | 1B REG | 1B REG 77 | byte op; 78 | byte reg1; 79 | byte reg2; 80 | byte reg3; 81 | } hvm_gen_item_op_a3; 82 | typedef struct hvm_gen_item_op_b1 { 83 | HVM_GEN_ITEM_HEAD; 84 | // 1B OP | 1B REG | 4B SYM 85 | byte op; 86 | byte reg; 87 | uint32_t sym; 88 | } hvm_gen_item_op_b1; 89 | typedef struct hvm_gen_item_op_b2 { 90 | HVM_GEN_ITEM_HEAD; 91 | // 1B OP | 4B SYM | 1B REG 92 | byte op; 93 | uint32_t sym; 94 | byte reg; 95 | } hvm_gen_item_op_b2; 96 | typedef struct hvm_gen_item_op_c { 97 | HVM_GEN_ITEM_HEAD; 98 | byte reg; 99 | uint32_t cnst; 100 | } hvm_gen_item_op_c; 101 | typedef struct hvm_gen_item_op_d1 { 102 | HVM_GEN_ITEM_HEAD; 103 | // 1B OP | 8B DEST 104 | byte op; 105 | uint64_t dest; 106 | } hvm_gen_item_op_d1; 107 | typedef struct hvm_gen_item_op_d2 { 108 | HVM_GEN_ITEM_HEAD; 109 | byte op; 110 | uint64_t dest; 111 | byte reg; 112 | } hvm_gen_item_op_d2; 113 | typedef struct hvm_gen_item_op_d3 { 114 | HVM_GEN_ITEM_HEAD; 115 | byte op; 116 | byte val; 117 | uint64_t dest; 118 | } hvm_gen_item_op_d3; 119 | typedef struct hvm_gen_item_op_e { 120 | HVM_GEN_ITEM_HEAD; 121 | byte op; 122 | int32_t diff; 123 | } hvm_gen_item_op_e; 124 | typedef struct hvm_gen_item_op_f { 125 | HVM_GEN_ITEM_HEAD; 126 | // 1B OP 127 | byte op; 128 | } hvm_gen_item_op_f; 129 | typedef struct hvm_gen_item_op_g { 130 | HVM_GEN_ITEM_HEAD; 131 | // 1B OP | 1B REG | 8B LITERAL 132 | byte op; 133 | byte reg; 134 | int64_t lit; 135 | } hvm_gen_item_op_g; 136 | typedef struct hvm_gen_item_op_h { 137 | HVM_GEN_ITEM_HEAD; 138 | // 1B OP | 1B REG | 4B CONST 139 | byte op; 140 | byte reg; 141 | uint32_t cnst; 142 | } hvm_gen_item_op_h; 143 | 144 | typedef struct hvm_gen_item_op_b2_symbol { 145 | HVM_GEN_ITEM_HEAD; 146 | byte op; 147 | char *symbol; 148 | byte reg; 149 | } hvm_gen_item_op_b2_symbol; 150 | // Alias 151 | typedef hvm_gen_item_op_b2_symbol hvm_gen_op_callsymbolic; 152 | typedef hvm_gen_item_op_b2_symbol hvm_gen_op_callprimitive; 153 | 154 | typedef struct hvm_gen_item_op_d1_label { 155 | HVM_GEN_ITEM_HEAD; 156 | byte op; 157 | char* dest; 158 | } hvm_gen_item_op_d1_label; 159 | 160 | typedef struct hvm_gen_item_op_d2_label { 161 | HVM_GEN_ITEM_HEAD; 162 | byte op; 163 | char *label; 164 | byte reg; 165 | } hvm_gen_item_op_d2_label; 166 | // Alias 167 | typedef hvm_gen_item_op_d2_label hvm_gen_op_call_label; 168 | 169 | typedef struct hvm_gen_item_op_d3_label { 170 | HVM_GEN_ITEM_HEAD; 171 | byte op; 172 | byte reg; 173 | char *label; 174 | } hvm_gen_item_op_d3_label; 175 | 176 | typedef struct hvm_gen_item_op_g_label { 177 | HVM_GEN_ITEM_HEAD; 178 | byte op; 179 | byte reg; 180 | char* label; 181 | } hvm_gen_item_op_g_label; 182 | 183 | 184 | typedef struct hvm_gen_op_call { 185 | HVM_GEN_ITEM_HEAD; 186 | byte op; 187 | uint64_t dest; 188 | byte reg; 189 | } hvm_gen_op_call; 190 | 191 | typedef struct hvm_gen_op_invokesymbolic { 192 | HVM_GEN_ITEM_HEAD; 193 | byte op; 194 | byte sym; 195 | byte ret; 196 | } hvm_gen_op_invokesymbolic; 197 | 198 | typedef struct hvm_gen_op_invokeaddress { 199 | HVM_GEN_ITEM_HEAD; 200 | byte op; 201 | byte addr; 202 | byte ret; 203 | } hvm_gen_op_invokeaddress; 204 | 205 | typedef struct hvm_gen_op_invokeprimitive { 206 | HVM_GEN_ITEM_HEAD; 207 | byte op; 208 | byte sym; 209 | byte ret; 210 | } hvm_gen_op_invokeprimitive; 211 | 212 | 213 | typedef enum { 214 | HVM_GEN_DATA_STRING, 215 | HVM_GEN_DATA_INTEGER, 216 | HVM_GEN_DATA_SYMBOL 217 | } hvm_gen_data_type; 218 | 219 | union hvm_gen_item_data { 220 | int64_t i64; 221 | char* string; 222 | }; 223 | 224 | typedef struct hvm_gen_item_op_h_data { 225 | HVM_GEN_ITEM_HEAD; 226 | /// Type of the literal data 227 | hvm_gen_data_type data_type; 228 | byte op; 229 | /// Destination register 230 | byte reg; 231 | /// Literal data 232 | union hvm_gen_item_data data; 233 | } hvm_gen_item_op_h_data; 234 | 235 | typedef struct hvm_gen_item_debug_entry { 236 | HVM_GEN_ITEM_HEAD; 237 | uint64_t ip; 238 | uint64_t line; 239 | char *name; 240 | unsigned char flags; 241 | } hvm_gen_item_debug_entry; 242 | 243 | /* 244 | typedef enum { 245 | HVM_MACRO_SUB 246 | } hvm_gen_macro_type; 247 | typedef struct hvm_gen_item_macro { 248 | hvm_gen_item_type type; 249 | hvm_gen_macro_type macro; 250 | char *name; 251 | } hvm_gen_item_macro; 252 | */ 253 | 254 | // Labels inside blocks (for use with JUMPs and GOTOs). 255 | typedef struct hvm_gen_item_label { 256 | HVM_GEN_ITEM_HEAD; 257 | char *name; 258 | } hvm_gen_item_label; 259 | 260 | // Subroutine label 261 | typedef struct hvm_gen_item_sub { 262 | HVM_GEN_ITEM_HEAD; 263 | char *name; 264 | } hvm_gen_item_sub; 265 | 266 | typedef struct hvm_gen_item_block { 267 | HVM_GEN_ITEM_HEAD; 268 | #ifdef GLIB_MAJOR_VERSION 269 | GArray *items; 270 | #else 271 | void *items; 272 | #endif 273 | } hvm_gen_item_block; 274 | 275 | typedef union hvm_gen_item { 276 | ///@cond 277 | hvm_gen_item_base base; 278 | hvm_gen_item_op_a1 op_a1; 279 | hvm_gen_item_op_a2 op_a2; 280 | hvm_gen_item_op_a3 op_a3; 281 | hvm_gen_item_op_b1 op_b1; 282 | hvm_gen_item_op_b2 op_b2; 283 | hvm_gen_item_op_c op_c; 284 | hvm_gen_item_op_d1 op_d1; 285 | hvm_gen_item_op_d2 op_d2; 286 | hvm_gen_item_op_d3 op_d3; 287 | hvm_gen_item_op_e op_e; 288 | hvm_gen_item_op_f op_f; 289 | hvm_gen_item_op_g op_g; 290 | hvm_gen_item_op_h op_h; 291 | 292 | hvm_gen_item_op_b2_symbol op_b2_symbol; 293 | hvm_gen_item_op_d1_label op_d1_label; 294 | hvm_gen_item_op_d2_label op_d2_label; 295 | hvm_gen_item_op_d3_label op_d3_label; 296 | hvm_gen_item_op_g_label op_g_label; 297 | hvm_gen_item_op_h_data op_h_data; 298 | 299 | hvm_gen_op_call op_call; 300 | hvm_gen_op_call_label op_call_label; 301 | hvm_gen_op_callsymbolic op_callsymbolic; 302 | hvm_gen_op_callprimitive op_callprimitive; 303 | 304 | hvm_gen_op_invokeprimitive op_invokeprimitive; 305 | hvm_gen_op_invokeaddress op_invokeaddress; 306 | 307 | // hvm_gen_item_macro macro; 308 | hvm_gen_item_label label; 309 | hvm_gen_item_sub sub; 310 | hvm_gen_item_block block; 311 | hvm_gen_item_debug_entry debug_entry; 312 | ///@endcond 313 | } hvm_gen_item; 314 | 315 | /// @brief Stores instructions, constants, etc. for a chunk. Can then generate the 316 | /// appropriate bytecode for that chunk. 317 | typedef struct hvm_gen { 318 | /// Root block of the generator. 319 | hvm_gen_item_block *block; 320 | char *file; 321 | } hvm_gen; 322 | 323 | hvm_gen *hvm_new_gen(); 324 | void hvm_gen_set_file(hvm_gen *gen, char *file); 325 | hvm_gen_item_label *hvm_new_item_label(); 326 | hvm_gen_item_block *hvm_new_item_block(); 327 | 328 | 329 | struct hvm_chunk *hvm_gen_chunk(hvm_gen *gen); 330 | 331 | void hvm_gen_noop(hvm_gen_item_block *block); 332 | void hvm_gen_die(hvm_gen_item_block *block); 333 | void hvm_gen_jump(hvm_gen_item_block *block, int32_t diff); 334 | void hvm_gen_goto(hvm_gen_item_block *block, uint64_t dest); 335 | void hvm_gen_gotoaddress(hvm_gen_item_block *block, byte reg); 336 | void hvm_gen_call(hvm_gen_item_block *block, uint64_t dest, byte ret); 337 | void hvm_gen_invokesymbolic(hvm_gen_item_block *block, byte sym, byte ret); 338 | void hvm_gen_invokeprimitive(hvm_gen_item_block *block, byte sym, byte ret); 339 | void hvm_gen_invokeaddress(hvm_gen_item_block *block, byte addr, byte ret); 340 | void hvm_gen_if(hvm_gen_item_block *block, byte val, uint64_t dest); 341 | void hvm_gen_return(hvm_gen_item_block *block, byte reg); 342 | void hvm_gen_move(hvm_gen_item_block *block, byte dest, byte src); 343 | void hvm_gen_throw(hvm_gen_item_block *block, byte reg); 344 | void hvm_gen_clearcatch(hvm_gen_item_block *block); 345 | void hvm_gen_clearexception(hvm_gen_item_block *block); 346 | void hvm_gen_setexception(hvm_gen_item_block *block, byte reg); 347 | 348 | void hvm_gen_getlocal(hvm_gen_item_block *block, byte val_reg, byte sym_reg); 349 | void hvm_gen_setlocal(hvm_gen_item_block *block, byte sym_reg, byte val_reg); 350 | 351 | void hvm_gen_getglobal(hvm_gen_item_block *block, byte val_reg, byte sym_reg); 352 | void hvm_gen_setglobal(hvm_gen_item_block *block, byte sym_reg, byte val_reg); 353 | 354 | void hvm_gen_getclosure(hvm_gen_item_block *block, byte reg); 355 | 356 | void hvm_gen_litinteger(hvm_gen_item_block *block, byte reg, int64_t val); 357 | 358 | void hvm_gen_arraypush(hvm_gen_item_block *block, byte arr, byte val); 359 | void hvm_gen_arrayshift(hvm_gen_item_block *block, byte reg, byte arr); 360 | void hvm_gen_arraypop(hvm_gen_item_block *block, byte reg, byte arr); 361 | void hvm_gen_arrayunshift(hvm_gen_item_block *block, byte arr, byte val); 362 | void hvm_gen_arrayget(hvm_gen_item_block *block, byte reg, byte arr, byte idx); 363 | void hvm_gen_arrayremove(hvm_gen_item_block *block, byte reg, byte arr, byte idx); 364 | void hvm_gen_arrayset(hvm_gen_item_block *block, byte arr, byte idx, byte val); 365 | void hvm_gen_arraynew(hvm_gen_item_block *block, byte reg, byte size); 366 | void hvm_gen_arraylen(hvm_gen_item_block *block, byte len, byte arr); 367 | 368 | void hvm_gen_structget(hvm_gen_item_block *block, byte reg, byte strct, byte key); 369 | void hvm_gen_structdelete(hvm_gen_item_block *block, byte reg, byte strct, byte key); 370 | void hvm_gen_structset(hvm_gen_item_block *block, byte strct, byte key, byte val); 371 | void hvm_gen_structnew(hvm_gen_item_block *block, byte reg); 372 | 373 | void hvm_gen_setstring(hvm_gen_item_block *block, byte reg, uint32_t cnst); 374 | 375 | void hvm_gen_add(hvm_gen_item_block *block, byte a, byte b, byte c); 376 | 377 | void hvm_gen_lt(hvm_gen_item_block *block, byte a, byte b, byte c); 378 | void hvm_gen_gt(hvm_gen_item_block *block, byte a, byte b, byte c); 379 | void hvm_gen_lte(hvm_gen_item_block *block, byte a, byte b, byte c); 380 | void hvm_gen_gte(hvm_gen_item_block *block, byte a, byte b, byte c); 381 | void hvm_gen_eq(hvm_gen_item_block *block, byte a, byte b, byte c); 382 | void hvm_gen_and(hvm_gen_item_block *block, byte a, byte b, byte c); 383 | 384 | // META-GENERATORS 385 | void hvm_gen_label(hvm_gen_item_block *block, char *name); 386 | void hvm_gen_goto_label(hvm_gen_item_block *block, char *name); 387 | void hvm_gen_set_string(hvm_gen_item_block *block, byte reg, char *string); 388 | void hvm_gen_set_symbol(hvm_gen_item_block *block, byte reg, char *string); 389 | void hvm_gen_set_integer(hvm_gen_item_block *block, byte reg, int64_t integer); 390 | void hvm_gen_push_block(hvm_gen_item_block *block, hvm_gen_item_block *push); 391 | 392 | /// Generate a CALL to a subroutine with the label `name` in the chunk. The 393 | /// address is resolved at chunk compile time. 394 | void hvm_gen_call_label(hvm_gen_item_block *block, char *label, byte ret); 395 | 396 | /// Generate a CALLSYMBOLIC to a subroutine with a given constant symbol. The 397 | /// symbol name is added to the chunk constant table, which is then 398 | /// symbolicated and adjusted at chunk load time. 399 | void hvm_gen_callsymbolic(hvm_gen_item_block *block, char *symbol, byte ret); 400 | /// Same as `hvm_gen_callsymbolic` except with a primitive. 401 | void hvm_gen_callprimitive(hvm_gen_item_block *block, char *symbol, byte ret); 402 | 403 | void hvm_gen_if_label(hvm_gen_item_block *block, byte reg, char *label); 404 | void hvm_gen_catch_label(hvm_gen_item_block *block, char *label, byte reg); 405 | 406 | // SYMBOLICATED SUB-ROUTINES 407 | // Call at the head of a sub-routine to set up a symbol in the symbol table 408 | // for the sub-routine. 409 | void hvm_gen_sub(hvm_gen_item_block *block, char *name); 410 | 411 | /// Create a LITINTEGER instruction with a relocated address for the given 412 | /// label in the chunk. 413 | void hvm_gen_litinteger_label(hvm_gen_item_block *block, byte reg, char *label); 414 | 415 | // Debug information generators 416 | void hvm_gen_set_debug_line(hvm_gen_item_block *block, uint64_t line); 417 | void hvm_gen_set_debug_entry(hvm_gen_item_block *block, uint64_t line, char *name); 418 | 419 | #endif 420 | -------------------------------------------------------------------------------- /src/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_HEAP_H 2 | #define HVM_HEAP_H 3 | 4 | // Manages allocating spaces on the heap. 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/jit-compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_JIT_COMPILER_H 2 | #define HVM_JIT_COMPILER_H 3 | /// @file jit-compiler.h 4 | 5 | // Define some LLVM pointer types if LLVM isn't present to define them for us. 6 | #ifndef LLVM_C_CORE_H 7 | typedef void* LLVMBasicBlockRef; 8 | typedef void* LLVMBuilderRef; 9 | typedef void* LLVMValueRef; 10 | #endif 11 | 12 | typedef struct hvm_trace_compiled_frame { 13 | } hvm_trace_compiled_frame; 14 | 15 | typedef struct hvm_jit_block { 16 | /// IP in the VM where this block begins 17 | uint64_t ip; 18 | /// LLVM BasicBlock itself 19 | LLVMBasicBlockRef basic_block; 20 | /// Next block in the linked list 21 | struct hvm_jit_block *next; 22 | } hvm_jit_block; 23 | 24 | typedef enum { 25 | HVM_COMPILE_DATA_ARRAYSET, 26 | HVM_COMPILE_DATA_ARRAYGET, 27 | HVM_COMPILE_DATA_ARRAYLEN, 28 | HVM_COMPILE_DATA_ADD, 29 | HVM_COMPILE_DATA_SETSYMBOL, 30 | HVM_COMPILE_DATA_SETSTRING, 31 | HVM_COMPILE_DATA_INVOKEPRIMITIVE, 32 | HVM_COMPILE_DATA_IF, 33 | HVM_COMPILE_DATA_GOTO, 34 | HVM_COMPILE_DATA_LITINTEGER, 35 | HVM_COMPILE_DATA_MOVE, 36 | HVM_COMPILE_DATA_EQ, 37 | HVM_COMPILE_DATA_GT, 38 | HVM_COMPILE_DATA_AND, 39 | HVM_COMPILE_DATA_RETURN, 40 | HVM_COMPILE_DATA_SETLOCAL, 41 | HVM_COMPILE_DATA_GETLOCAL 42 | } hvm_compile_data_type; 43 | 44 | #define HVM_COMPILE_DATA_HEAD hvm_compile_data_type type; 45 | 46 | #define HVM_UNKNOWN_TYPE -1 47 | 48 | /// Wrapped values provide meta-data storage around a particular value. These 49 | /// will allow us to track information around that value to do analysis 50 | /// and optimizations based on it. 51 | typedef struct hvm_compile_value { 52 | /// Register this value is located in. 53 | byte reg; 54 | /// Type of the value being referenced. 55 | char type; 56 | /// If we know that this will be a constant. 57 | bool constant; 58 | /// Constant object (this is the value contained in .constant_value) 59 | hvm_obj_ref *constant_object; 60 | // #ifdef LLVM_C_CORE_H 61 | // /// Reference to the LLVM value pointing to the constant object. 62 | // LLVMValueRef constant_value; 63 | // #else 64 | // void *constant_value; 65 | // #endif 66 | } hvm_compile_value; 67 | 68 | 69 | // Data item conventions: 70 | // .register_return: Register that the result of the instruction goes in. 71 | 72 | typedef struct hvm_compile_sequence_data_head { 73 | HVM_COMPILE_DATA_HEAD; 74 | } hvm_compile_sequence_data_head; 75 | 76 | typedef struct hvm_compile_sequence_data_move { 77 | HVM_COMPILE_DATA_HEAD; 78 | /// Register value will be copied to 79 | byte register_return; 80 | /// Value that will be in the register 81 | hvm_compile_value *value; 82 | } hvm_compile_sequence_data_move; 83 | 84 | typedef struct hvm_compile_sequence_data_goto { 85 | HVM_COMPILE_DATA_HEAD; 86 | hvm_jit_block *destination_block; 87 | } hvm_compile_sequence_data_goto; 88 | 89 | typedef struct hvm_compile_sequence_data_arrayset { 90 | HVM_COMPILE_DATA_HEAD; 91 | // Source-values for the data we're going to use to perform the array set 92 | // operation. 93 | LLVMValueRef array; 94 | LLVMValueRef index; 95 | LLVMValueRef value; 96 | } hvm_compile_sequence_data_arrayset; 97 | 98 | typedef struct hvm_compile_sequence_data_arrayget { 99 | HVM_COMPILE_DATA_HEAD; 100 | // Sourcing values (array and the index into the array) 101 | LLVMValueRef array; 102 | LLVMValueRef index; 103 | // Result value and result register 104 | LLVMValueRef value; 105 | byte register_return; 106 | } hvm_compile_sequence_data_arrayget; 107 | 108 | typedef struct hvm_compile_sequence_data_add { 109 | HVM_COMPILE_DATA_HEAD; 110 | LLVMValueRef operand1; 111 | LLVMValueRef operand2; 112 | // Results 113 | LLVMValueRef result; 114 | byte register_return; 115 | } hvm_compile_sequence_data_add; 116 | 117 | typedef struct hvm_compile_sequence_data_if { 118 | HVM_COMPILE_DATA_HEAD; 119 | hvm_jit_block *falsey_block; 120 | hvm_jit_block *truthy_block; 121 | } hvm_compile_sequence_data_if; 122 | 123 | typedef struct hvm_compile_sequence_data_setsymbol { 124 | HVM_COMPILE_DATA_HEAD; 125 | // Register that the `.symbol` would be placed into 126 | byte register_return; 127 | // The symbol that was retrieved up from the constant table 128 | LLVMValueRef value; 129 | // Index into the constant table 130 | uint32_t constant; 131 | } hvm_compile_sequence_data_setsymbol; 132 | 133 | typedef struct hvm_compile_sequence_data_setsymbol hvm_compile_sequence_data_setstring; 134 | 135 | typedef struct hvm_compile_sequence_data_litinteger { 136 | HVM_COMPILE_DATA_HEAD; 137 | // Register that the integer will be put in 138 | byte register_return; 139 | // The constant integer in LLVM 140 | LLVMValueRef value; 141 | } hvm_compile_sequence_data_litinteger; 142 | 143 | typedef struct hvm_compile_sequence_data_invokeprimitive { 144 | HVM_COMPILE_DATA_HEAD; 145 | // Source registers for the symbol ID 146 | byte register_symbol; 147 | // Value for the symbol ID for `hvm_vm_call_primitive(vm, symbol_id)` 148 | hvm_symbol_id symbol_id; 149 | LLVMValueRef symbol_value; 150 | // Result value and register 151 | LLVMValueRef value; 152 | byte register_return; 153 | } hvm_compile_sequence_data_invokeprimitive; 154 | 155 | typedef struct hvm_compile_sequence_data_getlocal { 156 | HVM_COMPILE_DATA_HEAD; 157 | // Slot allocated for this local variable; set up by 158 | // `hvm_jit_compile_pass_identify_locals` 159 | LLVMValueRef slot; 160 | } hvm_compile_sequence_data_getlocal; 161 | 162 | typedef struct hvm_compile_sequence_data_setlocal { 163 | HVM_COMPILE_DATA_HEAD; 164 | LLVMValueRef slot; 165 | } hvm_compile_sequence_data_setlocal; 166 | 167 | /// Structs used for figuring out and keeping track of data related to each 168 | /// sequence in the trace instruction sequence being compiled. 169 | typedef union hvm_compile_sequence_data { 170 | hvm_compile_sequence_data_head head; 171 | hvm_compile_sequence_data_move move; 172 | hvm_compile_sequence_data_arrayset arrayset; 173 | hvm_compile_sequence_data_arrayget arrayget; 174 | hvm_compile_sequence_data_add add; 175 | hvm_compile_sequence_data_setsymbol setsymbol; 176 | hvm_compile_sequence_data_setstring setstring; 177 | hvm_compile_sequence_data_if item_if; 178 | hvm_compile_sequence_data_goto item_goto; 179 | hvm_compile_sequence_data_litinteger litinteger; 180 | hvm_compile_sequence_data_getlocal getlocal; 181 | hvm_compile_sequence_data_setlocal setlocal; 182 | hvm_compile_sequence_data_invokeprimitive invokeprimitive; 183 | } hvm_compile_sequence_data; 184 | 185 | /// Holds all information relevant to a compilation of a trace (eg. instruction 186 | /// sequence compilation data). 187 | typedef struct hvm_compile_bundle { 188 | // VM execution frame 189 | struct hvm_frame *frame; 190 | // Instructions as they are compiled 191 | hvm_compile_sequence_data *data; 192 | 193 | /// Head of linked list of blocks 194 | hvm_jit_block *blocks_head; 195 | hvm_jit_block *blocks_tail; 196 | unsigned int blocks_length; 197 | 198 | // TODO: Keep track of registers and how they're being read and written. 199 | // void *llvm_module; 200 | // void *llvm_engine; 201 | // void *llvm_builder; 202 | #ifdef LLVM_C_CORE_H 203 | // Full LLVM types 204 | LLVMModuleRef llvm_module; 205 | LLVMExecutionEngineRef llvm_engine; 206 | LLVMBuilderRef llvm_builder; 207 | LLVMValueRef llvm_function; 208 | #endif 209 | } hvm_compile_bundle; 210 | 211 | /// Used by the compilation passes to share state and information about the 212 | /// code being compiled. 213 | struct hvm_jit_compile_context { 214 | /// Bundle of LLVM objects and such 215 | hvm_compile_bundle *bundle; 216 | /// Boxes for object references (emulating the virtual registers in the VM), 217 | /// however LLVM will optimize our stores and loads to/from these into faster 218 | /// phi nodes 219 | LLVMValueRef *general_regs; 220 | /// For knowing whether a register is constant or not 221 | bool *constant_regs; 222 | /// Local variable slots 223 | struct hvm_obj_struct *locals; 224 | /// Pointer to the VM we're compiling for 225 | hvm_vm *vm; 226 | /// Boxes for wrapped values corresponding to a register. 227 | hvm_compile_value **values; 228 | }; 229 | 230 | 231 | typedef enum { 232 | HVM_JIT_EXIT_BAILOUT, 233 | HVM_JIT_EXIT_RETURN 234 | } hvm_jit_exit_status; 235 | 236 | /// Exited with a bailout; VM will resume execution at `destination`. 237 | typedef struct hvm_jit_exit_bailout { 238 | hvm_jit_exit_status status; 239 | uint64_t destination; 240 | } hvm_jit_exit_bailout; 241 | 242 | /// JITed function reached a return statement successfully. Object returned 243 | /// is in `value`. 244 | typedef struct hvm_jit_exit_return { 245 | hvm_jit_exit_status status; 246 | hvm_obj_ref *value; 247 | } hvm_jit_exit_return; 248 | 249 | typedef union hvm_jit_exit { 250 | hvm_jit_exit_bailout bailout; 251 | hvm_jit_exit_return ret; 252 | } hvm_jit_exit; 253 | 254 | // Second argument is the pointer to the first element of the parameter 255 | // registers array. 256 | typedef void (*hvm_jit_native_function)(hvm_jit_exit*, hvm_obj_ref*); 257 | 258 | // External API 259 | 260 | /// Compile a given trace to native function. 261 | void hvm_jit_compile_trace(hvm_vm*, hvm_call_trace*); 262 | /// Run a compiled traced. 263 | hvm_jit_exit* hvm_jit_run_compiled_trace(hvm_vm*, hvm_call_trace*); 264 | 265 | /// Given a trace and a compilation bundle, actually compiles each item 266 | /// in the trace into the LLVM IR builder. 267 | void hvm_jit_compile_builder(hvm_vm*, hvm_call_trace*, hvm_compile_bundle*); 268 | 269 | /// Given a certain instruction address (that's the destination for a 270 | /// branch/jump somewhere in the block), finds an already-inserted LLVM IR 271 | /// basic-block for that address or creates-and-inserts a new one. 272 | hvm_jit_block* hvm_jit_compile_find_or_insert_block(LLVMValueRef, hvm_compile_bundle*, uint64_t); 273 | 274 | /// Set up a bailout from the current JIT state back to the normal VM state 275 | /// at the given instruction address; execution will resume at that address 276 | /// in the VM. 277 | LLVMBasicBlockRef hvm_jit_build_bailout_block(hvm_vm*, LLVMBuilderRef, LLVMValueRef parent_func, LLVMValueRef exit_value, void*, uint64_t); 278 | 279 | #endif 280 | -------------------------------------------------------------------------------- /src/jit-tracer.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "vm.h" 8 | #include "object.h" 9 | #include "frame.h" 10 | #include "symbol.h" 11 | #include "jit-tracer.h" 12 | 13 | hvm_call_trace *hvm_new_call_trace(hvm_vm *vm) { 14 | hvm_call_trace *trace = malloc(sizeof(hvm_call_trace)); 15 | trace->entry = vm->ip; 16 | trace->sequence_capacity = HVM_TRACE_INITIAL_SEQUENCE_SIZE; 17 | trace->sequence_length = 0; 18 | trace->sequence = malloc(sizeof(hvm_trace_sequence_item) * trace->sequence_capacity); 19 | trace->complete = false; 20 | trace->caller_tag = NULL; 21 | trace->compiled_function = NULL; 22 | return trace; 23 | } 24 | 25 | void hvm_jit_call_trace_check_expand_capacity(hvm_call_trace *trace) { 26 | if(trace->sequence_length >= trace->sequence_capacity) { 27 | trace->sequence_capacity = 2 * trace->sequence_capacity; 28 | size_t size = sizeof(hvm_trace_sequence_item) * trace->sequence_capacity; 29 | trace->sequence = realloc(trace->sequence, size); 30 | } 31 | } 32 | 33 | hvm_trace_sequence_item *hvm_jit_call_trace_find_ip(hvm_call_trace *trace, uint64_t ip) { 34 | for(unsigned int i = 0; i < trace->sequence_length; i++) { 35 | hvm_trace_sequence_item *item = &trace->sequence[i]; 36 | if(item->head.ip == ip) { 37 | return item; 38 | } 39 | } 40 | return NULL; 41 | } 42 | 43 | void hvm_jit_call_trace_push_instruction(hvm_vm *vm, hvm_call_trace *trace) { 44 | hvm_trace_sequence_item *item, *existing_item; 45 | 46 | // Skip tracing if we've already traced this instruction 47 | existing_item = hvm_jit_call_trace_find_ip(trace, vm->ip); 48 | if(existing_item != NULL) { 49 | trace->current_item = existing_item; 50 | return; 51 | } 52 | 53 | byte instr = vm->program[vm->ip]; 54 | bool do_increment = true; 55 | // fprintf(stderr, "trace instruction: %d\n", instr); 56 | item = &trace->sequence[trace->sequence_length]; 57 | // Keep track of the instruction the item originally came from 58 | item->head.ip = vm->ip; 59 | // Also note in the trace that this is the current item for our annotation 60 | // helpers. 61 | trace->current_item = item; 62 | 63 | switch(instr) { 64 | case HVM_OP_SETSTRING: 65 | case HVM_OP_SETSYMBOL: 66 | // TODO: Append a hvm_trace_sequence_item_setstring to our trace->sequence. 67 | if(instr == HVM_OP_SETSTRING) { 68 | item->setstring.head.type = HVM_TRACE_SEQUENCE_ITEM_SETSTRING; 69 | } else if(instr == HVM_OP_SETSYMBOL) { 70 | item->setsymbol.head.type = HVM_TRACE_SEQUENCE_ITEM_SETSYMBOL; 71 | } 72 | // 1B OP | 1B REG | 4B CONST 73 | item->setstring.register_return = vm->program[vm->ip + 1]; 74 | item->setstring.constant = *(uint32_t*)(&vm->program[vm->ip + 2]); 75 | break; 76 | 77 | case HVM_OP_INVOKEPRIMITIVE: 78 | item->invokeprimitive.head.type = HVM_TRACE_SEQUENCE_ITEM_INVOKEPRIMITIVE; 79 | item->invokeprimitive.register_symbol = vm->program[vm->ip + 1]; 80 | item->invokeprimitive.register_return = vm->program[vm->ip + 2]; 81 | // Look up the symbol of the primitive we're going to be invoking 82 | // TODO: Actually store the address of the register that's going to be 83 | // read so that we can speed up checks in the future. 84 | hvm_obj_ref *ref = hvm_vm_register_read(vm, item->invokeprimitive.register_symbol); 85 | assert(ref->type == HVM_SYMBOL); 86 | item->invokeprimitive.symbol_value = ref->data.u64; 87 | break; 88 | 89 | case HVM_OP_RETURN: 90 | item->item_return.head.type = HVM_TRACE_SEQUENCE_ITEM_RETURN; 91 | item->item_return.register_return = vm->program[vm->ip + 1]; 92 | // Look up the object being returned so we can annotate the trace 93 | // with its type. 94 | hvm_obj_ref *return_obj_ref = hvm_vm_register_read(vm, item->item_return.register_return); 95 | item->item_return.returning_type = return_obj_ref->type; 96 | // Mark this trace as complete 97 | trace->complete = true; 98 | // And register an index for it in the VM trace index 99 | unsigned short next_index = vm->traces_length + 1; 100 | // Make sure there's space for this trace 101 | assert(next_index < (HVM_MAX_TRACES - 1)); 102 | vm->traces[next_index] = trace; 103 | // Update the caller's tag with the index if possible 104 | if(trace->caller_tag) { 105 | hvm_subroutine_tag tag; 106 | hvm_subroutine_read_tag(trace->caller_tag, &tag); 107 | // Actually setting the index here (remember it's off-by-one so that 108 | // 0 can mean not-set) 109 | tag.trace_index = next_index + 1; 110 | hvm_subroutine_write_tag(trace->caller_tag, &tag); 111 | } 112 | fprintf(stderr, "trace: completed trace %p\n", trace); 113 | break; 114 | 115 | case HVM_OP_IF: 116 | item->item_if.head.type = HVM_TRACE_SEQUENCE_ITEM_IF; 117 | item->item_if.register_value = vm->program[vm->ip + 1]; 118 | item->item_if.destination = *(uint64_t*)(&vm->program[vm->ip + 2]); 119 | item->item_if.branched = false; 120 | break; 121 | 122 | case HVM_OP_GOTO: 123 | item->item_goto.head.type = HVM_TRACE_SEQUENCE_ITEM_GOTO; 124 | item->item_goto.destination = *(uint64_t*)(&vm->program[vm->ip + 1]); 125 | break; 126 | 127 | case HVM_OP_ADD: 128 | case HVM_OP_EQ: 129 | case HVM_OP_GT: 130 | case HVM_OP_LT: 131 | case HVM_OP_AND: 132 | if(instr == HVM_OP_ADD) { item->head.type = HVM_TRACE_SEQUENCE_ITEM_ADD; } 133 | if(instr == HVM_OP_EQ) { item->head.type = HVM_TRACE_SEQUENCE_ITEM_EQ; } 134 | if(instr == HVM_OP_LT) { item->head.type = HVM_TRACE_SEQUENCE_ITEM_LT; } 135 | if(instr == HVM_OP_GT) { item->head.type = HVM_TRACE_SEQUENCE_ITEM_GT; } 136 | if(instr == HVM_OP_AND) { item->head.type = HVM_TRACE_SEQUENCE_ITEM_AND; } 137 | item->add.register_return = vm->program[vm->ip + 1]; 138 | item->add.register_operand1 = vm->program[vm->ip + 2]; 139 | item->add.register_operand2 = vm->program[vm->ip + 3]; 140 | break; 141 | 142 | case HVM_OP_ARRAYSET: 143 | item->arrayset.head.type = HVM_TRACE_SEQUENCE_ITEM_ARRAYSET; 144 | item->arrayset.register_array = vm->program[vm->ip + 1]; 145 | item->arrayset.register_index = vm->program[vm->ip + 2]; 146 | item->arrayset.register_value = vm->program[vm->ip + 3]; 147 | break; 148 | 149 | case HVM_OP_ARRAYGET: 150 | item->arrayget.head.type = HVM_TRACE_SEQUENCE_ITEM_ARRAYGET; 151 | item->arrayget.register_return = vm->program[vm->ip + 1]; 152 | item->arrayget.register_array = vm->program[vm->ip + 2]; 153 | item->arrayget.register_index = vm->program[vm->ip + 3]; 154 | break; 155 | 156 | case HVM_OP_ARRAYLEN: 157 | item->arraylen.head.type = HVM_TRACE_SEQUENCE_ITEM_ARRAYLEN; 158 | item->arraylen.register_return = vm->program[vm->ip + 1]; 159 | item->arraylen.register_array = vm->program[vm->ip + 2]; 160 | break; 161 | 162 | case HVM_OP_ARRAYPUSH: 163 | item->arraypush.head.type = HVM_TRACE_SEQUENCE_ITEM_ARRAYPUSH; 164 | item->arraypush.register_array = vm->program[vm->ip + 1]; 165 | item->arraypush.register_value = vm->program[vm->ip + 2]; 166 | break; 167 | 168 | case HVM_OP_MOVE: 169 | item->move.head.type = HVM_TRACE_SEQUENCE_ITEM_MOVE; 170 | item->move.register_return = vm->program[vm->ip + 1]; 171 | item->move.register_source = vm->program[vm->ip + 2]; 172 | break; 173 | 174 | case HVM_OP_LITINTEGER: 175 | item->litinteger.head.type = HVM_TRACE_SEQUENCE_ITEM_LITINTEGER; 176 | item->litinteger.register_return = vm->program[vm->ip + 1]; 177 | item->litinteger.literal_value = *(int64_t*)(&vm->program[vm->ip + 2]); 178 | break; 179 | 180 | case HVM_OP_GETLOCAL: 181 | item->getlocal.head.type = HVM_TRACE_SEQUENCE_ITEM_GETLOCAL; 182 | item->getlocal.register_return = vm->program[vm->ip + 1]; 183 | item->getlocal.register_symbol = vm->program[vm->ip + 2]; 184 | break; 185 | 186 | case HVM_OP_SETLOCAL: 187 | item->setlocal.head.type = HVM_TRACE_SEQUENCE_ITEM_SETLOCAL; 188 | item->setlocal.register_symbol = vm->program[vm->ip + 1]; 189 | item->setlocal.register_value = vm->program[vm->ip + 2]; 190 | break; 191 | 192 | case HVM_OP_GETGLOBAL: 193 | item->getglobal.head.type = HVM_TRACE_SEQUENCE_ITEM_GETGLOBAL; 194 | item->getglobal.register_return = vm->program[vm->ip + 1]; 195 | item->getglobal.register_symbol = vm->program[vm->ip + 2]; 196 | break; 197 | 198 | case HVM_OP_SETGLOBAL: 199 | item->setglobal.head.type = HVM_TRACE_SEQUENCE_ITEM_SETGLOBAL; 200 | item->setglobal.register_symbol = vm->program[vm->ip + 1]; 201 | item->setglobal.register_value = vm->program[vm->ip + 2]; 202 | break; 203 | 204 | default: 205 | fprintf(stderr, "jit-tracer: Don't know what to do with instruction: %d\n", instr); 206 | do_increment = false; 207 | } 208 | if(do_increment == true) { 209 | trace->sequence_length += 1; 210 | hvm_jit_call_trace_check_expand_capacity(trace); 211 | } 212 | } 213 | 214 | void hvm_jit_tracer_before_instruction(hvm_vm *vm) { 215 | hvm_frame *frame = vm->top; 216 | if(frame->trace != NULL) { 217 | hvm_call_trace *trace = frame->trace; 218 | hvm_jit_call_trace_push_instruction(vm, trace); 219 | } 220 | } 221 | 222 | 223 | hvm_trace_sequence_item *hvm_jit_tracer_get_current_item(hvm_vm *vm) { 224 | hvm_frame *frame = vm->top; 225 | hvm_call_trace *trace = frame->trace; 226 | hvm_trace_sequence_item *item = trace->current_item; 227 | return item; 228 | } 229 | 230 | void hvm_jit_tracer_annotate_invokeprimitive_returned_type(hvm_vm *vm, hvm_obj_ref *val) { 231 | hvm_frame *frame = vm->top; 232 | hvm_call_trace *trace = frame->trace; 233 | // Get the current item (was pushed in `hvm_jit_call_trace_push_instruction`) 234 | hvm_trace_sequence_item *item = trace->current_item; 235 | assert(item->head.type == HVM_TRACE_SEQUENCE_ITEM_INVOKEPRIMITIVE); 236 | // Update the return object type annotation using the object handed to us 237 | // by the VM. 238 | item->invokeprimitive.returned_type = val->type; 239 | } 240 | 241 | void hvm_jit_tracer_annotate_if_branched(hvm_vm *vm, bool branched) { 242 | hvm_frame *frame = vm->top; 243 | hvm_call_trace *trace = frame->trace; 244 | hvm_trace_sequence_item *item = trace->current_item; 245 | // Check to make sure the item is the right type and set the 246 | // `.branched` property. 247 | assert(item->head.type == HVM_TRACE_SEQUENCE_ITEM_IF); 248 | item->item_if.branched = branched; 249 | } 250 | 251 | void hvm_jit_tracer_annotate_getlocal(hvm_vm *vm, hvm_symbol_id symbol) { 252 | hvm_trace_sequence_item *item = hvm_jit_tracer_get_current_item(vm); 253 | assert(item->head.type == HVM_TRACE_SEQUENCE_ITEM_GETLOCAL); 254 | item->getlocal.symbol_value = symbol; 255 | } 256 | 257 | void hvm_jit_tracer_annotate_setlocal(hvm_vm *vm, hvm_symbol_id symbol) { 258 | hvm_trace_sequence_item *item = hvm_jit_tracer_get_current_item(vm); 259 | assert(item->head.type == HVM_TRACE_SEQUENCE_ITEM_SETLOCAL); 260 | item->setlocal.symbol_value = symbol; 261 | } 262 | 263 | void hvm_jit_tracer_dump_trace(hvm_vm *vm, hvm_call_trace *trace) { 264 | printf(" idx ip type\n"); 265 | for(unsigned int i = 0; i < trace->sequence_length; i++) { 266 | hvm_trace_sequence_item *item = &trace->sequence[i]; 267 | unsigned int idx = i; 268 | uint64_t address = item->head.ip; 269 | printf(" %-4d 0x%08llX %-3d ", idx, address, item->head.type); 270 | // Forward declarations for variables used in the big `switch` 271 | const char *type; 272 | byte reg, reg1, reg2, reg3; 273 | char *symbol_name; 274 | uint32_t short_symbol_id; 275 | uint64_t u64; 276 | int64_t i64; 277 | 278 | switch(item->head.type) { 279 | case HVM_TRACE_SEQUENCE_ITEM_RETURN: 280 | type = hvm_human_name_for_obj_type(item->item_return.returning_type); 281 | printf("$%d.%s", item->item_return.register_return, type); 282 | break; 283 | case HVM_TRACE_SEQUENCE_ITEM_INVOKEPRIMITIVE: 284 | reg1 = item->invokeprimitive.register_return; 285 | reg2 = item->invokeprimitive.register_symbol; 286 | type = hvm_human_name_for_obj_type(item->invokeprimitive.returned_type); 287 | symbol_name = hvm_desymbolicate(vm->symbols, item->invokeprimitive.symbol_value); 288 | printf("$%-3d = invokeprimitive($%d = %s) -> %s", reg1, reg2, symbol_name, type); 289 | break; 290 | case HVM_TRACE_SEQUENCE_ITEM_ARRAYGET: 291 | reg1 = item->arrayset.register_array; 292 | reg2 = item->arrayset.register_index; 293 | reg3 = item->arrayset.register_value; 294 | printf("$%-3d = $%d.arrayget[$%d]", reg1, reg2, reg3); 295 | break; 296 | case HVM_TRACE_SEQUENCE_ITEM_ARRAYSET: 297 | reg1 = item->arrayset.register_array; 298 | reg2 = item->arrayset.register_index; 299 | reg3 = item->arrayset.register_value; 300 | printf("$%d.arrayset[$%d] = $%d", reg1, reg2, reg3); 301 | break; 302 | case HVM_TRACE_SEQUENCE_ITEM_SETSYMBOL: 303 | reg = item->setsymbol.register_return; 304 | short_symbol_id = item->setsymbol.constant; 305 | hvm_obj_ref *ref = hvm_const_pool_get_const(&vm->const_pool, short_symbol_id); 306 | symbol_name = hvm_desymbolicate(vm->symbols, ref->data.u64); 307 | printf("$%-3d = setsymbol(#%d = %s)", reg, short_symbol_id, symbol_name); 308 | break; 309 | case HVM_TRACE_SEQUENCE_ITEM_ADD: 310 | reg1 = item->add.register_return; 311 | reg2 = item->add.register_operand1; 312 | reg3 = item->add.register_operand2; 313 | printf("$%-3d = $%d + $%d", reg1, reg2, reg3); 314 | break; 315 | case HVM_TRACE_SEQUENCE_ITEM_IF: 316 | reg1 = item->item_if.register_value; 317 | u64 = item->item_if.destination; 318 | char *branched = (item->item_if.branched ? "yes" : "no"); 319 | printf("if($%d, 0x%08llX, branched = %s)", reg1, u64, branched); 320 | break; 321 | case HVM_TRACE_SEQUENCE_ITEM_GOTO: 322 | u64 = item->item_goto.destination; 323 | printf("goto(0x%08llX)", u64); 324 | break; 325 | case HVM_TRACE_SEQUENCE_ITEM_ARRAYLEN: 326 | reg1 = item->arraylen.register_return; 327 | reg2 = item->arraylen.register_array; 328 | printf("$%-3d = $%d.arraylen", reg1, reg2); 329 | break; 330 | case HVM_TRACE_SEQUENCE_ITEM_LITINTEGER: 331 | reg1 = item->litinteger.register_return; 332 | i64 = item->litinteger.literal_value; 333 | printf("$%-3d = litinteger(%lld)", reg1, i64); 334 | break; 335 | case HVM_TRACE_SEQUENCE_ITEM_MOVE: 336 | reg1 = item->move.register_return; 337 | reg2 = item->move.register_source; 338 | printf("$%-3d = $%d", reg1, reg2); 339 | break; 340 | case HVM_TRACE_SEQUENCE_ITEM_EQ: 341 | reg1 = item->eq.register_return; 342 | reg2 = item->eq.register_operand1; 343 | reg3 = item->eq.register_operand2; 344 | char *cmp = "=="; 345 | printf("$%-3d = $%-3d %s $%-3d", reg1, reg2, cmp, reg3); 346 | break; 347 | default: 348 | break; 349 | } 350 | 351 | printf("\n"); 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /src/jit-tracer.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_JIT_TRACER_H 2 | #define HVM_JIT_TRACER_H 3 | /// @file jit-tracer.h 4 | 5 | typedef enum { 6 | HVM_TRACE_SEQUENCE_ITEM_SETSTRING = 1, 7 | HVM_TRACE_SEQUENCE_ITEM_SETSYMBOL = 2, 8 | HVM_TRACE_SEQUENCE_ITEM_INVOKEPRIMITIVE = 3, 9 | HVM_TRACE_SEQUENCE_ITEM_RETURN = 4, 10 | HVM_TRACE_SEQUENCE_ITEM_IF = 5, 11 | HVM_TRACE_SEQUENCE_ITEM_GOTO = 6, 12 | HVM_TRACE_SEQUENCE_ITEM_ADD = 7, 13 | HVM_TRACE_SEQUENCE_ITEM_EQ = 8, 14 | HVM_TRACE_SEQUENCE_ITEM_LT = 9, 15 | HVM_TRACE_SEQUENCE_ITEM_GT = 10, 16 | HVM_TRACE_SEQUENCE_ITEM_AND = 11, 17 | HVM_TRACE_SEQUENCE_ITEM_ARRAYSET = 12, 18 | HVM_TRACE_SEQUENCE_ITEM_ARRAYGET = 13, 19 | HVM_TRACE_SEQUENCE_ITEM_MOVE = 14, 20 | HVM_TRACE_SEQUENCE_ITEM_LITINTEGER = 15, 21 | HVM_TRACE_SEQUENCE_ITEM_ARRAYLEN = 16, 22 | HVM_TRACE_SEQUENCE_ITEM_ARRAYPUSH = 17, 23 | HVM_TRACE_SEQUENCE_ITEM_GETLOCAL = 18, 24 | HVM_TRACE_SEQUENCE_ITEM_SETLOCAL = 19, 25 | HVM_TRACE_SEQUENCE_ITEM_GETGLOBAL = 20, 26 | HVM_TRACE_SEQUENCE_ITEM_SETGLOBAL = 21 27 | } hvm_trace_sequence_item_type; 28 | 29 | 30 | /// Initial components of every sequence item 31 | typedef struct hvm_trace_sequence_item_head { 32 | hvm_trace_sequence_item_type type; 33 | uint64_t ip; 34 | } hvm_trace_sequence_item_head; 35 | 36 | /// @brief Common prefix for instructions returning a value to a register. 37 | /// 38 | /// All instructions that write back to a register must confirm to the 39 | /// `hvm_trace_sequence_item_returning` form. The form is a 40 | /// `struct hvm_trace_sequence_item_head` followed immediately by 41 | /// a register_return byte field for the register into which will go 42 | /// the returned value. 43 | typedef struct hvm_trace_sequence_item_returning { 44 | hvm_trace_sequence_item_head head; 45 | byte register_return; 46 | } hvm_trace_sequence_item_returning; 47 | 48 | typedef struct hvm_trace_sequence_item_setstring { 49 | hvm_trace_sequence_item_head head; 50 | /// Destination register for the constant 51 | byte register_return; 52 | /// Index into the constant table 53 | uint32_t constant; 54 | } hvm_trace_sequence_item_setstring; 55 | 56 | // Copy SETSTRING for SETSYMBOL 57 | typedef struct hvm_trace_sequence_item_setstring hvm_trace_sequence_item_setsymbol; 58 | 59 | typedef struct hvm_trace_sequence_item_return { 60 | hvm_trace_sequence_item_head head; 61 | /// Register we're returning from 62 | byte register_return; 63 | /// Type of the object being returned 64 | hvm_obj_type returning_type; 65 | } hvm_trace_sequence_item_return; 66 | 67 | typedef struct hvm_trace_sequence_item_invokeprimitive { 68 | hvm_trace_sequence_item_head head; 69 | /// Register for the return value 70 | byte register_return; 71 | /// Register with the symbol for the primitive 72 | byte register_symbol; 73 | /// Symbol contained in that register 74 | hvm_symbol_id symbol_value; 75 | /// Type of the object returned from the primitive 76 | hvm_obj_type returned_type; 77 | } hvm_trace_sequence_item_invokeprimitive; 78 | 79 | typedef struct hvm_trace_sequence_item_if { 80 | hvm_trace_sequence_item_head head; 81 | /// Register with the value we're checking 82 | byte register_value; 83 | /// Destination we're jumping to if true 84 | uint64_t destination; 85 | /// Whether or not this if branch was taken (went to destination) 86 | bool branched; 87 | } hvm_trace_sequence_item_if; 88 | 89 | typedef struct hvm_trace_sequence_item_goto { 90 | hvm_trace_sequence_item_head head; 91 | /// Destination we're jumping to 92 | uint64_t destination; 93 | } hvm_trace_sequence_item_goto; 94 | 95 | typedef struct hvm_trace_sequence_item_add { 96 | hvm_trace_sequence_item_head head; 97 | byte register_return; 98 | byte register_operand1; 99 | byte register_operand2; 100 | } hvm_trace_sequence_item_add; 101 | 102 | // Equality check follows same format as add operation 103 | typedef hvm_trace_sequence_item_add hvm_trace_sequence_item_eq; 104 | typedef hvm_trace_sequence_item_add hvm_trace_sequence_item_and; 105 | typedef hvm_trace_sequence_item_add hvm_trace_sequence_item_lt; 106 | typedef hvm_trace_sequence_item_add hvm_trace_sequence_item_gt; 107 | 108 | typedef struct hvm_trace_sequence_item_arrayset { 109 | hvm_trace_sequence_item_head head; 110 | byte register_array; 111 | byte register_index; 112 | byte register_value; 113 | } hvm_trace_sequence_item_arrayset; 114 | 115 | typedef struct hvm_trace_sequence_item_arrayget { 116 | hvm_trace_sequence_item_head head; 117 | byte register_return; 118 | byte register_array; 119 | byte register_index; 120 | } hvm_trace_sequence_item_arrayget; 121 | 122 | typedef struct hvm_trace_sequence_item_arraylen { 123 | hvm_trace_sequence_item_head head; 124 | byte register_return; 125 | byte register_array; 126 | } hvm_trace_sequence_item_arraylen; 127 | 128 | typedef struct hvm_trace_sequence_item_arraypush { 129 | hvm_trace_sequence_item_head head; 130 | byte register_array; 131 | byte register_value; 132 | } hvm_trace_sequence_item_arraypush; 133 | 134 | typedef struct hvm_trace_sequence_item_move { 135 | hvm_trace_sequence_item_head head; 136 | byte register_return; 137 | byte register_source; 138 | } hvm_trace_sequence_item_move; 139 | 140 | typedef struct hvm_trace_sequence_item_litinteger { 141 | hvm_trace_sequence_item_head head; 142 | byte register_return; 143 | int64_t literal_value; 144 | } hvm_trace_sequence_item_litinteger; 145 | 146 | typedef struct hvm_trace_sequence_item_getlocal { 147 | hvm_trace_sequence_item_head head; 148 | byte register_return; 149 | byte register_symbol; 150 | /// Symbol of the local being looked up 151 | hvm_symbol_id symbol_value; 152 | } hvm_trace_sequence_item_getlocal; 153 | 154 | typedef struct hvm_trace_sequence_item_setlocal { 155 | hvm_trace_sequence_item_head head; 156 | byte register_symbol; 157 | byte register_value; 158 | /// Symbol of the local being set 159 | hvm_symbol_id symbol_value; 160 | } hvm_trace_sequence_item_setlocal; 161 | 162 | typedef struct hvm_trace_sequence_item_getglobal { 163 | hvm_trace_sequence_item_head head; 164 | byte register_return; 165 | byte register_symbol; 166 | } hvm_trace_sequence_item_getglobal; 167 | 168 | typedef struct hvm_trace_sequence_item_setglobal { 169 | hvm_trace_sequence_item_head head; 170 | byte register_symbol; 171 | byte register_value; 172 | } hvm_trace_sequence_item_setglobal; 173 | 174 | 175 | /// @brief Item in a traced instruction sequence. 176 | /// 177 | /// Big union of all the different instructions that could be traced. The 178 | /// first field will always be a hvm_trace_sequence_item_type identifying 179 | /// the kind instruction the item is storing. 180 | typedef union hvm_trace_sequence_item { 181 | hvm_trace_sequence_item_head head; 182 | hvm_trace_sequence_item_returning returning; 183 | 184 | hvm_trace_sequence_item_setstring setstring; 185 | hvm_trace_sequence_item_setsymbol setsymbol; 186 | hvm_trace_sequence_item_invokeprimitive invokeprimitive; 187 | hvm_trace_sequence_item_return item_return; 188 | hvm_trace_sequence_item_if item_if; 189 | hvm_trace_sequence_item_goto item_goto; 190 | hvm_trace_sequence_item_add add; 191 | hvm_trace_sequence_item_eq eq; 192 | hvm_trace_sequence_item_lt lt; 193 | hvm_trace_sequence_item_gt gt; 194 | hvm_trace_sequence_item_and and; 195 | hvm_trace_sequence_item_arrayset arrayset; 196 | hvm_trace_sequence_item_arrayget arrayget; 197 | hvm_trace_sequence_item_arraylen arraylen; 198 | hvm_trace_sequence_item_arraypush arraypush; 199 | hvm_trace_sequence_item_move move; 200 | hvm_trace_sequence_item_litinteger litinteger; 201 | hvm_trace_sequence_item_getlocal getlocal; 202 | hvm_trace_sequence_item_setlocal setlocal; 203 | hvm_trace_sequence_item_getglobal getglobal; 204 | hvm_trace_sequence_item_setglobal setglobal; 205 | } hvm_trace_sequence_item; 206 | 207 | /// Stores information about a call site (traces, JIT blocks, etc.). 208 | typedef struct hvm_call_site { 209 | uint64_t ip; 210 | } hvm_call_site; 211 | 212 | /// Start traces off with space for 64 instructions. 213 | #define HVM_TRACE_INITIAL_SEQUENCE_SIZE 64 214 | 215 | /// Each trace of a subroutine call. 216 | typedef struct hvm_call_trace { 217 | /// IP for the entry point of the trace. 218 | uint64_t entry; 219 | /// Meta-instruction sequence for the trace 220 | hvm_trace_sequence_item *sequence; 221 | /// Current item in the sequence that is being traced 222 | hvm_trace_sequence_item *current_item; 223 | /// Maximum of at least 4 billion instructions should be enough 224 | unsigned int sequence_length; 225 | /// Capacity of the sequence 226 | unsigned int sequence_capacity; 227 | /// Whether or not the trace is done and ready for analysis 228 | bool complete; 229 | 230 | /// Pointer to the tag in the bytecode of the caller for us to update with 231 | /// the trace's index. 232 | byte *caller_tag; 233 | 234 | /// Pointer to LLVMValueRef for our compiled function 235 | void *compiled_function; 236 | } hvm_call_trace; 237 | 238 | /// Allocate a new trace. The entry IP will be set to the current VM IP. 239 | /// The trace is initialized with an empty sequence and marked incomplete. 240 | hvm_call_trace *hvm_new_call_trace(hvm_vm *vm); 241 | /// Called by the instruction dispatch loop while in the JIT dispatcher. 242 | void hvm_jit_tracer_before_instruction(hvm_vm *vm); 243 | 244 | // Special hooks for annotating instructions (invoked by the instruction 245 | // execution code in the JIT dispatcher). 246 | void hvm_jit_tracer_annotate_invokeprimitive_returned_type(hvm_vm*, hvm_obj_ref*); 247 | void hvm_jit_tracer_annotate_if_branched(hvm_vm*, bool branched); 248 | void hvm_jit_tracer_annotate_getlocal(hvm_vm *vm, hvm_symbol_id symbol); 249 | void hvm_jit_tracer_annotate_setlocal(hvm_vm *vm, hvm_symbol_id symbol); 250 | 251 | /// Write the trace to STDOUT. Format is similar to that of the chunk 252 | /// disassembler. 253 | void hvm_jit_tracer_dump_trace(hvm_vm*, hvm_call_trace*); 254 | 255 | #endif 256 | -------------------------------------------------------------------------------- /src/object.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_OBJECT_H 2 | #define HVM_OBJECT_H 3 | 4 | ///@relates hvm_obj_struct 5 | #define HVM_STRUCT_INITIAL_HEAP_SIZE 8 6 | ///@relates hvm_obj_struct 7 | #define HVM_STRUCT_HEAP_MEMORY_SIZE(S) (S * (sizeof(hvm_symbol_id) + sizeof(void*))) 8 | #define HVM_STRUCT_HEAP_GROWTH_RATE 2 9 | 10 | // Objects are either primitive or composite. 11 | 12 | typedef enum { 13 | HVM_NULL = 0, 14 | HVM_INTEGER = 1, 15 | HVM_FLOAT = 2, 16 | HVM_STRING = 3, 17 | HVM_STRUCTURE = 4, 18 | HVM_ARRAY = 5, 19 | HVM_SYMBOL = 6,// Internally same as HVM_INTEGER 20 | HVM_INTERNAL = 7, 21 | HVM_EXCEPTION = 8 22 | } hvm_obj_type; 23 | 24 | /// @brief Union of types for the data field in hvm_obj_ref. 25 | union hvm_obj_ref_data { 26 | /// Signed integer (used by HVM_INTEGER) 27 | int64_t i64; 28 | /// Unsigned integer (used by HVM_SYMBOL) 29 | uint64_t u64; 30 | /// Void pointer 31 | void* v; 32 | }; 33 | 34 | /// Exempts an object from garbage collection (and should eventually 35 | /// auto-promote to ancient generation). 36 | #define HVM_OBJ_FLAG_CONSTANT 0x2 37 | /// Flags an object as being tracked by the garbage collector. 38 | #define HVM_OBJ_FLAG_GC_TRACKED 0x4 39 | /// Instructs the GC not to follow objects referenced by this object. 40 | /// For INTERNAL objects this tells the GC to not to try to free the memory 41 | /// at `.data.v`. 42 | #define HVM_OBJ_FLAG_NO_FOLLOW 0x8 43 | 44 | /// Base reference to an object. 45 | typedef struct hvm_obj_ref { 46 | /// Type of data stored in/pointed to by the reference. 47 | hvm_obj_type type; 48 | /// 8 bytes of data to play with; can be used as pointer or literal. 49 | union hvm_obj_ref_data data; 50 | /// Internal flags for the reference. 51 | byte flags; 52 | /// Pointer to its entry in the GC heap 53 | void *entry; 54 | } hvm_obj_ref; 55 | 56 | 57 | /// Each zone will hold 65,536 `hvm_obj_ref` 58 | #define HVM_OBJ_REF_POOL_ZONE_SIZE 65536 59 | #define HVM_OBJ_REF_POOL_ZONE_FULL HVM_OBJ_REF_POOL_ZONE_SIZE 60 | 61 | typedef struct hvm_obj_ref_pool_zone { 62 | hvm_obj_ref *refs; 63 | /// Index of the first free slot in this zone, set this to 64 | /// HVM_OBJ_REF_POOL_ZONE_FULL to indicate that this zone is full 65 | unsigned int earliest_free; 66 | // Doubly-linked-list to preceding and following zones 67 | void *prev; 68 | void *next; 69 | } hvm_obj_ref_pool_zone; 70 | 71 | typedef struct hvm_obj_ref_pool { 72 | hvm_obj_ref_pool_zone *head; 73 | hvm_obj_ref_pool_zone *tail; 74 | /// First zone with empty slots 75 | hvm_obj_ref_pool_zone *earliest_free; 76 | } hvm_obj_ref_pool; 77 | 78 | // TYPES ---------------------------------------------------------------------- 79 | 80 | typedef struct hvm_obj_string { 81 | /// Raw string data (NULL-terminated) 82 | char* data; 83 | } hvm_obj_string; 84 | 85 | /// Dynamic array complex data type. 86 | typedef struct hvm_obj_array { 87 | #ifdef GLIB_MAJOR_VERSION 88 | /// Pointer to the internal glib GArray instance. 89 | GArray *array; 90 | #else 91 | /// @cond 92 | void *array; 93 | /// @endcond 94 | #endif 95 | // hvm_obj_ref** data; 96 | // unsigned int length; 97 | } hvm_obj_array; 98 | 99 | /// Pair of symbol ID and object references in a hvm_obj_struct's heap. 100 | typedef struct hvm_obj_struct_heap_pair { 101 | /// Symbol key 102 | hvm_symbol_id id; 103 | /// Object value 104 | hvm_obj_ref* obj; 105 | } hvm_obj_struct_heap_pair; 106 | 107 | /// @brief Structure/table complex data type. 108 | /// @details `memory size (bytes) = heap_size (pairs) * 2 (words/pair) * 8 (bytes/word)` 109 | typedef struct hvm_obj_struct { 110 | /// Binary heap of symbol-integer-and-pointer pairs. 111 | hvm_obj_struct_heap_pair** heap; 112 | /// Number of pairs allocated for the binary heap. 113 | unsigned int heap_size; 114 | /// Number of pairs in the binary heap. 115 | unsigned int heap_length; 116 | } hvm_obj_struct; 117 | 118 | 119 | // CONSTRUCTORS 120 | hvm_obj_string *hvm_new_obj_string(); 121 | hvm_obj_array *hvm_new_obj_array(); 122 | hvm_obj_array *hvm_new_obj_array_with_length(hvm_obj_ref*); 123 | /// Construct a new structure. 124 | /// @memberof hvm_obj_struct 125 | hvm_obj_struct *hvm_new_obj_struct(); 126 | 127 | // DESTRUCTORS 128 | void hvm_obj_free(hvm_obj_ref *ref); 129 | void hvm_obj_struct_free(hvm_obj_struct*); 130 | 131 | bool hvm_obj_is_falsey(hvm_obj_ref *ref); 132 | bool hvm_obj_is_truthy(hvm_obj_ref *ref); 133 | 134 | // Internal struct manipulation 135 | hvm_obj_ref *hvm_obj_struct_internal_get(hvm_obj_struct*, hvm_symbol_id); 136 | void hvm_obj_struct_internal_set(hvm_obj_struct*, hvm_symbol_id, hvm_obj_ref*); 137 | // External manipulation (via object refs) 138 | void hvm_obj_struct_set(hvm_obj_ref*, hvm_obj_ref*, hvm_obj_ref*); 139 | hvm_obj_ref* hvm_obj_struct_get(hvm_obj_ref*, hvm_obj_ref*); 140 | hvm_obj_ref* hvm_obj_struct_delete(hvm_obj_ref*, hvm_obj_ref*); 141 | void hvm_obj_print_structure(hvm_vm *vm, hvm_obj_struct *strct); 142 | 143 | hvm_obj_ref *hvm_new_obj_ref(); 144 | void hvm_obj_ref_set_string(hvm_obj_ref*, hvm_obj_string*); 145 | 146 | /// New pool-based `hvm_obj_ref` allocator 147 | hvm_obj_ref *hvm_obj_ref_new_from_pool(hvm_vm*); 148 | hvm_obj_ref_pool *hvm_obj_ref_pool_new(); 149 | void hvm_obj_ref_free(hvm_vm*, hvm_obj_ref*); 150 | 151 | hvm_obj_ref *hvm_new_obj_int(hvm_vm*); 152 | hvm_obj_ref *hvm_obj_int_add(hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 153 | hvm_obj_ref *hvm_obj_int_sub(hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 154 | hvm_obj_ref *hvm_obj_int_mul(hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 155 | hvm_obj_ref *hvm_obj_int_div(hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 156 | hvm_obj_ref *hvm_obj_int_mod(hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 157 | hvm_obj_ref *hvm_obj_int_lt (hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 158 | hvm_obj_ref *hvm_obj_int_gt (hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 159 | hvm_obj_ref *hvm_obj_int_lte(hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 160 | hvm_obj_ref *hvm_obj_int_gte(hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 161 | hvm_obj_ref *hvm_obj_int_eq (hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 162 | 163 | hvm_obj_ref *hvm_obj_cmp_and(hvm_vm*, hvm_obj_ref*, hvm_obj_ref*); 164 | 165 | void hvm_obj_array_push(hvm_obj_ref*, hvm_obj_ref*); 166 | void hvm_obj_array_unshift(hvm_obj_ref*, hvm_obj_ref*); 167 | hvm_obj_ref* hvm_obj_array_shift(hvm_obj_ref*); 168 | hvm_obj_ref* hvm_obj_array_pop(hvm_obj_ref*); 169 | hvm_obj_ref* hvm_obj_array_get(hvm_obj_ref*, hvm_obj_ref*); 170 | void hvm_obj_array_set(hvm_obj_ref*, hvm_obj_ref*, hvm_obj_ref*); 171 | hvm_obj_ref* hvm_obj_array_remove(hvm_obj_ref*, hvm_obj_ref*); 172 | hvm_obj_ref* hvm_obj_array_len(hvm_vm*, hvm_obj_ref*); 173 | 174 | uint64_t hvm_array_len(hvm_obj_array *arr); 175 | hvm_obj_ref* hvm_obj_array_internal_get(hvm_obj_array*, uint64_t); 176 | 177 | // UTILITIES ------------------------------------------------------------------ 178 | hvm_obj_ref *hvm_new_obj_ref_string_data(char *data); 179 | const char *hvm_human_name_for_obj_type(hvm_obj_type type); 180 | 181 | // PRIMITIVE 182 | // Composed of just metadata and primitive value. 183 | // Types: null, integer, float, (symbol) 184 | 185 | // INDIRECT PRIMITIVE 186 | // Types: string 187 | 188 | // COMPOSITE 189 | // Types: structure, array 190 | 191 | 192 | // OLD ------------------------------------------------------------------------ 193 | 194 | // COMPOSITE 195 | // Metadata and dynamic slots. Dynamic slots are by default looked up in a 196 | // compact hash stored inline with the object (need some very specific 197 | // tuning for hash growth factors/stages). 198 | 199 | // COMPOSITE TYPED 200 | // Metadata, static slots, and dynamic slots. Static slots are stored as a 201 | // constant-sized table between the metadata and dynamic slot data. Operations 202 | // working with known types can therefore cache lookups into constant indexes 203 | // into the static slot table. 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /src/symbol.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "vm.h" 9 | #include "symbol.h" 10 | 11 | hvm_symbol_store *hvm_new_symbol_store() { 12 | hvm_symbol_store *st = malloc(sizeof(hvm_symbol_store)); 13 | st->next_id = 1; 14 | st->size = HVM_SYMBOL_TABLE_INITIAL_SIZE; 15 | st->symbols = malloc(sizeof(hvm_symbol_store_entry*) * st->size); 16 | return st; 17 | } 18 | 19 | char *hvm_sym_strclone(char *str) { 20 | size_t len = strlen(str); 21 | char *clone = malloc(sizeof(char) * (size_t)(len + 1)); 22 | strcpy(clone, str); 23 | return clone; 24 | } 25 | 26 | void hvm_symbol_store_expand(hvm_symbol_store *st) { 27 | st->size = st->size * HVM_SYMBOL_TABLE_GROWTH_RATE; 28 | st->symbols = realloc(st->symbols, sizeof(hvm_symbol_store_entry*) * st->size); 29 | } 30 | 31 | hvm_symbol_store_entry *hvm_symbol_store_add(hvm_symbol_store *st, char *value) { 32 | hvm_symbol_id id = st->next_id; 33 | hvm_symbol_store_entry *entry = malloc(sizeof(hvm_symbol_store_entry)); 34 | entry->value = hvm_sym_strclone(value); 35 | entry->id = id; 36 | if(id >= (st->size - 1)) { 37 | hvm_symbol_store_expand(st); 38 | } 39 | st->symbols[id] = entry; 40 | st->next_id += 1; 41 | // fprintf(stderr, "symbol_add: %llu = %s\n", entry->id, entry->value); 42 | return entry; 43 | } 44 | 45 | hvm_symbol_id hvm_symbolicate(hvm_symbol_store *st, char *value) { 46 | hvm_symbol_store_entry *entry; 47 | hvm_symbol_id i; 48 | for(i = 1; i < st->next_id; i++) { 49 | entry = st->symbols[i]; 50 | if(strcmp(entry->value, value) == 0) { 51 | assert(i == entry->id); 52 | return entry->id; 53 | } 54 | } 55 | entry = hvm_symbol_store_add(st, value); 56 | return entry->id; 57 | } 58 | 59 | char *hvm_desymbolicate(hvm_symbol_store *st, hvm_symbol_id id) { 60 | hvm_symbol_store_entry *entry; 61 | hvm_symbol_id i; 62 | for(i = 1; i < st->next_id; i++) { 63 | entry = st->symbols[i]; 64 | if(id == entry->id) { 65 | return entry->value; 66 | } 67 | } 68 | return NULL; 69 | } 70 | -------------------------------------------------------------------------------- /src/symbol.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_SYMBOL_H 2 | #define HVM_SYMBOL_H 3 | /// @file symbol.h 4 | 5 | // Start with 128 slots in the symbol table 6 | // #define HVM_SYMBOL_TABLE_INITIAL_SIZE 2 7 | #define HVM_SYMBOL_TABLE_INITIAL_SIZE 128 8 | // Double in size when out of space 9 | #define HVM_SYMBOL_TABLE_GROWTH_RATE 2 10 | 11 | /// Internal symbol table. 12 | typedef struct hvm_symbol_store { 13 | /// Heap data. 14 | struct hvm_symbol_store_entry** symbols; 15 | /// Next index in the heap. 16 | hvm_symbol_id next_id; 17 | /// Size of the allocated heap (in entries). 18 | uint64_t size; 19 | } hvm_symbol_store; 20 | 21 | /// Entry mapping ID to string in the symbol store. 22 | typedef struct hvm_symbol_store_entry { 23 | /// Identifier (index) of the entry in the table. 24 | hvm_symbol_id id; 25 | /// String value/name of the symbol. 26 | char* value; 27 | } hvm_symbol_store_entry; 28 | 29 | /// Create a new symbol store 30 | /// @memberof hvm_symbol_store 31 | hvm_symbol_store *hvm_new_symbol_store(); 32 | /// Look up/add a string symbol to the symbol store 33 | /// @memberof hvm_symbol_store 34 | /// @returns Symbol ID for the string 35 | hvm_symbol_id hvm_symbolicate(hvm_symbol_store*, char*); 36 | 37 | char *hvm_desymbolicate(hvm_symbol_store *st, hvm_symbol_id id); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/vm.h: -------------------------------------------------------------------------------- 1 | #ifndef HVM_VM_H 2 | #define HVM_VM_H 3 | /// @file vm.h 4 | 5 | #include 6 | 7 | #define HVM_PTR_TO_UINT64(P) *((uint64_t*)&P) 8 | 9 | typedef unsigned char byte; 10 | 11 | /// VM opcode (256 max) 12 | typedef byte hvm_opcode; 13 | /// VM instruction 14 | typedef uint64_t hvm_instruction; 15 | 16 | /// Internal ID of a symbol. 17 | typedef uint64_t hvm_symbol_id; 18 | 19 | /// Size of chunks to be allocated for storing bytecodes. 20 | #define HVM_GENERATOR_GROW_RATE 65536 21 | 22 | /// Initial size (in bytes) for program data. 23 | /// @relates hvm_vm 24 | #define HVM_PROGRAM_INITIAL_CAPACITY 16384 25 | 26 | #define HVM_DEBUG_ENTRIES_INITIAL_CAPACITY 1024 27 | #define HVM_DEBUG_ENTRIES_GROW_FUNCTION(V) (V * 2) 28 | 29 | #define HVM_PROGRAM_GROW_FUNCTION(V) (V * 2) 30 | 31 | /// Maximum stack size (in frames) 32 | /// @relates hvm_vm 33 | #define HVM_STACK_SIZE 16384 34 | 35 | /// Threshold for a function to be hot and ready for tracing and compiling 36 | #define HVM_TRACE_THRESHOLD 2 37 | 38 | /// Maximum number of traces we can collect 39 | #define HVM_MAX_TRACES 65535 // 2^16 40 | 41 | extern struct hvm_obj_ref* hvm_const_null; 42 | 43 | /// Generates bytecode. 44 | /// @memberof hvm_generator 45 | // void hvm_generate_bytecode(struct hvm_gen*); 46 | 47 | /// @brief Constant pools map an integer to a constant. 48 | /// @details Can store approximately 4 billion constants (32-bit indexes). 49 | typedef struct hvm_const_pool { 50 | /// Entries in the pool: array of pointers to object references. 51 | struct hvm_obj_ref** entries; 52 | /// Index of the next index for an entry to be inserted at (ie. the length). 53 | uint32_t next_index; 54 | /// Number of possible entries in the pool. 55 | uint32_t size; 56 | } hvm_const_pool; 57 | 58 | /// Start with 128 slots in the constant pool 59 | /// @relates hvm_constant_pool 60 | #define HVM_CONSTANT_POOL_INITIAL_SIZE 128 61 | /// @relates hvm_constant_pool 62 | #define HVM_CONSTANT_POOL_GROWTH_RATE 2 63 | 64 | extern unsigned char HVM_DEBUG_FLAG_HIDE_BACKTRACE; 65 | 66 | #define HVM_GENERAL_REGISTERS 128 67 | #define HVM_ARGUMENT_REGISTERS 16 68 | // One more than arg regs. Last one is $pn special reg. 69 | #define HVM_PARAMETER_REGISTERS 17 70 | 71 | #define HVM_REG_ZERO 128 72 | #define HVM_REG_NULL 129 73 | #define HVM_REG_ARG_OFFSET 130 74 | #define HVM_REG_PARAM_OFFSET 146 75 | 76 | #define HVM_TOTAL_REGISTERS 163 77 | 78 | bool hvm_is_gen_reg(byte i); 79 | bool hvm_is_arg_reg(byte i); 80 | bool hvm_is_param_reg(byte i); 81 | 82 | byte hvm_vm_reg_gen(byte i); 83 | byte hvm_vm_reg_zero(); 84 | byte hvm_vm_reg_null(); 85 | byte hvm_vm_reg_arg(byte i); 86 | byte hvm_vm_reg_param(byte i); 87 | 88 | /// Instance of the VM. 89 | typedef struct hvm_vm { 90 | /// Root of call stack 91 | struct hvm_frame* root; 92 | /// Top of call stack (current execution frame) 93 | struct hvm_frame* top; 94 | /// Call stack 95 | struct hvm_frame* stack; 96 | /// Index of the current stack frame (total frames = stack_depth + 1) 97 | uint32_t stack_depth; 98 | 99 | /// Current exception (NULL for no exception) 100 | struct hvm_obj_ref* exception; 101 | /// Debug entries 102 | struct hvm_chunk_debug_entry* debug_entries; 103 | uint64_t debug_entries_capacity; 104 | uint64_t debug_entries_size; 105 | 106 | /// Instruction pointer (indexes bytes in the program) 107 | uint64_t ip; 108 | /// Data for instructions 109 | byte* program; 110 | /// Amount of available data for instructions (in bytes) 111 | uint64_t program_capacity; 112 | /// Size of program memory (in bytes) 113 | uint64_t program_size; 114 | 115 | /// Pool of constants (dynamic array). 116 | hvm_const_pool const_pool; 117 | /// Symbol table: resolves symbols to code locations 118 | struct hvm_obj_struct *symbol_table; 119 | /// General purpose registers ($r0...$rN) 120 | struct hvm_obj_ref* general_regs[HVM_GENERAL_REGISTERS]; 121 | /// Ephemeral argument registers (not preserved between subroutine calls) 122 | struct hvm_obj_ref* arg_regs[HVM_ARGUMENT_REGISTERS]; 123 | /// Ephemeral parameter registers 124 | struct hvm_obj_ref* param_regs[HVM_PARAMETER_REGISTERS]; 125 | 126 | /// Pool for object references to be allocated and freed 127 | struct hvm_obj_ref_pool *ref_pool; 128 | /// Object space for the garbage collector 129 | struct hvm_gc1_obj_space *obj_space; 130 | 131 | /// VM-wide global variables 132 | struct hvm_obj_struct *globals; 133 | /// Symbol lookup 134 | struct hvm_symbol_store *symbols; 135 | /// Primitives 136 | struct hvm_obj_struct *primitives; 137 | 138 | /// Debugger information store 139 | void *debugger; 140 | 141 | /// Whether or not JIT'ing is enabled 142 | bool jit_enabled; 143 | /// Whether or not it's tracing 144 | bool is_tracing; 145 | /// Special flag to tell it to *always* trace 146 | bool always_trace; 147 | /// Array of traces that have been collected and are ready for compilation 148 | struct hvm_call_trace* traces[HVM_MAX_TRACES]; 149 | /// Number of traces in .traces 150 | unsigned short traces_length; 151 | } hvm_vm; 152 | 153 | /// Create a new virtual machine. 154 | /// @memberof hvm_vm 155 | hvm_vm *hvm_new_vm(); 156 | /// Begin executing the virtual machine. 157 | /// @memberof hvm_vm 158 | void hvm_vm_run(hvm_vm*); 159 | 160 | /// Load a chunk into the VM. 161 | /// @memberof hvm_vm 162 | void hvm_vm_load_chunk(hvm_vm *vm, void *cv); 163 | 164 | /// Internal function for copying argument registers to parameter registers 165 | /// upon invocation of a subroutine. 166 | /// @memberof hvm_vm 167 | void hvm_vm_copy_regs(hvm_vm*); 168 | 169 | struct hvm_obj_ref *hvm_vm_register_read(hvm_vm *vm, byte reg); 170 | 171 | /// Set a constant in the VM constant table. 172 | /// @memberof hvm_vm 173 | /// @param vm 174 | /// @param id 175 | /// @param obj 176 | /// @retval hvm_obj_ref 177 | void hvm_vm_set_const(hvm_vm *vm, uint32_t id, struct hvm_obj_ref* obj); 178 | /// Get a constant. 179 | /// @memberof hvm_vm 180 | /// @param vm 181 | /// @param id 182 | /// @retval hvm_obj_ref 183 | struct hvm_obj_ref* hvm_vm_get_const(hvm_vm *vm, uint32_t id); 184 | 185 | struct hvm_obj_ref* hvm_const_pool_get_const(hvm_const_pool*, uint32_t); 186 | void hvm_const_pool_set_const(hvm_const_pool*, uint32_t, struct hvm_obj_ref*); 187 | uint32_t hvm_vm_add_const(hvm_vm *vm, struct hvm_obj_ref* obj); 188 | 189 | 190 | /// Get a local variable from a stack frame. 191 | /// @memberof hvm_vm 192 | struct hvm_obj_ref* hvm_get_local(struct hvm_frame*, hvm_symbol_id); 193 | /// Set a local variable in a stack frame. 194 | /// @memberof hvm_vm 195 | void hvm_set_local(struct hvm_frame*, hvm_symbol_id, struct hvm_obj_ref*); 196 | 197 | 198 | /// Get a global variable from the VM global struct (by symbol ID). 199 | /// @memberof hvm_vm 200 | struct hvm_obj_ref* hvm_get_global(hvm_vm*, hvm_symbol_id); 201 | /// Set a global variable in the VM. 202 | /// @memberof hvm_vm 203 | void hvm_set_global(hvm_vm*, hvm_symbol_id, struct hvm_obj_ref*); 204 | 205 | /// Call the primitive referenced by the given HVM_SYMBOL object. 206 | /// @memberof hvm_vm 207 | struct hvm_obj_ref *hvm_vm_call_primitive(hvm_vm*, struct hvm_obj_ref*); 208 | 209 | /// Construct a closure structure object from the current scope state of 210 | /// the VM. 211 | /// @memberof hvm_vm 212 | struct hvm_obj_ref* hvm_vm_build_closure(hvm_vm *vm); 213 | 214 | /// Utility function for cloning a NULL-terminated character string. 215 | /// Warning: Allocates memory! 216 | char *hvm_util_strclone(char *str); 217 | 218 | /// Opcodes 219 | typedef enum { 220 | HVM_OP_NOOP = 0, // 1B OP 221 | HVM_OP_DIE = 1, // 1B OP 222 | HVM_OP_JUMP = 2, // 1B OP | 4B DIFF 223 | HVM_OP_GOTO = 3, // 1B OP | 8B DEST 224 | HVM_OP_RETURN = 4, // 1B OP | 1B REG 225 | HVM_OP_IF = 5, // 1B OP | 1B REG | 8B DEST 226 | // Calls 227 | HVM_OP_CALL = 6, // 1B OP | 3B TAG | 8B DEST | 1B REG 228 | HVM_OP_CALLSYMBOLIC = 57, // 1B OP | 3B TAG | 4B CONST | 1B REG 229 | HVM_OP_CALLPRIMITIVE = 58, // 1B OP | 3B TAG | 4B CONST | 1B REG 230 | HVM_OP_TAILCALL = 7, // 1B OP | 3B TAG | 8B DEST 231 | // Invocations 232 | HVM_OP_INVOKESYMBOLIC = 8, // 1B OP | 3B TAG | 1B REG | 1B REG 233 | HVM_OP_INVOKEADDRESS = 9, // 1B OP | 3B TAG | 1B REG | 1B REG 234 | HVM_OP_INVOKEPRIMITIVE = 10,// 1B OP | 1B REG | 1B REG (untagged) 235 | 236 | HVM_OP_SETSTRING = 11, // 1B OP | 1B REG | 4B CONST 237 | HVM_OP_SETINTEGER = 12,// 1B OP | 1B REG | 4B CONST 238 | HVM_OP_SETFLOAT = 13, // 1B OP | 1B REG | 4B CONST 239 | HVM_OP_SETSTRUCT = 14, // 1B OP | 1B REG | 4B CONST 240 | HVM_OP_SETSYMBOL = 15, // 1B OP | 1B REG | 4B CONST 241 | HVM_OP_SETNULL = 16, // 1B OP | 1B REG 242 | 243 | HVM_OP_LITINTEGER = 41,// 1B OP | 1B REG | 8B LIT 244 | 245 | HVM_OP_SYMBOLICATE = 42,// 1B OP | 1B REG | 1B REG 246 | 247 | HVM_OP_GETLOCAL = 17, // 1B OP | 1B REG | 1B REG 248 | HVM_OP_SETLOCAL = 18, // 1B OP | 1B REG | 1B REG 249 | HVM_OP_GETGLOBAL = 19, // 1B OP | 1B REG | 1B REG 250 | HVM_OP_SETGLOBAL = 20, // 1B OP | 1B REG | 1B REG 251 | 252 | HVM_OP_GETCLOSURE = 56, // 1B OP | 1B REG 253 | 254 | HVM_OP_ADD = 21, // 1B OP | 3B REGs 255 | HVM_OP_SUB = 22, // 1B OP | 3B REGs 256 | HVM_OP_MUL = 23, // 1B OP | 3B REGs 257 | HVM_OP_DIV = 24, // 1B OP | 3B REGs 258 | HVM_OP_MOD = 25, // 1B OP | 3B REGs 259 | HVM_OP_POW = 26, // 1B OP | 3B REGs 260 | 261 | HVM_OP_LT = 45, // 1B OP | 3B REGs 262 | HVM_OP_GT = 46, // 1B OP | 3B REGs 263 | HVM_OP_LTE = 47, // 1B OP | 3B REGs 264 | HVM_OP_GTE = 48, // 1B OP | 3B REGs 265 | HVM_OP_EQ = 49, // 1B OP | 3B REGs 266 | HVM_OP_AND = 60, // 1B OP | 3B REGs 267 | 268 | // TODO: Bitwise instructions 269 | 270 | HVM_OP_CATCH = 50, // 1B OP | 8B DEST | 1B REG 271 | HVM_OP_CLEARCATCH = 51, // 1B OP 272 | HVM_OP_CLEAREXCEPTION = 52, // 1B OP 273 | HVM_OP_SETEXCEPTION = 53, // 1B OP | 1B REG 274 | HVM_OP_THROW = 54, // 1B OP | 1B REG 275 | HVM_OP_GETEXCEPTIONDATA = 55, // 1B OP | 2B REG 276 | // TODO: RETHROW 277 | 278 | HVM_OP_ARRAYPUSH = 27, // 1B OP | 2B REGS 279 | HVM_OP_ARRAYSHIFT = 28, // 1B OP | 2B REGS 280 | HVM_OP_ARRAYPOP = 29, // 1B OP | 2B REGS 281 | HVM_OP_ARRAYUNSHIFT = 30, // 1B OP | 2B REGS 282 | HVM_OP_ARRAYGET = 31, // 1B OP | 3B REGS ( 1 = 2[3] ) 283 | HVM_OP_ARRAYSET = 32, // 1B OP | 3B REGS ( 1[2] = 3 ) 284 | HVM_OP_ARRAYREMOVE = 33, // 1B OP | 3B REGS 285 | HVM_OP_ARRAYNEW = 34, // 1B OP | 2B REGS 286 | HVM_OP_ARRAYLEN = 35, // 1B OP | 2B REGS ( 1 = length(2) ) 287 | 288 | HVM_OP_STRUCTSET = 36, // 1B OP | 3B REGS 289 | HVM_OP_STRUCTGET = 37, // 1B OP | 3B REGS 290 | HVM_OP_STRUCTDELETE = 38, // 1B OP | 3B REGS 291 | HVM_OP_STRUCTNEW = 39, // 1B OP | 1B REG 292 | HVM_OP_STRUCTHAS = 40, // 1B OP | 3B REGS 293 | 294 | HVM_OP_MOVE = 43,// 1B OP | 1B REG | 1B REG 295 | 296 | HVM_OP_GOTOADDRESS = 44,// 1B OP | 1B REG 297 | 298 | } hvm_opcodes; 299 | 300 | /// Size of subroutine tags (in bytes) 301 | #define HVM_SUBROUTINE_TAG_SIZE 3 302 | 303 | // Struct for subroutine tags (3 bytes) 304 | typedef struct hvm_subroutine_tag { 305 | unsigned short heat;// 8 bits (max is 255) 306 | unsigned short trace_index;// 16 bits 307 | } hvm_subroutine_tag; 308 | 309 | void hvm_subroutine_read_tag(byte *tag_start, hvm_subroutine_tag *tag); 310 | void hvm_subroutine_write_tag(byte *tag_start, hvm_subroutine_tag *tag); 311 | 312 | #endif 313 | -------------------------------------------------------------------------------- /test/sandbox/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | $cflags = "-g -Wall -std=c99 -I../../include #{`pkg-config --cflags glib-2.0 lua5.1`.strip}" 3 | 4 | # Link against shared library 5 | $ldflags = "../../libhivm-db.a -liconv -lz -lpthread -ledit -lcurses -lm -lc++ #{`pkg-config --libs glib-2.0 lua5.1`.strip}"# -dead_strip -why_load" 6 | if `uname -s`.strip == 'Darwin' 7 | $ldflags += " -macosx_version_min 10.10" 8 | end 9 | 10 | task 'default' => ['test_vm'] 11 | 12 | desc "Build VM tester" 13 | file 'test_vm.o' => ['test_vm.c', '../../libhivm-db.a'] do |t| 14 | sh "cc #{$cflags} -c #{t.prerequisites.first}" 15 | end 16 | 17 | desc 'Link VM tester executable' 18 | file 'test_vm' => ['test_vm.o'] do |t| 19 | sh "ld #{t.prerequisites.last} #{$ldflags} -o #{t.name}" 20 | end 21 | 22 | desc 'Clean' 23 | task 'clean' do 24 | sh "rm -f test_vm test_vm.o" 25 | end 26 | -------------------------------------------------------------------------------- /test/sandbox/libc.d: -------------------------------------------------------------------------------- 1 | /* http://docs.oracle.com/cd/E18752_01/html/819-5488/gcgkk.html#gcglh */ 2 | 3 | /* pid$target::free: */ 4 | 5 | /* Trace entries into all functions */ 6 | pid$target:::entry 7 | { 8 | @[probefunc] = count(); 9 | } 10 | -------------------------------------------------------------------------------- /test/sandbox/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export DYLD_LIBRARY_PATH=`pwd`/../..:$DYLD_LIBRARY_PATH 3 | ./test_vm 4 | -------------------------------------------------------------------------------- /test/sandbox/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // gcc test.c -g -o test 6 | 7 | void pointer_fiddling() { 8 | uint64_t a; 9 | void* b; 10 | 11 | b = malloc(1); 12 | 13 | printf("b: %p\n", b); 14 | 15 | a = (uint64_t)b; 16 | printf("a: 0x%llx\n", a); 17 | } 18 | 19 | void byte_fiddling() { 20 | uint32_t i = 0x0800; 21 | printf("i: 0x%lx\n", (unsigned long)i); 22 | 23 | void *p = &i; 24 | for(int o = 0; o < 4; o++) { 25 | // These are equivalent. 26 | //unsigned char c = ((unsigned char*)(&i))[o]; 27 | unsigned char c = *((unsigned char*)&i + o); 28 | // Have to cast to unsigned char before offsetting. This will make it go by 29 | // bytes instead of ints (4 bytes). 30 | printf("c[%d]: 0x%hhx\n", o, c); 31 | } 32 | } 33 | 34 | void memcpy_fiddling() { 35 | // Testing a little memcpy 36 | unsigned char c[8]; 37 | 38 | void *a = malloc(sizeof(void)); 39 | memcpy(c, &a, 8); 40 | 41 | printf("a: %p\n", a); 42 | for(int i = 0; i < 8; i++) { 43 | printf("%d: %u\n", i, c[i]); 44 | } 45 | 46 | void *b; 47 | memcpy(&b, c, 8); 48 | printf("b: %p\n", b); 49 | 50 | uint64_t d; 51 | d = (uint64_t)b; 52 | printf("d: 0x%llx\n", d); 53 | printf("d: %lld\n", d); 54 | 55 | // Make compiler treat it as a double without coercion/conversion. 56 | double f = *((double*)&d); 57 | printf("f: %f\n", f); 58 | // Proof that no conversion occurred. 59 | uint64_t g = *((uint64_t*)&f); 60 | printf("g: %lld\n", g); 61 | } 62 | 63 | int main(int argc, char *argv[]) { 64 | // memcpy_fiddling(); 65 | // byte_fiddling(); 66 | pointer_fiddling(); 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /test/sandbox/test_vm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "hvm.h" 8 | #include "hvm_symbol.h" 9 | #include "hvm_object.h" 10 | #include "hvm_chunk.h" 11 | #include "hvm_generator.h" 12 | #include "hvm_bootstrap.h" 13 | #include "hvm_debug.h" 14 | 15 | 16 | 17 | /* 18 | void test_heap() { 19 | hvm_obj_string *str = hvm_new_obj_string(); 20 | str->data = "test"; 21 | hvm_obj_ref *ref = hvm_new_obj_ref(); 22 | hvm_obj_ref_set_string(ref, str); 23 | 24 | hvm_obj_string *str2; 25 | hvm_obj_ref *ref2;; 26 | 27 | hvm_obj_struct *s = hvm_new_obj_struct(); 28 | hvm_obj_ref *sref = hvm_new_obj_ref(); 29 | sref->type = HVM_STRUCTURE; 30 | sref->data.v = s; 31 | 32 | hvm_obj_ref *sym = hvm_new_obj_ref(); 33 | sym->type = HVM_SYMBOL; 34 | sym->data.u64 = 0; 35 | hvm_obj_struct_set(sref, sym, ref); 36 | 37 | ref2 = hvm_obj_struct_get(sref, sym); 38 | str2 = ref2->data.v; 39 | printf("str2->data: %s\n", str2->data); 40 | } 41 | */ 42 | 43 | void test_loop(hvm_gen *gen) { 44 | char i = hvm_vm_reg_gen(1), n = hvm_vm_reg_gen(2), incr = hvm_vm_reg_gen(3), z = hvm_vm_reg_gen(0); 45 | 46 | hvm_gen_litinteger(gen->block, i, 1); 47 | hvm_gen_litinteger(gen->block, n, 10); 48 | hvm_gen_litinteger(gen->block, incr, 1);// Incrementing by one 49 | hvm_gen_label(gen->block, "loop"); 50 | hvm_gen_lt(gen->block, z, n, i); // 0 = n < i 51 | hvm_gen_if_label(gen->block, z, "end"); 52 | hvm_gen_move(gen->block, hvm_vm_reg_arg(0), i); 53 | hvm_gen_set_symbol(gen->block, z, "int_to_string"); 54 | hvm_gen_invokeprimitive(gen->block, z, z); 55 | hvm_gen_move(gen->block, hvm_vm_reg_arg(0), z); 56 | hvm_gen_set_symbol(gen->block, z, "print"); 57 | hvm_gen_invokeprimitive(gen->block, z, hvm_vm_reg_null()); 58 | hvm_gen_set_string(gen->block, hvm_vm_reg_arg(0), "\n"); 59 | hvm_gen_invokeprimitive(gen->block, z, hvm_vm_reg_null()); 60 | hvm_gen_add(gen->block, i, i, incr); 61 | hvm_gen_goto_label(gen->block, "loop"); 62 | hvm_gen_label(gen->block, "end"); 63 | // hvm_gen_die(gen->block); 64 | } 65 | 66 | void test_exception_catch(hvm_gen *gen) { 67 | char z = hvm_vm_reg_gen(0), e = hvm_vm_reg_gen(1); 68 | 69 | hvm_gen_goto_label(gen->block, "main"); 70 | 71 | hvm_gen_sub(gen->block, "thrower"); 72 | hvm_gen_catch_label(gen->block, "catch", hvm_vm_reg_null()); 73 | hvm_gen_set_symbol(gen->block, hvm_vm_reg_gen(2), "missing"); 74 | hvm_gen_getlocal(gen->block, hvm_vm_reg_gen(1), hvm_vm_reg_gen(2)); 75 | hvm_gen_return(gen->block, hvm_vm_reg_null()); 76 | 77 | hvm_gen_label(gen->block, "main"); 78 | hvm_gen_callsymbolic(gen->block, "thrower", hvm_vm_reg_null()); 79 | hvm_gen_die(gen->block); 80 | 81 | // Exception handler 82 | hvm_gen_label(gen->block, "catch"); 83 | hvm_gen_set_string(gen->block, hvm_vm_reg_arg(0), "Caught exception!\n"); 84 | // hvm_gen_set_symbol(gen->block, z, "print"); 85 | // hvm_gen_invokeprimitive(gen->block, z, hvm_vm_reg_null()); 86 | hvm_gen_callprimitive(gen->block, "print", hvm_vm_reg_null()); 87 | hvm_gen_setexception(gen->block, e); 88 | hvm_gen_move(gen->block, hvm_vm_reg_arg(0), e); 89 | // hvm_gen_set_symbol(gen->block, z, "print_exception"); 90 | // hvm_gen_invokeprimitive(gen->block, z, hvm_vm_reg_null()); 91 | hvm_gen_callprimitive(gen->block, "print_exception", hvm_vm_reg_null()); 92 | hvm_gen_clearcatch(gen->block); 93 | hvm_gen_die(gen->block); 94 | } 95 | 96 | void test_closure(hvm_gen *gen) { 97 | char sym = hvm_vm_reg_gen(0), a = hvm_vm_reg_gen(1); 98 | hvm_gen_litinteger(gen->block, a, 1); 99 | hvm_gen_set_symbol(gen->block, sym, "a"); 100 | hvm_gen_setlocal(gen->block, sym, a); 101 | hvm_gen_getclosure(gen->block, a); 102 | 103 | hvm_gen_move(gen->block, hvm_vm_reg_arg(0), a); 104 | hvm_gen_set_symbol(gen->block, sym, "debug_print_struct"); 105 | hvm_gen_invokeprimitive(gen->block, sym, hvm_vm_reg_null()); 106 | 107 | // hvm_gen_die(gen->block); 108 | } 109 | 110 | void test_generator() { 111 | hvm_gen *gen = hvm_new_gen(); 112 | hvm_gen_set_file(gen, "test"); 113 | // hvm_gen_set_symbol(gen->block, hvm_vm_reg_gen(1), "_test"); 114 | // hvm_gen_callsymbolic(gen->block, hvm_vm_reg_gen(1), hvm_vm_reg_gen(2)); 115 | // hvm_gen_die(gen->block); 116 | // hvm_gen_sub(gen->block, "_test"); 117 | // hvm_gen_goto_label(gen->block, "label"); 118 | // hvm_gen_label(gen->block, "label"); 119 | // hvm_gen_litinteger(gen->block, hvm_vm_reg_gen(3), 1); 120 | // hvm_gen_litinteger(gen->block, hvm_vm_reg_gen(4), 2); 121 | // hvm_gen_add(gen->block, hvm_vm_reg_gen(5), hvm_vm_reg_gen(3), hvm_vm_reg_gen(4)); 122 | // hvm_gen_return(gen->block, hvm_vm_reg_gen(5)); 123 | 124 | // byte sym = hvm_vm_reg_gen(0); 125 | 126 | 127 | /* 128 | hvm_gen_set_debug_entry(gen->block, 0, "(main)"); 129 | //hvm_gen_set_symbol(gen->block, sym, "subroutine"); 130 | //hvm_gen_invokesymbolic(gen->block, sym, hvm_vm_reg_null()); 131 | hvm_gen_callsymbolic_symbol(gen->block, "subroutine", hvm_vm_reg_null()); 132 | hvm_gen_callsymbolic_symbol(gen->block, "subroutine", hvm_vm_reg_null()); 133 | hvm_gen_callsymbolic_symbol(gen->block, "subroutine", hvm_vm_reg_null()); 134 | hvm_gen_callsymbolic_symbol(gen->block, "subroutine", hvm_vm_reg_null()); 135 | hvm_gen_die(gen->block); 136 | 137 | hvm_gen_sub(gen->block, "subroutine"); 138 | hvm_gen_set_debug_entry(gen->block, 1, "subroutine"); 139 | hvm_gen_litinteger(gen->block, hvm_vm_reg_gen(1), 1); 140 | // hvm_gen_set_symbol(gen->block, sym, "debug_begin"); 141 | // hvm_gen_invokeprimitive(gen->block, sym, hvm_vm_reg_null()); 142 | hvm_gen_callsymbolic_symbol(gen->block, "inner_subroutine", hvm_vm_reg_null()); 143 | hvm_gen_return(gen->block, hvm_vm_reg_gen(1)); 144 | 145 | hvm_gen_sub(gen->block, "inner_subroutine"); 146 | hvm_gen_set_string(gen->block, hvm_vm_reg_arg(0), "Hello world!\n"); 147 | hvm_gen_set_symbol(gen->block, hvm_vm_reg_gen(0), "print"); 148 | hvm_gen_invokeprimitive(gen->block, hvm_vm_reg_gen(0), hvm_vm_reg_null()); 149 | hvm_gen_return(gen->block, hvm_vm_reg_null()); 150 | */ 151 | 152 | 153 | // OLD TEST 154 | /* 155 | hvm_gen_set_string(gen->block, hvm_vm_reg_arg(0), "Hello world!\n"); 156 | hvm_gen_set_symbol(gen->block, hvm_vm_reg_gen(0), "print"); 157 | hvm_gen_invokeprimitive(gen->block, hvm_vm_reg_gen(0), hvm_vm_reg_null()); 158 | 159 | hvm_gen_set_symbol(gen->block, hvm_vm_reg_gen(0), "exit"); 160 | hvm_gen_invokeprimitive(gen->block, hvm_vm_reg_gen(0), hvm_vm_reg_null()); 161 | // hvm_gen_die(gen->block); 162 | */ 163 | 164 | test_exception_catch(gen); 165 | /* 166 | test_loop(gen); 167 | // test_closure(gen); 168 | 169 | hvm_gen_set_symbol(gen->block, hvm_vm_reg_gen(0), "gc_run"); 170 | hvm_gen_invokeprimitive(gen->block, hvm_vm_reg_gen(0), hvm_vm_reg_null()); 171 | 172 | test_closure(gen); 173 | hvm_gen_set_symbol(gen->block, hvm_vm_reg_gen(0), "gc_run"); 174 | hvm_gen_invokeprimitive(gen->block, hvm_vm_reg_gen(0), hvm_vm_reg_null()); 175 | 176 | hvm_gen_die(gen->block); 177 | */ 178 | 179 | /* 180 | // Hijinks-like code 181 | byte obj, func, sym, arg_sym, string_reg, sym_reg, console; 182 | 183 | hvm_gen_goto_label(gen->block, "defs"); 184 | 185 | hvm_gen_sub(gen->block, "_js_new_object"); 186 | obj = hvm_vm_reg_gen(0); 187 | hvm_gen_structnew(gen->block, obj); 188 | hvm_gen_return(gen->block, obj); 189 | 190 | hvm_gen_sub(gen->block, "_js_new_function"); 191 | func = hvm_vm_reg_gen(0); 192 | sym = hvm_vm_reg_gen(1); 193 | arg_sym = hvm_vm_reg_param(0); 194 | hvm_gen_set_symbol(gen->block, sym, "_js_new_object"); 195 | hvm_gen_callsymbolic(gen->block, sym, func); // Func will be object struct 196 | // Now set the internal symbol 197 | hvm_gen_set_symbol(gen->block, sym, "_js_symbol"); 198 | hvm_gen_structset(gen->block, func, sym, arg_sym); 199 | hvm_gen_return(gen->block, func); 200 | 201 | // Log function 202 | hvm_gen_sub(gen->block, "console.log"); 203 | hvm_gen_set_debug_entry(gen->block, 0, "console.log"); 204 | string_reg = hvm_vm_reg_param(0); 205 | sym_reg = hvm_vm_reg_gen(0); 206 | // Copy string parameter into the argument 207 | hvm_gen_move(gen->block, hvm_vm_reg_arg(0), string_reg); 208 | hvm_gen_set_symbol(gen->block, sym_reg, "print"); 209 | hvm_gen_invokeprimitive(gen->block, sym_reg, hvm_vm_reg_null()); 210 | hvm_gen_return(gen->block, hvm_vm_reg_null()); 211 | 212 | // Building the console object 213 | hvm_gen_label(gen->block, "defs"); 214 | hvm_gen_set_debug_entry(gen->block, 0, "(main)"); 215 | // Creating the function 216 | func = hvm_vm_reg_gen(0); 217 | sym = hvm_vm_reg_gen(1); 218 | hvm_gen_set_symbol(gen->block, sym, "_js_new_function"); 219 | hvm_gen_set_symbol(gen->block, hvm_vm_reg_arg(0), "console.log"); 220 | hvm_gen_callsymbolic(gen->block, sym, func); 221 | // Adding the function to the console object 222 | console = hvm_vm_reg_gen(2); 223 | hvm_gen_set_symbol(gen->block, sym, "_js_new_object"); 224 | hvm_gen_callsymbolic(gen->block, sym, console); // console now an object 225 | // Add function object in func to console 226 | hvm_gen_set_symbol(gen->block, sym, "log"); 227 | hvm_gen_structset(gen->block, console, sym, func); 228 | // Add console to the locals and globals 229 | hvm_gen_set_symbol(gen->block, sym, "console"); 230 | hvm_gen_setlocal(gen->block, sym, console); 231 | hvm_gen_setglobal(gen->block, sym, console); 232 | 233 | hvm_gen_die(gen->block); 234 | */ 235 | 236 | /* 237 | hvm_gen_set_integer(gen->block, 0, 1); 238 | hvm_gen_set_string(gen->block, 1, "test"); 239 | hvm_gen_add(gen->block, 2, 0, 1); 240 | */ 241 | 242 | /* 243 | hvm_gen_goto_label(gen->block, "tail"); 244 | hvm_gen_label(gen->block, "head"); 245 | hvm_gen_set_symbol(gen->block, hvm_vm_reg_gen(0), "print"); 246 | hvm_gen_set_string(gen->block, hvm_vm_reg_arg(0), "test\n"); 247 | // TODO: Currently doesn't throw an exception if symbol not found 248 | // hvm_gen_callsymbolic(gen->block, 0, hvm_vm_reg_null()); 249 | hvm_gen_invokeprimitive(gen->block, hvm_vm_reg_gen(0), hvm_vm_reg_null()); 250 | hvm_gen_die(gen->block); 251 | hvm_gen_label(gen->block, "tail"); 252 | hvm_gen_litinteger_label(gen->block, 1, "head"); 253 | hvm_gen_gotoaddress(gen->block, 1); 254 | hvm_gen_die(gen->block); 255 | */ 256 | 257 | hvm_chunk *chunk = hvm_gen_chunk(gen); 258 | hvm_chunk_disassemble(chunk); 259 | 260 | hvm_vm *vm = hvm_new_vm(); 261 | hvm_bootstrap_primitives(vm); 262 | 263 | printf("LOADING...\n"); 264 | // hvm_vm_load_chunk(vm, chunk); 265 | 266 | // printf("AFTER LOADING:\n"); 267 | // hvm_print_data(vm->program, vm->program_size); 268 | // return; 269 | 270 | //hvm_debug_begin(vm); 271 | 272 | printf("RUNNING...\n"); 273 | hvm_vm_run(vm); 274 | 275 | return; 276 | // printf("\nAFTER RUNNING:\n"); 277 | // hvm_obj_ref *reg = vm->general_regs[hvm_vm_reg_gen(2)]; 278 | // printf("$2->type = %d\n", reg->type); 279 | // assert(reg->type == HVM_INTEGER); 280 | // printf("$2->value = %lld\n", reg->data.i64); 281 | } 282 | 283 | 284 | void test_ref_pool() { 285 | hvm_vm *vm = hvm_new_vm(); 286 | hvm_obj_ref *middle; 287 | int i; 288 | int max = 1000000; 289 | 290 | for(i = 0; i < max; i++) { 291 | hvm_obj_ref *ref = hvm_obj_ref_new_from_pool(vm); 292 | if(i == (max / 2)) { 293 | middle = ref; 294 | } 295 | } 296 | printf("middle = %p\n", middle); 297 | 298 | hvm_obj_ref_free(vm, middle); 299 | } 300 | 301 | 302 | int main(int argc, char **argv) { 303 | // test_generator(); 304 | // test_heap(); 305 | 306 | test_ref_pool(); 307 | 308 | /* 309 | hvm_vm *vm = hvm_new_vm(); 310 | 311 | hvm_obj_string *str = hvm_new_obj_string(); 312 | str->data = "test"; 313 | hvm_obj_ref *ref = hvm_new_obj_ref(); 314 | hvm_obj_ref_set_string(ref, str); 315 | hvm_vm_set_const(vm, 1234567, ref); 316 | 317 | vm->program[0] = HVM_OP_NOOP; 318 | vm->program[1] = HVM_OP_SETSTRING; 319 | vm->program[2] = 1;// register destination 320 | *(uint32_t*)&vm->program[3] = 1234567;// 32-bit integer const index 321 | 322 | vm->program[7] = HVM_OP_NOOP; 323 | vm->program[8] = HVM_OP_DIE; 324 | 325 | hvm_vm_run(vm); 326 | 327 | hvm_obj_ref *reg; 328 | reg = vm->general_regs[1]; 329 | printf("reg: %p\n", reg); 330 | printf("reg->type: %d\n", reg->type); 331 | hvm_obj_string *str2; 332 | str2 = (hvm_obj_string*)(reg->data); 333 | printf("str2->data: %s\n", str2->data); 334 | */ 335 | 336 | /* 337 | hvm_symbol_table *st = new_hvm_symbol_table(); 338 | printf("size = %llu\n", st->size); 339 | uint64_t a, b, c, a2; 340 | a = hvm_symbolicate(st, "a"); 341 | b = hvm_symbolicate(st, "b"); 342 | c = hvm_symbolicate(st, "c"); 343 | a2 = hvm_symbolicate(st, "a"); 344 | printf("a: %llu\n", a); 345 | printf("b: %llu\n", b); 346 | printf("c: %llu\n", c); 347 | printf("a2: %llu\n", a2); 348 | printf("size = %llu\n", st->size); 349 | */ 350 | 351 | // hvm_obj_ref *i = hvm_new_obj_int(); 352 | // i->data.i64 = 10; 353 | // hvm_obj_array *a = hvm_new_obj_array_with_length(i); 354 | // hvm_obj_ref *ar = hvm_new_obj_ref(); 355 | // ar->type = HVM_ARRAY; 356 | // ar->data.v = a; 357 | // 358 | // hvm_obj_array_push(ar, i); 359 | // printf("a->array->len = %d\n", a->array->len); 360 | 361 | 362 | 363 | return 0; 364 | } 365 | -------------------------------------------------------------------------------- /test/sorting/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | $cflags = "-g -Wall -std=c99 -I../../include #{`pkg-config --cflags glib-2.0 lua5.1`.strip}" 3 | $ldflags = "../../libhivm.a -liconv -lz -lcurses #{`pkg-config --libs glib-2.0 lua5.1`.strip} -dead_strip" 4 | 5 | task 'default' => ['test_sorting'] 6 | 7 | desc 'Build VM tester object' 8 | file 'test_sorting.o' => ['test_sorting.c', '../../libhivm.a'] do 9 | sh "clang #{$cflags} -c test_sorting.c" 10 | end 11 | 12 | desc 'Build VM tester executable' 13 | file 'test_sorting' => ['test_sorting.o'] do |t| 14 | sh "clang++ #{t.prerequisites.first} #{$ldflags} -o #{t.name}" 15 | end 16 | 17 | desc 'Clean' 18 | task 'clean' do 19 | sh 'rm -f test_sorting' 20 | end 21 | -------------------------------------------------------------------------------- /test/sorting/test_sorting.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "hvm.h" 9 | #include "hvm_symbol.h" 10 | #include "hvm_object.h" 11 | #include "hvm_chunk.h" 12 | #include "hvm_generator.h" 13 | #include "hvm_bootstrap.h" 14 | #include "hvm_debug.h" 15 | 16 | static char *array = "array"; 17 | 18 | void build_array(hvm_gen *gen, unsigned int size) { 19 | srand(2014); 20 | // Array register 21 | byte ar = hvm_vm_reg_gen(1); 22 | byte r2 = hvm_vm_reg_gen(2); 23 | hvm_gen_litinteger(gen->block, r2, (int64_t)size); 24 | // Create array in $ar with size $r2. 25 | hvm_gen_arraynew(gen->block, ar, r2); 26 | 27 | // `rand` symbol to invoke the rand primitive for build the array 28 | byte sym = hvm_vm_reg_gen(0); 29 | hvm_gen_set_symbol(gen->block, sym, "rand"); 30 | 31 | // Initial state for our loop 32 | byte idx = hvm_vm_reg_gen(2); 33 | byte last_idx = hvm_vm_reg_gen(3); 34 | byte r4 = hvm_vm_reg_gen(4); 35 | byte increment = hvm_vm_reg_gen(5); 36 | hvm_gen_litinteger(gen->block, increment, 1); 37 | hvm_gen_litinteger(gen->block, idx, 0); 38 | hvm_gen_litinteger(gen->block, last_idx, (int64_t)size); 39 | // Loop condition (A = B < C) 40 | hvm_gen_label(gen->block, "build_array_condition"); 41 | hvm_gen_eq(gen->block, r4, idx, last_idx);// $r4 = ($idx == $len) 42 | hvm_gen_if_label(gen->block, r4, "build_array_end"); 43 | // Body of the loop 44 | // Set the array 45 | hvm_gen_invokeprimitive(gen->block, sym, r4);// rand() -> $r4 46 | hvm_gen_arrayset(gen->block, ar, idx, r4);// $ar[$idx] = $r4 47 | // hvm_gen_arraypush(gen->block, ar, r4);// $ar << $r4 48 | // Increment the index 49 | hvm_gen_add(gen->block, idx, idx, increment); 50 | hvm_gen_goto_label(gen->block, "build_array_condition"); 51 | // End 52 | hvm_gen_label(gen->block, "build_array_end"); 53 | hvm_gen_set_symbol(gen->block, sym, array); 54 | // local:array = $ar 55 | hvm_gen_setlocal(gen->block, sym, ar); 56 | } 57 | 58 | #define ENTRY(L, N) hvm_gen_set_debug_entry(gen->block, L, N); 59 | 60 | void define_insertion_sort(hvm_gen *gen) { 61 | hvm_gen_sub(gen->block, "insertion_sort"); 62 | 63 | byte sym = hvm_vm_reg_gen(0); 64 | byte i = hvm_vm_reg_gen(1); 65 | byte j = hvm_vm_reg_gen(2); 66 | byte x = hvm_vm_reg_gen(3); 67 | byte len = hvm_vm_reg_gen(4); 68 | byte r4 = hvm_vm_reg_gen(5); 69 | byte r5 = hvm_vm_reg_gen(6); 70 | byte arr = hvm_vm_reg_gen(7); 71 | byte scratch = hvm_vm_reg_gen(8); 72 | 73 | byte a_jminus1 = hvm_vm_reg_gen(11); 74 | byte a_j = hvm_vm_reg_gen(12); 75 | 76 | byte zero = hvm_vm_reg_gen(20); 77 | hvm_gen_litinteger(gen->block, zero, 0); 78 | byte neg1 = hvm_vm_reg_gen(21); 79 | hvm_gen_litinteger(gen->block, neg1, -1); 80 | byte one = hvm_vm_reg_gen(22); 81 | hvm_gen_litinteger(gen->block, one, 1); 82 | 83 | // byte i_as_string = hvm_vm_reg_gen(23); 84 | 85 | // hvm_gen_move(gen->block, arr, hvm_vm_reg_param(0)); 86 | // Esoteric get/setlocal testing 87 | // Put the param in local:array 88 | hvm_gen_move(gen->block, scratch, hvm_vm_reg_param(0)); 89 | hvm_gen_set_symbol(gen->block, sym, "array"); 90 | hvm_gen_setlocal(gen->block, sym, scratch); 91 | // Then fetch it out into $arr 92 | hvm_gen_getlocal(gen->block, arr, sym); 93 | 94 | ENTRY(1, "$i = 1"); 95 | hvm_gen_litinteger(gen->block, i, 1); 96 | // $len = arraylen($arr) 97 | hvm_gen_arraylen(gen->block, len, arr); 98 | //hvm_gen_add(gen->block, len, len, neg1); 99 | hvm_gen_label(gen->block, "insertion_sort_condition"); 100 | ENTRY(1, "$r4 = ($i == $len)"); 101 | hvm_gen_eq(gen->block, r4, i, len); 102 | hvm_gen_if_label(gen->block, r4, "insertion_sort_end"); 103 | // Loop body 104 | ENTRY(1, "$x = $arr[$i]"); 105 | hvm_gen_arrayget(gen->block, x, arr, i); 106 | 107 | // Printing $i as string 108 | // hvm_gen_set_symbol(gen->block, sym, "int_to_string"); 109 | // hvm_gen_move(gen->block, hvm_vm_reg_arg(0), i); 110 | // hvm_gen_invokeprimitive(gen->block, sym, i_as_string); 111 | // hvm_gen_move(gen->block, hvm_vm_reg_arg(0), i_as_string); 112 | // hvm_gen_set_symbol(gen->block, sym, "print"); 113 | // hvm_gen_invokeprimitive(gen->block, sym, hvm_vm_reg_null()); 114 | // hvm_gen_set_string(gen->block, hvm_vm_reg_arg(0), "\n"); 115 | // hvm_gen_invokeprimitive(gen->block, sym, hvm_vm_reg_null()); 116 | 117 | // $j = $i 118 | hvm_gen_move(gen->block, j, i); 119 | // while $j > 0 and A[j-1] > A[j] 120 | hvm_gen_label(gen->block, "insertion_sort_inner_condition"); 121 | ENTRY(1, "$r4 = $j > 0"); 122 | hvm_gen_gt(gen->block, r4, j, zero); 123 | // Checking the left side of the AND 124 | hvm_gen_eq(gen->block, r5, r4, zero); 125 | hvm_gen_if_label(gen->block, r5, "insertion_sort_inner_end"); 126 | ENTRY(1, "$a_jminus1 = $arr[j - 1]"); 127 | hvm_gen_add(gen->block, r5, j, neg1); 128 | hvm_gen_arrayget(gen->block, a_jminus1, arr, r5); 129 | ENTRY(1, "$a_j = $arr[j]"); 130 | hvm_gen_arrayget(gen->block, a_j, arr, j); 131 | ENTRY(1, "$r5 = $a_jminus1 > $a_j"); 132 | hvm_gen_gt(gen->block, r5, a_jminus1, x); 133 | ENTRY(1, "$r4 = $r4 AND $r5"); 134 | hvm_gen_and(gen->block, r4, r4, r5); 135 | hvm_gen_if_label(gen->block, r4, "insertion_sort_inner_body"); 136 | hvm_gen_goto_label(gen->block, "insertion_sort_inner_end"); 137 | // Inner while body 138 | hvm_gen_label(gen->block, "insertion_sort_inner_body"); 139 | ENTRY(1, "$arr[$j] = $a_jminus1"); 140 | hvm_gen_arrayset(gen->block, arr, j, a_jminus1); 141 | ENTRY(1, "$j = $j - 1"); 142 | hvm_gen_add(gen->block, j, j, neg1); 143 | hvm_gen_goto_label(gen->block, "insertion_sort_inner_condition"); 144 | // Left inner while 145 | hvm_gen_label(gen->block, "insertion_sort_inner_end"); 146 | ENTRY(1, "$arr[$j] = $x"); 147 | hvm_gen_arrayset(gen->block, arr, j, x); 148 | ENTRY(1, "$i = $i + 1"); 149 | hvm_gen_add(gen->block, i, i, one); 150 | hvm_gen_goto_label(gen->block, "insertion_sort_condition"); 151 | // Left for loop 152 | hvm_gen_label(gen->block, "insertion_sort_end"); 153 | 154 | // Print our current trace for debugging 155 | // hvm_gen_set_symbol(gen->block, sym, "debug_print_current_frame_trace"); 156 | // hvm_gen_invokeprimitive(gen->block, sym, hvm_vm_reg_null()); 157 | 158 | // Return the array 159 | hvm_gen_return(gen->block, arr); 160 | } 161 | 162 | int main(int argc, char **argv) { 163 | static const unsigned int array_size = 250; 164 | static const int64_t runs = 4; 165 | 166 | hvm_gen *gen = hvm_new_gen(); 167 | hvm_gen_set_file(gen, "sorting"); 168 | 169 | // Add the sequence to build the big array 170 | build_array(gen, array_size); 171 | hvm_gen_goto_label(gen->block, "program"); 172 | // Then insert our insertion sort subroutine 173 | define_insertion_sort(gen); 174 | 175 | // Main program 176 | hvm_gen_label(gen->block, "program"); 177 | byte sym = hvm_vm_reg_gen(0); 178 | byte ret = hvm_vm_reg_gen(1); 179 | byte local_array = hvm_vm_reg_gen(2); 180 | byte array_copy = hvm_vm_reg_gen(3); 181 | byte timing = hvm_vm_reg_gen(4); 182 | byte timings_array = hvm_vm_reg_gen(104); 183 | byte results_array = hvm_vm_reg_gen(105); 184 | 185 | hvm_gen_arraynew(gen->block, timings_array, hvm_vm_reg_null()); 186 | hvm_gen_arraynew(gen->block, results_array, hvm_vm_reg_null()); 187 | // hvm_gen_set_symbol(gen->block, sym, "timings"); 188 | // hvm_gen_setlocal(gen->block, sym, timings_array); 189 | 190 | // Do the loop to invoke insertion sort a few times 191 | byte idx = hvm_vm_reg_gen(100); 192 | byte lim = hvm_vm_reg_gen(101); 193 | byte cond = hvm_vm_reg_gen(102); 194 | byte i = hvm_vm_reg_gen(103); 195 | hvm_gen_litinteger(gen->block, idx, 0); 196 | hvm_gen_litinteger(gen->block, lim, runs); 197 | hvm_gen_label(gen->block, "loop_condition"); 198 | // $cond = $idx == $lim 199 | hvm_gen_eq(gen->block, cond, idx, lim); 200 | hvm_gen_if_label(gen->block, cond, "loop_end"); 201 | // Body of the loop 202 | // $local_array = local:array 203 | hvm_gen_set_symbol(gen->block, sym, array); 204 | hvm_gen_getlocal(gen->block, local_array, sym); 205 | // arg:0 = $local_array 206 | hvm_gen_move(gen->block, hvm_vm_reg_arg(0), local_array); 207 | // $array_copy = prim:array_clone($local_array) 208 | hvm_gen_set_symbol(gen->block, sym, "array_clone"); 209 | hvm_gen_invokeprimitive(gen->block, sym, array_copy); 210 | // arraypush $timings_array (time_as_int()) 211 | hvm_gen_set_symbol(gen->block, sym, "time_as_int"); 212 | hvm_gen_invokeprimitive(gen->block, sym, timing); 213 | hvm_gen_arraypush(gen->block, timings_array, timing); 214 | // arg:0 = $array_copy 215 | hvm_gen_move(gen->block, hvm_vm_reg_arg(0), array_copy); 216 | // $ret = #insertion_sort() 217 | hvm_gen_callsymbolic(gen->block, "insertion_sort", ret); 218 | // arraypush $timings_array (time_as_int()) 219 | hvm_gen_set_symbol(gen->block, sym, "time_as_int"); 220 | hvm_gen_invokeprimitive(gen->block, sym, timing); 221 | hvm_gen_arraypush(gen->block, timings_array, timing); 222 | // arraypush $results_array $ret 223 | hvm_gen_arraypush(gen->block, results_array, ret); 224 | // $idx = $idx + 1 225 | hvm_gen_litinteger(gen->block, i, 1); 226 | hvm_gen_add(gen->block, idx, idx, i); 227 | hvm_gen_goto_label(gen->block, "loop_condition"); 228 | // End of loop 229 | hvm_gen_label(gen->block, "loop_end"); 230 | hvm_gen_die(gen->block); 231 | 232 | hvm_chunk *chunk = hvm_gen_chunk(gen); 233 | // hvm_chunk_disassemble(chunk); 234 | // return 0; 235 | 236 | hvm_vm *vm = hvm_new_vm(); 237 | hvm_bootstrap_primitives(vm); 238 | // Set the VM to *always* trace 239 | // vm->always_trace = TRUE; 240 | 241 | printf("LOADING...\n"); 242 | hvm_vm_load_chunk(vm, chunk); 243 | 244 | // printf("AFTER LOADING:\n"); 245 | // hvm_print_data(vm->program, vm->program_size); 246 | 247 | // hvm_debug_begin(vm); 248 | 249 | // char buff[256]; 250 | // printf("Press ENTER to continue...\n"); 251 | // fgets(buff, 256, stdin); 252 | 253 | printf("RUNNING...\n"); 254 | hvm_vm_run(vm); 255 | 256 | printf("\nDONE\n\n"); 257 | 258 | // hvm_obj_ref *arrref = hvm_get_local(vm->top, hvm_symbolicate(vm->symbols, array)); 259 | 260 | // hvm_obj_ref *arrref = hvm_vm_register_read(vm, timings_array); 261 | // hvm_obj_ref *arrref = hvm_vm_register_read(vm, local_array); 262 | // assert(arrref->type == HVM_ARRAY); 263 | // hvm_obj_array *arr = arrref->data.v; 264 | 265 | /* 266 | printf("timings_array = [%u]{\n", arr->array->len); 267 | for(unsigned int i = 0; i < arr->array->len; i++) { 268 | hvm_obj_ref *intval = g_array_index(arr->array, hvm_obj_ref*, i); 269 | assert(intval->type == HVM_INTEGER); 270 | printf(" %4u = %lld\n", i, intval->data.i64); 271 | } 272 | printf("}\n\n"); 273 | 274 | printf("computed timings = {\n"); 275 | for(unsigned int i = 0; i < arr->array->len; i += 2) { 276 | hvm_obj_ref *start = g_array_index(arr->array, hvm_obj_ref*, i); 277 | hvm_obj_ref *end = g_array_index(arr->array, hvm_obj_ref*, i + 1); 278 | 279 | int64_t st = start->data.i64; 280 | int64_t et = end->data.i64; 281 | unsigned int run = ((i == 0 ? 1 : i) / 2) + 1; 282 | 283 | printf(" run[%d] = %lld microseconds\n", run, et - st); 284 | } 285 | printf("}\n"); 286 | */ 287 | 288 | /* 289 | for(unsigned int i = 0; i < arr->array->len; i += 1) { 290 | hvm_obj_ref *resultref = g_array_index(arr->array, hvm_obj_ref*, i); 291 | hvm_obj_array *result = resultref->data.v; 292 | 293 | printf("result[%d] =\n", i); 294 | for(unsigned int x = 0; x < result->array->len; x += 1) { 295 | hvm_obj_ref *intref = g_array_index(result->array, hvm_obj_ref*, x); 296 | int64_t _i = intref->data.i64; 297 | printf(" [%4d] = %lld\n", x, _i); 298 | } 299 | } 300 | */ 301 | 302 | /* 303 | printf("local_array[%d] =\n", arr->array->len); 304 | for(unsigned int x = 0; x < arr->array->len; x += 1) { 305 | hvm_obj_ref *intref = g_array_index(arr->array, hvm_obj_ref*, x); 306 | int64_t _i = intref->data.i64; 307 | printf(" [%4d] = %lld\n", x, _i); 308 | } 309 | */ 310 | 311 | // printf("\nAFTER RUNNING:\n"); 312 | // hvm_obj_ref *reg = vm->general_regs[hvm_vm_reg_gen(2)]; 313 | // printf("$2->type = %d\n", reg->type); 314 | // assert(reg->type == HVM_INTEGER); 315 | // printf("$2->value = %lld\n", reg->data.i64); 316 | 317 | return 0; 318 | } 319 | -------------------------------------------------------------------------------- /test/unit-old/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | $cflags = "-g -Wall -std=c99 ../../libhivm.a -I../../include -liconv #{`pkg-config --libs glib-2.0`.strip}" 3 | 4 | task 'default' => ['test'] 5 | 6 | desc "Build VM tester" 7 | file 'test' => ['test.c'] do |t| 8 | sh "cc test.c #{$cflags} -o #{t.name}" 9 | end 10 | 11 | desc 'Clean' 12 | task 'clean' do 13 | sh "rm -f test" 14 | end 15 | -------------------------------------------------------------------------------- /test/unit-old/test.c: -------------------------------------------------------------------------------- 1 | #include "../../deps/greatest.h" 2 | 3 | #include 4 | 5 | #include "hvm.h" 6 | #include "hvm_symbol.h" 7 | #include "hvm_object.h" 8 | #include "hvm_chunk.h" 9 | #include "hvm_generator.h" 10 | #include "hvm_bootstrap.h" 11 | 12 | hvm_vm *run_chunk(hvm_chunk *chunk) { 13 | hvm_vm *vm = hvm_new_vm(); 14 | hvm_bootstrap_primitives(vm); 15 | hvm_vm_load_chunk(vm, chunk); 16 | hvm_vm_run(vm); 17 | return vm; 18 | } 19 | hvm_vm *gen_chunk_and_run(hvm_gen *gen) { 20 | hvm_chunk *chunk = hvm_gen_chunk(gen); 21 | hvm_vm *vm = run_chunk(chunk); 22 | return vm; 23 | } 24 | 25 | TEST int_comparison_test() { 26 | hvm_gen *gen = hvm_new_gen(); 27 | char lt_reg = 0, gt_reg = 1, lte_reg = 2, gte_reg = 3; 28 | char b = 10, c = 11; 29 | hvm_obj_ref *obj; 30 | 31 | hvm_gen_litinteger(gen->block, hvm_vm_reg_gen(b), 1); 32 | hvm_gen_litinteger(gen->block, hvm_vm_reg_gen(c), 2); 33 | // Less than 34 | hvm_gen_lt(gen->block, hvm_vm_reg_gen(lt_reg), hvm_vm_reg_gen(b), hvm_vm_reg_gen(c)); 35 | // Greater than 36 | hvm_gen_gt(gen->block, hvm_vm_reg_gen(gt_reg), hvm_vm_reg_gen(c), hvm_vm_reg_gen(b)); 37 | // Less than or equal 38 | hvm_gen_lte(gen->block, hvm_vm_reg_gen(lte_reg), hvm_vm_reg_gen(b), hvm_vm_reg_gen(c)); 39 | // Greater than or equal 40 | hvm_gen_gte(gen->block, hvm_vm_reg_gen(gte_reg), hvm_vm_reg_gen(c), hvm_vm_reg_gen(b)); 41 | hvm_gen_die(gen->block); 42 | 43 | hvm_vm *vm = gen_chunk_and_run(gen); 44 | 45 | // Assertions 46 | obj = vm->general_regs[hvm_vm_reg_gen(lt_reg)]; 47 | ASSERT_EQ(obj->data.i64, 1); 48 | obj = vm->general_regs[hvm_vm_reg_gen(gt_reg)]; 49 | ASSERT_EQ(obj->data.i64, 1); 50 | obj = vm->general_regs[hvm_vm_reg_gen(lte_reg)]; 51 | ASSERT_EQ(obj->data.i64, 1); 52 | obj = vm->general_regs[hvm_vm_reg_gen(gte_reg)]; 53 | ASSERT_EQ(obj->data.i64, 1); 54 | 55 | PASS(); 56 | } 57 | 58 | SUITE(test_suite) { 59 | RUN_TEST(int_comparison_test); 60 | } 61 | 62 | GREATEST_MAIN_DEFS(); 63 | int main(int argc, char *argv[]) { 64 | GREATEST_MAIN_BEGIN(); 65 | RUN_SUITE(test_suite); 66 | GREATEST_MAIN_END(); 67 | return 0; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /test/unit/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | libhivm = '../../libhivm-db.a' 3 | 4 | $cflags = "-g -Wall -std=c99 -I../../include" 5 | 6 | packageLibs = `pkg-config --libs glib-2.0 lua5.1`.strip 7 | $ldflags = "#{libhivm} -liconv -lz -lcurses #{packageLibs} -dead_strip" 8 | 9 | test_bins = [] 10 | 11 | tests = Dir['test_*.c'] 12 | tests.each do |src| 13 | bin = src.sub '.c', '' 14 | obj = src.sub '.c', '.o' 15 | # Add it to the list of generated test binaries 16 | test_bins << bin 17 | 18 | desc "Build #{bin} test binary" 19 | file bin => [src, 'preamble.h', libhivm] do |t| 20 | sh "clang -c #{src} #{$cflags}" 21 | sh "clang++ #{obj} #{$ldflags} -o #{bin}" 22 | end 23 | end 24 | 25 | desc 'Build all tests' 26 | task 'build' => test_bins 27 | 28 | require 'open3' 29 | 30 | desc 'Run all tests' 31 | task 'default' => ['build'] do |t| 32 | passed = [] 33 | failed = [] 34 | total = 0 35 | test_bins.each do |bin| 36 | Open3.popen3("./#{bin}") {|stdin, stdout, stderr, wait| 37 | stdout.each_char {|c| 38 | # Skip newlines 39 | next if c == "\n" 40 | print c 41 | } 42 | # TODO: Collect errors from stderr via `stderr.each_line` 43 | status = wait.value 44 | if status.exitstatus == 0 45 | passed << bin 46 | else 47 | failed << bin 48 | end 49 | total += 1 50 | } 51 | end 52 | puts "\n\n" 53 | # Print summary 54 | puts "Total: #{total.to_s} tests" 55 | puts "Pass: #{passed.length}, fail: #{failed.length}." 56 | end 57 | 58 | desc 'Clean' 59 | task 'clean' do 60 | sh "rm -f #{test_bins.join ' '}" 61 | end 62 | -------------------------------------------------------------------------------- /test/unit/preamble.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "hvm.h" 7 | #include "hvm_symbol.h" 8 | #include "hvm_object.h" 9 | #include "hvm_chunk.h" 10 | #include "hvm_generator.h" 11 | #include "hvm_bootstrap.h" 12 | 13 | // Each file is an independent set of assertions. The preamble provides the 14 | // functionality required for each test in a suite. 15 | 16 | // Assertion reports ("." for pass, "x" for failure) are written to STDOUT and 17 | // failure messages are written to STDERR with a trailing new line separating 18 | // them. 19 | 20 | #ifndef MAX_ASSERTION_ERRS 21 | #define MAX_ASSERTION_ERRS 100 22 | #endif 23 | 24 | int _assertion_errc = 0; 25 | const char *_assertion_errv[MAX_ASSERTION_ERRS]; 26 | 27 | void assert_true(bool val, const char *msg) { 28 | int i = _assertion_errc; 29 | if(!val) { 30 | _assertion_errv[i] = msg; 31 | _assertion_errc += 1; 32 | fputs("x", stdout); 33 | fprintf(stderr, "%s\n", msg); 34 | } else { 35 | fputs(".", stdout); 36 | } 37 | } 38 | 39 | int done() { 40 | fputs("\n", stdout); 41 | return (_assertion_errc > 0) ? 1 : 0; 42 | } 43 | 44 | 45 | // Utilities ------------------------------------------------------------------ 46 | 47 | hvm_vm *run_chunk(hvm_chunk *chunk) { 48 | hvm_vm *vm = hvm_new_vm(); 49 | hvm_bootstrap_primitives(vm); 50 | hvm_vm_load_chunk(vm, chunk); 51 | hvm_vm_run(vm); 52 | return vm; 53 | } 54 | hvm_vm *gen_chunk_and_run(hvm_gen *gen) { 55 | hvm_chunk *chunk = hvm_gen_chunk(gen); 56 | hvm_vm *vm = run_chunk(chunk); 57 | return vm; 58 | } 59 | -------------------------------------------------------------------------------- /test/unit/test_array.c: -------------------------------------------------------------------------------- 1 | #include "preamble.h" 2 | 3 | int main(int argc, char const *argv[]) { 4 | hvm_gen *gen = hvm_new_gen(); 5 | 6 | int64_t test_value = 42; 7 | 8 | byte reg0 = hvm_vm_reg_gen(0); 9 | byte reg_empty_array = hvm_vm_reg_gen(1); 10 | byte reg_key = hvm_vm_reg_gen(2); 11 | byte reg_value_to_set = hvm_vm_reg_gen(3); 12 | byte reg_value_retrieved = hvm_vm_reg_gen(4); 13 | byte reg_scratch_array = hvm_vm_reg_gen(5); 14 | byte reg_index = hvm_vm_reg_gen(6); 15 | hvm_obj_ref *obj; 16 | 17 | // Creating an empty array works 18 | hvm_gen_arraynew(gen->block, reg_empty_array, hvm_vm_reg_null()); 19 | 20 | // Creating a 1-item array for get-set tests 21 | hvm_gen_litinteger(gen->block, reg0, 1); 22 | hvm_gen_arraynew(gen->block, reg_scratch_array, reg0); 23 | // Set the value 24 | hvm_gen_litinteger(gen->block, reg_value_to_set, test_value); 25 | hvm_gen_litinteger(gen->block, reg_index, 0); 26 | hvm_gen_arrayset(gen->block, reg_scratch_array, reg_index, reg_value_to_set); 27 | // Get the value into the _retrieved register 28 | hvm_gen_arrayget(gen->block, reg_value_retrieved, reg_scratch_array, reg_index); 29 | 30 | hvm_gen_die(gen->block); 31 | 32 | hvm_vm *vm = gen_chunk_and_run(gen); 33 | 34 | return done(); 35 | } 36 | -------------------------------------------------------------------------------- /test/unit/test_call.c: -------------------------------------------------------------------------------- 1 | #include "preamble.h" 2 | 3 | int main(int argc, char const *argv[]) { 4 | hvm_gen *gen = hvm_new_gen(); 5 | 6 | int64_t val_callsymbolic = 1; 7 | int64_t val_call = 2; 8 | 9 | byte reg0 = hvm_vm_reg_gen(0); 10 | byte reg1 = hvm_vm_reg_gen(1); 11 | byte reg2 = hvm_vm_reg_gen(2); 12 | byte reg3 = hvm_vm_reg_gen(3); 13 | byte reg4 = hvm_vm_reg_gen(4); 14 | hvm_obj_ref *obj; 15 | 16 | hvm_gen_goto_label(gen->block, "main"); 17 | 18 | // For testing CALLSYMBOLIC (call using a symbol to be looked up in the 19 | // subroutine map) 20 | hvm_gen_sub(gen->block, "callsymbolic"); 21 | hvm_gen_litinteger(gen->block, reg0, val_callsymbolic); 22 | hvm_gen_return(gen->block, reg0); 23 | 24 | // For testing CALL with a hard-coded (but also relocated address) 25 | hvm_gen_sub(gen->block, "call"); 26 | hvm_gen_litinteger(gen->block, reg0, val_call); 27 | hvm_gen_return(gen->block, reg0); 28 | 29 | hvm_gen_label(gen->block, "main"); 30 | hvm_gen_callsymbolic(gen->block, "callsymbolic", reg1); 31 | hvm_gen_call_label(gen->block, "call", reg2); 32 | // Let's also do a basic test of CALLPRIMITIVE by creating an array in reg3 33 | // and cloning it into reg4 34 | hvm_gen_arraynew(gen->block, reg3, hvm_vm_reg_null()); 35 | hvm_gen_move(gen->block, hvm_vm_reg_arg(0), reg3); 36 | hvm_gen_callprimitive(gen->block, "array_clone", reg4); 37 | hvm_gen_die(gen->block); 38 | 39 | hvm_vm *vm = gen_chunk_and_run(gen); 40 | 41 | // Assertions 42 | obj = vm->general_regs[reg1]; 43 | assert_true(obj->data.i64 == val_callsymbolic, "Expected CALLSYMBOLIC return to be 1"); 44 | obj = vm->general_regs[reg2]; 45 | assert_true(obj->data.i64 == val_call, "Expected CALL return to be 2"); 46 | // Check that the array_clone'd array in reg4 is different from the one 47 | // in reg3 48 | hvm_obj_ref *arr3 = vm->general_regs[reg3]; 49 | hvm_obj_ref *arr4 = vm->general_regs[reg4]; 50 | assert_true(arr4->type == HVM_ARRAY, "Expected array_clone to return an array"); 51 | assert_true(arr3->data.v != arr4->data.v, "Expected array_clone to return a new array"); 52 | 53 | return done(); 54 | } 55 | -------------------------------------------------------------------------------- /test/unit/test_exception.c: -------------------------------------------------------------------------------- 1 | #include "preamble.h" 2 | 3 | int main(int argc, char const *argv[]) { 4 | hvm_gen *gen = hvm_new_gen(); 5 | hvm_obj_ref *obj0 = NULL; 6 | hvm_obj_ref *obj1 = NULL; 7 | 8 | byte reg0 = hvm_vm_reg_gen(0); 9 | byte reg1 = hvm_vm_reg_gen(1); 10 | 11 | // Immediately jump to the code that's going to throw the exception 12 | hvm_gen_goto_label(gen->block, "main"); 13 | 14 | // Label for the code to actually catch the exception 15 | hvm_gen_label(gen->block, "catch"); 16 | // Do a straight die so the test harness can check the register and make 17 | // sure there's an exception object in it 18 | hvm_gen_die(gen->block); 19 | 20 | hvm_gen_label(gen->block, "main"); 21 | // Register our exception handler for the frame 22 | hvm_gen_catch_label(gen->block, "catch", reg0); 23 | hvm_gen_structnew(gen->block, reg1); 24 | hvm_gen_throw(gen->block, reg1); 25 | hvm_gen_die(gen->block); 26 | 27 | hvm_vm *vm = gen_chunk_and_run(gen); 28 | 29 | obj0 = vm->general_regs[0]; 30 | obj1 = vm->general_regs[1]; 31 | // Make sure the object thrown and the object in the catch register are 32 | // the same 33 | assert_true(obj0 == obj1, "Expected thrown object and caught object to be the same"); 34 | 35 | return done(); 36 | } 37 | -------------------------------------------------------------------------------- /test/unit/test_int_comparison.c: -------------------------------------------------------------------------------- 1 | #include "preamble.h" 2 | 3 | int main(int argc, char const *argv[]) { 4 | hvm_gen *gen = hvm_new_gen(); 5 | char lt_reg = 0, gt_reg = 1, lte_reg = 2, gte_reg = 3; 6 | char b = 10, c = 11; 7 | hvm_obj_ref *obj; 8 | 9 | hvm_gen_litinteger(gen->block, hvm_vm_reg_gen(b), 1); 10 | hvm_gen_litinteger(gen->block, hvm_vm_reg_gen(c), 2); 11 | // Less than 12 | hvm_gen_lt(gen->block, hvm_vm_reg_gen(lt_reg), hvm_vm_reg_gen(b), hvm_vm_reg_gen(c)); 13 | // Greater than 14 | hvm_gen_gt(gen->block, hvm_vm_reg_gen(gt_reg), hvm_vm_reg_gen(c), hvm_vm_reg_gen(b)); 15 | // Less than or equal 16 | hvm_gen_lte(gen->block, hvm_vm_reg_gen(lte_reg), hvm_vm_reg_gen(b), hvm_vm_reg_gen(c)); 17 | // Greater than or equal 18 | hvm_gen_gte(gen->block, hvm_vm_reg_gen(gte_reg), hvm_vm_reg_gen(c), hvm_vm_reg_gen(b)); 19 | hvm_gen_die(gen->block); 20 | 21 | hvm_vm *vm = gen_chunk_and_run(gen); 22 | 23 | // Assertions 24 | obj = vm->general_regs[hvm_vm_reg_gen(lt_reg)]; 25 | assert_true(obj->data.i64 == 1, "Less-than test register should be 1"); 26 | obj = vm->general_regs[hvm_vm_reg_gen(gt_reg)]; 27 | assert_true(obj->data.i64 == 1, "Greater-than test register should be 1"); 28 | obj = vm->general_regs[hvm_vm_reg_gen(lte_reg)]; 29 | assert_true(obj->data.i64 == 1, "Less-than-or-equal test register should be 1"); 30 | obj = vm->general_regs[hvm_vm_reg_gen(gte_reg)]; 31 | assert_true(obj->data.i64 == 1, "Greater-than-or-equal test register should be 1"); 32 | 33 | return done(); 34 | } 35 | -------------------------------------------------------------------------------- /test/unit/test_invokeaddress.c: -------------------------------------------------------------------------------- 1 | #include "preamble.h" 2 | 3 | int main(int argc, char const *argv[]) { 4 | hvm_gen *gen = hvm_new_gen(); 5 | 6 | int64_t val = 1; 7 | byte reg0 = hvm_vm_reg_gen(0); 8 | byte reg1 = hvm_vm_reg_gen(1); 9 | hvm_obj_ref *obj0, *obj1; 10 | char *invoke_label = "invoke"; 11 | char *main_label = "main"; 12 | 13 | hvm_gen_goto_label(gen->block, main_label); 14 | 15 | // Setup the invoke subroutine 16 | hvm_gen_sub(gen->block, invoke_label); 17 | hvm_gen_litinteger(gen->block, reg0, val); 18 | hvm_gen_return(gen->block, reg0); 19 | 20 | hvm_gen_label(gen->block, main_label); 21 | // Save the address for the invoke label in a register for use 22 | hvm_gen_litinteger_label(gen->block, reg1, invoke_label); 23 | // Call INVOKEADDRESS with result in reg0 24 | hvm_gen_invokeaddress(gen->block, reg1, reg0); 25 | hvm_gen_die(gen->block); 26 | 27 | hvm_vm *vm = gen_chunk_and_run(gen); 28 | 29 | // Assertions 30 | 31 | // Make sure that an okay-looking address value was set in reg1 32 | obj1 = vm->general_regs[reg1]; 33 | int64_t addr = obj1->data.i64; 34 | assert_true(obj1->type == HVM_INTEGER, "Expected integer in register 1"); 35 | assert_true(addr > 0 && addr < 32, "Expected reasonable value in register 1"); 36 | 37 | // Make sure that the invocation returned the expected value (in reg0) 38 | obj0 = vm->general_regs[reg0]; 39 | assert_true(obj0->type == HVM_INTEGER, "Expected integer in register 0"); 40 | assert_true(obj0->data.i64 == val, "Expected register 0 to contain value 1"); 41 | 42 | return done(); 43 | } 44 | -------------------------------------------------------------------------------- /test/unit/test_loop.c: -------------------------------------------------------------------------------- 1 | #include "preamble.h" 2 | 3 | int main(int argc, char const *argv[]) { 4 | hvm_gen *gen = hvm_new_gen(); 5 | 6 | int64_t val_start = 0; 7 | int64_t val_iterations = 5; 8 | int64_t val_incr = 2; 9 | 10 | byte reg0 = hvm_vm_reg_gen(0); 11 | byte reg_ctr = hvm_vm_reg_gen(1); 12 | byte reg_max = hvm_vm_reg_gen(2); 13 | byte reg_acc = hvm_vm_reg_gen(3); 14 | hvm_obj_ref *obj; 15 | 16 | // Set up the starting counter, the maximum number of iterations, and 17 | // the accumulator 18 | hvm_gen_litinteger(gen->block, reg_ctr, val_start); 19 | hvm_gen_litinteger(gen->block, reg_max, val_iterations); 20 | hvm_gen_litinteger(gen->block, reg_acc, 0); 21 | 22 | hvm_gen_label(gen->block, "condition"); 23 | hvm_gen_eq(gen->block, reg0, reg_ctr, reg_max); 24 | hvm_gen_if_label(gen->block, reg0, "end"); 25 | 26 | // Do the summation and increment the counter 27 | hvm_gen_litinteger(gen->block, reg0, val_incr); 28 | hvm_gen_add(gen->block, reg_acc, reg_acc, reg0); 29 | hvm_gen_litinteger(gen->block, reg0, 1); 30 | hvm_gen_add(gen->block, reg_ctr, reg_ctr, reg0); 31 | hvm_gen_goto_label(gen->block, "condition"); 32 | 33 | hvm_gen_label(gen->block, "end"); 34 | hvm_gen_die(gen->block); 35 | 36 | hvm_vm *vm = gen_chunk_and_run(gen); 37 | 38 | // Assertions 39 | obj = vm->general_regs[reg_ctr]; 40 | assert_true(obj->data.i64 == val_iterations, "Expected counter to equal number of iterations"); 41 | 42 | obj = vm->general_regs[reg_acc]; 43 | assert_true(obj->data.i64 == (val_iterations * val_incr), "Expected accumulator to be correct value"); 44 | 45 | return done(); 46 | } 47 | -------------------------------------------------------------------------------- /test/unit/test_structure.c: -------------------------------------------------------------------------------- 1 | #include "preamble.h" 2 | 3 | int main(int argc, char const *argv[]) { 4 | hvm_gen *gen = hvm_new_gen(); 5 | byte sym = hvm_vm_reg_gen(0); 6 | byte strct = hvm_vm_reg_gen(1); 7 | byte reg2 = hvm_vm_reg_gen(2); 8 | byte reg3 = hvm_vm_reg_gen(3); 9 | hvm_obj_ref *obj; 10 | 11 | // Integer constant 12 | hvm_gen_litinteger(gen->block, reg2, 1); 13 | 14 | // Create and set on structure 15 | hvm_gen_structnew(gen->block, strct); 16 | hvm_gen_set_symbol(gen->block, sym, "key"); 17 | hvm_gen_structset(gen->block, strct, sym, reg2); 18 | // Get from the structure into reg3 19 | hvm_gen_structget(gen->block, reg3, strct, sym); 20 | 21 | hvm_gen_die(gen->block); 22 | 23 | hvm_vm *vm = gen_chunk_and_run(gen); 24 | 25 | // Check that integer in $reg3 is expected 26 | obj = vm->general_regs[reg3]; 27 | assert_true(obj->type == HVM_INTEGER, "General register 3 should be integer"); 28 | assert_true(obj->data.i64 == 1, "General register 3 should be 1"); 29 | // Check that there's a structure in $strct 30 | obj = vm->general_regs[strct]; 31 | assert_true(obj->type == HVM_STRUCTURE, "Expected structure"); 32 | 33 | return done(); 34 | } 35 | --------------------------------------------------------------------------------