Merge branch 'master' into cleanup
authorDale Weiler <weilercdale@gmail.com>
Sun, 3 Jan 2016 03:30:35 +0000 (22:30 -0500)
committerDale Weiler <weilercdale@gmail.com>
Sun, 3 Jan 2016 03:30:35 +0000 (22:30 -0500)
106 files changed:
.travis.yml
BSDmakefile [deleted file]
CHANGES [deleted file]
INSTALL [deleted file]
Makefile
PORTING [deleted file]
README
TODO [deleted file]
algo.h [new file with mode: 0644]
ansi.c [deleted file]
ast.c [deleted file]
ast.cpp [new file with mode: 0644]
ast.h
code.c [deleted file]
code.cpp [new file with mode: 0644]
conout.c [deleted file]
conout.cpp [new file with mode: 0644]
correct.c [deleted file]
distro/Makefile [deleted file]
distro/archbsd/git/PKGBUILD [deleted file]
distro/archbsd/release/PKGBUILD [deleted file]
distro/archbsd/this/Makefile [deleted file]
distro/archlinux/git/PKGBUILD [deleted file]
distro/archlinux/release/PKGBUILD [deleted file]
distro/archlinux/this/Makefile [deleted file]
distro/deb/Makefile [deleted file]
distro/fedora/spec/INSTALL [deleted file]
distro/fedora/spec/gmqcc.spec [deleted file]
distro/fedora/this/Makefile [deleted file]
distro/gentoo/INSTALL [deleted file]
distro/gentoo/gmqcc-0.3.0.ebuild [deleted file]
distro/slackware/this/Makefile [deleted file]
distro/slackware/this/slack-desc [deleted file]
distro/win32/Makefile [deleted file]
distro/win64/Makefile [deleted file]
doc/gmqpak.1 [deleted file]
doc/html/download.c [deleted file]
doc/html/gmqcc.png [deleted file]
doc/html/style.css [deleted file]
doc/specification.tex [deleted file]
exec.c [deleted file]
exec.cpp [new file with mode: 0644]
fold.c [deleted file]
fold.cpp [new file with mode: 0644]
fold.h [new file with mode: 0644]
fs.c [deleted file]
ftepp.c [deleted file]
ftepp.cpp [new file with mode: 0644]
gmqcc.h
hash.c [deleted file]
include.mk [deleted file]
intrin.c [deleted file]
intrin.cpp [new file with mode: 0644]
intrin.h [new file with mode: 0644]
ir.c [deleted file]
ir.cpp [new file with mode: 0644]
ir.h
lexer.c [deleted file]
lexer.cpp [new file with mode: 0644]
lexer.h
main.c [deleted file]
main.cpp [new file with mode: 0644]
misc/check-proj.sh [deleted file]
misc/nexuiz_export.sh [deleted file]
misc/xonotic_export.sh [deleted file]
msvc.c [deleted file]
msvc/gmqcc.sln [deleted file]
msvc/gmqcc/gmqcc.vcxproj [deleted file]
msvc/gmqcc/gmqcc.vcxproj.filters [deleted file]
msvc/pak/pak.vcxproj [deleted file]
msvc/pak/pak.vcxproj.filters [deleted file]
msvc/qcvm/qcvm.vcxproj [deleted file]
msvc/qcvm/qcvm.vcxproj.filters [deleted file]
msvc/testsuite/testsuite.vcxproj [deleted file]
msvc/testsuite/testsuite.vcxproj.filters [deleted file]
opts.c [deleted file]
opts.cpp [new file with mode: 0644]
opts.def
pak.c [deleted file]
parser.c [deleted file]
parser.cpp [new file with mode: 0644]
parser.h
platform.h [deleted file]
stat.c [deleted file]
stat.cpp [new file with mode: 0644]
syntax/README [deleted file]
syntax/geany/README [deleted file]
syntax/geany/filetypes.qc [deleted file]
syntax/gtksourceview/README [deleted file]
syntax/gtksourceview/qc.lang [deleted file]
syntax/jedit/README [deleted file]
syntax/jedit/qc.xml [deleted file]
syntax/kate/README [deleted file]
syntax/kate/qc.xml [deleted file]
syntax/nano/README [deleted file]
syntax/nano/qc.nanorc [deleted file]
test.c [deleted file]
test.cpp [new file with mode: 0644]
tests/fieldfuncs.qc [new file with mode: 0644]
tests/fieldfuncs.tmpl [new file with mode: 0644]
tests/forloop.qc [new file with mode: 0644]
tests/forloop.tmpl [new file with mode: 0644]
utf8.c [deleted file]
utf8.cpp [new file with mode: 0644]
util.c [deleted file]
util.cpp [new file with mode: 0644]

index 4b6f9595939150d67b64c7380bbc0bce022376ad..830f373cebfae37fa14ed957173b775db810b39d 100644 (file)
@@ -1,9 +1,27 @@
-language: c
+language: cpp
+
 compiler:
   - gcc
   - clang
-# Change this to your needs
-script: make && make check
+
+before_install:
+  # g++4.8.1
+  - if [ "$CXX" == "g++" ]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi
+  # clang 3.4
+  - if [ "$CXX" == "clang++" ]; then sudo add-apt-repository -y ppa:h-rayflood/llvm; fi
+  - sudo apt-get update -qq
+
+install:
+  # g++4.8.1
+  - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi
+  - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8"; fi
+  # clang 3.4
+  - if [ "$CXX" == "clang++" ]; then sudo apt-get install --allow-unauthenticated -qq clang-3.4; fi
+  - if [ "$CXX" == "clang++" ]; then export CXX="clang++-3.4"; fi
+
+script:
+    - make test
+
 notifications:
     irc:
         channels:
diff --git a/BSDmakefile b/BSDmakefile
deleted file mode 100644 (file)
index 44a742a..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-#
-# This is the Makefile for the BSD flavor
-#
-.include "include.mk"
-
-GITTEST  != git describe --always 2>/dev/null
-VALTEST  != valgrind --version 2>/dev/null
-GITINFO  :=
-
-.if $(GITTEST)
-    GITINFO != git describe --always
-.endif
-
-.if $(CC) == clang
-    CFLAGS +=   -Weverything\
-                -Wno-padded\
-                -Wno-format-nonliteral\
-                -Wno-disabled-macro-expansion\
-                -Wno-conversion\
-                -Wno-float-equal\
-                -Wno-unknown-warning-option\
-                -Wno-cast-align\
-                -Wno-assign-enum\
-                -Wno-empty-body\
-                -Wno-date-time\
-                -pedantic-errors
-.else
-.    if $(CC) != g++
-       CFLAGS += -Wmissing-prototypes -Wstrict-prototypes
-.    endif
-
-.    if $(CC) != tcc
-       CFLAGS += -pedantic-errors
-.    else
-       CFLAGS += -Wno-pointer-sign -fno-common
-.    endif
-.endif
-
-.if !$(VALTEST)
-    CFLAGS += -DNVALGRIND
-.endif
-
-
-CFLAGS += -DGMQCC_GITINFO=\"$(GITINFO)\" $(OPTIONAL)
-DEPS != for i in $(OBJ_C) $(OBJ_P) $(OBJ_T) $(OBJ_X); do echo $$i; done | sort | uniq
-
-QCVM      = qcvm
-GMQCC     = gmqcc
-TESTSUITE = testsuite
-PAK       = gmqpak
-
-#standard rules
-c.o: ${.IMPSRC}
-       $(CC) -c ${.IMPSRC} -o ${.TARGET} $(CFLAGS) $(CPPFLAGS)
-
-$(QCVM): $(OBJ_X)
-       $(CC) -o ${.TARGET} ${.IMPSRC} $(LDFLAGS) $(LIBS) $(OBJ_X)
-
-$(GMQCC): $(OBJ_C)
-       $(CC) -o ${.TARGET} ${.IMPSRC} $(LDFLAGS) $(LIBS) $(OBJ_C)
-
-$(TESTSUITE): $(OBJ_T)
-       $(CC) -o ${.TARGET} ${.IMPSRC} $(LDFLAGS) $(LIBS) $(OBJ_T)
-
-$(PAK): $(OBJ_P)
-       $(CC) -o ${.TARGET} ${.IMPSRC} $(LDFLAGS) $(OBJ_P)
-
-all: $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK)
-
-check: all
-       @ ./$(TESTSUITE)
-test: all
-       @ ./$(TESTSUITE)
-
-strip: $(GMQCC) $(QCVM) $(TESTSUITE)
-       strip $(GMQCC)
-       strip $(QCVM)
-       strip $(TESTSUITE)
-
-clean:
-       rm -rf *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat gource.mp4 *.exe gm-qcc.tgz ./cov-int
-
-depend:
-       @makedepend -Y -f BSDmakefile -w 65536 2> /dev/null ${DEPS:C/\.o/.c/g}
-
-coverity:
-       @cov-build --dir cov-int $(MAKE) -f BSDmakefile
-       @tar czf gm-qcc.tgz cov-int
-       @rm -rf cov-int
-       @echo gm-qcc.tgz generated, submit for analysis
-
-install: install-gmqcc install-qcvm install-gmqpak install-doc
-install-gmqcc: $(GMQCC)
-       install -d -m755              $(DESTDIR)$(BINDIR)
-       install    -m755 $(GMQCC)     $(DESTDIR)$(BINDIR)/$(GMQCC)
-install-qcvm: $(QCVM)
-       install -d -m755              $(DESTDIR)$(BINDIR)
-       install    -m755 $(QCVM)      $(DESTDIR)$(BINDIR)/$(QCVM)
-install-gmqpak: $(PAK)
-       install -d -m755              $(DESTDIR)$(BINDIR)
-       install    -m755 $(PAK)       $(DESTDIR)$(BINDIR)/$(PAK)
-install-doc:
-       install -d -m755              $(DESTDIR)$(MANDIR)/man1
-       install    -m644 doc/gmqcc.1  $(DESTDIR)$(MANDIR)/man1/
-       install    -m644 doc/qcvm.1   $(DESTDIR)$(MANDIR)/man1/
-       install    -m644 doc/gmqpak.1 $(DESTDIR)$(MANDIR)/man1/
-
-# DO NOT DELETE
-
-ansi.o: platform.h gmqcc.h opts.def
-ast.o: gmqcc.h opts.def ast.h ir.h parser.h lexer.h
-code.o: gmqcc.h opts.def
-conout.o: gmqcc.h opts.def
-correct.o: gmqcc.h opts.def
-exec.o: gmqcc.h opts.def
-fold.o: ast.h ir.h gmqcc.h opts.def parser.h lexer.h
-fs.o: gmqcc.h opts.def platform.h
-ftepp.o: gmqcc.h opts.def lexer.h
-hash.o: gmqcc.h opts.def
-intrin.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h
-ir.o: gmqcc.h opts.def ir.h
-lexer.o: gmqcc.h opts.def lexer.h
-main.o: gmqcc.h opts.def lexer.h
-opts.o: gmqcc.h opts.def
-pak.o: gmqcc.h opts.def
-parser.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h
-stat.o: gmqcc.h opts.def
-test.o: gmqcc.h opts.def platform.h
-utf8.o: gmqcc.h opts.def
-util.o: gmqcc.h opts.def platform.h
diff --git a/CHANGES b/CHANGES
deleted file mode 100644 (file)
index 1dfa59d..0000000
--- a/CHANGES
+++ /dev/null
@@ -1,195 +0,0 @@
-Release 0.3.5:
-    * Preprocessor:
-        - Added warning for when preprocessor directives are used in
-          a macro.
-    * Language:
-        - Added vector bit operations.
-        - Added vector cross product operator `><` for -std=gmqcc.
-        - Added `[[eraseable]]` and `[[accumulation]]` attributes.
-        - Removed &~= operator for -std=gmqcc.
-        - #pragmas which are ignored are actually ignored to EOL.
-    * Compilation:
-        - Fixed constant folding.
-        - Fixed column printing in diagnostics.
-        - Added support for columns in LNOF.
-        - Shadowed locals fix.
-        - Improved warning diagnostics for precedence usage and other
-          various diagnostics.
-        - Added constant folding logic for builtin functions.
-        - Prevent divide by zero in constant folding.
-        - Added unary elision optimization, cases where a unary expression
-          cancels itself out, e.g `-(-a)` now simplifies to `a`.
-        - Added strength reduce optimization, for divisions e.g `(A / 10)`
-          now strength reduces to `(A * (1 / 10)` where `(1 / 10)` is
-          evaluated at compile time.
-        - Added superfluous expression elision optimization, e.g `(A + 0),
-          (A - 0), (A * 1), (A / 1)` can be simplified to just `A`.
-        - Quake world now compiles with -std=qcc
-        - Constant folding for `if than` now works for ternary expressions
-          too.
-        - Fixed `[[alias]]` attribute type checking, now you can't alias
-          invalid types.
-    * QCVM:
-        - Properly exits on error now.
-    * Lexer:
-        - Now prints the character the lexer fails on instead of the
-          token value.
-    * Testsuite:
-        - Important fixes to the testsuite (removal of false positives
-          and false negitives).
-        - Added a new utility `check-proj.sh` which downloads various Quake
-          mods and attempts to compile them.
-    * Commandline:
-        - Made -fshort-logic, -funtyped-nil and -fvariadic-args default
-          for -std=gmqcc.
-    * Build:
-        - Generate PDFs of man documents for Windows releases.
-        - Archlinux/Archbsd package now generates MTREE.
-    * Documentation:
-        - Fixed mdoc(s).
-    * Misc:
-        - Added some export scripts for xonotic and nexuiz which generate
-          specially crafted packages that are used by the check-proj script.
-
-2013-08-20 v0.3.0
-    * Language:
-        - Return assignments, the ability to assign to the return keyword
-          as if it were a local variable.
-        - Added bitwise XOR operator (^)
-        - Array initializers: e.g float a[] = {1, 2, 3};
-        - Fix bug that dissalowed language operators to be used in strings.
-    * Compilation:
-        - Optimized memory usage (now uses on average %3 less memory for
-          compilation).
-        - Fixed dotranslate (translatable strings)
-        - Rewrote constant folding optimization pass for the parser.
-        - New additional dead-code-elimination-consatant-fold pass for
-          if statements whos expression can be evaluated at compile-time
-          (allowing the if/else branch to be entierly elided at compile-time).
-        - Added support for columns in error diagnostics.
-        - Limit corrector to <= 16 byte strings.
-        - Improved hash function for hashtable (old hash function had 15% error,
-          this speeds up compilation)
-        - Improved performance of in-house allocator with branch-hinting, speeds
-          up compilation of Xonotic by 3 seconds!
-    * QCVM:
-        - Escape strings for -printdefs
-    * Commandline:
-        - Added statistic dumps, gives information about the number of used
-          hashtables, vectors, and number of unique sizes of vectors and
-          hashtables. The amount of memory used for vectors. As well as the
-          number of strdups used in total for compilation.
-        - Added compile statistic dumps, gives information about the compiled
-          binary, and LNO, such as the size, CRC, the number of times a
-          specific optimization was applied, etc.
-        - Make -std=qcc default.
-    * Testsuite:
-        - Fixed a floating point exception rasied by modulo operation in
-          -memchk.
-        - Added support for the test-suite to source tests and task-template
-          files from subdirectories in the test/ directory.
-        - Now prints the number of failed tests (if any) after all tests
-          are attempted.
-        - Fixed some bugs with error handling resulting in false-positives.
-    * Build:
-        - Can now be compiled with TCC (Tiny C compiler) and function as
-          intended (previously couldn't due to bug in TCC codegen).
-        - Added Gentoo ebuilds.
-        - Added Win32 Makefile for building Win32 packages.
-        - Added Slackware pkg build files.
-        - Added Fedora spec files.
-        - Added Makefile for the BSD make variant.
-    * Misc:
-        - Added valgrind memcheck hook to in-house allocator to help aid
-          in finding invalid reads/writes, and more accurate leaks.
-
-2012-04-27 v0.2.9
-    * Preprocessor:
-        - __VA_ARGS__ support
-        _ __VA_ARGS__ indexing
-        - Predefined macros like __DATE__, __TIME__, ...
-          (check the manpage for a full list).
-        - Signed numbers as single token in the.
-        - Fixes some issues with #if operations on macros.
-        - Speed improvements.
-    * Language:
-        - Untyped `nil` keyword.
-        - Removed the `noreturn` keyword.
-        - Added generic attribute syntax and reintroduced `noreturn`
-          as [[noreturn]].
-        - Added [[deprecated]] and [[deprecated("message")]].
-        - Support for `static` variables in functions.
-        - Support for labeled loops.
-        - UTF-8 Support.
-        - enum support: without enum-types
-          (ie no `typedef enum { } foo;`).
-        - Accessing vector components via the dot operator on all
-          expressions. Eg: `(3 * v).y`.
-        - Type restricted variadict parameters:
-          ie: `void print(string...);`.
-        - Accessing varargs from QC via: `...(index, type)`
-        - New operators: ** (exponentiation), % (modulo), etc.
-        - Enumeration attributes: `flag`, `reverse`.
-    * Compilation:
-        - Various optimizations and progs-size reductions.
-        - A new spell-checking algorithm tries to hint you at existing
-          variables on error.
-        - Some problems with VM related vector-instructions issues
-          have been solved in both DP and our own executor. A new
-          compatbility option (enabled by default) has been added for
-          now: -flegacy-vector-maths.
-        - Compiler intrinsics: __builtin_floor, __builtin_mod,
-          __builtin_exp, __builtin_isnan.
-        - Improved memory tracing.
-        - Speed improvements.
-    * QCVM:
-        - Improved commandline argument handling.
-        - More builtins: sqrt(), normalize(), floor().
-    * Commandline:
-        - Nicer memory dumps.
-        - Support for making individual warnings an error
-        - via -Werror-<warning>.
-        - added --add-info.
-    * Testsuite:
-        - Support for QCFLAGS to run tests with several additional
-          flags.
-        - Added support for preprocessor tests.
-        - Added preprocessor tests.
-        - Added defs.qh (auto included) for qcvm definitions.
-    * Syntax Highlighting:
-        - Added various syntax highlighting description files for
-          various text editors / integrated development envirorments,
-          including support for: geany, kate, kwrite, kdevelop, QtCreator,
-          gtksourceview, gedit, sany, nano, jedit.
-    * Build:
-        - Build scripts for building debian, archlinux and archbsd
-          packages for x86, and x86_64.
-        - Makefile targets for gource visualization, and render of
-          gource visualization.
-
-
-2012-12-27 Hotfix v0.2.2
-    * Liferanges
-    * Crashes
-
-2012-12-23 Hotfix v0.2.1
-    * General bugfixes
-
-2012-12-23 Release 0.2
-    * Preprocessor:
-        - Added xonotic compatible preprocessor.
-    * Language
-        - Basic xonotic compatibility.
-        - Array support.
-        - Added fteqcc's string escape sequences.
-        - Support for `noref`.
-        - Support for `goto` with labels like in fteqcc.
-        - `break` and `continue`.
-        - Short circuit logic.
-        - Support for translatable strings via _("str") like in
-          fteqcc.
-    * Compilation
-        - Warnings about uninitialized values.
-
-2012-11-17 Release 0.1
-    * Compiles id1 code.
diff --git a/INSTALL b/INSTALL
deleted file mode 100644 (file)
index ffd45bd..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,62 +0,0 @@
-                           Installing gmqcc
-
-1. Prerequisites
-    - A C-Compiler such as gcc or clang
-    - GNU Make or BSD Make
-
-2. Compilation
-    If using GNU make program
-        make
-
-    If using BSD make program, the BSDmakefile should be
-    used instead when you invoke make, if however it ignores BSDmakefile
-    you can supply it with -f.
-
-        make -f BSDmakefile
-
-    If no error appears, the following binary files will have been
-    created:
-        - gmqcc
-        - qcvm
-        - gmqpak
-
-3. Installation
-    The `install' target will install the 2 binaries to /usr/local/bin
-    by default.
-    The Makefile honors the following variables:
-
-        - DESTDIR: The installation directory root.
-        - PREFIX:  The installation prefix, default: /usr/local
-        - BINDIR:  Directory for binary executables,
-                   deafult: $PREFIX/bin
-
-    To install to /usr/local run:
-
-        make install
-
-    To install to /usr run:
-
-        make PREFIX=/usr install
-
-    To install to a package-staging directory such as $pkgdir when
-    writing a build script file:
-
-        make DESTDIR=$pkgdir install
-
-
-    ArchLinux PKGBUILDs (release and git build) can be found in the
-    respective folders in ./distro/archlinux
-    
-    ArchBSD PKGBUILDs (release and git build) can be found in the
-    respective folders in ./distro/archbsd
-
-    Slackware SlackBuilds (git build) can be found in ./distro/slackware
-    
-    Gentoo ebuilds (release) can be found in ./distro/gentoo, as well
-    as a README explaining how to build them
-    
-    Debian archives (git build) can be created invoking make in
-    ./distro/deb
-
-    Fedora spec files (release) can be found in ./distro/fedora, as well
-    as a README explaining how to build them.
index 530bd4cd4ea42249b3315b14951ec65724b3e56a..c6a6327ed02269a35d0146662a6edac6007ccbbc 100644 (file)
--- a/Makefile
+++ b/Makefile
-include include.mk
-
-UNAME  ?= $(shell uname)
-CYGWIN  = $(findstring CYGWIN, $(UNAME))
-MINGW   = $(findstring MINGW,  $(UNAME))
-
-# turn on tons of warnings if clang is present
-# but also turn off the STUPID ONES
-ifeq ($(CC), clang)
-       CFLAGS +=                              \
-           -Weverything                       \
-           -Wno-padded                        \
-           -Wno-format-nonliteral             \
-           -Wno-disabled-macro-expansion      \
-           -Wno-conversion                    \
-           -Wno-float-equal                   \
-           -Wno-unknown-warning-option        \
-           -Wno-cast-align                    \
-           -Wno-assign-enum                   \
-           -Wno-empty-body                    \
-           -Wno-date-time                     \
-           -pedantic-errors
-else
-       ifneq ($(CC), g++)
-               CFLAGS += -Wmissing-prototypes -Wstrict-prototypes
-       endif
-
-       ifneq ($(CC), tcc)
-               CFLAGS += -pedantic-errors
-       else
-               CFLAGS += -Wno-pointer-sign -fno-common
-       endif
-endif
-
-ifneq ($(shell git describe --always 2>/dev/null),)
-       CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\""
-endif
-
-ifeq ($(shell valgrind --version 2>/dev/null),)
-       CFLAGS += -DNVALGRIND
-endif
-
-# do this last otherwise there is whitespace in the command output and
-# it makes my OCD act up
-CFLAGS += $(OPTIONAL_CFLAGS)
-LDFLAGS += $(OPTIONAL_LDFLAGS)
-
-#we have duplicate object files when dealing with creating a simple list
-#for dependinces. To combat this we use some clever recrusive-make to
-#filter the list and remove duplicates which we use for make depend
-RMDUP = $(if $1,$(firstword $1) $(call RMDUP,$(filter-out $(firstword $1),$1)))
-DEPS := $(call RMDUP, $(OBJ_P) $(OBJ_T) $(OBJ_C) $(OBJ_X))
-
-ifneq ("$(CYGWIN)", "")
-       #nullify the common variables that
-       #most *nix systems have (for windows)
-       PREFIX   :=
-       BINDIR   :=
-       DATADIR  :=
-       MANDIR   :=
-       QCVM      = qcvm.exe
-       GMQCC     = gmqcc.exe
-       TESTSUITE = testsuite.exe
-       PAK       = gmqpak.exe
-       CFLAGS   += -DNVALGRIND
-else
-ifneq ("$(MINGW)", "")
-       #nullify the common variables that
-       #most *nix systems have (for windows)
-       PREFIX   :=
-       BINDIR   :=
-       DATADIR  :=
-       MANDIR   :=
-       QCVM      = qcvm.exe
-       GMQCC     = gmqcc.exe
-       TESTSUITE = testsuite.exe
-       PAK       = gmqpak.exe
-       CFLAGS   += -DNVALGRIND
-else
-       QCVM      = qcvm
-       GMQCC     = gmqcc
-       TESTSUITE = testsuite
-       PAK       = gmqpak
-endif
-endif
-
-#standard rules
-c.o:
-       $(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS)
-
-$(QCVM): $(OBJ_X)
-       $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
-
-$(GMQCC): $(OBJ_C) $(OBJ_D)
-       $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
-
-$(TESTSUITE): $(OBJ_T)
-       $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
-
-$(PAK): $(OBJ_P)
-       $(CC) -o $@ $^ $(LDFLAGS)
-
-all: $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK)
-
-check: all
-       @ ./$(TESTSUITE)
-test: all
-       @ ./$(TESTSUITE)
-
-strip: $(GMQCC) $(QCVM) $(TESTSUITE)
-       strip $(GMQCC)
-       strip $(QCVM)
-       strip $(TESTSUITE)
+CXX ?= clang++
+CXXFLAGS = \
+       -std=c++11 \
+       -Wall \
+       -Wextra \
+       -fno-exceptions \
+       -fno-rtti \
+       -MD \
+       -g3
+
+CSRCS = \
+       ast.cpp \
+       code.cpp \
+       conout.cpp \
+       fold.cpp \
+       ftepp.cpp \
+       intrin.cpp \
+       ir.cpp \
+       lexer.cpp \
+       main.cpp \
+       opts.cpp \
+       parser.cpp \
+       stat.cpp \
+       utf8.cpp \
+       util.cpp
+
+TSRCS = \
+       conout.cpp \
+       opts.cpp \
+       stat.cpp \
+       test.cpp \
+       util.cpp
+
+VSRCS = \
+       exec.cpp \
+       stat.cpp \
+       util.cpp
+
+COBJS = $(CSRCS:.cpp=.o)
+TOBJS = $(TSRCS:.cpp=.o)
+VOBJS = $(VSRCS:.cpp=.o)
+
+CDEPS = $(CSRCS:.cpp=.d)
+TDEPS = $(TSRCS:.cpp=.d)
+VDEPS = $(VSRCS:.cpp=.d)
+
+CBIN = gmqcc
+TBIN = testsuite
+VBIN = qcvm
+
+all: $(CBIN) $(TBIN) $(VBIN)
+
+$(CBIN): $(COBJS)
+       $(CXX) $(COBJS) -o $@
+
+$(TBIN): $(TOBJS)
+       $(CXX) $(TOBJS) -o $@
+
+$(VBIN): $(VOBJS)
+       $(CXX) $(VOBJS) -o $@
+
+.cpp.o:
+       $(CXX) -c $(CXXFLAGS) $< -o $@
+
+test: $(CBIN) $(TBIN) $(VBIN)
+       @./$(TBIN)
 
 clean:
-       rm -rf *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat gource.mp4 *.exe gm-qcc.tgz ./cov-int
-
-depend:
-       @ makedepend -Y -w 65536 2> /dev/null $(subst .o,.c,$(DEPS))
-
-
-coverity:
-       @cov-build --dir cov-int $(MAKE)
-       @tar czf gm-qcc.tgz cov-int
-       @rm -rf cov-int
-       @echo gm-qcc.tgz generated, submit for analysis
-
-#install rules
-install: install-gmqcc install-qcvm install-gmqpak install-doc
-install-gmqcc: $(GMQCC)
-       install -d -m755               $(DESTDIR)$(BINDIR)
-       install    -m755  $(GMQCC)     $(DESTDIR)$(BINDIR)/$(GMQCC)
-install-qcvm: $(QCVM)
-       install -d -m755               $(DESTDIR)$(BINDIR)
-       install    -m755  $(QCVM)      $(DESTDIR)$(BINDIR)/$(QCVM)
-install-gmqpak: $(PAK)
-       install -d -m755               $(DESTDIR)$(BINDIR)
-       install    -m755  $(PAK)       $(DESTDIR)$(BINDIR)/$(PAK)
-install-doc:
-       install -d -m755               $(DESTDIR)$(MANDIR)/man1
-       install    -m644  doc/gmqcc.1  $(DESTDIR)$(MANDIR)/man1/
-       install    -m644  doc/qcvm.1   $(DESTDIR)$(MANDIR)/man1/
-       install    -m644  doc/gmqpak.1 $(DESTDIR)$(MANDIR)/man1/
-
-# DO NOT DELETE
+       rm -f *.d
+       rm -f $(COBJS) $(CDEPS) $(CBIN)
+       rm -f $(TOBJS) $(TDEPS) $(TBIN)
+       rm -f $(VOBJS) $(VDEPS) $(VBIN)
 
-ansi.o: platform.h gmqcc.h opts.def
-util.o: gmqcc.h opts.def platform.h
-hash.o: gmqcc.h opts.def
-stat.o: gmqcc.h opts.def
-fs.o: gmqcc.h opts.def platform.h
-opts.o: gmqcc.h opts.def
-conout.o: gmqcc.h opts.def
-pak.o: gmqcc.h opts.def
-test.o: gmqcc.h opts.def platform.h
-main.o: gmqcc.h opts.def lexer.h
-lexer.o: gmqcc.h opts.def lexer.h
-parser.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h
-code.o: gmqcc.h opts.def
-ast.o: gmqcc.h opts.def ast.h ir.h parser.h lexer.h
-ir.o: gmqcc.h opts.def ir.h
-ftepp.o: gmqcc.h opts.def lexer.h
-utf8.o: gmqcc.h opts.def
-correct.o: gmqcc.h opts.def
-fold.o: ast.h ir.h gmqcc.h opts.def parser.h lexer.h
-intrin.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h
-exec.o: gmqcc.h opts.def
+-include *.d
diff --git a/PORTING b/PORTING
deleted file mode 100644 (file)
index bbdb274..0000000
--- a/PORTING
+++ /dev/null
@@ -1,4 +0,0 @@
-Porting gmqcc to a new platform is farily trivial, in most cases ansi.c
-will be sufficent enough to get it to run on your favorite platform. If
-however it isn't you can duplicate ansi.c and change it accordingly.
-Changes to platform.h may also be required.
diff --git a/README b/README
index 36e1cf75d1170704e62e61e3c87430ae19b28fe1..993b8fb5d3b5f760426fa7efc8c017427d992b0d 100644 (file)
--- a/README
+++ b/README
@@ -1,18 +1 @@
-GMQCC: An improved Quake C compiler
-
-For licensing:           see the LICENSE file.
-For installation notes:  see the INSTALL file.
-For a list of authors:   see the AUTHORS file.
-For a list of changes:   see the CHANGES file.
-
-For documentation:
-    See the manpages, or visit the documentation online at
-    http://graphitemaster.github.com/gmqcc/doc.html
-
-For syntax highlighting description files, or information
-regarding how to install them:
-    See the README in syntax directory
-
-For description on porting GMQCC to other platforms, or information
-on how to approach porting GMQCC to more 'exotic' platforms:
-    See the PORTING file.
+An improved QuakeC compiler
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index 356c4b8..0000000
--- a/TODO
+++ /dev/null
@@ -1,64 +0,0 @@
-GMQCC is quite feature complete.  But that doesn't address the fact that
-it can be improved.  This is a list of things that we'd like to support
-in the distant future.  When the time comes, we can just select a topic
-from here and open a ticket for it on the issue tracker.  But for the
-meantime, this is sort of a cultivating flat file database.
-
-Optimizations:
-    The following are optimizations that can be implemented after the
-    transformation into static-single assignment (SSA).
-
-        Global Value Numbering:
-            Eliminate redundancy by constructing a value graph of the source
-            then determining which values are computed by equivalent expressions.
-            Similar to Common Subexpression Elimination (CSE), however expressions
-            are determined via underlying equivalence, opposed to lexically identical
-            expressions (CSE).
-
-    The following are optimizations that can be implemented before the
-    transformation into a binary (code generator).
-
-        Code factoring:
-            The process of finding sequences of code that are identical,
-            or can be parameterized or reordered to be identical.
-            Which can be replaced with calls to a shared subroutine. To
-            reduce duplicated code. (Size optimization)
-
-Language Features:
-    The following are language features that we'd like to see implemented in the
-    future.
-
-    AST Macros:
-        Macros with sanity.  Not textual substiution.
-
-    Classes:
-        Like C++, but minus the stupidity:
-            - No type operator overloads
-            - Keep operator overloading for basic operators though.
-            - No inheritance
-            - No virtuals / pure virtuals
-            - Essentially "C structs but with operators" :)
-
-    Overloaded Functions:
-        Ability to make individual functions with the same name, but take
-        different amount of arguments or type of arguments.
-
-    Default Argument Substiution:
-        Ability to specify default values for arguments in functions.
-        void foo(string bar, string baz="default");
-        Supplying just one argument will expand the second argument to
-        become "default", otherwise if two arguments are specified then
-        the "default" string is overrode with what ever the user passes.
-
-    Namespaces:
-        There is already a ticket open on this. They'd work just like C++
-        identically even.
-
-Testsuite:
-    The following are things we'd like to see added to the testsuite
-    in the distant future:
-
-    Interface:
-        Ability to select individual tests, or set parameters manually
-        opposed to using the static task-template files. (A method to
-        override them rather).
diff --git a/algo.h b/algo.h
new file mode 100644 (file)
index 0000000..3218b1b
--- /dev/null
+++ b/algo.h
@@ -0,0 +1,18 @@
+#ifndef GMQCC_ALGO_HDR
+#define GMQCC_ALGO_HDR
+
+namespace algo {
+
+template<typename ITER>
+void shiftback(ITER element, ITER end) {
+    //typename ITER::value_type backup(move(*element)); // hold the element
+    typename std::remove_reference<decltype(*element)>::type backup(move(*element)); // hold the element
+    ITER p = element++;
+    for (; element != end; p = element++)
+        *p = move(*element);
+    *p = move(backup);
+}
+
+} // ::algo
+
+#endif
diff --git a/ansi.c b/ansi.c
deleted file mode 100644 (file)
index 1317e15..0000000
--- a/ansi.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2012, 2013, 2014, 2015
- *     Dale Weiler
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-#define GMQCC_PLATFORM_HEADER
-#include <string.h>
-#include <stdlib.h>
-
-#include "platform.h"
-#include "gmqcc.h"
-
-int platform_vsnprintf(char *buffer, size_t bytes, const char *format, va_list arg) {
-    return vsnprintf(buffer, bytes, format, arg);
-}
-
-int platform_vsscanf(const char *str, const char *format, va_list arg) {
-    return vsscanf(str, format, arg);
-}
-
-const struct tm *platform_localtime(const time_t *timer) {
-    return localtime(timer);
-}
-
-const char *platform_ctime(const time_t *timer) {
-    return ctime(timer);
-}
-
-char *platform_strncat(char *dest, const char *src, size_t num) {
-    return strncat(dest, src, num);
-}
-
-const char *platform_getenv(const char *var) {
-    return getenv(var);
-}
-
-int platform_vasprintf(char **dat, const char *fmt, va_list args) {
-    int     ret;
-    int     len;
-    char   *tmp = NULL;
-    char    buf[128];
-    va_list cpy;
-
-    va_copy(cpy, args);
-    len = vsnprintf(buf, sizeof(buf), fmt, cpy);
-    va_end (cpy);
-
-    if (len < 0)
-        return len;
-
-    if (len < (int)sizeof(buf)) {
-        *dat = util_strdup(buf);
-        return len;
-    }
-
-    tmp = (char*)mem_a(len + 1);
-    if ((ret = vsnprintf(tmp, len + 1, fmt, args)) != len) {
-        mem_d(tmp);
-        *dat = NULL;
-        return -1;
-    }
-
-    *dat = tmp;
-    return len;
-}
-
-char *platform_strcat(char *dest, const char *src) {
-    return strcat(dest, src);
-}
-
-char *platform_strncpy(char *dest, const char *src, size_t num) {
-    return strncpy(dest, src, num);
-}
-
-const char *platform_strerror(int err) {
-    return strerror(err);
-}
-
-FILE *platform_fopen(const char *filename, const char *mode) {
-    return fopen(filename, mode);
-}
-
-size_t platform_fread(void *ptr, size_t size, size_t count, FILE *stream) {
-    return fread(ptr, size, count, stream);
-}
-
-size_t platform_fwrite(const void *ptr, size_t size, size_t count, FILE *stream) {
-    return fwrite(ptr, size, count, stream);
-}
-
-int platform_fflush(FILE *stream) {
-    return fflush(stream);
-}
-
-int platform_vfprintf(FILE *stream, const char *format, va_list arg) {
-    return vfprintf(stream, format, arg);
-}
-
-int platform_fclose(FILE *stream) {
-    return fclose(stream);
-}
-
-int platform_ferror(FILE *stream) {
-    return ferror(stream);
-}
-
-int platform_fgetc(FILE *stream) {
-    return fgetc(stream);
-}
-
-int platform_fputs(const char *str, FILE *stream) {
-    return fputs(str, stream);
-}
-
-int platform_fseek(FILE *stream, long offset, int origin) {
-    return fseek(stream, offset, origin);
-}
-
-long platform_ftell(FILE *stream) {
-    return ftell(stream);
-}
-
-int platform_mkdir(const char *path, int mode) {
-    /*
-     * For some reason mingw32 just doesn't have a correct mkdir impl
-     * so we handle that here.
-     */
-#   ifdef _WIN32
-    (void)mode;
-    return mkdir(path);
-#   else
-    return mkdir(path, mode);
-#   endif /*!_WIN32*/
-}
-
-DIR *platform_opendir(const char *path) {
-    return opendir(path);
-}
-
-int platform_closedir(DIR *dir) {
-    return closedir(dir);
-}
-
-struct dirent *platform_readdir(DIR *dir) {
-    return readdir(dir);
-}
-
-int platform_isatty(int fd) {
-    return isatty(fd);
-}
diff --git a/ast.c b/ast.c
deleted file mode 100644 (file)
index 4a07cc2..0000000
--- a/ast.c
+++ /dev/null
@@ -1,3486 +0,0 @@
-/*
- * Copyright (C) 2012, 2013, 2014, 2015
- *     Wolfgang Bumiller
- *     Dale Weiler
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-#include <stdlib.h>
-#include <string.h>
-
-#include "gmqcc.h"
-#include "ast.h"
-#include "parser.h"
-
-#define ast_instantiate(T, ctx, destroyfn)                          \
-    T* self = (T*)mem_a(sizeof(T));                                 \
-    if (!self) {                                                    \
-        return NULL;                                                \
-    }                                                               \
-    ast_node_init((ast_node*)self, ctx, TYPE_##T);                  \
-    ( (ast_node*)self )->destroy = (ast_node_delete*)destroyfn
-
-/*
- * forward declarations, these need not be in ast.h for obvious
- * static reasons.
- */
-static bool ast_member_codegen(ast_member*, ast_function*, bool lvalue, ir_value**);
-static void ast_array_index_delete(ast_array_index*);
-static bool ast_array_index_codegen(ast_array_index*, ast_function*, bool lvalue, ir_value**);
-static void ast_argpipe_delete(ast_argpipe*);
-static bool ast_argpipe_codegen(ast_argpipe*, ast_function*, bool lvalue, ir_value**);
-static void ast_store_delete(ast_store*);
-static bool ast_store_codegen(ast_store*, ast_function*, bool lvalue, ir_value**);
-static void ast_ifthen_delete(ast_ifthen*);
-static bool ast_ifthen_codegen(ast_ifthen*, ast_function*, bool lvalue, ir_value**);
-static void ast_ternary_delete(ast_ternary*);
-static bool ast_ternary_codegen(ast_ternary*, ast_function*, bool lvalue, ir_value**);
-static void ast_loop_delete(ast_loop*);
-static bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**);
-static void ast_breakcont_delete(ast_breakcont*);
-static bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**);
-static void ast_switch_delete(ast_switch*);
-static bool ast_switch_codegen(ast_switch*, ast_function*, bool lvalue, ir_value**);
-static void ast_label_delete(ast_label*);
-static void ast_label_register_goto(ast_label*, ast_goto*);
-static bool ast_label_codegen(ast_label*, ast_function*, bool lvalue, ir_value**);
-static bool ast_goto_codegen(ast_goto*, ast_function*, bool lvalue, ir_value**);
-static void ast_goto_delete(ast_goto*);
-static void ast_call_delete(ast_call*);
-static bool ast_call_codegen(ast_call*, ast_function*, bool lvalue, ir_value**);
-static bool ast_block_codegen(ast_block*, ast_function*, bool lvalue, ir_value**);
-static void ast_unary_delete(ast_unary*);
-static bool ast_unary_codegen(ast_unary*, ast_function*, bool lvalue, ir_value**);
-static void ast_entfield_delete(ast_entfield*);
-static bool ast_entfield_codegen(ast_entfield*, ast_function*, bool lvalue, ir_value**);
-static void ast_return_delete(ast_return*);
-static bool ast_return_codegen(ast_return*, ast_function*, bool lvalue, ir_value**);
-static void ast_binstore_delete(ast_binstore*);
-static bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**);
-static void ast_binary_delete(ast_binary*);
-static bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**);
-static bool ast_state_codegen(ast_state*, ast_function*, bool lvalue, ir_value**);
-
-/* It must not be possible to get here. */
-static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
-{
-    (void)self;
-    con_err("ast node missing destroy()\n");
-    exit(EXIT_FAILURE);
-}
-
-/* Initialize main ast node aprts */
-static void ast_node_init(ast_node *self, lex_ctx_t ctx, int nodetype)
-{
-    self->context = ctx;
-    self->destroy = &_ast_node_destroy;
-    self->keep    = false;
-    self->nodetype = nodetype;
-    self->side_effects = false;
-}
-
-/* weight and side effects */
-static void _ast_propagate_effects(ast_node *self, ast_node *other)
-{
-    if (ast_side_effects(other))
-        ast_side_effects(self) = true;
-}
-#define ast_propagate_effects(s,o) _ast_propagate_effects(((ast_node*)(s)), ((ast_node*)(o)))
-
-/* General expression initialization */
-static void ast_expression_init(ast_expression *self,
-                                ast_expression_codegen *codegen)
-{
-    self->codegen  = codegen;
-    self->vtype    = TYPE_VOID;
-    self->next     = NULL;
-    self->outl     = NULL;
-    self->outr     = NULL;
-    self->params   = NULL;
-    self->count    = 0;
-    self->varparam = NULL;
-    self->flags    = 0;
-    if (OPTS_OPTION_BOOL(OPTION_COVERAGE))
-        self->flags |= AST_FLAG_BLOCK_COVERAGE;
-}
-
-static void ast_expression_delete(ast_expression *self)
-{
-    size_t i;
-    if (self->next)
-        ast_delete(self->next);
-    for (i = 0; i < vec_size(self->params); ++i) {
-        ast_delete(self->params[i]);
-    }
-    vec_free(self->params);
-    if (self->varparam)
-        ast_delete(self->varparam);
-}
-
-static void ast_expression_delete_full(ast_expression *self)
-{
-    ast_expression_delete(self);
-    mem_d(self);
-}
-
-ast_value* ast_value_copy(const ast_value *self)
-{
-    size_t i;
-    const ast_expression *fromex;
-    ast_expression       *selfex;
-    ast_value *cp = ast_value_new(self->expression.node.context, self->name, self->expression.vtype);
-    if (self->expression.next) {
-        cp->expression.next = ast_type_copy(self->expression.node.context, self->expression.next);
-    }
-    fromex   = &self->expression;
-    selfex = &cp->expression;
-    selfex->count    = fromex->count;
-    selfex->flags    = fromex->flags;
-    for (i = 0; i < vec_size(fromex->params); ++i) {
-        ast_value *v = ast_value_copy(fromex->params[i]);
-        vec_push(selfex->params, v);
-    }
-    return cp;
-}
-
-void ast_type_adopt_impl(ast_expression *self, const ast_expression *other)
-{
-    size_t i;
-    const ast_expression *fromex;
-    ast_expression       *selfex;
-    self->vtype = other->vtype;
-    if (other->next) {
-        self->next = (ast_expression*)ast_type_copy(ast_ctx(self), other->next);
-    }
-    fromex = other;
-    selfex = self;
-    selfex->count    = fromex->count;
-    selfex->flags    = fromex->flags;
-    for (i = 0; i < vec_size(fromex->params); ++i) {
-        ast_value *v = ast_value_copy(fromex->params[i]);
-        vec_push(selfex->params, v);
-    }
-}
-
-static ast_expression* ast_shallow_type(lex_ctx_t ctx, int vtype)
-{
-    ast_instantiate(ast_expression, ctx, ast_expression_delete_full);
-    ast_expression_init(self, NULL);
-    self->codegen = NULL;
-    self->next    = NULL;
-    self->vtype   = vtype;
-    return self;
-}
-
-ast_expression* ast_type_copy(lex_ctx_t ctx, const ast_expression *ex)
-{
-    size_t i;
-    const ast_expression *fromex;
-    ast_expression       *selfex;
-
-    if (!ex)
-        return NULL;
-    else
-    {
-        ast_instantiate(ast_expression, ctx, ast_expression_delete_full);
-        ast_expression_init(self, NULL);
-
-        fromex = ex;
-        selfex = self;
-
-        /* This may never be codegen()d */
-        selfex->codegen = NULL;
-
-        selfex->vtype = fromex->vtype;
-        if (fromex->next)
-            selfex->next = ast_type_copy(ctx, fromex->next);
-        else
-            selfex->next = NULL;
-
-        selfex->count    = fromex->count;
-        selfex->flags    = fromex->flags;
-        for (i = 0; i < vec_size(fromex->params); ++i) {
-            ast_value *v = ast_value_copy(fromex->params[i]);
-            vec_push(selfex->params, v);
-        }
-
-        return self;
-    }
-}
-
-bool ast_compare_type(ast_expression *a, ast_expression *b)
-{
-    if (a->vtype == TYPE_NIL ||
-        b->vtype == TYPE_NIL)
-        return true;
-    if (a->vtype != b->vtype)
-        return false;
-    if (!a->next != !b->next)
-        return false;
-    if (vec_size(a->params) != vec_size(b->params))
-        return false;
-    if ((a->flags & AST_FLAG_TYPE_MASK) !=
-        (b->flags & AST_FLAG_TYPE_MASK) )
-    {
-        return false;
-    }
-    if (vec_size(a->params)) {
-        size_t i;
-        for (i = 0; i < vec_size(a->params); ++i) {
-            if (!ast_compare_type((ast_expression*)a->params[i],
-                                  (ast_expression*)b->params[i]))
-                return false;
-        }
-    }
-    if (a->next)
-        return ast_compare_type(a->next, b->next);
-    return true;
-}
-
-static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsize, size_t pos)
-{
-    const char *typestr;
-    size_t typelen;
-    size_t i;
-
-    if (!e) {
-        if (pos + 6 >= bufsize)
-            goto full;
-        util_strncpy(buf + pos, "(null)", 6);
-        return pos + 6;
-    }
-
-    if (pos + 1 >= bufsize)
-        goto full;
-
-    switch (e->vtype) {
-        case TYPE_VARIANT:
-            util_strncpy(buf + pos, "(variant)", 9);
-            return pos + 9;
-
-        case TYPE_FIELD:
-            buf[pos++] = '.';
-            return ast_type_to_string_impl(e->next, buf, bufsize, pos);
-
-        case TYPE_POINTER:
-            if (pos + 3 >= bufsize)
-                goto full;
-            buf[pos++] = '*';
-            buf[pos++] = '(';
-            pos = ast_type_to_string_impl(e->next, buf, bufsize, pos);
-            if (pos + 1 >= bufsize)
-                goto full;
-            buf[pos++] = ')';
-            return pos;
-
-        case TYPE_FUNCTION:
-            pos = ast_type_to_string_impl(e->next, buf, bufsize, pos);
-            if (pos + 2 >= bufsize)
-                goto full;
-            if (!vec_size(e->params)) {
-                buf[pos++] = '(';
-                buf[pos++] = ')';
-                return pos;
-            }
-            buf[pos++] = '(';
-            pos = ast_type_to_string_impl((ast_expression*)(e->params[0]), buf, bufsize, pos);
-            for (i = 1; i < vec_size(e->params); ++i) {
-                if (pos + 2 >= bufsize)
-                    goto full;
-                buf[pos++] = ',';
-                buf[pos++] = ' ';
-                pos = ast_type_to_string_impl((ast_expression*)(e->params[i]), buf, bufsize, pos);
-            }
-            if (pos + 1 >= bufsize)
-                goto full;
-            buf[pos++] = ')';
-            return pos;
-
-        case TYPE_ARRAY:
-            pos = ast_type_to_string_impl(e->next, buf, bufsize, pos);
-            if (pos + 1 >= bufsize)
-                goto full;
-            buf[pos++] = '[';
-            pos += util_snprintf(buf + pos, bufsize - pos - 1, "%i", (int)e->count);
-            if (pos + 1 >= bufsize)
-                goto full;
-            buf[pos++] = ']';
-            return pos;
-
-        default:
-            typestr = type_name[e->vtype];
-            typelen = strlen(typestr);
-            if (pos + typelen >= bufsize)
-                goto full;
-            util_strncpy(buf + pos, typestr, typelen);
-            return pos + typelen;
-    }
-
-full:
-    buf[bufsize-3] = '.';
-    buf[bufsize-2] = '.';
-    buf[bufsize-1] = '.';
-    return bufsize;
-}
-
-void ast_type_to_string(ast_expression *e, char *buf, size_t bufsize)
-{
-    size_t pos = ast_type_to_string_impl(e, buf, bufsize-1, 0);
-    buf[pos] = 0;
-}
-
-static bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_value **out);
-ast_value* ast_value_new(lex_ctx_t ctx, const char *name, int t)
-{
-    ast_instantiate(ast_value, ctx, ast_value_delete);
-    ast_expression_init((ast_expression*)self,
-                        (ast_expression_codegen*)&ast_value_codegen);
-    self->expression.node.keep = true; /* keep */
-
-    self->name = name ? util_strdup(name) : NULL;
-    self->expression.vtype = t;
-    self->expression.next  = NULL;
-    self->isfield  = false;
-    self->cvq      = CV_NONE;
-    self->hasvalue = false;
-    self->isimm    = false;
-    self->inexact  = false;
-    self->uses     = 0;
-    memset(&self->constval, 0, sizeof(self->constval));
-    self->initlist = NULL;
-
-    self->ir_v           = NULL;
-    self->ir_values      = NULL;
-    self->ir_value_count = 0;
-
-    self->setter = NULL;
-    self->getter = NULL;
-    self->desc   = NULL;
-
-    self->argcounter = NULL;
-    self->intrinsic = false;
-
-    return self;
-}
-
-void ast_value_delete(ast_value* self)
-{
-    if (self->name)
-        mem_d((void*)self->name);
-    if (self->argcounter)
-        mem_d((void*)self->argcounter);
-    if (self->hasvalue) {
-        switch (self->expression.vtype)
-        {
-        case TYPE_STRING:
-            mem_d((void*)self->constval.vstring);
-            break;
-        case TYPE_FUNCTION:
-            /* unlink us from the function node */
-            self->constval.vfunc->vtype = NULL;
-            break;
-        /* NOTE: delete function? currently collected in
-         * the parser structure
-         */
-        default:
-            break;
-        }
-    }
-    if (self->ir_values)
-        mem_d(self->ir_values);
-
-    if (self->desc)
-        mem_d(self->desc);
-
-    if (self->initlist) {
-        if (self->expression.next->vtype == TYPE_STRING) {
-            /* strings are allocated, free them */
-            size_t i, len = vec_size(self->initlist);
-            /* in theory, len should be expression.count
-             * but let's not take any chances */
-            for (i = 0; i < len; ++i) {
-                if (self->initlist[i].vstring)
-                    mem_d(self->initlist[i].vstring);
-            }
-        }
-        vec_free(self->initlist);
-    }
-
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-void ast_value_params_add(ast_value *self, ast_value *p)
-{
-    vec_push(self->expression.params, p);
-}
-
-bool ast_value_set_name(ast_value *self, const char *name)
-{
-    if (self->name)
-        mem_d((void*)self->name);
-    self->name = util_strdup(name);
-    return !!self->name;
-}
-
-ast_binary* ast_binary_new(lex_ctx_t ctx, int op,
-                           ast_expression* left, ast_expression* right)
-{
-    ast_instantiate(ast_binary, ctx, ast_binary_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_binary_codegen);
-
-    if (ast_istype(right, ast_unary) && OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) {
-        ast_unary      *unary  = ((ast_unary*)right);
-        ast_expression *normal = unary->operand;
-
-        /* make a-(-b) => a + b */
-        if (unary->op == VINSTR_NEG_F || unary->op == VINSTR_NEG_V) {
-            if (op == INSTR_SUB_F) {
-                op = INSTR_ADD_F;
-                right = normal;
-                ++opts_optimizationcount[OPTIM_PEEPHOLE];
-            } else if (op == INSTR_SUB_V) {
-                op = INSTR_ADD_V;
-                right = normal;
-                ++opts_optimizationcount[OPTIM_PEEPHOLE];
-            }
-        }
-    }
-
-    self->op = op;
-    self->left = left;
-    self->right = right;
-    self->right_first = false;
-
-    ast_propagate_effects(self, left);
-    ast_propagate_effects(self, right);
-
-    if (op >= INSTR_EQ_F && op <= INSTR_GT)
-        self->expression.vtype = TYPE_FLOAT;
-    else if (op == INSTR_AND || op == INSTR_OR) {
-        if (OPTS_FLAG(PERL_LOGIC))
-            ast_type_adopt(self, right);
-        else
-            self->expression.vtype = TYPE_FLOAT;
-    }
-    else if (op == INSTR_BITAND || op == INSTR_BITOR)
-        self->expression.vtype = TYPE_FLOAT;
-    else if (op == INSTR_MUL_VF || op == INSTR_MUL_FV)
-        self->expression.vtype = TYPE_VECTOR;
-    else if (op == INSTR_MUL_V)
-        self->expression.vtype = TYPE_FLOAT;
-    else
-        self->expression.vtype = left->vtype;
-
-    /* references all */
-    self->refs = AST_REF_ALL;
-
-    return self;
-}
-
-void ast_binary_delete(ast_binary *self)
-{
-    if (self->refs & AST_REF_LEFT)  ast_unref(self->left);
-    if (self->refs & AST_REF_RIGHT) ast_unref(self->right);
-
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_binstore* ast_binstore_new(lex_ctx_t ctx, int storop, int op,
-                               ast_expression* left, ast_expression* right)
-{
-    ast_instantiate(ast_binstore, ctx, ast_binstore_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_binstore_codegen);
-
-    ast_side_effects(self) = true;
-
-    self->opstore = storop;
-    self->opbin   = op;
-    self->dest    = left;
-    self->source  = right;
-
-    self->keep_dest = false;
-
-    ast_type_adopt(self, left);
-    return self;
-}
-
-void ast_binstore_delete(ast_binstore *self)
-{
-    if (!self->keep_dest)
-        ast_unref(self->dest);
-    ast_unref(self->source);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_unary* ast_unary_new(lex_ctx_t ctx, int op,
-                         ast_expression *expr)
-{
-    ast_instantiate(ast_unary, ctx, ast_unary_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_unary_codegen);
-
-    self->op      = op;
-    self->operand = expr;
-
-
-    if (ast_istype(expr, ast_unary) && OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) {
-        ast_unary *prev = (ast_unary*)((ast_unary*)expr)->operand;
-
-        /* Handle for double negation */
-        if (((ast_unary*)expr)->op == op)
-            prev = (ast_unary*)((ast_unary*)expr)->operand;
-
-        if (ast_istype(prev, ast_unary)) {
-            ast_expression_delete((ast_expression*)self);
-            mem_d(self);
-            ++opts_optimizationcount[OPTIM_PEEPHOLE];
-            return prev;
-        }
-    }
-
-    ast_propagate_effects(self, expr);
-
-    if ((op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) || op == VINSTR_NEG_F) {
-        self->expression.vtype = TYPE_FLOAT;
-    } else if (op == VINSTR_NEG_V) {
-        self->expression.vtype = TYPE_VECTOR;
-    } else {
-        compile_error(ctx, "cannot determine type of unary operation %s", util_instr_str[op]);
-    }
-
-    return self;
-}
-
-void ast_unary_delete(ast_unary *self)
-{
-    if (self->operand) ast_unref(self->operand);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_return* ast_return_new(lex_ctx_t ctx, ast_expression *expr)
-{
-    ast_instantiate(ast_return, ctx, ast_return_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_return_codegen);
-
-    self->operand = expr;
-
-    if (expr)
-        ast_propagate_effects(self, expr);
-
-    return self;
-}
-
-void ast_return_delete(ast_return *self)
-{
-    if (self->operand)
-        ast_unref(self->operand);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_entfield* ast_entfield_new(lex_ctx_t ctx, ast_expression *entity, ast_expression *field)
-{
-    if (field->vtype != TYPE_FIELD) {
-        compile_error(ctx, "ast_entfield_new with expression not of type field");
-        return NULL;
-    }
-    return ast_entfield_new_force(ctx, entity, field, field->next);
-}
-
-ast_entfield* ast_entfield_new_force(lex_ctx_t ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype)
-{
-    ast_instantiate(ast_entfield, ctx, ast_entfield_delete);
-
-    if (!outtype) {
-        mem_d(self);
-        /* Error: field has no type... */
-        return NULL;
-    }
-
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_entfield_codegen);
-
-    self->entity = entity;
-    self->field  = field;
-    ast_propagate_effects(self, entity);
-    ast_propagate_effects(self, field);
-
-    ast_type_adopt(self, outtype);
-    return self;
-}
-
-void ast_entfield_delete(ast_entfield *self)
-{
-    ast_unref(self->entity);
-    ast_unref(self->field);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_member* ast_member_new(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const char *name)
-{
-    ast_instantiate(ast_member, ctx, ast_member_delete);
-    if (field >= 3) {
-        mem_d(self);
-        return NULL;
-    }
-
-    if (owner->vtype != TYPE_VECTOR &&
-        owner->vtype != TYPE_FIELD) {
-        compile_error(ctx, "member-access on an invalid owner of type %s", type_name[owner->vtype]);
-        mem_d(self);
-        return NULL;
-    }
-
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_member_codegen);
-    self->expression.node.keep = true; /* keep */
-
-    if (owner->vtype == TYPE_VECTOR) {
-        self->expression.vtype = TYPE_FLOAT;
-        self->expression.next  = NULL;
-    } else {
-        self->expression.vtype = TYPE_FIELD;
-        self->expression.next = ast_shallow_type(ctx, TYPE_FLOAT);
-    }
-
-    self->rvalue = false;
-    self->owner  = owner;
-    ast_propagate_effects(self, owner);
-
-    self->field = field;
-    if (name)
-        self->name = util_strdup(name);
-    else
-        self->name = NULL;
-
-    return self;
-}
-
-void ast_member_delete(ast_member *self)
-{
-    /* The owner is always an ast_value, which has .keep=true,
-     * also: ast_members are usually deleted after the owner, thus
-     * this will cause invalid access
-    ast_unref(self->owner);
-     * once we allow (expression).x to access a vector-member, we need
-     * to change this: preferably by creating an alternate ast node for this
-     * purpose that is not garbage-collected.
-    */
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self->name);
-    mem_d(self);
-}
-
-bool ast_member_set_name(ast_member *self, const char *name)
-{
-    if (self->name)
-        mem_d((void*)self->name);
-    self->name = util_strdup(name);
-    return !!self->name;
-}
-
-ast_array_index* ast_array_index_new(lex_ctx_t ctx, ast_expression *array, ast_expression *index)
-{
-    ast_expression *outtype;
-    ast_instantiate(ast_array_index, ctx, ast_array_index_delete);
-
-    outtype = array->next;
-    if (!outtype) {
-        mem_d(self);
-        /* Error: field has no type... */
-        return NULL;
-    }
-
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_array_index_codegen);
-
-    self->array = array;
-    self->index = index;
-    ast_propagate_effects(self, array);
-    ast_propagate_effects(self, index);
-
-    ast_type_adopt(self, outtype);
-    if (array->vtype == TYPE_FIELD && outtype->vtype == TYPE_ARRAY) {
-        if (self->expression.vtype != TYPE_ARRAY) {
-            compile_error(ast_ctx(self), "array_index node on type");
-            ast_array_index_delete(self);
-            return NULL;
-        }
-        self->array = outtype;
-        self->expression.vtype = TYPE_FIELD;
-    }
-
-    return self;
-}
-
-void ast_array_index_delete(ast_array_index *self)
-{
-    if (self->array)
-        ast_unref(self->array);
-    if (self->index)
-        ast_unref(self->index);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_argpipe* ast_argpipe_new(lex_ctx_t ctx, ast_expression *index)
-{
-    ast_instantiate(ast_argpipe, ctx, ast_argpipe_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_argpipe_codegen);
-    self->index = index;
-    self->expression.vtype = TYPE_NOEXPR;
-    return self;
-}
-
-void ast_argpipe_delete(ast_argpipe *self)
-{
-    if (self->index)
-        ast_unref(self->index);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_ifthen* ast_ifthen_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse)
-{
-    ast_instantiate(ast_ifthen, ctx, ast_ifthen_delete);
-    if (!ontrue && !onfalse) {
-        /* because it is invalid */
-        mem_d(self);
-        return NULL;
-    }
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_ifthen_codegen);
-
-    self->cond     = cond;
-    self->on_true  = ontrue;
-    self->on_false = onfalse;
-    ast_propagate_effects(self, cond);
-    if (ontrue)
-        ast_propagate_effects(self, ontrue);
-    if (onfalse)
-        ast_propagate_effects(self, onfalse);
-
-    return self;
-}
-
-void ast_ifthen_delete(ast_ifthen *self)
-{
-    ast_unref(self->cond);
-    if (self->on_true)
-        ast_unref(self->on_true);
-    if (self->on_false)
-        ast_unref(self->on_false);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_ternary* ast_ternary_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse)
-{
-    ast_expression *exprtype = ontrue;
-    ast_instantiate(ast_ternary, ctx, ast_ternary_delete);
-    /* This time NEITHER must be NULL */
-    if (!ontrue || !onfalse) {
-        mem_d(self);
-        return NULL;
-    }
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_ternary_codegen);
-
-    self->cond     = cond;
-    self->on_true  = ontrue;
-    self->on_false = onfalse;
-    ast_propagate_effects(self, cond);
-    ast_propagate_effects(self, ontrue);
-    ast_propagate_effects(self, onfalse);
-
-    if (ontrue->vtype == TYPE_NIL)
-        exprtype = onfalse;
-    ast_type_adopt(self, exprtype);
-
-    return self;
-}
-
-void ast_ternary_delete(ast_ternary *self)
-{
-    /* the if()s are only there because computed-gotos can set them
-     * to NULL
-     */
-    if (self->cond)     ast_unref(self->cond);
-    if (self->on_true)  ast_unref(self->on_true);
-    if (self->on_false) ast_unref(self->on_false);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_loop* ast_loop_new(lex_ctx_t ctx,
-                       ast_expression *initexpr,
-                       ast_expression *precond, bool pre_not,
-                       ast_expression *postcond, bool post_not,
-                       ast_expression *increment,
-                       ast_expression *body)
-{
-    ast_instantiate(ast_loop, ctx, ast_loop_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_loop_codegen);
-
-    self->initexpr  = initexpr;
-    self->precond   = precond;
-    self->postcond  = postcond;
-    self->increment = increment;
-    self->body      = body;
-
-    self->pre_not   = pre_not;
-    self->post_not  = post_not;
-
-    if (initexpr)
-        ast_propagate_effects(self, initexpr);
-    if (precond)
-        ast_propagate_effects(self, precond);
-    if (postcond)
-        ast_propagate_effects(self, postcond);
-    if (increment)
-        ast_propagate_effects(self, increment);
-    if (body)
-        ast_propagate_effects(self, body);
-
-    return self;
-}
-
-void ast_loop_delete(ast_loop *self)
-{
-    if (self->initexpr)
-        ast_unref(self->initexpr);
-    if (self->precond)
-        ast_unref(self->precond);
-    if (self->postcond)
-        ast_unref(self->postcond);
-    if (self->increment)
-        ast_unref(self->increment);
-    if (self->body)
-        ast_unref(self->body);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_breakcont* ast_breakcont_new(lex_ctx_t ctx, bool iscont, unsigned int levels)
-{
-    ast_instantiate(ast_breakcont, ctx, ast_breakcont_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_breakcont_codegen);
-
-    self->is_continue = iscont;
-    self->levels      = levels;
-
-    return self;
-}
-
-void ast_breakcont_delete(ast_breakcont *self)
-{
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_switch* ast_switch_new(lex_ctx_t ctx, ast_expression *op)
-{
-    ast_instantiate(ast_switch, ctx, ast_switch_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_switch_codegen);
-
-    self->operand = op;
-    self->cases   = NULL;
-
-    ast_propagate_effects(self, op);
-
-    return self;
-}
-
-void ast_switch_delete(ast_switch *self)
-{
-    size_t i;
-    ast_unref(self->operand);
-
-    for (i = 0; i < vec_size(self->cases); ++i) {
-        if (self->cases[i].value)
-            ast_unref(self->cases[i].value);
-        ast_unref(self->cases[i].code);
-    }
-    vec_free(self->cases);
-
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_label* ast_label_new(lex_ctx_t ctx, const char *name, bool undefined)
-{
-    ast_instantiate(ast_label, ctx, ast_label_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_label_codegen);
-
-    self->expression.vtype = TYPE_NOEXPR;
-
-    self->name      = util_strdup(name);
-    self->irblock   = NULL;
-    self->gotos     = NULL;
-    self->undefined = undefined;
-
-    return self;
-}
-
-void ast_label_delete(ast_label *self)
-{
-    mem_d((void*)self->name);
-    vec_free(self->gotos);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-static void ast_label_register_goto(ast_label *self, ast_goto *g)
-{
-    vec_push(self->gotos, g);
-}
-
-ast_goto* ast_goto_new(lex_ctx_t ctx, const char *name)
-{
-    ast_instantiate(ast_goto, ctx, ast_goto_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_goto_codegen);
-
-    self->name    = util_strdup(name);
-    self->target  = NULL;
-    self->irblock_from = NULL;
-
-    return self;
-}
-
-void ast_goto_delete(ast_goto *self)
-{
-    mem_d((void*)self->name);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-void ast_goto_set_label(ast_goto *self, ast_label *label)
-{
-    self->target = label;
-}
-
-ast_state* ast_state_new(lex_ctx_t ctx, ast_expression *frame, ast_expression *think)
-{
-    ast_instantiate(ast_state, ctx, ast_state_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_state_codegen);
-    self->framenum  = frame;
-    self->nextthink = think;
-    return self;
-}
-
-void ast_state_delete(ast_state *self)
-{
-    if (self->framenum)
-        ast_unref(self->framenum);
-    if (self->nextthink)
-        ast_unref(self->nextthink);
-
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_call* ast_call_new(lex_ctx_t ctx,
-                       ast_expression *funcexpr)
-{
-    ast_instantiate(ast_call, ctx, ast_call_delete);
-    if (!funcexpr->next) {
-        compile_error(ctx, "not a function");
-        mem_d(self);
-        return NULL;
-    }
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_call_codegen);
-
-    ast_side_effects(self) = true;
-
-    self->params   = NULL;
-    self->func     = funcexpr;
-    self->va_count = NULL;
-
-    ast_type_adopt(self, funcexpr->next);
-
-    return self;
-}
-
-void ast_call_delete(ast_call *self)
-{
-    size_t i;
-    for (i = 0; i < vec_size(self->params); ++i)
-        ast_unref(self->params[i]);
-    vec_free(self->params);
-
-    if (self->func)
-        ast_unref(self->func);
-
-    if (self->va_count)
-        ast_unref(self->va_count);
-
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-static bool ast_call_check_vararg(ast_call *self, ast_expression *va_type, ast_expression *exp_type)
-{
-    char texp[1024];
-    char tgot[1024];
-    if (!exp_type)
-        return true;
-    if (!va_type || !ast_compare_type(va_type, exp_type))
-    {
-        if (va_type && exp_type)
-        {
-            ast_type_to_string(va_type,  tgot, sizeof(tgot));
-            ast_type_to_string(exp_type, texp, sizeof(texp));
-            if (OPTS_FLAG(UNSAFE_VARARGS)) {
-                if (compile_warning(ast_ctx(self), WARN_UNSAFE_TYPES,
-                                    "piped variadic argument differs in type: constrained to type %s, expected type %s",
-                                    tgot, texp))
-                    return false;
-            } else {
-                compile_error(ast_ctx(self),
-                              "piped variadic argument differs in type: constrained to type %s, expected type %s",
-                              tgot, texp);
-                return false;
-            }
-        }
-        else
-        {
-            ast_type_to_string(exp_type, texp, sizeof(texp));
-            if (OPTS_FLAG(UNSAFE_VARARGS)) {
-                if (compile_warning(ast_ctx(self), WARN_UNSAFE_TYPES,
-                                    "piped variadic argument may differ in type: expected type %s",
-                                    texp))
-                    return false;
-            } else {
-                compile_error(ast_ctx(self),
-                              "piped variadic argument may differ in type: expected type %s",
-                              texp);
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-bool ast_call_check_types(ast_call *self, ast_expression *va_type)
-{
-    char texp[1024];
-    char tgot[1024];
-    size_t i;
-    bool   retval = true;
-    const  ast_expression *func = self->func;
-    size_t count = vec_size(self->params);
-    if (count > vec_size(func->params))
-        count = vec_size(func->params);
-
-    for (i = 0; i < count; ++i) {
-        if (ast_istype(self->params[i], ast_argpipe)) {
-            /* warn about type safety instead */
-            if (i+1 != count) {
-                compile_error(ast_ctx(self), "argpipe must be the last parameter to a function call");
-                return false;
-            }
-            if (!ast_call_check_vararg(self, va_type, (ast_expression*)func->params[i]))
-                retval = false;
-        }
-        else if (!ast_compare_type(self->params[i], (ast_expression*)(func->params[i])))
-        {
-            ast_type_to_string(self->params[i], tgot, sizeof(tgot));
-            ast_type_to_string((ast_expression*)func->params[i], texp, sizeof(texp));
-            compile_error(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s",
-                     (unsigned int)(i+1), texp, tgot);
-            /* we don't immediately return */
-            retval = false;
-        }
-    }
-    count = vec_size(self->params);
-    if (count > vec_size(func->params) && func->varparam) {
-        for (; i < count; ++i) {
-            if (ast_istype(self->params[i], ast_argpipe)) {
-                /* warn about type safety instead */
-                if (i+1 != count) {
-                    compile_error(ast_ctx(self), "argpipe must be the last parameter to a function call");
-                    return false;
-                }
-                if (!ast_call_check_vararg(self, va_type, func->varparam))
-                    retval = false;
-            }
-            else if (!ast_compare_type(self->params[i], func->varparam))
-            {
-                ast_type_to_string(self->params[i], tgot, sizeof(tgot));
-                ast_type_to_string(func->varparam, texp, sizeof(texp));
-                compile_error(ast_ctx(self), "invalid type for variadic parameter %u in function call: expected %s, got %s",
-                         (unsigned int)(i+1), texp, tgot);
-                /* we don't immediately return */
-                retval = false;
-            }
-        }
-    }
-    return retval;
-}
-
-ast_store* ast_store_new(lex_ctx_t ctx, int op,
-                         ast_expression *dest, ast_expression *source)
-{
-    ast_instantiate(ast_store, ctx, ast_store_delete);
-    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_store_codegen);
-
-    ast_side_effects(self) = true;
-
-    self->op = op;
-    self->dest = dest;
-    self->source = source;
-
-    ast_type_adopt(self, dest);
-
-    return self;
-}
-
-void ast_store_delete(ast_store *self)
-{
-    ast_unref(self->dest);
-    ast_unref(self->source);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-ast_block* ast_block_new(lex_ctx_t ctx)
-{
-    ast_instantiate(ast_block, ctx, ast_block_delete);
-    ast_expression_init((ast_expression*)self,
-                        (ast_expression_codegen*)&ast_block_codegen);
-
-    self->locals  = NULL;
-    self->exprs   = NULL;
-    self->collect = NULL;
-
-    return self;
-}
-
-bool ast_block_add_expr(ast_block *self, ast_expression *e)
-{
-    ast_propagate_effects(self, e);
-    vec_push(self->exprs, e);
-    if (self->expression.next) {
-        ast_delete(self->expression.next);
-        self->expression.next = NULL;
-    }
-    ast_type_adopt(self, e);
-    return true;
-}
-
-void ast_block_collect(ast_block *self, ast_expression *expr)
-{
-    vec_push(self->collect, expr);
-    expr->node.keep = true;
-}
-
-void ast_block_delete(ast_block *self)
-{
-    size_t i;
-    for (i = 0; i < vec_size(self->exprs); ++i)
-        ast_unref(self->exprs[i]);
-    vec_free(self->exprs);
-    for (i = 0; i < vec_size(self->locals); ++i)
-        ast_delete(self->locals[i]);
-    vec_free(self->locals);
-    for (i = 0; i < vec_size(self->collect); ++i)
-        ast_delete(self->collect[i]);
-    vec_free(self->collect);
-    ast_expression_delete((ast_expression*)self);
-    mem_d(self);
-}
-
-void ast_block_set_type(ast_block *self, ast_expression *from)
-{
-    if (self->expression.next)
-        ast_delete(self->expression.next);
-    ast_type_adopt(self, from);
-}
-
-ast_function* ast_function_new(lex_ctx_t ctx, const char *name, ast_value *vtype)
-{
-    ast_instantiate(ast_function, ctx, ast_function_delete);
-
-    if (!vtype) {
-        compile_error(ast_ctx(self), "internal error: ast_function_new condition 0");
-        goto cleanup;
-    } else if (vtype->hasvalue || vtype->expression.vtype != TYPE_FUNCTION) {
-        compile_error(ast_ctx(self), "internal error: ast_function_new condition %i %i type=%i (probably 2 bodies?)",
-                 (int)!vtype,
-                 (int)vtype->hasvalue,
-                 vtype->expression.vtype);
-        goto cleanup;
-    }
-
-    self->vtype  = vtype;
-    self->name   = name ? util_strdup(name) : NULL;
-    self->blocks = NULL;
-
-    self->labelcount = 0;
-    self->builtin = 0;
-
-    self->ir_func = NULL;
-    self->curblock = NULL;
-
-    self->breakblocks    = NULL;
-    self->continueblocks = NULL;
-
-    vtype->hasvalue = true;
-    vtype->constval.vfunc = self;
-
-    self->varargs          = NULL;
-    self->argc             = NULL;
-    self->fixedparams      = NULL;
-    self->return_value     = NULL;
-
-    self->static_names     = NULL;
-    self->static_count     = 0;
-
-    return self;
-
-cleanup:
-    mem_d(self);
-    return NULL;
-}
-
-void ast_function_delete(ast_function *self)
-{
-    size_t i;
-    if (self->name)
-        mem_d((void*)self->name);
-    if (self->vtype) {
-        /* ast_value_delete(self->vtype); */
-        self->vtype->hasvalue = false;
-        self->vtype->constval.vfunc = NULL;
-        /* We use unref - if it was stored in a global table it is supposed
-         * to be deleted from *there*
-         */
-        ast_unref(self->vtype);
-    }
-    for (i = 0; i < vec_size(self->static_names); ++i)
-        mem_d(self->static_names[i]);
-    vec_free(self->static_names);
-    for (i = 0; i < vec_size(self->blocks); ++i)
-        ast_delete(self->blocks[i]);
-    vec_free(self->blocks);
-    vec_free(self->breakblocks);
-    vec_free(self->continueblocks);
-    if (self->varargs)
-        ast_delete(self->varargs);
-    if (self->argc)
-        ast_delete(self->argc);
-    if (self->fixedparams)
-        ast_unref(self->fixedparams);
-    if (self->return_value)
-        ast_unref(self->return_value);
-    mem_d(self);
-}
-
-const char* ast_function_label(ast_function *self, const char *prefix)
-{
-    size_t id;
-    size_t len;
-    char  *from;
-
-    if (!OPTS_OPTION_BOOL(OPTION_DUMP)    &&
-        !OPTS_OPTION_BOOL(OPTION_DUMPFIN) &&
-        !OPTS_OPTION_BOOL(OPTION_DEBUG))
-    {
-        return NULL;
-    }
-
-    id  = (self->labelcount++);
-    len = strlen(prefix);
-
-    from = self->labelbuf + sizeof(self->labelbuf)-1;
-    *from-- = 0;
-    do {
-        *from-- = (id%10) + '0';
-        id /= 10;
-    } while (id);
-    ++from;
-    memcpy(from - len, prefix, len);
-    return from - len;
-}
-
-/*********************************************************************/
-/* AST codegen part
- * by convention you must never pass NULL to the 'ir_value **out'
- * parameter. If you really don't care about the output, pass a dummy.
- * But I can't imagine a pituation where the output is truly unnecessary.
- */
-
-static void _ast_codegen_output_type(ast_expression *self, ir_value *out)
-{
-    if (out->vtype == TYPE_FIELD)
-        out->fieldtype = self->next->vtype;
-    if (out->vtype == TYPE_FUNCTION)
-        out->outtype = self->next->vtype;
-}
-
-#define codegen_output_type(a,o) (_ast_codegen_output_type(&((a)->expression),(o)))
-
-bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    (void)func;
-    (void)lvalue;
-    if (self->expression.vtype == TYPE_NIL) {
-        *out = func->ir_func->owner->nil;
-        return true;
-    }
-    /* NOTE: This is the codegen for a variable used in an expression.
-     * It is not the codegen to generate the value. For this purpose,
-     * ast_local_codegen and ast_global_codegen are to be used before this
-     * is executed. ast_function_codegen should take care of its locals,
-     * and the ast-user should take care of ast_global_codegen to be used
-     * on all the globals.
-     */
-    if (!self->ir_v) {
-        char tname[1024]; /* typename is reserved in C++ */
-        ast_type_to_string((ast_expression*)self, tname, sizeof(tname));
-        compile_error(ast_ctx(self), "ast_value used before generated %s %s", tname, self->name);
-        return false;
-    }
-    *out = self->ir_v;
-    return true;
-}
-
-static bool ast_global_array_set(ast_value *self)
-{
-    size_t count = vec_size(self->initlist);
-    size_t i;
-
-    if (count > self->expression.count) {
-        compile_error(ast_ctx(self), "too many elements in initializer");
-        count = self->expression.count;
-    }
-    else if (count < self->expression.count) {
-        /* add this?
-        compile_warning(ast_ctx(self), "not all elements are initialized");
-        */
-    }
-
-    for (i = 0; i != count; ++i) {
-        switch (self->expression.next->vtype) {
-            case TYPE_FLOAT:
-                if (!ir_value_set_float(self->ir_values[i], self->initlist[i].vfloat))
-                    return false;
-                break;
-            case TYPE_VECTOR:
-                if (!ir_value_set_vector(self->ir_values[i], self->initlist[i].vvec))
-                    return false;
-                break;
-            case TYPE_STRING:
-                if (!ir_value_set_string(self->ir_values[i], self->initlist[i].vstring))
-                    return false;
-                break;
-            case TYPE_ARRAY:
-                /* we don't support them in any other place yet either */
-                compile_error(ast_ctx(self), "TODO: nested arrays");
-                return false;
-            case TYPE_FUNCTION:
-                /* this requiers a bit more work - similar to the fields I suppose */
-                compile_error(ast_ctx(self), "global of type function not properly generated");
-                return false;
-            case TYPE_FIELD:
-                if (!self->initlist[i].vfield) {
-                    compile_error(ast_ctx(self), "field constant without vfield set");
-                    return false;
-                }
-                if (!self->initlist[i].vfield->ir_v) {
-                    compile_error(ast_ctx(self), "field constant generated before its field");
-                    return false;
-                }
-                if (!ir_value_set_field(self->ir_values[i], self->initlist[i].vfield->ir_v))
-                    return false;
-                break;
-            default:
-                compile_error(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
-                break;
-        }
-    }
-    return true;
-}
-
-static bool check_array(ast_value *self, ast_value *array)
-{
-    if (array->expression.flags & AST_FLAG_ARRAY_INIT && !array->initlist) {
-        compile_error(ast_ctx(self), "array without size: %s", self->name);
-        return false;
-    }
-    /* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
-    if (!array->expression.count || array->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE)) {
-        compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count);
-        return false;
-    }
-    return true;
-}
-
-bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
-{
-    ir_value *v = NULL;
-
-    if (self->expression.vtype == TYPE_NIL) {
-        compile_error(ast_ctx(self), "internal error: trying to generate a variable of TYPE_NIL");
-        return false;
-    }
-
-    if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
-    {
-        ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->vtype);
-        if (!func)
-            return false;
-        func->context = ast_ctx(self);
-        func->value->context = ast_ctx(self);
-
-        self->constval.vfunc->ir_func = func;
-        self->ir_v = func->value;
-        if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
-            self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
-        if (self->expression.flags & AST_FLAG_ERASEABLE)
-            self->ir_v->flags |= IR_FLAG_ERASABLE;
-        if (self->expression.flags & AST_FLAG_BLOCK_COVERAGE)
-            func->flags |= IR_FLAG_BLOCK_COVERAGE;
-        /* The function is filled later on ast_function_codegen... */
-        return true;
-    }
-
-    if (isfield && self->expression.vtype == TYPE_FIELD) {
-        ast_expression *fieldtype = self->expression.next;
-
-        if (self->hasvalue) {
-            compile_error(ast_ctx(self), "TODO: constant field pointers with value");
-            goto error;
-        }
-
-        if (fieldtype->vtype == TYPE_ARRAY) {
-            size_t ai;
-            char   *name;
-            size_t  namelen;
-
-            ast_expression *elemtype;
-            int             vtype;
-            ast_value      *array = (ast_value*)fieldtype;
-
-            if (!ast_istype(fieldtype, ast_value)) {
-                compile_error(ast_ctx(self), "internal error: ast_value required");
-                return false;
-            }
-
-            if (!check_array(self, array))
-                return false;
-
-            elemtype = array->expression.next;
-            vtype = elemtype->vtype;
-
-            v = ir_builder_create_field(ir, self->name, vtype);
-            if (!v) {
-                compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name);
-                return false;
-            }
-            v->context = ast_ctx(self);
-            v->unique_life = true;
-            v->locked      = true;
-            array->ir_v = self->ir_v = v;
-
-            if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
-                self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
-            if (self->expression.flags & AST_FLAG_ERASEABLE)
-                self->ir_v->flags |= IR_FLAG_ERASABLE;
-
-            namelen = strlen(self->name);
-            name    = (char*)mem_a(namelen + 16);
-            util_strncpy(name, self->name, namelen);
-
-            array->ir_values = (ir_value**)mem_a(sizeof(array->ir_values[0]) * array->expression.count);
-            array->ir_values[0] = v;
-            for (ai = 1; ai < array->expression.count; ++ai) {
-                util_snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
-                array->ir_values[ai] = ir_builder_create_field(ir, name, vtype);
-                if (!array->ir_values[ai]) {
-                    mem_d(name);
-                    compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", name);
-                    return false;
-                }
-                array->ir_values[ai]->context = ast_ctx(self);
-                array->ir_values[ai]->unique_life = true;
-                array->ir_values[ai]->locked      = true;
-                if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
-                    self->ir_values[ai]->flags |= IR_FLAG_INCLUDE_DEF;
-            }
-            mem_d(name);
-        }
-        else
-        {
-            v = ir_builder_create_field(ir, self->name, self->expression.next->vtype);
-            if (!v)
-                return false;
-            v->context = ast_ctx(self);
-            self->ir_v = v;
-            if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
-                self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
-
-            if (self->expression.flags & AST_FLAG_ERASEABLE)
-                self->ir_v->flags |= IR_FLAG_ERASABLE;
-        }
-        return true;
-    }
-
-    if (self->expression.vtype == TYPE_ARRAY) {
-        size_t ai;
-        char   *name;
-        size_t  namelen;
-
-        ast_expression *elemtype = self->expression.next;
-        int vtype = elemtype->vtype;
-
-        if (self->expression.flags & AST_FLAG_ARRAY_INIT && !self->expression.count) {
-            compile_error(ast_ctx(self), "array `%s' has no size", self->name);
-            return false;
-        }
-
-        /* same as with field arrays */
-        if (!check_array(self, self))
-            return false;
-
-        v = ir_builder_create_global(ir, self->name, vtype);
-        if (!v) {
-            compile_error(ast_ctx(self), "ir_builder_create_global failed `%s`", self->name);
-            return false;
-        }
-        v->context = ast_ctx(self);
-        v->unique_life = true;
-        v->locked      = true;
-
-        if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
-            v->flags |= IR_FLAG_INCLUDE_DEF;
-        if (self->expression.flags & AST_FLAG_ERASEABLE)
-            self->ir_v->flags |= IR_FLAG_ERASABLE;
-
-        namelen = strlen(self->name);
-        name    = (char*)mem_a(namelen + 16);
-        util_strncpy(name, self->name, namelen);
-
-        self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count);
-        self->ir_values[0] = v;
-        for (ai = 1; ai < self->expression.count; ++ai) {
-            util_snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
-            self->ir_values[ai] = ir_builder_create_global(ir, name, vtype);
-            if (!self->ir_values[ai]) {
-                mem_d(name);
-                compile_error(ast_ctx(self), "ir_builder_create_global failed `%s`", name);
-                return false;
-            }
-            self->ir_values[ai]->context = ast_ctx(self);
-            self->ir_values[ai]->unique_life = true;
-            self->ir_values[ai]->locked      = true;
-            if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
-                self->ir_values[ai]->flags |= IR_FLAG_INCLUDE_DEF;
-        }
-        mem_d(name);
-    }
-    else
-    {
-        /* Arrays don't do this since there's no "array" value which spans across the
-         * whole thing.
-         */
-        v = ir_builder_create_global(ir, self->name, self->expression.vtype);
-        if (!v) {
-            compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name);
-            return false;
-        }
-        codegen_output_type(self, v);
-        v->context = ast_ctx(self);
-    }
-
-    /* link us to the ir_value */
-    v->cvq = self->cvq;
-    self->ir_v = v;
-
-    if (self->expression.flags & AST_FLAG_INCLUDE_DEF)
-        self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
-    if (self->expression.flags & AST_FLAG_ERASEABLE)
-        self->ir_v->flags |= IR_FLAG_ERASABLE;
-
-    /* initialize */
-    if (self->hasvalue) {
-        switch (self->expression.vtype)
-        {
-            case TYPE_FLOAT:
-                if (!ir_value_set_float(v, self->constval.vfloat))
-                    goto error;
-                break;
-            case TYPE_VECTOR:
-                if (!ir_value_set_vector(v, self->constval.vvec))
-                    goto error;
-                break;
-            case TYPE_STRING:
-                if (!ir_value_set_string(v, self->constval.vstring))
-                    goto error;
-                break;
-            case TYPE_ARRAY:
-                ast_global_array_set(self);
-                break;
-            case TYPE_FUNCTION:
-                compile_error(ast_ctx(self), "global of type function not properly generated");
-                goto error;
-                /* Cannot generate an IR value for a function,
-                 * need a pointer pointing to a function rather.
-                 */
-            case TYPE_FIELD:
-                if (!self->constval.vfield) {
-                    compile_error(ast_ctx(self), "field constant without vfield set");
-                    goto error;
-                }
-                if (!self->constval.vfield->ir_v) {
-                    compile_error(ast_ctx(self), "field constant generated before its field");
-                    goto error;
-                }
-                if (!ir_value_set_field(v, self->constval.vfield->ir_v))
-                    goto error;
-                break;
-            default:
-                compile_error(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
-                break;
-        }
-    }
-    return true;
-
-error: /* clean up */
-    if(v) ir_value_delete(v);
-    return false;
-}
-
-static bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
-{
-    ir_value *v = NULL;
-
-    if (self->expression.vtype == TYPE_NIL) {
-        compile_error(ast_ctx(self), "internal error: trying to generate a variable of TYPE_NIL");
-        return false;
-    }
-
-    if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
-    {
-        /* Do we allow local functions? I think not...
-         * this is NOT a function pointer atm.
-         */
-        return false;
-    }
-
-    if (self->expression.vtype == TYPE_ARRAY) {
-        size_t ai;
-        char   *name;
-        size_t  namelen;
-
-        ast_expression *elemtype = self->expression.next;
-        int vtype = elemtype->vtype;
-
-        func->flags |= IR_FLAG_HAS_ARRAYS;
-
-        if (param && !(self->expression.flags & AST_FLAG_IS_VARARG)) {
-            compile_error(ast_ctx(self), "array-parameters are not supported");
-            return false;
-        }
-
-        /* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
-        if (!check_array(self, self))
-            return false;
-
-        self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count);
-        if (!self->ir_values) {
-            compile_error(ast_ctx(self), "failed to allocate array values");
-            return false;
-        }
-
-        v = ir_function_create_local(func, self->name, vtype, param);
-        if (!v) {
-            compile_error(ast_ctx(self), "internal error: ir_function_create_local failed");
-            return false;
-        }
-        v->context = ast_ctx(self);
-        v->unique_life = true;
-        v->locked      = true;
-
-        namelen = strlen(self->name);
-        name    = (char*)mem_a(namelen + 16);
-        util_strncpy(name, self->name, namelen);
-
-        self->ir_values[0] = v;
-        for (ai = 1; ai < self->expression.count; ++ai) {
-            util_snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
-            self->ir_values[ai] = ir_function_create_local(func, name, vtype, param);
-            if (!self->ir_values[ai]) {
-                compile_error(ast_ctx(self), "internal_error: ir_builder_create_global failed on `%s`", name);
-                return false;
-            }
-            self->ir_values[ai]->context = ast_ctx(self);
-            self->ir_values[ai]->unique_life = true;
-            self->ir_values[ai]->locked      = true;
-        }
-        mem_d(name);
-    }
-    else
-    {
-        v = ir_function_create_local(func, self->name, self->expression.vtype, param);
-        if (!v)
-            return false;
-        codegen_output_type(self, v);
-        v->context = ast_ctx(self);
-    }
-
-    /* A constant local... hmmm...
-     * I suppose the IR will have to deal with this
-     */
-    if (self->hasvalue) {
-        switch (self->expression.vtype)
-        {
-            case TYPE_FLOAT:
-                if (!ir_value_set_float(v, self->constval.vfloat))
-                    goto error;
-                break;
-            case TYPE_VECTOR:
-                if (!ir_value_set_vector(v, self->constval.vvec))
-                    goto error;
-                break;
-            case TYPE_STRING:
-                if (!ir_value_set_string(v, self->constval.vstring))
-                    goto error;
-                break;
-            default:
-                compile_error(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
-                break;
-        }
-    }
-
-    /* link us to the ir_value */
-    v->cvq = self->cvq;
-    self->ir_v = v;
-
-    if (!ast_generate_accessors(self, func->owner))
-        return false;
-    return true;
-
-error: /* clean up */
-    ir_value_delete(v);
-    return false;
-}
-
-bool ast_generate_accessors(ast_value *self, ir_builder *ir)
-{
-    size_t i;
-    bool warn = OPTS_WARN(WARN_USED_UNINITIALIZED);
-    if (!self->setter || !self->getter)
-        return true;
-    for (i = 0; i < self->expression.count; ++i) {
-        if (!self->ir_values) {
-            compile_error(ast_ctx(self), "internal error: no array values generated for `%s`", self->name);
-            return false;
-        }
-        if (!self->ir_values[i]) {
-            compile_error(ast_ctx(self), "internal error: not all array values have been generated for `%s`", self->name);
-            return false;
-        }
-        if (self->ir_values[i]->life) {
-            compile_error(ast_ctx(self), "internal error: function containing `%s` already generated", self->name);
-            return false;
-        }
-    }
-
-    opts_set(opts.warn, WARN_USED_UNINITIALIZED, false);
-    if (self->setter) {
-        if (!ast_global_codegen  (self->setter, ir, false) ||
-            !ast_function_codegen(self->setter->constval.vfunc, ir) ||
-            !ir_function_finalize(self->setter->constval.vfunc->ir_func))
-        {
-            compile_error(ast_ctx(self), "internal error: failed to generate setter for `%s`", self->name);
-            opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
-            return false;
-        }
-    }
-    if (self->getter) {
-        if (!ast_global_codegen  (self->getter, ir, false) ||
-            !ast_function_codegen(self->getter->constval.vfunc, ir) ||
-            !ir_function_finalize(self->getter->constval.vfunc->ir_func))
-        {
-            compile_error(ast_ctx(self), "internal error: failed to generate getter for `%s`", self->name);
-            opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
-            return false;
-        }
-    }
-    for (i = 0; i < self->expression.count; ++i) {
-        vec_free(self->ir_values[i]->life);
-    }
-    opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
-    return true;
-}
-
-bool ast_function_codegen(ast_function *self, ir_builder *ir)
-{
-    ir_function *irf;
-    ir_value    *dummy;
-    ast_expression         *ec;
-    ast_expression_codegen *cgen;
-
-    size_t    i;
-
-    (void)ir;
-
-    irf = self->ir_func;
-    if (!irf) {
-        compile_error(ast_ctx(self), "internal error: ast_function's related ast_value was not generated yet");
-        return false;
-    }
-
-    /* fill the parameter list */
-    ec = &self->vtype->expression;
-    for (i = 0; i < vec_size(ec->params); ++i)
-    {
-        if (ec->params[i]->expression.vtype == TYPE_FIELD)
-            vec_push(irf->params, ec->params[i]->expression.next->vtype);
-        else
-            vec_push(irf->params, ec->params[i]->expression.vtype);
-        if (!self->builtin) {
-            if (!ast_local_codegen(ec->params[i], self->ir_func, true))
-                return false;
-        }
-    }
-
-    if (self->varargs) {
-        if (!ast_local_codegen(self->varargs, self->ir_func, true))
-            return false;
-        irf->max_varargs = self->varargs->expression.count;
-    }
-
-    if (self->builtin) {
-        irf->builtin = self->builtin;
-        return true;
-    }
-
-    /* have a local return value variable? */
-    if (self->return_value) {
-        if (!ast_local_codegen(self->return_value, self->ir_func, false))
-            return false;
-    }
-
-    if (!vec_size(self->blocks)) {
-        compile_error(ast_ctx(self), "function `%s` has no body", self->name);
-        return false;
-    }
-
-    irf->first = self->curblock = ir_function_create_block(ast_ctx(self), irf, "entry");
-    if (!self->curblock) {
-        compile_error(ast_ctx(self), "failed to allocate entry block for `%s`", self->name);
-        return false;
-    }
-
-    if (self->argc) {
-        ir_value *va_count;
-        ir_value *fixed;
-        ir_value *sub;
-        if (!ast_local_codegen(self->argc, self->ir_func, true))
-            return false;
-        cgen = self->argc->expression.codegen;
-        if (!(*cgen)((ast_expression*)(self->argc), self, false, &va_count))
-            return false;
-        cgen = self->fixedparams->expression.codegen;
-        if (!(*cgen)((ast_expression*)(self->fixedparams), self, false, &fixed))
-            return false;
-        sub = ir_block_create_binop(self->curblock, ast_ctx(self),
-                                    ast_function_label(self, "va_count"), INSTR_SUB_F,
-                                    ir_builder_get_va_count(ir), fixed);
-        if (!sub)
-            return false;
-        if (!ir_block_create_store_op(self->curblock, ast_ctx(self), INSTR_STORE_F,
-                                      va_count, sub))
-        {
-            return false;
-        }
-    }
-
-    for (i = 0; i < vec_size(self->blocks); ++i) {
-        cgen = self->blocks[i]->expression.codegen;
-        if (!(*cgen)((ast_expression*)self->blocks[i], self, false, &dummy))
-            return false;
-    }
-
-    /* TODO: check return types */
-    if (!self->curblock->final)
-    {
-        if (!self->vtype->expression.next ||
-            self->vtype->expression.next->vtype == TYPE_VOID)
-        {
-            return ir_block_create_return(self->curblock, ast_ctx(self), NULL);
-        }
-        else if (vec_size(self->curblock->entries) || self->curblock == irf->first)
-        {
-            if (self->return_value) {
-                cgen = self->return_value->expression.codegen;
-                if (!(*cgen)((ast_expression*)(self->return_value), self, false, &dummy))
-                    return false;
-                return ir_block_create_return(self->curblock, ast_ctx(self), dummy);
-            }
-            else if (compile_warning(ast_ctx(self), WARN_MISSING_RETURN_VALUES,
-                                "control reaches end of non-void function (`%s`) via %s",
-                                self->name, self->curblock->label))
-            {
-                return false;
-            }
-            return ir_block_create_return(self->curblock, ast_ctx(self), NULL);
-        }
-    }
-    return true;
-}
-
-static bool starts_a_label(ast_expression *ex)
-{
-    while (ex && ast_istype(ex, ast_block)) {
-        ast_block *b = (ast_block*)ex;
-        ex = b->exprs[0];
-    }
-    if (!ex)
-        return false;
-    return ast_istype(ex, ast_label);
-}
-
-/* Note, you will not see ast_block_codegen generate ir_blocks.
- * To the AST and the IR, blocks are 2 different things.
- * In the AST it represents a block of code, usually enclosed in
- * curly braces {...}.
- * While in the IR it represents a block in terms of control-flow.
- */
-bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    size_t i;
-
-    /* We don't use this
-     * Note: an ast-representation using the comma-operator
-     * of the form: (a, b, c) = x should not assign to c...
-     */
-    if (lvalue) {
-        compile_error(ast_ctx(self), "not an l-value (code-block)");
-        return false;
-    }
-
-    if (self->expression.outr) {
-        *out = self->expression.outr;
-        return true;
-    }
-
-    /* output is NULL at first, we'll have each expression
-     * assign to out output, thus, a comma-operator represention
-     * using an ast_block will return the last generated value,
-     * so: (b, c) + a  executed both b and c, and returns c,
-     * which is then added to a.
-     */
-    *out = NULL;
-
-    /* generate locals */
-    for (i = 0; i < vec_size(self->locals); ++i)
-    {
-        if (!ast_local_codegen(self->locals[i], func->ir_func, false)) {
-            if (OPTS_OPTION_BOOL(OPTION_DEBUG))
-                compile_error(ast_ctx(self), "failed to generate local `%s`", self->locals[i]->name);
-            return false;
-        }
-    }
-
-    for (i = 0; i < vec_size(self->exprs); ++i)
-    {
-        ast_expression_codegen *gen;
-        if (func->curblock->final && !starts_a_label(self->exprs[i])) {
-            if (compile_warning(ast_ctx(self->exprs[i]), WARN_UNREACHABLE_CODE, "unreachable statement"))
-                return false;
-            continue;
-        }
-        gen = self->exprs[i]->codegen;
-        if (!(*gen)(self->exprs[i], func, false, out))
-            return false;
-    }
-
-    self->expression.outr = *out;
-
-    return true;
-}
-
-bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-    ir_value *left  = NULL;
-    ir_value *right = NULL;
-
-    ast_value       *arr;
-    ast_value       *idx = 0;
-    ast_array_index *ai = NULL;
-
-    if (lvalue && self->expression.outl) {
-        *out = self->expression.outl;
-        return true;
-    }
-
-    if (!lvalue && self->expression.outr) {
-        *out = self->expression.outr;
-        return true;
-    }
-
-    if (ast_istype(self->dest, ast_array_index))
-    {
-
-        ai = (ast_array_index*)self->dest;
-        idx = (ast_value*)ai->index;
-
-        if (ast_istype(ai->index, ast_value) && idx->hasvalue && idx->cvq == CV_CONST)
-            ai = NULL;
-    }
-
-    if (ai) {
-        /* we need to call the setter */
-        ir_value  *iridx, *funval;
-        ir_instr  *call;
-
-        if (lvalue) {
-            compile_error(ast_ctx(self), "array-subscript assignment cannot produce lvalues");
-            return false;
-        }
-
-        arr = (ast_value*)ai->array;
-        if (!ast_istype(ai->array, ast_value) || !arr->setter) {
-            compile_error(ast_ctx(self), "value has no setter (%s)", arr->name);
-            return false;
-        }
-
-        cgen = idx->expression.codegen;
-        if (!(*cgen)((ast_expression*)(idx), func, false, &iridx))
-            return false;
-
-        cgen = arr->setter->expression.codegen;
-        if (!(*cgen)((ast_expression*)(arr->setter), func, true, &funval))
-            return false;
-
-        cgen = self->source->codegen;
-        if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
-            return false;
-
-        call = ir_block_create_call(func->curblock, ast_ctx(self), ast_function_label(func, "store"), funval, false);
-        if (!call)
-            return false;
-        ir_call_param(call, iridx);
-        ir_call_param(call, right);
-        self->expression.outr = right;
-    }
-    else
-    {
-        /* regular code */
-
-        cgen = self->dest->codegen;
-        /* lvalue! */
-        if (!(*cgen)((ast_expression*)(self->dest), func, true, &left))
-            return false;
-        self->expression.outl = left;
-
-        cgen = self->source->codegen;
-        /* rvalue! */
-        if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
-            return false;
-
-        if (!ir_block_create_store_op(func->curblock, ast_ctx(self), self->op, left, right))
-            return false;
-        self->expression.outr = right;
-    }
-
-    /* Theoretically, an assinment returns its left side as an
-     * lvalue, if we don't need an lvalue though, we return
-     * the right side as an rvalue, otherwise we have to
-     * somehow know whether or not we need to dereference the pointer
-     * on the left side - that is: OP_LOAD if it was an address.
-     * Also: in original QC we cannot OP_LOADP *anyway*.
-     */
-    *out = (lvalue ? left : right);
-
-    return true;
-}
-
-bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-    ir_value *left, *right;
-
-    /* A binary operation cannot yield an l-value */
-    if (lvalue) {
-        compile_error(ast_ctx(self), "not an l-value (binop)");
-        return false;
-    }
-
-    if (self->expression.outr) {
-        *out = self->expression.outr;
-        return true;
-    }
-
-    if ((OPTS_FLAG(SHORT_LOGIC) || OPTS_FLAG(PERL_LOGIC)) &&
-        (self->op == INSTR_AND || self->op == INSTR_OR))
-    {
-        /* NOTE: The short-logic path will ignore right_first */
-
-        /* short circuit evaluation */
-        ir_block *other, *merge;
-        ir_block *from_left, *from_right;
-        ir_instr *phi;
-        size_t    merge_id;
-
-        /* prepare end-block */
-        merge_id = vec_size(func->ir_func->blocks);
-        merge    = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_merge"));
-
-        /* generate the left expression */
-        cgen = self->left->codegen;
-        if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
-            return false;
-        /* remember the block */
-        from_left = func->curblock;
-
-        /* create a new block for the right expression */
-        other = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_other"));
-        if (self->op == INSTR_AND) {
-            /* on AND: left==true -> other */
-            if (!ir_block_create_if(func->curblock, ast_ctx(self), left, other, merge))
-                return false;
-        } else {
-            /* on OR: left==false -> other */
-            if (!ir_block_create_if(func->curblock, ast_ctx(self), left, merge, other))
-                return false;
-        }
-        /* use the likely flag */
-        vec_last(func->curblock->instr)->likely = true;
-
-        /* enter the right-expression's block */
-        func->curblock = other;
-        /* generate */
-        cgen = self->right->codegen;
-        if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
-            return false;
-        /* remember block */
-        from_right = func->curblock;
-
-        /* jump to the merge block */
-        if (!ir_block_create_jump(func->curblock, ast_ctx(self), merge))
-            return false;
-
-        vec_remove(func->ir_func->blocks, merge_id, 1);
-        vec_push(func->ir_func->blocks, merge);
-
-        func->curblock = merge;
-        phi = ir_block_create_phi(func->curblock, ast_ctx(self),
-                                  ast_function_label(func, "sce_value"),
-                                  self->expression.vtype);
-        ir_phi_add(phi, from_left, left);
-        ir_phi_add(phi, from_right, right);
-        *out = ir_phi_value(phi);
-        if (!*out)
-            return false;
-
-        if (!OPTS_FLAG(PERL_LOGIC)) {
-            /* cast-to-bool */
-            if (OPTS_FLAG(CORRECT_LOGIC) && (*out)->vtype == TYPE_VECTOR) {
-                *out = ir_block_create_unary(func->curblock, ast_ctx(self),
-                                             ast_function_label(func, "sce_bool_v"),
-                                             INSTR_NOT_V, *out);
-                if (!*out)
-                    return false;
-                *out = ir_block_create_unary(func->curblock, ast_ctx(self),
-                                             ast_function_label(func, "sce_bool"),
-                                             INSTR_NOT_F, *out);
-                if (!*out)
-                    return false;
-            }
-            else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && (*out)->vtype == TYPE_STRING) {
-                *out = ir_block_create_unary(func->curblock, ast_ctx(self),
-                                             ast_function_label(func, "sce_bool_s"),
-                                             INSTR_NOT_S, *out);
-                if (!*out)
-                    return false;
-                *out = ir_block_create_unary(func->curblock, ast_ctx(self),
-                                             ast_function_label(func, "sce_bool"),
-                                             INSTR_NOT_F, *out);
-                if (!*out)
-                    return false;
-            }
-            else {
-                *out = ir_block_create_binop(func->curblock, ast_ctx(self),
-                                             ast_function_label(func, "sce_bool"),
-                                             INSTR_AND, *out, *out);
-                if (!*out)
-                    return false;
-            }
-        }
-
-        self->expression.outr = *out;
-        codegen_output_type(self, *out);
-        return true;
-    }
-
-    if (self->right_first) {
-        cgen = self->right->codegen;
-        if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
-            return false;
-        cgen = self->left->codegen;
-        if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
-            return false;
-    } else {
-        cgen = self->left->codegen;
-        if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
-            return false;
-        cgen = self->right->codegen;
-        if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
-            return false;
-    }
-
-    *out = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "bin"),
-                                 self->op, left, right);
-    if (!*out)
-        return false;
-    self->expression.outr = *out;
-    codegen_output_type(self, *out);
-
-    return true;
-}
-
-bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-    ir_value *leftl = NULL, *leftr, *right, *bin;
-
-    ast_value       *arr;
-    ast_value       *idx = 0;
-    ast_array_index *ai = NULL;
-    ir_value        *iridx = NULL;
-
-    if (lvalue && self->expression.outl) {
-        *out = self->expression.outl;
-        return true;
-    }
-
-    if (!lvalue && self->expression.outr) {
-        *out = self->expression.outr;
-        return true;
-    }
-
-    if (ast_istype(self->dest, ast_array_index))
-    {
-
-        ai = (ast_array_index*)self->dest;
-        idx = (ast_value*)ai->index;
-
-        if (ast_istype(ai->index, ast_value) && idx->hasvalue && idx->cvq == CV_CONST)
-            ai = NULL;
-    }
-
-    /* for a binstore we need both an lvalue and an rvalue for the left side */
-    /* rvalue of destination! */
-    if (ai) {
-        cgen = idx->expression.codegen;
-        if (!(*cgen)((ast_expression*)(idx), func, false, &iridx))
-            return false;
-    }
-    cgen = self->dest->codegen;
-    if (!(*cgen)((ast_expression*)(self->dest), func, false, &leftr))
-        return false;
-
-    /* source as rvalue only */
-    cgen = self->source->codegen;
-    if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
-        return false;
-
-    /* now the binary */
-    bin = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "binst"),
-                                self->opbin, leftr, right);
-    self->expression.outr = bin;
-
-
-    if (ai) {
-        /* we need to call the setter */
-        ir_value  *funval;
-        ir_instr  *call;
-
-        if (lvalue) {
-            compile_error(ast_ctx(self), "array-subscript assignment cannot produce lvalues");
-            return false;
-        }
-
-        arr = (ast_value*)ai->array;
-        if (!ast_istype(ai->array, ast_value) || !arr->setter) {
-            compile_error(ast_ctx(self), "value has no setter (%s)", arr->name);
-            return false;
-        }
-
-        cgen = arr->setter->expression.codegen;
-        if (!(*cgen)((ast_expression*)(arr->setter), func, true, &funval))
-            return false;
-
-        call = ir_block_create_call(func->curblock, ast_ctx(self), ast_function_label(func, "store"), funval, false);
-        if (!call)
-            return false;
-        ir_call_param(call, iridx);
-        ir_call_param(call, bin);
-        self->expression.outr = bin;
-    } else {
-        /* now store them */
-        cgen = self->dest->codegen;
-        /* lvalue of destination */
-        if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftl))
-            return false;
-        self->expression.outl = leftl;
-
-        if (!ir_block_create_store_op(func->curblock, ast_ctx(self), self->opstore, leftl, bin))
-            return false;
-        self->expression.outr = bin;
-    }
-
-    /* Theoretically, an assinment returns its left side as an
-     * lvalue, if we don't need an lvalue though, we return
-     * the right side as an rvalue, otherwise we have to
-     * somehow know whether or not we need to dereference the pointer
-     * on the left side - that is: OP_LOAD if it was an address.
-     * Also: in original QC we cannot OP_LOADP *anyway*.
-     */
-    *out = (lvalue ? leftl : bin);
-
-    return true;
-}
-
-bool ast_unary_codegen(ast_unary *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-    ir_value *operand;
-
-    /* An unary operation cannot yield an l-value */
-    if (lvalue) {
-        compile_error(ast_ctx(self), "not an l-value (binop)");
-        return false;
-    }
-
-    if (self->expression.outr) {
-        *out = self->expression.outr;
-        return true;
-    }
-
-    cgen = self->operand->codegen;
-    /* lvalue! */
-    if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand))
-        return false;
-
-    *out = ir_block_create_unary(func->curblock, ast_ctx(self), ast_function_label(func, "unary"),
-                                 self->op, operand);
-    if (!*out)
-        return false;
-    self->expression.outr = *out;
-
-    return true;
-}
-
-bool ast_return_codegen(ast_return *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-    ir_value *operand;
-
-    *out = NULL;
-
-    /* In the context of a return operation, we don't actually return
-     * anything...
-     */
-    if (lvalue) {
-        compile_error(ast_ctx(self), "return-expression is not an l-value");
-        return false;
-    }
-
-    if (self->expression.outr) {
-        compile_error(ast_ctx(self), "internal error: ast_return cannot be reused, it bears no result!");
-        return false;
-    }
-    self->expression.outr = (ir_value*)1;
-
-    if (self->operand) {
-        cgen = self->operand->codegen;
-        /* lvalue! */
-        if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand))
-            return false;
-
-        if (!ir_block_create_return(func->curblock, ast_ctx(self), operand))
-            return false;
-    } else {
-        if (!ir_block_create_return(func->curblock, ast_ctx(self), NULL))
-            return false;
-    }
-
-    return true;
-}
-
-bool ast_entfield_codegen(ast_entfield *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-    ir_value *ent, *field;
-
-    /* This function needs to take the 'lvalue' flag into account!
-     * As lvalue we provide a field-pointer, as rvalue we provide the
-     * value in a temp.
-     */
-
-    if (lvalue && self->expression.outl) {
-        *out = self->expression.outl;
-        return true;
-    }
-
-    if (!lvalue && self->expression.outr) {
-        *out = self->expression.outr;
-        return true;
-    }
-
-    cgen = self->entity->codegen;
-    if (!(*cgen)((ast_expression*)(self->entity), func, false, &ent))
-        return false;
-
-    cgen = self->field->codegen;
-    if (!(*cgen)((ast_expression*)(self->field), func, false, &field))
-        return false;
-
-    if (lvalue) {
-        /* address! */
-        *out = ir_block_create_fieldaddress(func->curblock, ast_ctx(self), ast_function_label(func, "efa"),
-                                            ent, field);
-    } else {
-        *out = ir_block_create_load_from_ent(func->curblock, ast_ctx(self), ast_function_label(func, "efv"),
-                                             ent, field, self->expression.vtype);
-        /* Done AFTER error checking:
-        codegen_output_type(self, *out);
-        */
-    }
-    if (!*out) {
-        compile_error(ast_ctx(self), "failed to create %s instruction (output type %s)",
-                 (lvalue ? "ADDRESS" : "FIELD"),
-                 type_name[self->expression.vtype]);
-        return false;
-    }
-    if (!lvalue)
-        codegen_output_type(self, *out);
-
-    if (lvalue)
-        self->expression.outl = *out;
-    else
-        self->expression.outr = *out;
-
-    /* Hm that should be it... */
-    return true;
-}
-
-bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-    ir_value *vec;
-
-    /* in QC this is always an lvalue */
-    if (lvalue && self->rvalue) {
-        compile_error(ast_ctx(self), "not an l-value (member access)");
-        return false;
-    }
-    if (self->expression.outl) {
-        *out = self->expression.outl;
-        return true;
-    }
-
-    cgen = self->owner->codegen;
-    if (!(*cgen)((ast_expression*)(self->owner), func, false, &vec))
-        return false;
-
-    if (vec->vtype != TYPE_VECTOR &&
-        !(vec->vtype == TYPE_FIELD && self->owner->next->vtype == TYPE_VECTOR))
-    {
-        return false;
-    }
-
-    *out = ir_value_vector_member(vec, self->field);
-    self->expression.outl = *out;
-
-    return (*out != NULL);
-}
-
-bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_value *arr;
-    ast_value *idx;
-
-    if (!lvalue && self->expression.outr) {
-        *out = self->expression.outr;
-        return true;
-    }
-    if (lvalue && self->expression.outl) {
-        *out = self->expression.outl;
-        return true;
-    }
-
-    if (!ast_istype(self->array, ast_value)) {
-        compile_error(ast_ctx(self), "array indexing this way is not supported");
-        /* note this would actually be pointer indexing because the left side is
-         * not an actual array but (hopefully) an indexable expression.
-         * Once we get integer arithmetic, and GADDRESS/GSTORE/GLOAD instruction
-         * support this path will be filled.
-         */
-        return false;
-    }
-
-    arr = (ast_value*)self->array;
-    idx = (ast_value*)self->index;
-
-    if (!ast_istype(self->index, ast_value) || !idx->hasvalue || idx->cvq != CV_CONST) {
-        /* Time to use accessor functions */
-        ast_expression_codegen *cgen;
-        ir_value               *iridx, *funval;
-        ir_instr               *call;
-
-        if (lvalue) {
-            compile_error(ast_ctx(self), "(.2) array indexing here needs a compile-time constant");
-            return false;
-        }
-
-        if (!arr->getter) {
-            compile_error(ast_ctx(self), "value has no getter, don't know how to index it");
-            return false;
-        }
-
-        cgen = self->index->codegen;
-        if (!(*cgen)((ast_expression*)(self->index), func, false, &iridx))
-            return false;
-
-        cgen = arr->getter->expression.codegen;
-        if (!(*cgen)((ast_expression*)(arr->getter), func, true, &funval))
-            return false;
-
-        call = ir_block_create_call(func->curblock, ast_ctx(self), ast_function_label(func, "fetch"), funval, false);
-        if (!call)
-            return false;
-        ir_call_param(call, iridx);
-
-        *out = ir_call_value(call);
-        self->expression.outr = *out;
-        (*out)->vtype = self->expression.vtype;
-        codegen_output_type(self, *out);
-        return true;
-    }
-
-    if (idx->expression.vtype == TYPE_FLOAT) {
-        unsigned int arridx = idx->constval.vfloat;
-        if (arridx >= self->array->count)
-        {
-            compile_error(ast_ctx(self), "array index out of bounds: %i", arridx);
-            return false;
-        }
-        *out = arr->ir_values[arridx];
-    }
-    else if (idx->expression.vtype == TYPE_INTEGER) {
-        unsigned int arridx = idx->constval.vint;
-        if (arridx >= self->array->count)
-        {
-            compile_error(ast_ctx(self), "array index out of bounds: %i", arridx);
-            return false;
-        }
-        *out = arr->ir_values[arridx];
-    }
-    else {
-        compile_error(ast_ctx(self), "array indexing here needs an integer constant");
-        return false;
-    }
-    (*out)->vtype = self->expression.vtype;
-    codegen_output_type(self, *out);
-    return true;
-}
-
-bool ast_argpipe_codegen(ast_argpipe *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    *out = NULL;
-    if (lvalue) {
-        compile_error(ast_ctx(self), "argpipe node: not an lvalue");
-        return false;
-    }
-    (void)func;
-    (void)out;
-    compile_error(ast_ctx(self), "TODO: argpipe codegen not implemented");
-    return false;
-}
-
-bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-
-    ir_value *condval;
-    ir_value *dummy;
-
-    ir_block *cond;
-    ir_block *ontrue;
-    ir_block *onfalse;
-    ir_block *ontrue_endblock = NULL;
-    ir_block *onfalse_endblock = NULL;
-    ir_block *merge = NULL;
-    int       fold  = 0;
-
-    /* We don't output any value, thus also don't care about r/lvalue */
-    (void)out;
-    (void)lvalue;
-
-    if (self->expression.outr) {
-        compile_error(ast_ctx(self), "internal error: ast_ifthen cannot be reused, it bears no result!");
-        return false;
-    }
-    self->expression.outr = (ir_value*)1;
-
-    /* generate the condition */
-    cgen = self->cond->codegen;
-    if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval))
-        return false;
-    /* update the block which will get the jump - because short-logic or ternaries may have changed this */
-    cond = func->curblock;
-
-    /* try constant folding away the condition */
-    if ((fold = fold_cond_ifthen(condval, func, self)) != -1)
-        return fold;
-
-    if (self->on_true) {
-        /* create on-true block */
-        ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "ontrue"));
-        if (!ontrue)
-            return false;
-
-        /* enter the block */
-        func->curblock = ontrue;
-
-        /* generate */
-        cgen = self->on_true->codegen;
-        if (!(*cgen)((ast_expression*)(self->on_true), func, false, &dummy))
-            return false;
-
-        /* we now need to work from the current endpoint */
-        ontrue_endblock = func->curblock;
-    } else
-        ontrue = NULL;
-
-    /* on-false path */
-    if (self->on_false) {
-        /* create on-false block */
-        onfalse = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "onfalse"));
-        if (!onfalse)
-            return false;
-
-        /* enter the block */
-        func->curblock = onfalse;
-
-        /* generate */
-        cgen = self->on_false->codegen;
-        if (!(*cgen)((ast_expression*)(self->on_false), func, false, &dummy))
-            return false;
-
-        /* we now need to work from the current endpoint */
-        onfalse_endblock = func->curblock;
-    } else
-        onfalse = NULL;
-
-    /* Merge block were they all merge in to */
-    if (!ontrue || !onfalse || !ontrue_endblock->final || !onfalse_endblock->final)
-    {
-        merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "endif"));
-        if (!merge)
-            return false;
-        /* add jumps ot the merge block */
-        if (ontrue && !ontrue_endblock->final && !ir_block_create_jump(ontrue_endblock, ast_ctx(self), merge))
-            return false;
-        if (onfalse && !onfalse_endblock->final && !ir_block_create_jump(onfalse_endblock, ast_ctx(self), merge))
-            return false;
-
-        /* Now enter the merge block */
-        func->curblock = merge;
-    }
-
-    /* we create the if here, that way all blocks are ordered :)
-     */
-    if (!ir_block_create_if(cond, ast_ctx(self), condval,
-                            (ontrue  ? ontrue  : merge),
-                            (onfalse ? onfalse : merge)))
-    {
-        return false;
-    }
-
-    return true;
-}
-
-bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-
-    ir_value *condval;
-    ir_value *trueval, *falseval;
-    ir_instr *phi;
-
-    ir_block *cond = func->curblock;
-    ir_block *cond_out = NULL;
-    ir_block *ontrue, *ontrue_out = NULL;
-    ir_block *onfalse, *onfalse_out = NULL;
-    ir_block *merge;
-    int       fold  = 0;
-
-    /* Ternary can never create an lvalue... */
-    if (lvalue)
-        return false;
-
-    /* In theory it shouldn't be possible to pass through a node twice, but
-     * in case we add any kind of optimization pass for the AST itself, it
-     * may still happen, thus we remember a created ir_value and simply return one
-     * if it already exists.
-     */
-    if (self->expression.outr) {
-        *out = self->expression.outr;
-        return true;
-    }
-
-    /* In the following, contraty to ast_ifthen, we assume both paths exist. */
-
-    /* generate the condition */
-    func->curblock = cond;
-    cgen = self->cond->codegen;
-    if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval))
-        return false;
-    cond_out = func->curblock;
-
-    /* try constant folding away the condition */
-    if ((fold = fold_cond_ternary(condval, func, self)) != -1)
-        return fold;
-
-    /* create on-true block */
-    ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_T"));
-    if (!ontrue)
-        return false;
-    else
-    {
-        /* enter the block */
-        func->curblock = ontrue;
-
-        /* generate */
-        cgen = self->on_true->codegen;
-        if (!(*cgen)((ast_expression*)(self->on_true), func, false, &trueval))
-            return false;
-
-        ontrue_out = func->curblock;
-    }
-
-    /* create on-false block */
-    onfalse = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_F"));
-    if (!onfalse)
-        return false;
-    else
-    {
-        /* enter the block */
-        func->curblock = onfalse;
-
-        /* generate */
-        cgen = self->on_false->codegen;
-        if (!(*cgen)((ast_expression*)(self->on_false), func, false, &falseval))
-            return false;
-
-        onfalse_out = func->curblock;
-    }
-
-    /* create merge block */
-    merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_out"));
-    if (!merge)
-        return false;
-    /* jump to merge block */
-    if (!ir_block_create_jump(ontrue_out, ast_ctx(self), merge))
-        return false;
-    if (!ir_block_create_jump(onfalse_out, ast_ctx(self), merge))
-        return false;
-
-    /* create if instruction */
-    if (!ir_block_create_if(cond_out, ast_ctx(self), condval, ontrue, onfalse))
-        return false;
-
-    /* Now enter the merge block */
-    func->curblock = merge;
-
-    /* Here, now, we need a PHI node
-     * but first some sanity checking...
-     */
-    if (trueval->vtype != falseval->vtype && trueval->vtype != TYPE_NIL && falseval->vtype != TYPE_NIL) {
-        /* error("ternary with different types on the two sides"); */
-        compile_error(ast_ctx(self), "internal error: ternary operand types invalid");
-        return false;
-    }
-
-    /* create PHI */
-    phi = ir_block_create_phi(merge, ast_ctx(self), ast_function_label(func, "phi"), self->expression.vtype);
-    if (!phi) {
-        compile_error(ast_ctx(self), "internal error: failed to generate phi node");
-        return false;
-    }
-    ir_phi_add(phi, ontrue_out,  trueval);
-    ir_phi_add(phi, onfalse_out, falseval);
-
-    self->expression.outr = ir_phi_value(phi);
-    *out = self->expression.outr;
-
-    codegen_output_type(self, *out);
-
-    return true;
-}
-
-bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-
-    ir_value *dummy      = NULL;
-    ir_value *precond    = NULL;
-    ir_value *postcond   = NULL;
-
-    /* Since we insert some jumps "late" so we have blocks
-     * ordered "nicely", we need to keep track of the actual end-blocks
-     * of expressions to add the jumps to.
-     */
-    ir_block *bbody      = NULL, *end_bbody      = NULL;
-    ir_block *bprecond   = NULL, *end_bprecond   = NULL;
-    ir_block *bpostcond  = NULL, *end_bpostcond  = NULL;
-    ir_block *bincrement = NULL, *end_bincrement = NULL;
-    ir_block *bout       = NULL, *bin            = NULL;
-
-    /* let's at least move the outgoing block to the end */
-    size_t    bout_id;
-
-    /* 'break' and 'continue' need to be able to find the right blocks */
-    ir_block *bcontinue     = NULL;
-    ir_block *bbreak        = NULL;
-
-    ir_block *tmpblock      = NULL;
-
-    (void)lvalue;
-    (void)out;
-
-    if (self->expression.outr) {
-        compile_error(ast_ctx(self), "internal error: ast_loop cannot be reused, it bears no result!");
-        return false;
-    }
-    self->expression.outr = (ir_value*)1;
-
-    /* NOTE:
-     * Should we ever need some kind of block ordering, better make this function
-     * move blocks around than write a block ordering algorithm later... after all
-     * the ast and ir should work together, not against each other.
-     */
-
-    /* initexpr doesn't get its own block, it's pointless, it could create more blocks
-     * anyway if for example it contains a ternary.
-     */
-    if (self->initexpr)
-    {
-        cgen = self->initexpr->codegen;
-        if (!(*cgen)((ast_expression*)(self->initexpr), func, false, &dummy))
-            return false;
-    }
-
-    /* Store the block from which we enter this chaos */
-    bin = func->curblock;
-
-    /* The pre-loop condition needs its own block since we
-     * need to be able to jump to the start of that expression.
-     */
-    if (self->precond)
-    {
-        bprecond = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "pre_loop_cond"));
-        if (!bprecond)
-            return false;
-
-        /* the pre-loop-condition the least important place to 'continue' at */
-        bcontinue = bprecond;
-
-        /* enter */
-        func->curblock = bprecond;
-
-        /* generate */
-        cgen = self->precond->codegen;
-        if (!(*cgen)((ast_expression*)(self->precond), func, false, &precond))
-            return false;
-
-        end_bprecond = func->curblock;
-    } else {
-        bprecond = end_bprecond = NULL;
-    }
-
-    /* Now the next blocks won't be ordered nicely, but we need to
-     * generate them this early for 'break' and 'continue'.
-     */
-    if (self->increment) {
-        bincrement = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "loop_increment"));
-        if (!bincrement)
-            return false;
-        bcontinue = bincrement; /* increment comes before the pre-loop-condition */
-    } else {
-        bincrement = end_bincrement = NULL;
-    }
-
-    if (self->postcond) {
-        bpostcond = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "post_loop_cond"));
-        if (!bpostcond)
-            return false;
-        bcontinue = bpostcond; /* postcond comes before the increment */
-    } else {
-        bpostcond = end_bpostcond = NULL;
-    }
-
-    bout_id = vec_size(func->ir_func->blocks);
-    bout = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "after_loop"));
-    if (!bout)
-        return false;
-    bbreak = bout;
-
-    /* The loop body... */
-    /* if (self->body) */
-    {
-        bbody = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "loop_body"));
-        if (!bbody)
-            return false;
-
-        /* enter */
-        func->curblock = bbody;
-
-        vec_push(func->breakblocks,    bbreak);
-        if (bcontinue)
-            vec_push(func->continueblocks, bcontinue);
-        else
-            vec_push(func->continueblocks, bbody);
-
-        /* generate */
-        if (self->body) {
-            cgen = self->body->codegen;
-            if (!(*cgen)((ast_expression*)(self->body), func, false, &dummy))
-                return false;
-        }
-
-        end_bbody = func->curblock;
-        vec_pop(func->breakblocks);
-        vec_pop(func->continueblocks);
-    }
-
-    /* post-loop-condition */
-    if (self->postcond)
-    {
-        /* enter */
-        func->curblock = bpostcond;
-
-        /* generate */
-        cgen = self->postcond->codegen;
-        if (!(*cgen)((ast_expression*)(self->postcond), func, false, &postcond))
-            return false;
-
-        end_bpostcond = func->curblock;
-    }
-
-    /* The incrementor */
-    if (self->increment)
-    {
-        /* enter */
-        func->curblock = bincrement;
-
-        /* generate */
-        cgen = self->increment->codegen;
-        if (!(*cgen)((ast_expression*)(self->increment), func, false, &dummy))
-            return false;
-
-        end_bincrement = func->curblock;
-    }
-
-    /* In any case now, we continue from the outgoing block */
-    func->curblock = bout;
-
-    /* Now all blocks are in place */
-    /* From 'bin' we jump to whatever comes first */
-    if      (bprecond)   tmpblock = bprecond;
-    else                 tmpblock = bbody;    /* can never be null */
-
-    /* DEAD CODE
-    else if (bpostcond)  tmpblock = bpostcond;
-    else                 tmpblock = bout;
-    */
-
-    if (!ir_block_create_jump(bin, ast_ctx(self), tmpblock))
-        return false;
-
-    /* From precond */
-    if (bprecond)
-    {
-        ir_block *ontrue, *onfalse;
-        ontrue = bbody; /* can never be null */
-
-        /* all of this is dead code
-        else if (bincrement) ontrue = bincrement;
-        else                 ontrue = bpostcond;
-        */
-
-        onfalse = bout;
-        if (self->pre_not) {
-            tmpblock = ontrue;
-            ontrue   = onfalse;
-            onfalse  = tmpblock;
-        }
-        if (!ir_block_create_if(end_bprecond, ast_ctx(self), precond, ontrue, onfalse))
-            return false;
-    }
-
-    /* from body */
-    if (bbody)
-    {
-        if      (bincrement) tmpblock = bincrement;
-        else if (bpostcond)  tmpblock = bpostcond;
-        else if (bprecond)   tmpblock = bprecond;
-        else                 tmpblock = bbody;
-        if (!end_bbody->final && !ir_block_create_jump(end_bbody, ast_ctx(self), tmpblock))
-            return false;
-    }
-
-    /* from increment */
-    if (bincrement)
-    {
-        if      (bpostcond)  tmpblock = bpostcond;
-        else if (bprecond)   tmpblock = bprecond;
-        else if (bbody)      tmpblock = bbody;
-        else                 tmpblock = bout;
-        if (!ir_block_create_jump(end_bincrement, ast_ctx(self), tmpblock))
-            return false;
-    }
-
-    /* from postcond */
-    if (bpostcond)
-    {
-        ir_block *ontrue, *onfalse;
-        if      (bprecond)   ontrue = bprecond;
-        else                 ontrue = bbody; /* can never be null */
-
-        /* all of this is dead code
-        else if (bincrement) ontrue = bincrement;
-        else                 ontrue = bpostcond;
-        */
-
-        onfalse = bout;
-        if (self->post_not) {
-            tmpblock = ontrue;
-            ontrue   = onfalse;
-            onfalse  = tmpblock;
-        }
-        if (!ir_block_create_if(end_bpostcond, ast_ctx(self), postcond, ontrue, onfalse))
-            return false;
-    }
-
-    /* Move 'bout' to the end */
-    vec_remove(func->ir_func->blocks, bout_id, 1);
-    vec_push(func->ir_func->blocks, bout);
-
-    return true;
-}
-
-bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ir_block *target;
-
-    *out = NULL;
-
-    if (lvalue) {
-        compile_error(ast_ctx(self), "break/continue expression is not an l-value");
-        return false;
-    }
-
-    if (self->expression.outr) {
-        compile_error(ast_ctx(self), "internal error: ast_breakcont cannot be reused!");
-        return false;
-    }
-    self->expression.outr = (ir_value*)1;
-
-    if (self->is_continue)
-        target = func->continueblocks[vec_size(func->continueblocks)-1-self->levels];
-    else
-        target = func->breakblocks[vec_size(func->breakblocks)-1-self->levels];
-
-    if (!target) {
-        compile_error(ast_ctx(self), "%s is lacking a target block", (self->is_continue ? "continue" : "break"));
-        return false;
-    }
-
-    if (!ir_block_create_jump(func->curblock, ast_ctx(self), target))
-        return false;
-    return true;
-}
-
-bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-
-    ast_switch_case *def_case     = NULL;
-    ir_block        *def_bfall    = NULL;
-    ir_block        *def_bfall_to = NULL;
-    bool set_def_bfall_to = false;
-
-    ir_value *dummy     = NULL;
-    ir_value *irop      = NULL;
-    ir_block *bout      = NULL;
-    ir_block *bfall     = NULL;
-    size_t    bout_id;
-    size_t    c;
-
-    char      typestr[1024];
-    uint16_t  cmpinstr;
-
-    if (lvalue) {
-        compile_error(ast_ctx(self), "switch expression is not an l-value");
-        return false;
-    }
-
-    if (self->expression.outr) {
-        compile_error(ast_ctx(self), "internal error: ast_switch cannot be reused!");
-        return false;
-    }
-    self->expression.outr = (ir_value*)1;
-
-    (void)lvalue;
-    (void)out;
-
-    cgen = self->operand->codegen;
-    if (!(*cgen)((ast_expression*)(self->operand), func, false, &irop))
-        return false;
-
-    if (!vec_size(self->cases))
-        return true;
-
-    cmpinstr = type_eq_instr[irop->vtype];
-    if (cmpinstr >= VINSTR_END) {
-        ast_type_to_string(self->operand, typestr, sizeof(typestr));
-        compile_error(ast_ctx(self), "invalid type to perform a switch on: %s", typestr);
-        return false;
-    }
-
-    bout_id = vec_size(func->ir_func->blocks);
-    bout = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "after_switch"));
-    if (!bout)
-        return false;
-
-    /* setup the break block */
-    vec_push(func->breakblocks, bout);
-
-    /* Now create all cases */
-    for (c = 0; c < vec_size(self->cases); ++c) {
-        ir_value *cond, *val;
-        ir_block *bcase, *bnot;
-        size_t bnot_id;
-
-        ast_switch_case *swcase = &self->cases[c];
-
-        if (swcase->value) {
-            /* A regular case */
-            /* generate the condition operand */
-            cgen = swcase->value->codegen;
-            if (!(*cgen)((ast_expression*)(swcase->value), func, false, &val))
-                return false;
-            /* generate the condition */
-            cond = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "switch_eq"), cmpinstr, irop, val);
-            if (!cond)
-                return false;
-
-            bcase = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "case"));
-            bnot_id = vec_size(func->ir_func->blocks);
-            bnot = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "not_case"));
-            if (!bcase || !bnot)
-                return false;
-            if (set_def_bfall_to) {
-                set_def_bfall_to = false;
-                def_bfall_to = bcase;
-            }
-            if (!ir_block_create_if(func->curblock, ast_ctx(self), cond, bcase, bnot))
-                return false;
-
-            /* Make the previous case-end fall through */
-            if (bfall && !bfall->final) {
-                if (!ir_block_create_jump(bfall, ast_ctx(self), bcase))
-                    return false;
-            }
-
-            /* enter the case */
-            func->curblock = bcase;
-            cgen = swcase->code->codegen;
-            if (!(*cgen)((ast_expression*)swcase->code, func, false, &dummy))
-                return false;
-
-            /* remember this block to fall through from */
-            bfall = func->curblock;
-
-            /* enter the else and move it down */
-            func->curblock = bnot;
-            vec_remove(func->ir_func->blocks, bnot_id, 1);
-            vec_push(func->ir_func->blocks, bnot);
-        } else {
-            /* The default case */
-            /* Remember where to fall through from: */
-            def_bfall = bfall;
-            bfall     = NULL;
-            /* remember which case it was */
-            def_case  = swcase;
-            /* And the next case will be remembered */
-            set_def_bfall_to = true;
-        }
-    }
-
-    /* Jump from the last bnot to bout */
-    if (bfall && !bfall->final && !ir_block_create_jump(bfall, ast_ctx(self), bout)) {
-        /*
-        astwarning(ast_ctx(bfall), WARN_???, "missing break after last case");
-        */
-        return false;
-    }
-
-    /* If there was a default case, put it down here */
-    if (def_case) {
-        ir_block *bcase;
-
-        /* No need to create an extra block */
-        bcase = func->curblock;
-
-        /* Insert the fallthrough jump */
-        if (def_bfall && !def_bfall->final) {
-            if (!ir_block_create_jump(def_bfall, ast_ctx(self), bcase))
-                return false;
-        }
-
-        /* Now generate the default code */
-        cgen = def_case->code->codegen;
-        if (!(*cgen)((ast_expression*)def_case->code, func, false, &dummy))
-            return false;
-
-        /* see if we need to fall through */
-        if (def_bfall_to && !func->curblock->final)
-        {
-            if (!ir_block_create_jump(func->curblock, ast_ctx(self), def_bfall_to))
-                return false;
-        }
-    }
-
-    /* Jump from the last bnot to bout */
-    if (!func->curblock->final && !ir_block_create_jump(func->curblock, ast_ctx(self), bout))
-        return false;
-    /* enter the outgoing block */
-    func->curblock = bout;
-
-    /* restore the break block */
-    vec_pop(func->breakblocks);
-
-    /* Move 'bout' to the end, it's nicer */
-    vec_remove(func->ir_func->blocks, bout_id, 1);
-    vec_push(func->ir_func->blocks, bout);
-
-    return true;
-}
-
-bool ast_label_codegen(ast_label *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    size_t i;
-    ir_value *dummy;
-
-    if (self->undefined) {
-        compile_error(ast_ctx(self), "internal error: ast_label never defined");
-        return false;
-    }
-
-    *out = NULL;
-    if (lvalue) {
-        compile_error(ast_ctx(self), "internal error: ast_label cannot be an lvalue");
-        return false;
-    }
-
-    /* simply create a new block and jump to it */
-    self->irblock = ir_function_create_block(ast_ctx(self), func->ir_func, self->name);
-    if (!self->irblock) {
-        compile_error(ast_ctx(self), "failed to allocate label block `%s`", self->name);
-        return false;
-    }
-    if (!func->curblock->final) {
-        if (!ir_block_create_jump(func->curblock, ast_ctx(self), self->irblock))
-            return false;
-    }
-
-    /* enter the new block */
-    func->curblock = self->irblock;
-
-    /* Generate all the leftover gotos */
-    for (i = 0; i < vec_size(self->gotos); ++i) {
-        if (!ast_goto_codegen(self->gotos[i], func, false, &dummy))
-            return false;
-    }
-
-    return true;
-}
-
-bool ast_goto_codegen(ast_goto *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    *out = NULL;
-    if (lvalue) {
-        compile_error(ast_ctx(self), "internal error: ast_goto cannot be an lvalue");
-        return false;
-    }
-
-    if (self->target->irblock) {
-        if (self->irblock_from) {
-            /* we already tried once, this is the callback */
-            self->irblock_from->final = false;
-            if (!ir_block_create_goto(self->irblock_from, ast_ctx(self), self->target->irblock)) {
-                compile_error(ast_ctx(self), "failed to generate goto to `%s`", self->name);
-                return false;
-            }
-        }
-        else
-        {
-            if (!ir_block_create_goto(func->curblock, ast_ctx(self), self->target->irblock)) {
-                compile_error(ast_ctx(self), "failed to generate goto to `%s`", self->name);
-                return false;
-            }
-        }
-    }
-    else
-    {
-        /* the target has not yet been created...
-         * close this block in a sneaky way:
-         */
-        func->curblock->final = true;
-        self->irblock_from = func->curblock;
-        ast_label_register_goto(self->target, self);
-    }
-
-    return true;
-}
-
-#include <stdio.h>
-bool ast_state_codegen(ast_state *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-
-    ir_value *frameval, *thinkval;
-
-    if (lvalue) {
-        compile_error(ast_ctx(self), "not an l-value (state operation)");
-        return false;
-    }
-    if (self->expression.outr) {
-        compile_error(ast_ctx(self), "internal error: ast_state cannot be reused!");
-        return false;
-    }
-    *out = NULL;
-
-    cgen = self->framenum->codegen;
-    if (!(*cgen)((ast_expression*)(self->framenum), func, false, &frameval))
-        return false;
-    if (!frameval)
-        return false;
-
-    cgen = self->nextthink->codegen;
-    if (!(*cgen)((ast_expression*)(self->nextthink), func, false, &thinkval))
-        return false;
-    if (!frameval)
-        return false;
-
-    if (!ir_block_create_state_op(func->curblock, ast_ctx(self), frameval, thinkval)) {
-        compile_error(ast_ctx(self), "failed to create STATE instruction");
-        return false;
-    }
-
-    self->expression.outr = (ir_value*)1;
-    return true;
-}
-
-bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out)
-{
-    ast_expression_codegen *cgen;
-    ir_value              **params;
-    ir_instr               *callinstr;
-    size_t i;
-
-    ir_value *funval = NULL;
-
-    /* return values are never lvalues */
-    if (lvalue) {
-        compile_error(ast_ctx(self), "not an l-value (function call)");
-        return false;
-    }
-
-    if (self->expression.outr) {
-        *out = self->expression.outr;
-        return true;
-    }
-
-    cgen = self->func->codegen;
-    if (!(*cgen)((ast_expression*)(self->func), func, false, &funval))
-        return false;
-    if (!funval)
-        return false;
-
-    params = NULL;
-
-    /* parameters */
-    for (i = 0; i < vec_size(self->params); ++i)
-    {
-        ir_value *param;
-        ast_expression *expr = self->params[i];
-
-        cgen = expr->codegen;
-        if (!(*cgen)(expr, func, false, &param))
-            goto error;
-        if (!param)
-            goto error;
-        vec_push(params, param);
-    }
-
-    /* varargs counter */
-    if (self->va_count) {
-        ir_value   *va_count;
-        ir_builder *builder = func->curblock->owner->owner;
-        cgen = self->va_count->codegen;
-        if (!(*cgen)((ast_expression*)(self->va_count), func, false, &va_count))
-            return false;
-        if (!ir_block_create_store_op(func->curblock, ast_ctx(self), INSTR_STORE_F,
-                                      ir_builder_get_va_count(builder), va_count))
-        {
-            return false;
-        }
-    }
-
-    callinstr = ir_block_create_call(func->curblock, ast_ctx(self),
-                                     ast_function_label(func, "call"),
-                                     funval, !!(self->func->flags & AST_FLAG_NORETURN));
-    if (!callinstr)
-        goto error;
-
-    for (i = 0; i < vec_size(params); ++i) {
-        ir_call_param(callinstr, params[i]);
-    }
-
-    *out = ir_call_value(callinstr);
-    self->expression.outr = *out;
-
-    codegen_output_type(self, *out);
-
-    vec_free(params);
-    return true;
-error:
-    vec_free(params);
-    return false;
-}
diff --git a/ast.cpp b/ast.cpp
new file mode 100644 (file)
index 0000000..cf8ffc7
--- /dev/null
+++ b/ast.cpp
@@ -0,0 +1,3051 @@
+#include <new>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gmqcc.h"
+#include "ast.h"
+#include "fold.h"
+//#include "parser.h"
+
+#include "algo.h"
+
+/* Initialize main ast node aprts */
+ast_node::ast_node(lex_ctx_t ctx, int node_type)
+    : m_context(ctx)
+    , m_node_type(node_type)
+    , m_keep_node(false)
+    , m_side_effects(false)
+{
+}
+
+ast_node::~ast_node()
+{
+}
+
+/* weight and side effects */
+void ast_node::propagateSideEffects(const ast_node *other)
+{
+    if (other->m_side_effects)
+        m_side_effects = true;
+}
+
+/* General expression initialization */
+ast_expression::ast_expression(lex_ctx_t ctx, int nodetype, qc_type type)
+    : ast_node(ctx, nodetype)
+    , m_vtype(type)
+{
+    if (OPTS_OPTION_BOOL(OPTION_COVERAGE))
+        m_flags |= AST_FLAG_BLOCK_COVERAGE;
+}
+ast_expression::ast_expression(lex_ctx_t ctx, int nodetype)
+    : ast_expression(ctx, nodetype, TYPE_VOID)
+{}
+
+ast_expression::~ast_expression()
+{
+    if (m_next)
+        delete m_next;
+    if (m_varparam)
+        delete m_varparam;
+}
+
+ast_expression::ast_expression(ast_copy_type_t, const ast_expression &other)
+    : ast_expression(ast_copy_type, other.m_context, other)
+{}
+
+ast_expression::ast_expression(ast_copy_type_t, lex_ctx_t ctx, const ast_expression &other)
+    : ast_expression(ast_copy_type, TYPE_ast_expression, ctx, other)
+{}
+
+ast_expression::ast_expression(ast_copy_type_t, int nodetype, const ast_expression &other)
+    : ast_expression(ast_copy_type, nodetype, other.m_context, other)
+{}
+
+ast_expression::ast_expression(ast_copy_type_t, int nodetype, lex_ctx_t ctx, const ast_expression &other)
+    : ast_expression(ctx, nodetype)
+{
+    m_vtype = other.m_vtype;
+    m_count = other.m_count;
+    m_flags = other.m_flags;
+    if (other.m_next)
+        m_next = new ast_expression(ast_copy_type, *other.m_next);
+    m_type_params.reserve(other.m_type_params.size());
+    for (auto &it : other.m_type_params)
+        m_type_params.emplace_back(new ast_value(ast_copy_type, *it));
+}
+
+
+ast_expression *ast_expression::shallowType(lex_ctx_t ctx, qc_type vtype) {
+    auto expr = new ast_expression(ctx, TYPE_ast_expression);
+    expr->m_vtype = vtype;
+    return expr;
+}
+
+void ast_expression::adoptType(const ast_expression &other)
+{
+    m_vtype = other.m_vtype;
+    if (other.m_next)
+        m_next = new ast_expression(ast_copy_type, *other.m_next);
+    m_count = other.m_count;
+    m_flags = other.m_flags;
+    m_type_params.clear();
+    m_type_params.reserve(other.m_type_params.size());
+    for (auto &it : other.m_type_params)
+        m_type_params.emplace_back(new ast_value(ast_copy_type, *it));
+}
+
+bool ast_expression::compareType(const ast_expression &other) const
+{
+    if (m_vtype == TYPE_NIL ||
+        other.m_vtype == TYPE_NIL)
+        return true;
+    if (m_vtype != other.m_vtype)
+        return false;
+    if (!m_next != !other.m_next)
+        return false;
+    if (m_type_params.size() != other.m_type_params.size())
+        return false;
+    if ((m_flags & AST_FLAG_TYPE_MASK) !=
+        (other.m_flags & AST_FLAG_TYPE_MASK) )
+    {
+        return false;
+    }
+    if (m_type_params.size()) {
+        size_t i;
+        for (i = 0; i < m_type_params.size(); ++i) {
+            if (!m_type_params[i]->compareType(*other.m_type_params[i]))
+                return false;
+        }
+    }
+    if (m_next)
+        return m_next->compareType(*other.m_next);
+    return true;
+}
+
+bool ast_expression::codegen(ast_function*, bool, ir_value**) {
+    compile_error(m_context, "ast_expression::codegen called!");
+    abort();
+    return false;
+}
+
+ast_value::ast_value(ast_copy_type_t, const ast_value &other, const std::string &name)
+    : ast_value(ast_copy_type, static_cast<const ast_expression&>(other), name)
+{
+    m_keep_node = true; // keep values, always
+    memset(&m_constval, 0, sizeof(m_constval));
+}
+
+ast_value::ast_value(ast_copy_type_t, const ast_value &other)
+    : ast_value(ast_copy_type, static_cast<const ast_expression&>(other), other.m_name)
+{
+    m_keep_node = true; // keep values, always
+    memset(&m_constval, 0, sizeof(m_constval));
+}
+
+ast_value::ast_value(ast_copy_type_t, const ast_expression &other, const std::string &name)
+    : ast_expression(ast_copy_type, TYPE_ast_value, other)
+    , m_name(name)
+{
+    m_keep_node = true; // keep values, always
+    memset(&m_constval, 0, sizeof(m_constval));
+}
+
+ast_value::ast_value(lex_ctx_t ctx, const std::string &name, qc_type t)
+    : ast_expression(ctx, TYPE_ast_value, t)
+    , m_name(name)
+{
+    m_keep_node = true; // keep values, always
+    memset(&m_constval, 0, sizeof(m_constval));
+}
+
+ast_value::~ast_value()
+{
+    if (m_argcounter)
+        mem_d((void*)m_argcounter);
+    if (m_hasvalue) {
+        switch (m_vtype)
+        {
+        case TYPE_STRING:
+            mem_d((void*)m_constval.vstring);
+            break;
+        case TYPE_FUNCTION:
+            // unlink us from the function node
+            m_constval.vfunc->m_function_type = nullptr;
+            break;
+        // NOTE: delete function? currently collected in
+        // the parser structure
+        default:
+            break;
+        }
+    }
+
+    // initlist imples an array which implies .next in the expression exists.
+    if (m_initlist.size() && m_next->m_vtype == TYPE_STRING) {
+        for (auto &it : m_initlist)
+            if (it.vstring)
+                mem_d(it.vstring);
+    }
+}
+
+static size_t ast_type_to_string_impl(const ast_expression *e, char *buf, size_t bufsize, size_t pos)
+{
+    const char *typestr;
+    size_t typelen;
+    size_t i;
+
+    if (!e) {
+        if (pos + 6 >= bufsize)
+            goto full;
+        util_strncpy(buf + pos, "(null)", 6);
+        return pos + 6;
+    }
+
+    if (pos + 1 >= bufsize)
+        goto full;
+
+    switch (e->m_vtype) {
+        case TYPE_VARIANT:
+            util_strncpy(buf + pos, "(variant)", 9);
+            return pos + 9;
+
+        case TYPE_FIELD:
+            buf[pos++] = '.';
+            return ast_type_to_string_impl(e->m_next, buf, bufsize, pos);
+
+        case TYPE_POINTER:
+            if (pos + 3 >= bufsize)
+                goto full;
+            buf[pos++] = '*';
+            buf[pos++] = '(';
+            pos = ast_type_to_string_impl(e->m_next, buf, bufsize, pos);
+            if (pos + 1 >= bufsize)
+                goto full;
+            buf[pos++] = ')';
+            return pos;
+
+        case TYPE_FUNCTION:
+            pos = ast_type_to_string_impl(e->m_next, buf, bufsize, pos);
+            if (pos + 2 >= bufsize)
+                goto full;
+            if (e->m_type_params.empty()) {
+                buf[pos++] = '(';
+                buf[pos++] = ')';
+                return pos;
+            }
+            buf[pos++] = '(';
+            pos = ast_type_to_string_impl(e->m_type_params[0].get(), buf, bufsize, pos);
+            for (i = 1; i < e->m_type_params.size(); ++i) {
+                if (pos + 2 >= bufsize)
+                    goto full;
+                buf[pos++] = ',';
+                buf[pos++] = ' ';
+                pos = ast_type_to_string_impl(e->m_type_params[i].get(), buf, bufsize, pos);
+            }
+            if (pos + 1 >= bufsize)
+                goto full;
+            buf[pos++] = ')';
+            return pos;
+
+        case TYPE_ARRAY:
+            pos = ast_type_to_string_impl(e->m_next, buf, bufsize, pos);
+            if (pos + 1 >= bufsize)
+                goto full;
+            buf[pos++] = '[';
+            pos += util_snprintf(buf + pos, bufsize - pos - 1, "%i", (int)e->m_count);
+            if (pos + 1 >= bufsize)
+                goto full;
+            buf[pos++] = ']';
+            return pos;
+
+        default:
+            typestr = type_name[e->m_vtype];
+            typelen = strlen(typestr);
+            if (pos + typelen >= bufsize)
+                goto full;
+            util_strncpy(buf + pos, typestr, typelen);
+            return pos + typelen;
+    }
+
+full:
+    buf[bufsize-3] = '.';
+    buf[bufsize-2] = '.';
+    buf[bufsize-1] = '.';
+    return bufsize;
+}
+
+void ast_type_to_string(const ast_expression *e, char *buf, size_t bufsize)
+{
+    size_t pos = ast_type_to_string_impl(e, buf, bufsize-1, 0);
+    buf[pos] = 0;
+}
+
+void ast_value::addParam(ast_value *p)
+{
+    m_type_params.emplace_back(p);
+}
+
+ast_binary::ast_binary(lex_ctx_t ctx, int op,
+                       ast_expression* left, ast_expression* right)
+    : ast_expression(ctx, TYPE_ast_binary)
+    , m_op(op)
+    // m_left/m_right happen after the peephole step right below
+    , m_right_first(false)
+{
+    if (ast_istype(right, ast_unary) && OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) {
+        ast_unary      *unary  = ((ast_unary*)right);
+        ast_expression *normal = unary->m_operand;
+
+        /* make a-(-b) => a + b */
+        if (unary->m_op == VINSTR_NEG_F || unary->m_op == VINSTR_NEG_V) {
+            if (op == INSTR_SUB_F) {
+                op = INSTR_ADD_F;
+                right = normal;
+                ++opts_optimizationcount[OPTIM_PEEPHOLE];
+            } else if (op == INSTR_SUB_V) {
+                op = INSTR_ADD_V;
+                right = normal;
+                ++opts_optimizationcount[OPTIM_PEEPHOLE];
+            }
+        }
+    }
+
+    m_left = left;
+    m_right = right;
+
+    propagateSideEffects(left);
+    propagateSideEffects(right);
+
+    if (op >= INSTR_EQ_F && op <= INSTR_GT)
+        m_vtype = TYPE_FLOAT;
+    else if (op == INSTR_AND || op == INSTR_OR) {
+        if (OPTS_FLAG(PERL_LOGIC))
+            adoptType(*right);
+        else
+            m_vtype = TYPE_FLOAT;
+    }
+    else if (op == INSTR_BITAND || op == INSTR_BITOR)
+        m_vtype = TYPE_FLOAT;
+    else if (op == INSTR_MUL_VF || op == INSTR_MUL_FV)
+        m_vtype = TYPE_VECTOR;
+    else if (op == INSTR_MUL_V)
+        m_vtype = TYPE_FLOAT;
+    else
+        m_vtype = left->m_vtype;
+
+    // references all
+    m_refs = AST_REF_ALL;
+}
+
+ast_binary::~ast_binary()
+{
+    if (m_refs & AST_REF_LEFT)  ast_unref(m_left);
+    if (m_refs & AST_REF_RIGHT) ast_unref(m_right);
+}
+
+ast_binstore::ast_binstore(lex_ctx_t ctx, int storop, int mathop,
+                           ast_expression* left, ast_expression* right)
+    : ast_expression(ctx, TYPE_ast_binstore)
+    , m_opstore(storop)
+    , m_opbin(mathop)
+    , m_dest(left)
+    , m_source(right)
+    , m_keep_dest(false)
+{
+    m_side_effects = true;
+    adoptType(*left);
+}
+
+ast_binstore::~ast_binstore()
+{
+    if (!m_keep_dest)
+        ast_unref(m_dest);
+    ast_unref(m_source);
+}
+
+ast_unary* ast_unary::make(lex_ctx_t ctx, int op, ast_expression *expr)
+{
+    // handle double negation, double bitwise or logical not
+    if (op == opid2('!','P') ||
+        op == opid2('~','P') ||
+        op == opid2('-','P'))
+    {
+        if (ast_istype(expr, ast_unary) && OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) {
+            ast_unary *unary = reinterpret_cast<ast_unary*>(expr);
+            if (unary->m_op == op) {
+                auto out = reinterpret_cast<ast_unary*>(unary->m_operand);
+                unary->m_operand = nullptr;
+                delete unary;
+                ++opts_optimizationcount[OPTIM_PEEPHOLE];
+                return out;
+            }
+        }
+    }
+
+    return new ast_unary(ctx, op, expr);
+}
+
+ast_unary::ast_unary(lex_ctx_t ctx, int op, ast_expression *expr)
+    : ast_expression(ctx, TYPE_ast_unary)
+    , m_op(op)
+    , m_operand(expr)
+{
+    propagateSideEffects(expr);
+    if ((op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) || op == VINSTR_NEG_F) {
+        m_vtype = TYPE_FLOAT;
+    } else if (op == VINSTR_NEG_V) {
+        m_vtype = TYPE_VECTOR;
+    } else {
+        compile_error(ctx, "cannot determine type of unary operation %s", util_instr_str[op]);
+    }
+}
+
+ast_unary::~ast_unary()
+{
+    if (m_operand)
+        ast_unref(m_operand);
+}
+
+ast_return::ast_return(lex_ctx_t ctx, ast_expression *expr)
+    : ast_expression(ctx, TYPE_ast_return)
+    , m_operand(expr)
+{
+    if (expr)
+        propagateSideEffects(expr);
+}
+
+ast_return::~ast_return()
+{
+    if (m_operand)
+        ast_unref(m_operand);
+}
+
+ast_entfield::ast_entfield(lex_ctx_t ctx, ast_expression *entity, ast_expression *field)
+    : ast_entfield(ctx, entity, field, field->m_next)
+{
+    if (field->m_vtype != TYPE_FIELD)
+        compile_error(ctx, "ast_entfield with expression not of type field");
+}
+
+ast_entfield::ast_entfield(lex_ctx_t ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype)
+    : ast_expression(ctx, TYPE_ast_entfield)
+    , m_entity(entity)
+    , m_field(field)
+{
+    propagateSideEffects(m_entity);
+    propagateSideEffects(m_field);
+
+    if (!outtype) {
+        compile_error(ctx, "ast_entfield: field has no type");
+        m_vtype = TYPE_VOID;
+    }
+    else
+        adoptType(*outtype);
+}
+
+ast_entfield::~ast_entfield()
+{
+    ast_unref(m_entity);
+    ast_unref(m_field);
+}
+
+ast_member *ast_member::make(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const std::string &name)
+{
+    if (field >= 3) {
+        compile_error(ctx, "ast_member: invalid field (>=3): %u", field);
+        return nullptr;
+    }
+    if (owner->m_vtype != TYPE_VECTOR &&
+        owner->m_vtype != TYPE_FIELD)
+    {
+        compile_error(ctx, "member-access on an invalid owner of type %s", type_name[owner->m_vtype]);
+        return nullptr;
+    }
+    return new ast_member(ctx, owner, field, name);
+}
+
+ast_member::ast_member(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const std::string &name)
+    : ast_expression(ctx, TYPE_ast_member)
+    , m_owner(owner)
+    , m_field(field)
+    , m_name(name)
+    , m_rvalue(false)
+{
+    m_keep_node = true;
+
+    if (m_owner->m_vtype == TYPE_VECTOR) {
+        m_vtype = TYPE_FLOAT;
+        m_next  = nullptr;
+    } else {
+        m_vtype = TYPE_FIELD;
+        m_next = ast_expression::shallowType(ctx, TYPE_FLOAT);
+    }
+
+    propagateSideEffects(owner);
+}
+
+ast_member::~ast_member()
+{
+    // The owner is always an ast_value, which has .keep_node=true,
+    // also: ast_members are usually deleted after the owner, thus
+    // this will cause invalid access
+        //ast_unref(self->m_owner);
+    // once we allow (expression).x to access a vector-member, we need
+    // to change this: preferably by creating an alternate ast node for this
+    // purpose that is not garbage-collected.
+}
+
+ast_array_index* ast_array_index::make(lex_ctx_t ctx, ast_expression *array, ast_expression *index)
+{
+    ast_expression *outtype = array->m_next;
+    if (!outtype) {
+        // field has no type
+        return nullptr;
+    }
+
+    return new ast_array_index(ctx, array, index);
+}
+
+ast_array_index::ast_array_index(lex_ctx_t ctx, ast_expression *array, ast_expression *index)
+    : ast_expression(ctx, TYPE_ast_array_index)
+    , m_array(array)
+    , m_index(index)
+{
+    propagateSideEffects(array);
+    propagateSideEffects(index);
+
+    ast_expression *outtype = m_array->m_next;
+    adoptType(*outtype);
+
+    if (array->m_vtype == TYPE_FIELD && outtype->m_vtype == TYPE_ARRAY) {
+        // FIXME: investigate - this is not possible after adoptType
+        //if (m_vtype != TYPE_ARRAY) {
+        //    compile_error(self->m_context, "array_index node on type");
+        //    ast_array_index_delete(self);
+        //    return nullptr;
+        //}
+
+        m_array = outtype;
+        m_vtype = TYPE_FIELD;
+    }
+}
+
+ast_array_index::~ast_array_index()
+{
+    if (m_array)
+        ast_unref(m_array);
+    if (m_index)
+        ast_unref(m_index);
+}
+
+ast_argpipe::ast_argpipe(lex_ctx_t ctx, ast_expression *index)
+    : ast_expression(ctx, TYPE_ast_argpipe)
+    , m_index(index)
+{
+    m_vtype = TYPE_NOEXPR;
+}
+
+ast_argpipe::~ast_argpipe()
+{
+    if (m_index)
+        ast_unref(m_index);
+}
+
+ast_store::ast_store(lex_ctx_t ctx, int op, ast_expression *dest, ast_expression *source)
+    : ast_expression(ctx, TYPE_ast_store)
+    , m_op(op)
+    , m_dest(dest)
+    , m_source(source)
+{
+    m_side_effects = true;
+    adoptType(*dest);
+}
+
+ast_store::~ast_store()
+{
+    ast_unref(m_dest);
+    ast_unref(m_source);
+}
+
+ast_ifthen::ast_ifthen(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse)
+    : ast_expression(ctx, TYPE_ast_ifthen)
+    , m_cond(cond)
+    , m_on_true(ontrue)
+    , m_on_false(onfalse)
+{
+    propagateSideEffects(cond);
+    if (ontrue)
+        propagateSideEffects(ontrue);
+    if (onfalse)
+        propagateSideEffects(onfalse);
+}
+
+ast_ifthen::~ast_ifthen()
+{
+    ast_unref(m_cond);
+    if (m_on_true)
+        ast_unref(m_on_true);
+    if (m_on_false)
+        ast_unref(m_on_false);
+}
+
+ast_ternary::ast_ternary(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse)
+    : ast_expression(ctx, TYPE_ast_ternary)
+    , m_cond(cond)
+    , m_on_true(ontrue)
+    , m_on_false(onfalse)
+{
+    propagateSideEffects(cond);
+    propagateSideEffects(ontrue);
+    propagateSideEffects(onfalse);
+
+    if (ontrue->m_vtype == TYPE_NIL)
+        adoptType(*onfalse);
+    else
+        adoptType(*ontrue);
+}
+
+ast_ternary::~ast_ternary()
+{
+    /* the if()s are only there because computed-gotos can set them
+     * to nullptr
+     */
+    if (m_cond)     ast_unref(m_cond);
+    if (m_on_true)  ast_unref(m_on_true);
+    if (m_on_false) ast_unref(m_on_false);
+}
+
+ast_loop::ast_loop(lex_ctx_t ctx,
+                   ast_expression *initexpr,
+                   ast_expression *precond, bool pre_not,
+                   ast_expression *postcond, bool post_not,
+                   ast_expression *increment,
+                   ast_expression *body)
+    : ast_expression(ctx, TYPE_ast_loop)
+    , m_initexpr(initexpr)
+    , m_precond(precond)
+    , m_postcond(postcond)
+    , m_increment(increment)
+    , m_body(body)
+    , m_pre_not(pre_not)
+    , m_post_not(post_not)
+{
+    if (initexpr)
+        propagateSideEffects(initexpr);
+    if (precond)
+        propagateSideEffects(precond);
+    if (postcond)
+        propagateSideEffects(postcond);
+    if (increment)
+        propagateSideEffects(increment);
+    if (body)
+        propagateSideEffects(body);
+}
+
+ast_loop::~ast_loop()
+{
+    if (m_initexpr)
+        ast_unref(m_initexpr);
+    if (m_precond)
+        ast_unref(m_precond);
+    if (m_postcond)
+        ast_unref(m_postcond);
+    if (m_increment)
+        ast_unref(m_increment);
+    if (m_body)
+        ast_unref(m_body);
+}
+
+ast_breakcont::ast_breakcont(lex_ctx_t ctx, bool iscont, unsigned int levels)
+    : ast_expression(ctx, TYPE_ast_breakcont)
+    , m_is_continue(iscont)
+    , m_levels(levels)
+{
+}
+
+ast_breakcont::~ast_breakcont()
+{
+}
+
+ast_switch::ast_switch(lex_ctx_t ctx, ast_expression *op)
+    : ast_expression(ctx, TYPE_ast_switch)
+    , m_operand(op)
+{
+    propagateSideEffects(op);
+}
+
+ast_switch::~ast_switch()
+{
+    ast_unref(m_operand);
+
+    for (auto &it : m_cases) {
+        if (it.m_value)
+            ast_unref(it.m_value);
+        ast_unref(it.m_code);
+    }
+}
+
+ast_label::ast_label(lex_ctx_t ctx, const std::string &name, bool undefined)
+    : ast_expression(ctx, TYPE_ast_label)
+    , m_name(name)
+    , m_irblock(nullptr)
+    , m_undefined(undefined)
+{
+    m_vtype = TYPE_NOEXPR;
+}
+
+ast_label::~ast_label()
+{
+}
+
+void ast_label::registerGoto(ast_goto *g)
+{
+   m_gotos.push_back(g);
+}
+
+ast_goto::ast_goto(lex_ctx_t ctx, const std::string &name)
+    : ast_expression(ctx, TYPE_ast_goto)
+    , m_name(name)
+    , m_target(nullptr)
+    , m_irblock_from(nullptr)
+{
+}
+
+ast_goto::~ast_goto()
+{
+}
+
+void ast_goto::setLabel(ast_label *label)
+{
+    m_target = label;
+}
+
+ast_state::ast_state(lex_ctx_t ctx, ast_expression *frame, ast_expression *think)
+    : ast_expression(ctx, TYPE_ast_expression)
+    , m_framenum(frame)
+    , m_nextthink(think)
+{
+}
+
+ast_state::~ast_state()
+{
+    if (m_framenum)
+        ast_unref(m_framenum);
+    if (m_nextthink)
+        ast_unref(m_nextthink);
+}
+
+ast_call *ast_call::make(lex_ctx_t ctx, ast_expression *funcexpr)
+{
+    if (!funcexpr->m_next) {
+        compile_error(ctx, "not a function");
+        return nullptr;
+    }
+    return new ast_call(ctx, funcexpr);
+}
+
+ast_call::ast_call(lex_ctx_t ctx, ast_expression *funcexpr)
+    : ast_expression(ctx, TYPE_ast_call)
+    , m_func(funcexpr)
+    , m_va_count(nullptr)
+{
+    m_side_effects = true;
+    adoptType(*funcexpr->m_next);
+}
+
+ast_call::~ast_call()
+{
+    for (auto &it : m_params)
+        ast_unref(it);
+
+    if (m_func)
+        ast_unref(m_func);
+
+    if (m_va_count)
+        ast_unref(m_va_count);
+}
+
+bool ast_call::checkVararg(ast_expression *va_type, ast_expression *exp_type) const
+{
+    char texp[1024];
+    char tgot[1024];
+    if (!exp_type)
+        return true;
+    if (!va_type || !va_type->compareType(*exp_type))
+    {
+        if (va_type && exp_type)
+        {
+            ast_type_to_string(va_type,  tgot, sizeof(tgot));
+            ast_type_to_string(exp_type, texp, sizeof(texp));
+            if (OPTS_FLAG(UNSAFE_VARARGS)) {
+                if (compile_warning(m_context, WARN_UNSAFE_TYPES,
+                                    "piped variadic argument differs in type: constrained to type %s, expected type %s",
+                                    tgot, texp))
+                    return false;
+            } else {
+                compile_error(m_context,
+                              "piped variadic argument differs in type: constrained to type %s, expected type %s",
+                              tgot, texp);
+                return false;
+            }
+        }
+        else
+        {
+            ast_type_to_string(exp_type, texp, sizeof(texp));
+            if (OPTS_FLAG(UNSAFE_VARARGS)) {
+                if (compile_warning(m_context, WARN_UNSAFE_TYPES,
+                                    "piped variadic argument may differ in type: expected type %s",
+                                    texp))
+                    return false;
+            } else {
+                compile_error(m_context,
+                              "piped variadic argument may differ in type: expected type %s",
+                              texp);
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool ast_call::checkTypes(ast_expression *va_type) const
+{
+    char texp[1024];
+    char tgot[1024];
+    size_t i;
+    bool retval = true;
+
+    size_t count = m_params.size();
+    if (count > m_func->m_type_params.size())
+        count = m_func->m_type_params.size();
+
+    for (i = 0; i < count; ++i) {
+        if (ast_istype(m_params[i], ast_argpipe)) {
+            /* warn about type safety instead */
+            if (i+1 != count) {
+                compile_error(m_context, "argpipe must be the last parameter to a function call");
+                return false;
+            }
+            if (!checkVararg(va_type, m_func->m_type_params[i].get()))
+                retval = false;
+        }
+        else if (!m_params[i]->compareType(*m_func->m_type_params[i]))
+        {
+            ast_type_to_string(m_params[i], tgot, sizeof(tgot));
+            ast_type_to_string(m_func->m_type_params[i].get(), texp, sizeof(texp));
+            compile_error(m_context, "invalid type for parameter %u in function call: expected %s, got %s",
+                     (unsigned int)(i+1), texp, tgot);
+            /* we don't immediately return */
+            retval = false;
+        }
+    }
+    count = m_params.size();
+    if (count > m_func->m_type_params.size() && m_func->m_varparam) {
+        for (; i < count; ++i) {
+            if (ast_istype(m_params[i], ast_argpipe)) {
+                /* warn about type safety instead */
+                if (i+1 != count) {
+                    compile_error(m_context, "argpipe must be the last parameter to a function call");
+                    return false;
+                }
+                if (!checkVararg(va_type, m_func->m_varparam))
+                    retval = false;
+            }
+            else if (!m_params[i]->compareType(*m_func->m_varparam))
+            {
+                ast_type_to_string(m_params[i], tgot, sizeof(tgot));
+                ast_type_to_string(m_func->m_varparam, texp, sizeof(texp));
+                compile_error(m_context, "invalid type for variadic parameter %u in function call: expected %s, got %s",
+                         (unsigned int)(i+1), texp, tgot);
+                /* we don't immediately return */
+                retval = false;
+            }
+        }
+    }
+    return retval;
+}
+
+ast_block::ast_block(lex_ctx_t ctx)
+    : ast_expression(ctx, TYPE_ast_block)
+{
+}
+
+ast_block::~ast_block()
+{
+    for (auto &it : m_exprs) ast_unref(it);
+    for (auto &it : m_locals) delete it;
+    for (auto &it : m_collect) delete it;
+}
+
+void ast_block::setType(const ast_expression &from)
+{
+    if (m_next)
+        delete m_next;
+    adoptType(from);
+}
+
+
+bool ast_block::addExpr(ast_expression *e)
+{
+    propagateSideEffects(e);
+    m_exprs.push_back(e);
+    if (m_next) {
+        delete m_next;
+        m_next = nullptr;
+    }
+    adoptType(*e);
+    return true;
+}
+
+void ast_block::collect(ast_expression *expr)
+{
+    m_collect.push_back(expr);
+    expr->m_keep_node = true;
+}
+
+ast_function *ast_function::make(lex_ctx_t ctx, const std::string &name, ast_value *vtype)
+{
+    if (!vtype) {
+        compile_error(ctx, "internal error: ast_function_new condition 0");
+        return nullptr;
+    } else if (vtype->m_hasvalue || vtype->m_vtype != TYPE_FUNCTION) {
+        compile_error(ctx, "internal error: ast_function_new condition %i %i type=%i (probably 2 bodies?)",
+                 (int)!vtype,
+                 (int)vtype->m_hasvalue,
+                 vtype->m_vtype);
+        return nullptr;
+    }
+    return new ast_function(ctx, name, vtype);
+}
+
+ast_function::ast_function(lex_ctx_t ctx, const std::string &name, ast_value *vtype)
+    : ast_node(ctx, TYPE_ast_function)
+    , m_function_type(vtype)
+    , m_name(name)
+    , m_builtin(0)
+    , m_static_count(0)
+    , m_ir_func(nullptr)
+    , m_curblock(nullptr)
+    , m_labelcount(0)
+    , m_varargs(nullptr)
+    , m_argc(nullptr)
+    , m_fixedparams(nullptr)
+    , m_return_value(nullptr)
+{
+    vtype->m_hasvalue = true;
+    vtype->m_constval.vfunc = this;
+}
+
+ast_function::~ast_function()
+{
+    if (m_function_type) {
+        // ast_value_delete(m_function_type);
+        m_function_type->m_hasvalue = false;
+        m_function_type->m_constval.vfunc = nullptr;
+        // We use unref - if it was stored in a global table it is supposed
+        // to be deleted from *there*
+        ast_unref(m_function_type);
+    }
+
+    if (m_fixedparams)
+        ast_unref(m_fixedparams);
+    if (m_return_value)
+        ast_unref(m_return_value);
+
+    // force this to be cleared before m_varargs/m_argc as blocks might
+    // try to access them via ast_unref()
+    m_blocks.clear();
+}
+
+const char* ast_function::makeLabel(const char *prefix)
+{
+    size_t id;
+    size_t len;
+    char  *from;
+
+    if (!OPTS_OPTION_BOOL(OPTION_DUMP)    &&
+        !OPTS_OPTION_BOOL(OPTION_DUMPFIN) &&
+        !OPTS_OPTION_BOOL(OPTION_DEBUG))
+    {
+        return nullptr;
+    }
+
+    id  = (m_labelcount++);
+    len = strlen(prefix);
+
+    from = m_labelbuf + sizeof(m_labelbuf)-1;
+    *from-- = 0;
+    do {
+        *from-- = (id%10) + '0';
+        id /= 10;
+    } while (id);
+    ++from;
+    memcpy(from - len, prefix, len);
+    return from - len;
+}
+
+/*********************************************************************/
+/* AST codegen part
+ * by convention you must never pass nullptr to the 'ir_value **out'
+ * parameter. If you really don't care about the output, pass a dummy.
+ * But I can't imagine a pituation where the output is truly unnecessary.
+ */
+
+static void codegen_output_type(ast_expression *self, ir_value *out)
+{
+    if (out->m_vtype == TYPE_FIELD)
+        out->m_fieldtype = self->m_next->m_vtype;
+    if (out->m_vtype == TYPE_FUNCTION)
+        out->m_outtype = self->m_next->m_vtype;
+}
+
+bool ast_value::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    (void)func;
+    (void)lvalue;
+    if (m_vtype == TYPE_NIL) {
+        *out = func->m_ir_func->m_owner->m_nil;
+        return true;
+    }
+    // NOTE: This is the codegen for a variable used in an expression.
+    // It is not the codegen to generate the value storage. For this purpose,
+    // generateLocal and generateGlobal are to be used before this
+    // is executed. ast_function::generateFunction should take care of its
+    // locals, and the ast-user should take care of generateGlobal to be used
+    // on all the globals.
+    if (!m_ir_v) {
+        char tname[1024]; /* typename is reserved in C++ */
+        ast_type_to_string(this, tname, sizeof(tname));
+        compile_error(m_context, "ast_value used before generated %s %s", tname, m_name);
+        return false;
+    }
+    *out = m_ir_v;
+    return true;
+}
+
+bool ast_value::setGlobalArray()
+{
+    size_t count = m_initlist.size();
+    size_t i;
+
+    if (count > m_count) {
+        compile_error(m_context, "too many elements in initializer");
+        count = m_count;
+    }
+    else if (count < m_count) {
+        /* add this?
+        compile_warning(m_context, "not all elements are initialized");
+        */
+    }
+
+    for (i = 0; i != count; ++i) {
+        switch (m_next->m_vtype) {
+            case TYPE_FLOAT:
+                if (!m_ir_values[i]->setFloat(m_initlist[i].vfloat))
+                    return false;
+                break;
+            case TYPE_VECTOR:
+                if (!m_ir_values[i]->setVector(m_initlist[i].vvec))
+                    return false;
+                break;
+            case TYPE_STRING:
+                if (!m_ir_values[i]->setString(m_initlist[i].vstring))
+                    return false;
+                break;
+            case TYPE_ARRAY:
+                /* we don't support them in any other place yet either */
+                compile_error(m_context, "TODO: nested arrays");
+                return false;
+            case TYPE_FUNCTION:
+                /* this requiers a bit more work - similar to the fields I suppose */
+                compile_error(m_context, "global of type function not properly generated");
+                return false;
+            case TYPE_FIELD:
+                if (!m_initlist[i].vfield) {
+                    compile_error(m_context, "field constant without vfield set");
+                    return false;
+                }
+                if (!m_initlist[i].vfield->m_ir_v) {
+                    compile_error(m_context, "field constant generated before its field");
+                    return false;
+                }
+                if (!m_ir_values[i]->setField(m_initlist[i].vfield->m_ir_v))
+                    return false;
+                break;
+            default:
+                compile_error(m_context, "TODO: global constant type %i", m_vtype);
+                break;
+        }
+    }
+    return true;
+}
+
+bool ast_value::checkArray(const ast_value &array) const
+{
+    if (array.m_flags & AST_FLAG_ARRAY_INIT && array.m_initlist.empty()) {
+        compile_error(m_context, "array without size: %s", m_name);
+        return false;
+    }
+    // we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements
+    if (!array.m_count || array.m_count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE)) {
+        compile_error(m_context, "Invalid array of size %lu", (unsigned long)array.m_count);
+        return false;
+    }
+    return true;
+}
+
+bool ast_value::generateGlobal(ir_builder *ir, bool isfield)
+{
+    if (m_vtype == TYPE_NIL) {
+        compile_error(m_context, "internal error: trying to generate a variable of TYPE_NIL");
+        return false;
+    }
+
+    if (m_hasvalue && m_vtype == TYPE_FUNCTION)
+        return generateGlobalFunction(ir);
+
+    if (isfield && m_vtype == TYPE_FIELD)
+        return generateGlobalField(ir);
+
+    ir_value *v = nullptr;
+    if (m_vtype == TYPE_ARRAY) {
+        v = prepareGlobalArray(ir);
+        if (!v)
+            return false;
+    } else {
+        // Arrays don't do this since there's no "array" value which spans across the
+        // whole thing.
+        v = ir->createGlobal(m_name, m_vtype);
+        if (!v) {
+            compile_error(m_context, "ir_builder::createGlobal failed on `%s`", m_name);
+            return false;
+        }
+        codegen_output_type(this, v);
+        v->m_context = m_context;
+    }
+
+    /* link us to the ir_value */
+    v->m_cvq = m_cvq;
+    m_ir_v = v;
+
+    if (m_flags & AST_FLAG_INCLUDE_DEF)
+        m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF;
+    if (m_flags & AST_FLAG_ERASEABLE)
+        m_ir_v->m_flags |= IR_FLAG_ERASABLE;
+
+    /* initialize */
+    if (m_hasvalue) {
+        switch (m_vtype)
+        {
+            case TYPE_FLOAT:
+                if (!v->setFloat(m_constval.vfloat))
+                    return false;
+                break;
+            case TYPE_VECTOR:
+                if (!v->setVector(m_constval.vvec))
+                    return false;
+                break;
+            case TYPE_STRING:
+                if (!v->setString(m_constval.vstring))
+                    return false;
+                break;
+            case TYPE_ARRAY:
+                if (!setGlobalArray())
+                    return false;
+                break;
+            case TYPE_FUNCTION:
+                compile_error(m_context, "global of type function not properly generated");
+                return false;
+                /* Cannot generate an IR value for a function,
+                 * need a pointer pointing to a function rather.
+                 */
+            case TYPE_FIELD:
+                if (!m_constval.vfield) {
+                    compile_error(m_context, "field constant without vfield set");
+                    return false;
+                }
+                if (!m_constval.vfield->m_ir_v) {
+                    compile_error(m_context, "field constant generated before its field");
+                    return false;
+                }
+                if (!v->setField(m_constval.vfield->m_ir_v))
+                    return false;
+                break;
+            default:
+                compile_error(m_context, "TODO: global constant type %i", m_vtype);
+                break;
+        }
+    }
+
+    return true;
+}
+
+bool ast_value::generateGlobalFunction(ir_builder *ir)
+{
+    ir_function *func = ir->createFunction(m_name, m_next->m_vtype);
+    if (!func)
+        return false;
+    func->m_context = m_context;
+    func->m_value->m_context = m_context;
+
+    m_constval.vfunc->m_ir_func = func;
+    m_ir_v = func->m_value;
+    if (m_flags & AST_FLAG_INCLUDE_DEF)
+        m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF;
+    if (m_flags & AST_FLAG_ERASEABLE)
+        m_ir_v->m_flags |= IR_FLAG_ERASABLE;
+    if (m_flags & AST_FLAG_BLOCK_COVERAGE)
+        func->m_flags |= IR_FLAG_BLOCK_COVERAGE;
+    // The function is filled later on ast_function::generateFunction...
+    return true;
+}
+
+bool ast_value::generateGlobalField(ir_builder *ir)
+{
+    ast_expression *fieldtype = m_next;
+
+    if (m_hasvalue) {
+        compile_error(m_context, "TODO: constant field pointers with value");
+        return false;
+    }
+
+    if (fieldtype->m_vtype == TYPE_ARRAY) {
+        if (!ast_istype(fieldtype, ast_value)) {
+            compile_error(m_context, "internal error: ast_value required");
+            return false;
+        }
+        ast_value      *array = reinterpret_cast<ast_value*>(fieldtype);
+
+        if (!checkArray(*array))
+            return false;
+
+        ast_expression *elemtype = array->m_next;
+        qc_type vtype = elemtype->m_vtype;
+
+        ir_value *v = ir->createField(m_name, vtype);
+        if (!v) {
+            compile_error(m_context, "ir_builder::createGlobal failed on `%s`", m_name);
+            return false;
+        }
+        v->m_context = m_context;
+        v->m_unique_life = true;
+        v->m_locked      = true;
+        array->m_ir_v = m_ir_v = v;
+
+        if (m_flags & AST_FLAG_INCLUDE_DEF)
+            m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF;
+        if (m_flags & AST_FLAG_ERASEABLE)
+            m_ir_v->m_flags |= IR_FLAG_ERASABLE;
+
+        const size_t namelen = m_name.length();
+        std::unique_ptr<char[]> name(new char[namelen+16]);
+        util_strncpy(name.get(), m_name.c_str(), namelen);
+
+        array->m_ir_values.resize(array->m_count);
+        array->m_ir_values[0] = v;
+        for (size_t ai = 1; ai < array->m_count; ++ai) {
+            util_snprintf(name.get() + namelen, 16, "[%u]", (unsigned int)ai);
+            array->m_ir_values[ai] = ir->createField(name.get(), vtype);
+            if (!array->m_ir_values[ai]) {
+                compile_error(m_context, "ir_builder::createGlobal failed on `%s`", name.get());
+                return false;
+            }
+            array->m_ir_values[ai]->m_context = m_context;
+            array->m_ir_values[ai]->m_unique_life = true;
+            array->m_ir_values[ai]->m_locked      = true;
+            if (m_flags & AST_FLAG_INCLUDE_DEF)
+                m_ir_values[ai]->m_flags |= IR_FLAG_INCLUDE_DEF;
+        }
+    }
+    else
+    {
+        ir_value *v = ir->createField(m_name, m_next->m_vtype);
+        if (!v)
+            return false;
+        v->m_context = m_context;
+        m_ir_v = v;
+        if (m_flags & AST_FLAG_INCLUDE_DEF)
+            m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF;
+
+        if (m_flags & AST_FLAG_ERASEABLE)
+            m_ir_v->m_flags |= IR_FLAG_ERASABLE;
+    }
+    return true;
+}
+
+ir_value *ast_value::prepareGlobalArray(ir_builder *ir)
+{
+    ast_expression *elemtype = m_next;
+    qc_type vtype = elemtype->m_vtype;
+
+    if (m_flags & AST_FLAG_ARRAY_INIT && !m_count) {
+        compile_error(m_context, "array `%s' has no size", m_name);
+        return nullptr;
+    }
+
+    /* same as with field arrays */
+    if (!checkArray(*this))
+        return nullptr;
+
+    ir_value *v = ir->createGlobal(m_name, vtype);
+    if (!v) {
+        compile_error(m_context, "ir_builder::createGlobal failed `%s`", m_name);
+        return nullptr;
+    }
+    v->m_context = m_context;
+    v->m_unique_life = true;
+    v->m_locked      = true;
+
+    if (m_flags & AST_FLAG_INCLUDE_DEF)
+        v->m_flags |= IR_FLAG_INCLUDE_DEF;
+    if (m_flags & AST_FLAG_ERASEABLE)
+        m_ir_v->m_flags |= IR_FLAG_ERASABLE;
+
+    const size_t namelen = m_name.length();
+    std::unique_ptr<char[]> name(new char[namelen+16]);
+    util_strncpy(name.get(), m_name.c_str(), namelen);
+
+    m_ir_values.resize(m_count);
+    m_ir_values[0] = v;
+    for (size_t ai = 1; ai < m_count; ++ai) {
+        util_snprintf(name.get() + namelen, 16, "[%u]", (unsigned int)ai);
+        m_ir_values[ai] = ir->createGlobal(name.get(), vtype);
+        if (!m_ir_values[ai]) {
+            compile_error(m_context, "ir_builder::createGlobal failed `%s`", name.get());
+            return nullptr;
+        }
+        m_ir_values[ai]->m_context = m_context;
+        m_ir_values[ai]->m_unique_life = true;
+        m_ir_values[ai]->m_locked      = true;
+        if (m_flags & AST_FLAG_INCLUDE_DEF)
+            m_ir_values[ai]->m_flags |= IR_FLAG_INCLUDE_DEF;
+    }
+
+    return v;
+}
+
+bool ast_value::generateLocal(ir_function *func, bool param)
+{
+    if (m_vtype == TYPE_NIL) {
+        compile_error(m_context, "internal error: trying to generate a variable of TYPE_NIL");
+        return false;
+    }
+
+    if (m_hasvalue && m_vtype == TYPE_FUNCTION)
+    {
+        /* Do we allow local functions? I think not...
+         * this is NOT a function pointer atm.
+         */
+        return false;
+    }
+
+    ir_value *v = nullptr;
+    if (m_vtype == TYPE_ARRAY) {
+        ast_expression *elemtype = m_next;
+        qc_type vtype = elemtype->m_vtype;
+
+        func->m_flags |= IR_FLAG_HAS_ARRAYS;
+
+        if (param && !(m_flags & AST_FLAG_IS_VARARG)) {
+            compile_error(m_context, "array-parameters are not supported");
+            return false;
+        }
+
+        /* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
+        if (!checkArray(*this))
+            return false;
+
+        m_ir_values.resize(m_count);
+        v = ir_function_create_local(func, m_name, vtype, param);
+        if (!v) {
+            compile_error(m_context, "internal error: ir_function_create_local failed");
+            return false;
+        }
+        v->m_context = m_context;
+        v->m_unique_life = true;
+        v->m_locked      = true;
+
+        const size_t namelen = m_name.length();
+        std::unique_ptr<char[]> name(new char[namelen+16]);
+        util_strncpy(name.get(), m_name.c_str(), namelen);
+
+        m_ir_values[0] = v;
+        for (size_t ai = 1; ai < m_count; ++ai) {
+            util_snprintf(name.get() + namelen, 16, "[%u]", (unsigned int)ai);
+            m_ir_values[ai] = ir_function_create_local(func, name.get(), vtype, param);
+            if (!m_ir_values[ai]) {
+                compile_error(m_context, "internal_error: ir_builder::createGlobal failed on `%s`", name.get());
+                return false;
+            }
+            m_ir_values[ai]->m_context = m_context;
+            m_ir_values[ai]->m_unique_life = true;
+            m_ir_values[ai]->m_locked      = true;
+        }
+    }
+    else
+    {
+        v = ir_function_create_local(func, m_name, m_vtype, param);
+        if (!v)
+            return false;
+        codegen_output_type(this, v);
+        v->m_context = m_context;
+    }
+
+    // A constant local... hmmm...
+    // I suppose the IR will have to deal with this
+    if (m_hasvalue) {
+        switch (m_vtype)
+        {
+            case TYPE_FLOAT:
+                if (!v->setFloat(m_constval.vfloat))
+                    goto error;
+                break;
+            case TYPE_VECTOR:
+                if (!v->setVector(m_constval.vvec))
+                    goto error;
+                break;
+            case TYPE_STRING:
+                if (!v->setString(m_constval.vstring))
+                    goto error;
+                break;
+            default:
+                compile_error(m_context, "TODO: global constant type %i", m_vtype);
+                break;
+        }
+    }
+
+    // link us to the ir_value
+    v->m_cvq = m_cvq;
+    m_ir_v = v;
+
+    if (!generateAccessors(func->m_owner))
+        return false;
+    return true;
+
+error: /* clean up */
+    delete v;
+    return false;
+}
+
+bool ast_value::generateAccessors(ir_builder *ir)
+{
+    size_t i;
+    bool warn = OPTS_WARN(WARN_USED_UNINITIALIZED);
+    if (!m_setter || !m_getter)
+        return true;
+    if (m_count && m_ir_values.empty()) {
+        compile_error(m_context, "internal error: no array values generated for `%s`", m_name);
+        return false;
+    }
+    for (i = 0; i < m_count; ++i) {
+        if (!m_ir_values[i]) {
+            compile_error(m_context, "internal error: not all array values have been generated for `%s`", m_name);
+            return false;
+        }
+        if (!m_ir_values[i]->m_life.empty()) {
+            compile_error(m_context, "internal error: function containing `%s` already generated", m_name);
+            return false;
+        }
+    }
+
+    opts_set(opts.warn, WARN_USED_UNINITIALIZED, false);
+    if (m_setter) {
+        if (!m_setter->generateGlobal(ir, false) ||
+            !m_setter->m_constval.vfunc->generateFunction(ir) ||
+            !ir_function_finalize(m_setter->m_constval.vfunc->m_ir_func))
+        {
+            compile_error(m_context, "internal error: failed to generate setter for `%s`", m_name);
+            opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
+            return false;
+        }
+    }
+    if (m_getter) {
+        if (!m_getter->generateGlobal(ir, false) ||
+            !m_getter->m_constval.vfunc->generateFunction(ir) ||
+            !ir_function_finalize(m_getter->m_constval.vfunc->m_ir_func))
+        {
+            compile_error(m_context, "internal error: failed to generate getter for `%s`", m_name);
+            opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
+            return false;
+        }
+    }
+    for (i = 0; i < m_count; ++i)
+        m_ir_values[i]->m_life.clear();
+    opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
+    return true;
+}
+
+bool ast_function::generateFunction(ir_builder *ir)
+{
+    (void)ir;
+
+    ir_value *dummy;
+
+    ir_function *irf = m_ir_func;
+    if (!irf) {
+        compile_error(m_context, "internal error: ast_function's related ast_value was not generated yet");
+        return false;
+    }
+
+    /* fill the parameter list */
+    for (auto &it : m_function_type->m_type_params) {
+        if (it->m_vtype == TYPE_FIELD)
+            vec_push(irf->m_params, it->m_next->m_vtype);
+        else
+            vec_push(irf->m_params, it->m_vtype);
+        if (!m_builtin) {
+            if (!it->generateLocal(m_ir_func, true))
+                return false;
+        }
+    }
+
+    if (m_varargs) {
+        if (!m_varargs->generateLocal(m_ir_func, true))
+            return false;
+        irf->m_max_varargs = m_varargs->m_count;
+    }
+
+    if (m_builtin) {
+        irf->m_builtin = m_builtin;
+        return true;
+    }
+
+    /* have a local return value variable? */
+    if (m_return_value) {
+        if (!m_return_value->generateLocal(m_ir_func, false))
+            return false;
+    }
+
+    if (m_blocks.empty()) {
+        compile_error(m_context, "function `%s` has no body", m_name);
+        return false;
+    }
+
+    irf->m_first = m_curblock = ir_function_create_block(m_context, irf, "entry");
+    if (!m_curblock) {
+        compile_error(m_context, "failed to allocate entry block for `%s`", m_name);
+        return false;
+    }
+
+    if (m_argc) {
+        ir_value *va_count;
+        ir_value *fixed;
+        ir_value *sub;
+        if (!m_argc->generateLocal(m_ir_func, true))
+            return false;
+        if (!m_argc->codegen(this, false, &va_count))
+            return false;
+        if (!m_fixedparams->codegen(this, false, &fixed))
+            return false;
+        sub = ir_block_create_binop(m_curblock, m_context,
+                                    makeLabel("va_count"), INSTR_SUB_F,
+                                    ir->get_va_count(), fixed);
+        if (!sub)
+            return false;
+        if (!ir_block_create_store_op(m_curblock, m_context, INSTR_STORE_F,
+                                      va_count, sub))
+        {
+            return false;
+        }
+    }
+
+    for (auto &it : m_blocks) {
+        if (!it->codegen(this, false, &dummy))
+          return false;
+    }
+
+    /* TODO: check return types */
+    if (!m_curblock->m_final)
+    {
+        if (!m_function_type->m_next ||
+            m_function_type->m_next->m_vtype == TYPE_VOID)
+        {
+            return ir_block_create_return(m_curblock, m_context, nullptr);
+        }
+        else if (vec_size(m_curblock->m_entries) || m_curblock == irf->m_first)
+        {
+            if (m_return_value) {
+                if (!m_return_value->codegen(this, false, &dummy))
+                    return false;
+                return ir_block_create_return(m_curblock, m_context, dummy);
+            }
+            else if (compile_warning(m_context, WARN_MISSING_RETURN_VALUES,
+                                "control reaches end of non-void function (`%s`) via %s",
+                                m_name.c_str(), m_curblock->m_label.c_str()))
+            {
+                return false;
+            }
+            return ir_block_create_return(m_curblock, m_context, nullptr);
+        }
+    }
+    return true;
+}
+
+static bool starts_a_label(const ast_expression *ex)
+{
+    while (ex && ast_istype(ex, ast_block)) {
+        auto b = reinterpret_cast<const ast_block*>(ex);
+        ex = b->m_exprs[0];
+    }
+    if (!ex)
+        return false;
+    return ast_istype(ex, ast_label);
+}
+
+/* Note, you will not see ast_block_codegen generate ir_blocks.
+ * To the AST and the IR, blocks are 2 different things.
+ * In the AST it represents a block of code, usually enclosed in
+ * curly braces {...}.
+ * While in the IR it represents a block in terms of control-flow.
+ */
+bool ast_block::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    /* We don't use this
+     * Note: an ast-representation using the comma-operator
+     * of the form: (a, b, c) = x should not assign to c...
+     */
+    if (lvalue) {
+        compile_error(m_context, "not an l-value (code-block)");
+        return false;
+    }
+
+    if (m_outr) {
+        *out = m_outr;
+        return true;
+    }
+
+    /* output is nullptr at first, we'll have each expression
+     * assign to out output, thus, a comma-operator represention
+     * using an ast_block will return the last generated value,
+     * so: (b, c) + a  executed both b and c, and returns c,
+     * which is then added to a.
+     */
+    *out = nullptr;
+
+    /* generate locals */
+    for (auto &it : m_locals) {
+        if (!it->generateLocal(func->m_ir_func, false)) {
+            if (OPTS_OPTION_BOOL(OPTION_DEBUG))
+                compile_error(m_context, "failed to generate local `%s`", it->m_name);
+            return false;
+        }
+    }
+
+    for (auto &it : m_exprs) {
+        if (func->m_curblock->m_final && !starts_a_label(it)) {
+            if (compile_warning(it->m_context, WARN_UNREACHABLE_CODE, "unreachable statement"))
+                return false;
+            continue;
+        }
+        if (!it->codegen(func, false, out))
+            return false;
+    }
+
+    m_outr = *out;
+
+    return true;
+}
+
+bool ast_store::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *left  = nullptr;
+    ir_value *right = nullptr;
+
+    ast_value       *idx = 0;
+    ast_array_index *ai = nullptr;
+
+    if (lvalue && m_outl) {
+        *out = m_outl;
+        return true;
+    }
+
+    if (!lvalue && m_outr) {
+        *out = m_outr;
+        return true;
+    }
+
+    if (ast_istype(m_dest, ast_array_index))
+    {
+
+        ai = (ast_array_index*)m_dest;
+        idx = (ast_value*)ai->m_index;
+
+        if (ast_istype(ai->m_index, ast_value) && idx->m_hasvalue && idx->m_cvq == CV_CONST)
+            ai = nullptr;
+    }
+
+    if (ai) {
+        /* we need to call the setter */
+        ir_value  *iridx, *funval;
+        ir_instr  *call;
+
+        if (lvalue) {
+            compile_error(m_context, "array-subscript assignment cannot produce lvalues");
+            return false;
+        }
+
+        auto arr = reinterpret_cast<ast_value*>(ai->m_array);
+        if (!ast_istype(ai->m_array, ast_value) || !arr->m_setter) {
+            compile_error(m_context, "value has no setter (%s)", arr->m_name);
+            return false;
+        }
+
+        if (!idx->codegen(func, false, &iridx))
+            return false;
+
+        if (!arr->m_setter->codegen(func, true, &funval))
+            return false;
+
+        if (!m_source->codegen(func, false, &right))
+            return false;
+
+        call = ir_block_create_call(func->m_curblock, m_context, func->makeLabel("store"), funval, false);
+        if (!call)
+            return false;
+        ir_call_param(call, iridx);
+        ir_call_param(call, right);
+        m_outr = right;
+    }
+    else
+    {
+        // regular code
+
+        // lvalue!
+        if (!m_dest->codegen(func, true, &left))
+            return false;
+        m_outl = left;
+
+        /* rvalue! */
+        if (!m_source->codegen(func, false, &right))
+            return false;
+
+        if (!ir_block_create_store_op(func->m_curblock, m_context, m_op, left, right))
+            return false;
+        m_outr = right;
+    }
+
+    /* Theoretically, an assinment returns its left side as an
+     * lvalue, if we don't need an lvalue though, we return
+     * the right side as an rvalue, otherwise we have to
+     * somehow know whether or not we need to dereference the pointer
+     * on the left side - that is: OP_LOAD if it was an address.
+     * Also: in original QC we cannot OP_LOADP *anyway*.
+     */
+    *out = (lvalue ? left : right);
+
+    return true;
+}
+
+bool ast_binary::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *left, *right;
+
+    /* A binary operation cannot yield an l-value */
+    if (lvalue) {
+        compile_error(m_context, "not an l-value (binop)");
+        return false;
+    }
+
+    if (m_outr) {
+        *out = m_outr;
+        return true;
+    }
+
+    if ((OPTS_FLAG(SHORT_LOGIC) || OPTS_FLAG(PERL_LOGIC)) &&
+        (m_op == INSTR_AND || m_op == INSTR_OR))
+    {
+        /* NOTE: The short-logic path will ignore right_first */
+
+        /* short circuit evaluation */
+        ir_block *other, *merge;
+        ir_block *from_left, *from_right;
+        ir_instr *phi;
+        size_t    merge_id;
+
+        /* prepare end-block */
+        merge_id = func->m_ir_func->m_blocks.size();
+        merge    = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("sce_merge"));
+
+        /* generate the left expression */
+        if (!m_left->codegen(func, false, &left))
+            return false;
+        /* remember the block */
+        from_left = func->m_curblock;
+
+        /* create a new block for the right expression */
+        other = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("sce_other"));
+        if (m_op == INSTR_AND) {
+            /* on AND: left==true -> other */
+            if (!ir_block_create_if(func->m_curblock, m_context, left, other, merge))
+                return false;
+        } else {
+            /* on OR: left==false -> other */
+            if (!ir_block_create_if(func->m_curblock, m_context, left, merge, other))
+                return false;
+        }
+        /* use the likely flag */
+        vec_last(func->m_curblock->m_instr)->m_likely = true;
+
+        /* enter the right-expression's block */
+        func->m_curblock = other;
+        /* generate */
+        if (!m_right->codegen(func, false, &right))
+            return false;
+        /* remember block */
+        from_right = func->m_curblock;
+
+        /* jump to the merge block */
+        if (!ir_block_create_jump(func->m_curblock, m_context, merge))
+            return false;
+
+        algo::shiftback(func->m_ir_func->m_blocks.begin() + merge_id,
+                        func->m_ir_func->m_blocks.end());
+        // FIXME::DELME::
+        //func->m_ir_func->m_blocks[merge_id].release();
+        //func->m_ir_func->m_blocks.erase(func->m_ir_func->m_blocks.begin() + merge_id);
+        //func->m_ir_func->m_blocks.emplace_back(merge);
+
+        func->m_curblock = merge;
+        phi = ir_block_create_phi(func->m_curblock, m_context,
+                                  func->makeLabel("sce_value"),
+                                  m_vtype);
+        ir_phi_add(phi, from_left, left);
+        ir_phi_add(phi, from_right, right);
+        *out = ir_phi_value(phi);
+        if (!*out)
+            return false;
+
+        if (!OPTS_FLAG(PERL_LOGIC)) {
+            /* cast-to-bool */
+            if (OPTS_FLAG(CORRECT_LOGIC) && (*out)->m_vtype == TYPE_VECTOR) {
+                *out = ir_block_create_unary(func->m_curblock, m_context,
+                                             func->makeLabel("sce_bool_v"),
+                                             INSTR_NOT_V, *out);
+                if (!*out)
+                    return false;
+                *out = ir_block_create_unary(func->m_curblock, m_context,
+                                             func->makeLabel("sce_bool"),
+                                             INSTR_NOT_F, *out);
+                if (!*out)
+                    return false;
+            }
+            else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && (*out)->m_vtype == TYPE_STRING) {
+                *out = ir_block_create_unary(func->m_curblock, m_context,
+                                             func->makeLabel("sce_bool_s"),
+                                             INSTR_NOT_S, *out);
+                if (!*out)
+                    return false;
+                *out = ir_block_create_unary(func->m_curblock, m_context,
+                                             func->makeLabel("sce_bool"),
+                                             INSTR_NOT_F, *out);
+                if (!*out)
+                    return false;
+            }
+            else {
+                *out = ir_block_create_binop(func->m_curblock, m_context,
+                                             func->makeLabel("sce_bool"),
+                                             INSTR_AND, *out, *out);
+                if (!*out)
+                    return false;
+            }
+        }
+
+        m_outr = *out;
+        codegen_output_type(this, *out);
+        return true;
+    }
+
+    if (m_right_first) {
+        if (!m_right->codegen(func, false, &right))
+            return false;
+        if (!m_left->codegen(func, false, &left))
+            return false;
+    } else {
+        if (!m_left->codegen(func, false, &left))
+            return false;
+        if (!m_right->codegen(func, false, &right))
+            return false;
+    }
+
+    *out = ir_block_create_binop(func->m_curblock, m_context, func->makeLabel("bin"),
+                                 m_op, left, right);
+    if (!*out)
+        return false;
+    m_outr = *out;
+    codegen_output_type(this, *out);
+
+    return true;
+}
+
+bool ast_binstore::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *leftl = nullptr, *leftr, *right, *bin;
+
+    ast_value       *arr;
+    ast_value       *idx = 0;
+    ast_array_index *ai = nullptr;
+    ir_value        *iridx = nullptr;
+
+    if (lvalue && m_outl) {
+        *out = m_outl;
+        return true;
+    }
+
+    if (!lvalue && m_outr) {
+        *out = m_outr;
+        return true;
+    }
+
+    if (ast_istype(m_dest, ast_array_index))
+    {
+
+        ai = (ast_array_index*)m_dest;
+        idx = (ast_value*)ai->m_index;
+
+        if (ast_istype(ai->m_index, ast_value) && idx->m_hasvalue && idx->m_cvq == CV_CONST)
+            ai = nullptr;
+    }
+
+    /* for a binstore we need both an lvalue and an rvalue for the left side */
+    /* rvalue of destination! */
+    if (ai) {
+        if (!idx->codegen(func, false, &iridx))
+            return false;
+    }
+    if (!m_dest->codegen(func, false, &leftr))
+        return false;
+
+    /* source as rvalue only */
+    if (!m_source->codegen(func, false, &right))
+        return false;
+
+    /* now the binary */
+    bin = ir_block_create_binop(func->m_curblock, m_context, func->makeLabel("binst"),
+                                m_opbin, leftr, right);
+    m_outr = bin;
+
+    if (ai) {
+        /* we need to call the setter */
+        ir_value  *funval;
+        ir_instr  *call;
+
+        if (lvalue) {
+            compile_error(m_context, "array-subscript assignment cannot produce lvalues");
+            return false;
+        }
+
+        arr = (ast_value*)ai->m_array;
+        if (!ast_istype(ai->m_array, ast_value) || !arr->m_setter) {
+            compile_error(m_context, "value has no setter (%s)", arr->m_name);
+            return false;
+        }
+
+        if (!arr->m_setter->codegen(func, true, &funval))
+            return false;
+
+        call = ir_block_create_call(func->m_curblock, m_context, func->makeLabel("store"), funval, false);
+        if (!call)
+            return false;
+        ir_call_param(call, iridx);
+        ir_call_param(call, bin);
+        m_outr = bin;
+    } else {
+        // now store them
+        // lvalue of destination
+        if (!m_dest->codegen(func, true, &leftl))
+            return false;
+        m_outl = leftl;
+
+        if (!ir_block_create_store_op(func->m_curblock, m_context, m_opstore, leftl, bin))
+            return false;
+        m_outr = bin;
+    }
+
+    /* Theoretically, an assinment returns its left side as an
+     * lvalue, if we don't need an lvalue though, we return
+     * the right side as an rvalue, otherwise we have to
+     * somehow know whether or not we need to dereference the pointer
+     * on the left side - that is: OP_LOAD if it was an address.
+     * Also: in original QC we cannot OP_LOADP *anyway*.
+     */
+    *out = (lvalue ? leftl : bin);
+
+    return true;
+}
+
+bool ast_unary::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *operand;
+
+    /* An unary operation cannot yield an l-value */
+    if (lvalue) {
+        compile_error(m_context, "not an l-value (binop)");
+        return false;
+    }
+
+    if (m_outr) {
+        *out = m_outr;
+        return true;
+    }
+
+    /* lvalue! */
+    if (!m_operand->codegen(func, false, &operand))
+        return false;
+
+    *out = ir_block_create_unary(func->m_curblock, m_context, func->makeLabel("unary"),
+                                 m_op, operand);
+    if (!*out)
+        return false;
+    m_outr = *out;
+
+    return true;
+}
+
+bool ast_return::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *operand;
+
+    *out = nullptr;
+
+    /* In the context of a return operation, we don't actually return
+     * anything...
+     */
+    if (lvalue) {
+        compile_error(m_context, "return-expression is not an l-value");
+        return false;
+    }
+
+    if (m_outr) {
+        compile_error(m_context, "internal error: ast_return cannot be reused, it bears no result!");
+        return false;
+    }
+    m_outr = (ir_value*)1;
+
+    if (m_operand) {
+        /* lvalue! */
+        if (!m_operand->codegen(func, false, &operand))
+            return false;
+
+        if (!ir_block_create_return(func->m_curblock, m_context, operand))
+            return false;
+    } else {
+        if (!ir_block_create_return(func->m_curblock, m_context, nullptr))
+            return false;
+    }
+
+    return true;
+}
+
+bool ast_entfield::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *ent, *field;
+
+    // This function needs to take the 'lvalue' flag into account!
+    // As lvalue we provide a field-pointer, as rvalue we provide the
+    // value in a temp.
+
+    if (lvalue && m_outl) {
+        *out = m_outl;
+        return true;
+    }
+
+    if (!lvalue && m_outr) {
+        *out = m_outr;
+        return true;
+    }
+
+    if (!m_entity->codegen(func, false, &ent))
+        return false;
+
+    if (!m_field->codegen(func, false, &field))
+        return false;
+
+    if (lvalue) {
+        /* address! */
+        *out = ir_block_create_fieldaddress(func->m_curblock, m_context, func->makeLabel("efa"),
+                                            ent, field);
+    } else {
+        *out = ir_block_create_load_from_ent(func->m_curblock, m_context, func->makeLabel("efv"),
+                                             ent, field, m_vtype);
+        /* Done AFTER error checking:
+        codegen_output_type(this, *out);
+        */
+    }
+    if (!*out) {
+        compile_error(m_context, "failed to create %s instruction (output type %s)",
+                 (lvalue ? "ADDRESS" : "FIELD"),
+                 type_name[m_vtype]);
+        return false;
+    }
+    if (!lvalue)
+        codegen_output_type(this, *out);
+
+    if (lvalue)
+        m_outl = *out;
+    else
+        m_outr = *out;
+
+    // Hm that should be it...
+    return true;
+}
+
+bool ast_member::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *vec;
+
+    /* in QC this is always an lvalue */
+    if (lvalue && m_rvalue) {
+        compile_error(m_context, "not an l-value (member access)");
+        return false;
+    }
+    if (m_outl) {
+        *out = m_outl;
+        return true;
+    }
+
+    if (!m_owner->codegen(func, false, &vec))
+        return false;
+
+    if (vec->m_vtype != TYPE_VECTOR &&
+        !(vec->m_vtype == TYPE_FIELD && m_owner->m_next->m_vtype == TYPE_VECTOR))
+    {
+        return false;
+    }
+
+    *out = vec->vectorMember(m_field);
+    m_outl = *out;
+
+    return (*out != nullptr);
+}
+
+bool ast_array_index::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ast_value *arr;
+    ast_value *idx;
+
+    if (!lvalue && m_outr) {
+        *out = m_outr;
+        return true;
+    }
+    if (lvalue && m_outl) {
+        *out = m_outl;
+        return true;
+    }
+
+    if (!ast_istype(m_array, ast_value)) {
+        compile_error(m_context, "array indexing this way is not supported");
+        /* note this would actually be pointer indexing because the left side is
+         * not an actual array but (hopefully) an indexable expression.
+         * Once we get integer arithmetic, and GADDRESS/GSTORE/GLOAD instruction
+         * support this path will be filled.
+         */
+        return false;
+    }
+
+    arr = reinterpret_cast<ast_value*>(m_array);
+    idx = reinterpret_cast<ast_value*>(m_index);
+
+    if (!ast_istype(m_index, ast_value) || !idx->m_hasvalue || idx->m_cvq != CV_CONST) {
+        /* Time to use accessor functions */
+        ir_value               *iridx, *funval;
+        ir_instr               *call;
+
+        if (lvalue) {
+            compile_error(m_context, "(.2) array indexing here needs a compile-time constant");
+            return false;
+        }
+
+        if (!arr->m_getter) {
+            compile_error(m_context, "value has no getter, don't know how to index it");
+            return false;
+        }
+
+        if (!m_index->codegen(func, false, &iridx))
+            return false;
+
+        if (!arr->m_getter->codegen(func, true, &funval))
+            return false;
+
+        call = ir_block_create_call(func->m_curblock, m_context, func->makeLabel("fetch"), funval, false);
+        if (!call)
+            return false;
+        ir_call_param(call, iridx);
+
+        *out = ir_call_value(call);
+        m_outr = *out;
+        (*out)->m_vtype = m_vtype;
+        codegen_output_type(this, *out);
+        return true;
+    }
+
+    if (idx->m_vtype == TYPE_FLOAT) {
+        unsigned int arridx = idx->m_constval.vfloat;
+        if (arridx >= m_array->m_count)
+        {
+            compile_error(m_context, "array index out of bounds: %i", arridx);
+            return false;
+        }
+        *out = arr->m_ir_values[arridx];
+    }
+    else if (idx->m_vtype == TYPE_INTEGER) {
+        unsigned int arridx = idx->m_constval.vint;
+        if (arridx >= m_array->m_count)
+        {
+            compile_error(m_context, "array index out of bounds: %i", arridx);
+            return false;
+        }
+        *out = arr->m_ir_values[arridx];
+    }
+    else {
+        compile_error(m_context, "array indexing here needs an integer constant");
+        return false;
+    }
+    (*out)->m_vtype = m_vtype;
+    codegen_output_type(this, *out);
+    return true;
+}
+
+bool ast_argpipe::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    *out = nullptr;
+    if (lvalue) {
+        compile_error(m_context, "argpipe node: not an lvalue");
+        return false;
+    }
+    (void)func;
+    (void)out;
+    compile_error(m_context, "TODO: argpipe codegen not implemented");
+    return false;
+}
+
+bool ast_ifthen::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *condval;
+    ir_value *dummy;
+
+    ir_block *cond;
+    ir_block *ontrue;
+    ir_block *onfalse;
+    ir_block *ontrue_endblock = nullptr;
+    ir_block *onfalse_endblock = nullptr;
+    ir_block *merge = nullptr;
+    int folded = 0;
+
+    /* We don't output any value, thus also don't care about r/lvalue */
+    (void)out;
+    (void)lvalue;
+
+    if (m_outr) {
+        compile_error(m_context, "internal error: ast_ifthen cannot be reused, it bears no result!");
+        return false;
+    }
+    m_outr = (ir_value*)1;
+
+    /* generate the condition */
+    if (!m_cond->codegen(func, false, &condval))
+        return false;
+    /* update the block which will get the jump - because short-logic or ternaries may have changed this */
+    cond = func->m_curblock;
+
+    /* try constant folding away the condition */
+    if ((folded = fold::cond_ifthen(condval, func, this)) != -1)
+        return folded;
+
+    if (m_on_true) {
+        /* create on-true block */
+        ontrue = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("ontrue"));
+        if (!ontrue)
+            return false;
+
+        /* enter the block */
+        func->m_curblock = ontrue;
+
+        /* generate */
+        if (!m_on_true->codegen(func, false, &dummy))
+            return false;
+
+        /* we now need to work from the current endpoint */
+        ontrue_endblock = func->m_curblock;
+    } else
+        ontrue = nullptr;
+
+    /* on-false path */
+    if (m_on_false) {
+        /* create on-false block */
+        onfalse = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("onfalse"));
+        if (!onfalse)
+            return false;
+
+        /* enter the block */
+        func->m_curblock = onfalse;
+
+        /* generate */
+        if (!m_on_false->codegen(func, false, &dummy))
+            return false;
+
+        /* we now need to work from the current endpoint */
+        onfalse_endblock = func->m_curblock;
+    } else
+        onfalse = nullptr;
+
+    /* Merge block were they all merge in to */
+    if (!ontrue || !onfalse || !ontrue_endblock->m_final || !onfalse_endblock->m_final)
+    {
+        merge = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("endif"));
+        if (!merge)
+            return false;
+        /* add jumps ot the merge block */
+        if (ontrue && !ontrue_endblock->m_final && !ir_block_create_jump(ontrue_endblock, m_context, merge))
+            return false;
+        if (onfalse && !onfalse_endblock->m_final && !ir_block_create_jump(onfalse_endblock, m_context, merge))
+            return false;
+
+        /* Now enter the merge block */
+        func->m_curblock = merge;
+    }
+
+    /* we create the if here, that way all blocks are ordered :)
+     */
+    if (!ir_block_create_if(cond, m_context, condval,
+                            (ontrue  ? ontrue  : merge),
+                            (onfalse ? onfalse : merge)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool ast_ternary::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *condval;
+    ir_value *trueval, *falseval;
+    ir_instr *phi;
+
+    ir_block *cond = func->m_curblock;
+    ir_block *cond_out = nullptr;
+    ir_block *ontrue, *ontrue_out = nullptr;
+    ir_block *onfalse, *onfalse_out = nullptr;
+    ir_block *merge;
+    int folded = 0;
+
+    /* Ternary can never create an lvalue... */
+    if (lvalue)
+        return false;
+
+    /* In theory it shouldn't be possible to pass through a node twice, but
+     * in case we add any kind of optimization pass for the AST itself, it
+     * may still happen, thus we remember a created ir_value and simply return one
+     * if it already exists.
+     */
+    if (m_outr) {
+        *out = m_outr;
+        return true;
+    }
+
+    /* In the following, contraty to ast_ifthen, we assume both paths exist. */
+
+    /* generate the condition */
+    func->m_curblock = cond;
+    if (!m_cond->codegen(func, false, &condval))
+        return false;
+    cond_out = func->m_curblock;
+
+    /* try constant folding away the condition */
+    if ((folded = fold::cond_ternary(condval, func, this)) != -1)
+        return folded;
+
+    /* create on-true block */
+    ontrue = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("tern_T"));
+    if (!ontrue)
+        return false;
+    else
+    {
+        /* enter the block */
+        func->m_curblock = ontrue;
+
+        /* generate */
+        if (!m_on_true->codegen(func, false, &trueval))
+            return false;
+
+        ontrue_out = func->m_curblock;
+    }
+
+    /* create on-false block */
+    onfalse = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("tern_F"));
+    if (!onfalse)
+        return false;
+    else
+    {
+        /* enter the block */
+        func->m_curblock = onfalse;
+
+        /* generate */
+        if (!m_on_false->codegen(func, false, &falseval))
+            return false;
+
+        onfalse_out = func->m_curblock;
+    }
+
+    /* create merge block */
+    merge = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("tern_out"));
+    if (!merge)
+        return false;
+    /* jump to merge block */
+    if (!ir_block_create_jump(ontrue_out, m_context, merge))
+        return false;
+    if (!ir_block_create_jump(onfalse_out, m_context, merge))
+        return false;
+
+    /* create if instruction */
+    if (!ir_block_create_if(cond_out, m_context, condval, ontrue, onfalse))
+        return false;
+
+    /* Now enter the merge block */
+    func->m_curblock = merge;
+
+    /* Here, now, we need a PHI node
+     * but first some sanity checking...
+     */
+    if (trueval->m_vtype != falseval->m_vtype && trueval->m_vtype != TYPE_NIL && falseval->m_vtype != TYPE_NIL) {
+        /* error("ternary with different types on the two sides"); */
+        compile_error(m_context, "internal error: ternary operand types invalid");
+        return false;
+    }
+
+    /* create PHI */
+    phi = ir_block_create_phi(merge, m_context, func->makeLabel("phi"), m_vtype);
+    if (!phi) {
+        compile_error(m_context, "internal error: failed to generate phi node");
+        return false;
+    }
+    ir_phi_add(phi, ontrue_out,  trueval);
+    ir_phi_add(phi, onfalse_out, falseval);
+
+    m_outr = ir_phi_value(phi);
+    *out = m_outr;
+
+    codegen_output_type(this, *out);
+
+    return true;
+}
+
+bool ast_loop::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *dummy      = nullptr;
+    ir_value *precond    = nullptr;
+    ir_value *postcond   = nullptr;
+
+    /* Since we insert some jumps "late" so we have blocks
+     * ordered "nicely", we need to keep track of the actual end-blocks
+     * of expressions to add the jumps to.
+     */
+    ir_block *bbody      = nullptr, *end_bbody      = nullptr;
+    ir_block *bprecond   = nullptr, *end_bprecond   = nullptr;
+    ir_block *bpostcond  = nullptr, *end_bpostcond  = nullptr;
+    ir_block *bincrement = nullptr, *end_bincrement = nullptr;
+    ir_block *bout       = nullptr, *bin            = nullptr;
+
+    /* let's at least move the outgoing block to the end */
+    size_t    bout_id;
+
+    /* 'break' and 'continue' need to be able to find the right blocks */
+    ir_block *bcontinue     = nullptr;
+    ir_block *bbreak        = nullptr;
+
+    ir_block *tmpblock      = nullptr;
+
+    (void)lvalue;
+    (void)out;
+
+    if (m_outr) {
+        compile_error(m_context, "internal error: ast_loop cannot be reused, it bears no result!");
+        return false;
+    }
+    m_outr = (ir_value*)1;
+
+    /* NOTE:
+     * Should we ever need some kind of block ordering, better make this function
+     * move blocks around than write a block ordering algorithm later... after all
+     * the ast and ir should work together, not against each other.
+     */
+
+    /* initexpr doesn't get its own block, it's pointless, it could create more blocks
+     * anyway if for example it contains a ternary.
+     */
+    if (m_initexpr)
+    {
+        if (!m_initexpr->codegen(func, false, &dummy))
+            return false;
+    }
+
+    /* Store the block from which we enter this chaos */
+    bin = func->m_curblock;
+
+    /* The pre-loop condition needs its own block since we
+     * need to be able to jump to the start of that expression.
+     */
+    if (m_precond)
+    {
+        bprecond = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("pre_loop_cond"));
+        if (!bprecond)
+            return false;
+
+        /* the pre-loop-condition the least important place to 'continue' at */
+        bcontinue = bprecond;
+
+        /* enter */
+        func->m_curblock = bprecond;
+
+        /* generate */
+        if (!m_precond->codegen(func, false, &precond))
+            return false;
+
+        end_bprecond = func->m_curblock;
+    } else {
+        bprecond = end_bprecond = nullptr;
+    }
+
+    /* Now the next blocks won't be ordered nicely, but we need to
+     * generate them this early for 'break' and 'continue'.
+     */
+    if (m_increment) {
+        bincrement = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("loop_increment"));
+        if (!bincrement)
+            return false;
+        bcontinue = bincrement; /* increment comes before the pre-loop-condition */
+    } else {
+        bincrement = end_bincrement = nullptr;
+    }
+
+    if (m_postcond) {
+        bpostcond = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("post_loop_cond"));
+        if (!bpostcond)
+            return false;
+        bcontinue = bpostcond; /* postcond comes before the increment */
+    } else {
+        bpostcond = end_bpostcond = nullptr;
+    }
+
+    bout_id = func->m_ir_func->m_blocks.size();
+    bout = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("after_loop"));
+    if (!bout)
+        return false;
+    bbreak = bout;
+
+    /* The loop body... */
+    /* if (m_body) */
+    {
+        bbody = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("loop_body"));
+        if (!bbody)
+            return false;
+
+        /* enter */
+        func->m_curblock = bbody;
+
+        func->m_breakblocks.push_back(bbreak);
+        if (bcontinue)
+            func->m_continueblocks.push_back(bcontinue);
+        else
+            func->m_continueblocks.push_back(bbody);
+
+        /* generate */
+        if (m_body) {
+            if (!m_body->codegen(func, false, &dummy))
+                return false;
+        }
+
+        end_bbody = func->m_curblock;
+        func->m_breakblocks.pop_back();
+        func->m_continueblocks.pop_back();
+    }
+
+    /* post-loop-condition */
+    if (m_postcond)
+    {
+        /* enter */
+        func->m_curblock = bpostcond;
+
+        /* generate */
+        if (!m_postcond->codegen(func, false, &postcond))
+            return false;
+
+        end_bpostcond = func->m_curblock;
+    }
+
+    /* The incrementor */
+    if (m_increment)
+    {
+        /* enter */
+        func->m_curblock = bincrement;
+
+        /* generate */
+        if (!m_increment->codegen(func, false, &dummy))
+            return false;
+
+        end_bincrement = func->m_curblock;
+    }
+
+    /* In any case now, we continue from the outgoing block */
+    func->m_curblock = bout;
+
+    /* Now all blocks are in place */
+    /* From 'bin' we jump to whatever comes first */
+    if      (bprecond)   tmpblock = bprecond;
+    else                 tmpblock = bbody;    /* can never be null */
+
+    /* DEAD CODE
+    else if (bpostcond)  tmpblock = bpostcond;
+    else                 tmpblock = bout;
+    */
+
+    if (!ir_block_create_jump(bin, m_context, tmpblock))
+        return false;
+
+    /* From precond */
+    if (bprecond)
+    {
+        ir_block *ontrue, *onfalse;
+        ontrue = bbody; /* can never be null */
+
+        /* all of this is dead code
+        else if (bincrement) ontrue = bincrement;
+        else                 ontrue = bpostcond;
+        */
+
+        onfalse = bout;
+        if (m_pre_not) {
+            tmpblock = ontrue;
+            ontrue   = onfalse;
+            onfalse  = tmpblock;
+        }
+        if (!ir_block_create_if(end_bprecond, m_context, precond, ontrue, onfalse))
+            return false;
+    }
+
+    /* from body */
+    if (bbody)
+    {
+        if      (bincrement) tmpblock = bincrement;
+        else if (bpostcond)  tmpblock = bpostcond;
+        else if (bprecond)   tmpblock = bprecond;
+        else                 tmpblock = bbody;
+        if (!end_bbody->m_final && !ir_block_create_jump(end_bbody, m_context, tmpblock))
+            return false;
+    }
+
+    /* from increment */
+    if (bincrement)
+    {
+        if      (bpostcond)  tmpblock = bpostcond;
+        else if (bprecond)   tmpblock = bprecond;
+        else if (bbody)      tmpblock = bbody;
+        else                 tmpblock = bout;
+        if (!ir_block_create_jump(end_bincrement, m_context, tmpblock))
+            return false;
+    }
+
+    /* from postcond */
+    if (bpostcond)
+    {
+        ir_block *ontrue, *onfalse;
+        if      (bprecond)   ontrue = bprecond;
+        else                 ontrue = bbody; /* can never be null */
+
+        /* all of this is dead code
+        else if (bincrement) ontrue = bincrement;
+        else                 ontrue = bpostcond;
+        */
+
+        onfalse = bout;
+        if (m_post_not) {
+            tmpblock = ontrue;
+            ontrue   = onfalse;
+            onfalse  = tmpblock;
+        }
+        if (!ir_block_create_if(end_bpostcond, m_context, postcond, ontrue, onfalse))
+            return false;
+    }
+
+    /* Move 'bout' to the end */
+    algo::shiftback(func->m_ir_func->m_blocks.begin() + bout_id,
+                    func->m_ir_func->m_blocks.end());
+    // FIXME::DELME::
+    //func->m_ir_func->m_blocks[bout_id].release(); // it's a vector<std::unique_ptr<>>
+    //func->m_ir_func->m_blocks.erase(func->m_ir_func->m_blocks.begin() + bout_id);
+    //func->m_ir_func->m_blocks.emplace_back(bout);
+
+    return true;
+}
+
+bool ast_breakcont::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_block *target;
+
+    *out = nullptr;
+
+    if (lvalue) {
+        compile_error(m_context, "break/continue expression is not an l-value");
+        return false;
+    }
+
+    if (m_outr) {
+        compile_error(m_context, "internal error: ast_breakcont cannot be reused!");
+        return false;
+    }
+    m_outr = (ir_value*)1;
+
+    if (m_is_continue)
+        target = func->m_continueblocks[func->m_continueblocks.size()-1-m_levels];
+    else
+        target = func->m_breakblocks[func->m_breakblocks.size()-1-m_levels];
+
+    if (!target) {
+        compile_error(m_context, "%s is lacking a target block", (m_is_continue ? "continue" : "break"));
+        return false;
+    }
+
+    if (!ir_block_create_jump(func->m_curblock, m_context, target))
+        return false;
+    return true;
+}
+
+bool ast_switch::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ast_switch_case *def_case     = nullptr;
+    ir_block        *def_bfall    = nullptr;
+    ir_block        *def_bfall_to = nullptr;
+    bool set_def_bfall_to = false;
+
+    ir_value *dummy     = nullptr;
+    ir_value *irop      = nullptr;
+    ir_block *bout      = nullptr;
+    ir_block *bfall     = nullptr;
+    size_t    bout_id;
+
+    char      typestr[1024];
+    uint16_t  cmpinstr;
+
+    if (lvalue) {
+        compile_error(m_context, "switch expression is not an l-value");
+        return false;
+    }
+
+    if (m_outr) {
+        compile_error(m_context, "internal error: ast_switch cannot be reused!");
+        return false;
+    }
+    m_outr = (ir_value*)1;
+
+    (void)lvalue;
+    (void)out;
+
+    if (!m_operand->codegen(func, false, &irop))
+        return false;
+
+    if (m_cases.empty())
+        return true;
+
+    cmpinstr = type_eq_instr[irop->m_vtype];
+    if (cmpinstr >= VINSTR_END) {
+        ast_type_to_string(m_operand, typestr, sizeof(typestr));
+        compile_error(m_context, "invalid type to perform a switch on: %s", typestr);
+        return false;
+    }
+
+    bout_id = func->m_ir_func->m_blocks.size();
+    bout = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("after_switch"));
+    if (!bout)
+        return false;
+
+    /* setup the break block */
+    func->m_breakblocks.push_back(bout);
+
+    /* Now create all cases */
+    for (auto &it : m_cases) {
+        ir_value *cond, *val;
+        ir_block *bcase, *bnot;
+        size_t bnot_id;
+
+        ast_switch_case *swcase = &it;
+
+        if (swcase->m_value) {
+            /* A regular case */
+            /* generate the condition operand */
+            if (!swcase->m_value->codegen(func, false, &val))
+                return false;
+            /* generate the condition */
+            cond = ir_block_create_binop(func->m_curblock, m_context, func->makeLabel("switch_eq"), cmpinstr, irop, val);
+            if (!cond)
+                return false;
+
+            bcase = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("case"));
+            bnot_id = func->m_ir_func->m_blocks.size();
+            bnot = ir_function_create_block(m_context, func->m_ir_func, func->makeLabel("not_case"));
+            if (!bcase || !bnot)
+                return false;
+            if (set_def_bfall_to) {
+                set_def_bfall_to = false;
+                def_bfall_to = bcase;
+            }
+            if (!ir_block_create_if(func->m_curblock, m_context, cond, bcase, bnot))
+                return false;
+
+            /* Make the previous case-end fall through */
+            if (bfall && !bfall->m_final) {
+                if (!ir_block_create_jump(bfall, m_context, bcase))
+                    return false;
+            }
+
+            /* enter the case */
+            func->m_curblock = bcase;
+            if (!swcase->m_code->codegen(func, false, &dummy))
+                return false;
+
+            /* remember this block to fall through from */
+            bfall = func->m_curblock;
+
+            /* enter the else and move it down */
+            func->m_curblock = bnot;
+            algo::shiftback(func->m_ir_func->m_blocks.begin() + bnot_id,
+                            func->m_ir_func->m_blocks.end());
+            // FIXME::DELME::
+            //func->m_ir_func->m_blocks[bnot_id].release();
+            //func->m_ir_func->m_blocks.erase(func->m_ir_func->m_blocks.begin() + bnot_id);
+            //func->m_ir_func->m_blocks.emplace_back(bnot);
+        } else {
+            /* The default case */
+            /* Remember where to fall through from: */
+            def_bfall = bfall;
+            bfall     = nullptr;
+            /* remember which case it was */
+            def_case  = swcase;
+            /* And the next case will be remembered */
+            set_def_bfall_to = true;
+        }
+    }
+
+    /* Jump from the last bnot to bout */
+    if (bfall && !bfall->m_final && !ir_block_create_jump(bfall, m_context, bout)) {
+        /*
+        astwarning(bfall->m_context, WARN_???, "missing break after last case");
+        */
+        return false;
+    }
+
+    /* If there was a default case, put it down here */
+    if (def_case) {
+        ir_block *bcase;
+
+        /* No need to create an extra block */
+        bcase = func->m_curblock;
+
+        /* Insert the fallthrough jump */
+        if (def_bfall && !def_bfall->m_final) {
+            if (!ir_block_create_jump(def_bfall, m_context, bcase))
+                return false;
+        }
+
+        /* Now generate the default code */
+        if (!def_case->m_code->codegen(func, false, &dummy))
+            return false;
+
+        /* see if we need to fall through */
+        if (def_bfall_to && !func->m_curblock->m_final)
+        {
+            if (!ir_block_create_jump(func->m_curblock, m_context, def_bfall_to))
+                return false;
+        }
+    }
+
+    /* Jump from the last bnot to bout */
+    if (!func->m_curblock->m_final && !ir_block_create_jump(func->m_curblock, m_context, bout))
+        return false;
+    /* enter the outgoing block */
+    func->m_curblock = bout;
+
+    /* restore the break block */
+    func->m_breakblocks.pop_back();
+
+    /* Move 'bout' to the end, it's nicer */
+    algo::shiftback(func->m_ir_func->m_blocks.begin() + bout_id,
+                    func->m_ir_func->m_blocks.end());
+    // FIXME::DELME::
+    //func->m_ir_func->m_blocks[bout_id].release();
+    //func->m_ir_func->m_blocks.erase(func->m_ir_func->m_blocks.begin() + bout_id);
+    //func->m_ir_func->m_blocks.emplace_back(bout);
+
+    return true;
+}
+
+bool ast_label::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *dummy;
+
+    if (m_undefined) {
+        compile_error(m_context, "internal error: ast_label never defined");
+        return false;
+    }
+
+    *out = nullptr;
+    if (lvalue) {
+        compile_error(m_context, "internal error: ast_label cannot be an lvalue");
+        return false;
+    }
+
+    /* simply create a new block and jump to it */
+    m_irblock = ir_function_create_block(m_context, func->m_ir_func, m_name.c_str());
+    if (!m_irblock) {
+        compile_error(m_context, "failed to allocate label block `%s`", m_name);
+        return false;
+    }
+    if (!func->m_curblock->m_final) {
+        if (!ir_block_create_jump(func->m_curblock, m_context, m_irblock))
+            return false;
+    }
+
+    /* enter the new block */
+    func->m_curblock = m_irblock;
+
+    /* Generate all the leftover gotos */
+    for (auto &it : m_gotos) {
+        if (!it->codegen(func, false, &dummy))
+            return false;
+    }
+
+    return true;
+}
+
+bool ast_goto::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    *out = nullptr;
+    if (lvalue) {
+        compile_error(m_context, "internal error: ast_goto cannot be an lvalue");
+        return false;
+    }
+
+    if (m_target->m_irblock) {
+        if (m_irblock_from) {
+            /* we already tried once, this is the callback */
+            m_irblock_from->m_final = false;
+            if (!ir_block_create_goto(m_irblock_from, m_context, m_target->m_irblock)) {
+                compile_error(m_context, "failed to generate goto to `%s`", m_name);
+                return false;
+            }
+        }
+        else
+        {
+            if (!ir_block_create_goto(func->m_curblock, m_context, m_target->m_irblock)) {
+                compile_error(m_context, "failed to generate goto to `%s`", m_name);
+                return false;
+            }
+        }
+    }
+    else
+    {
+        /* the target has not yet been created...
+         * close this block in a sneaky way:
+         */
+        func->m_curblock->m_final = true;
+        m_irblock_from = func->m_curblock;
+        m_target->registerGoto(this);
+    }
+
+    return true;
+}
+
+bool ast_state::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    ir_value *frameval, *thinkval;
+
+    if (lvalue) {
+        compile_error(m_context, "not an l-value (state operation)");
+        return false;
+    }
+    if (m_outr) {
+        compile_error(m_context, "internal error: ast_state cannot be reused!");
+        return false;
+    }
+    *out = nullptr;
+
+    if (!m_framenum->codegen(func, false, &frameval))
+        return false;
+    if (!frameval)
+        return false;
+
+    if (!m_nextthink->codegen(func, false, &thinkval))
+        return false;
+    if (!frameval)
+        return false;
+
+    if (!ir_block_create_state_op(func->m_curblock, m_context, frameval, thinkval)) {
+        compile_error(m_context, "failed to create STATE instruction");
+        return false;
+    }
+
+    m_outr = (ir_value*)1;
+    return true;
+}
+
+bool ast_call::codegen(ast_function *func, bool lvalue, ir_value **out)
+{
+    std::vector<ir_value*> params;
+    ir_instr *callinstr;
+
+    ir_value *funval = nullptr;
+
+    /* return values are never lvalues */
+    if (lvalue) {
+        compile_error(m_context, "not an l-value (function call)");
+        return false;
+    }
+
+    if (m_outr) {
+        *out = m_outr;
+        return true;
+    }
+
+    if (!m_func->codegen(func, false, &funval))
+        return false;
+    if (!funval)
+        return false;
+
+    /* parameters */
+    for (auto &it : m_params) {
+        ir_value *param;
+        if (!it->codegen(func, false, &param))
+            return false;
+        if (!param)
+            return false;
+        params.push_back(param);
+    }
+
+    /* varargs counter */
+    if (m_va_count) {
+        ir_value   *va_count;
+        ir_builder *builder = func->m_curblock->m_owner->m_owner;
+        if (!m_va_count->codegen(func, false, &va_count))
+            return false;
+        if (!ir_block_create_store_op(func->m_curblock, m_context, INSTR_STORE_F,
+                                      builder->get_va_count(), va_count))
+        {
+            return false;
+        }
+    }
+
+    callinstr = ir_block_create_call(func->m_curblock, m_context,
+                                     func->makeLabel("call"),
+                                     funval, !!(m_func->m_flags & AST_FLAG_NORETURN));
+    if (!callinstr)
+        return false;
+
+    for (auto &it : params)
+        ir_call_param(callinstr, it);
+
+    *out = ir_call_value(callinstr);
+    m_outr = *out;
+
+    codegen_output_type(this, *out);
+
+    return true;
+}
diff --git a/ast.h b/ast.h
index 5b556e1be52912e44a554c1b4e99a4d059b3097b..b305dac393f82001e018cc2a0d53d7c64394ffc4 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -1,28 +1,6 @@
-/*
- * Copyright (C) 2012, 2013, 2014, 2015
- *     Wolfgang Bumiller
- *     Dale Weiler
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
 #ifndef GMQCC_AST_HDR
 #define GMQCC_AST_HDR
+#include <vector>
 #include "ir.h"
 
 typedef uint16_t ast_flag_t;
@@ -31,30 +9,29 @@ typedef uint16_t ast_flag_t;
  * "main" ast node types for now.
  */
 
-typedef struct ast_node_common       ast_node;
-typedef struct ast_expression_common ast_expression;
-
-typedef struct ast_value_s       ast_value;
-typedef struct ast_function_s    ast_function;
-typedef struct ast_block_s       ast_block;
-typedef struct ast_binary_s      ast_binary;
-typedef struct ast_store_s       ast_store;
-typedef struct ast_binstore_s    ast_binstore;
-typedef struct ast_entfield_s    ast_entfield;
-typedef struct ast_ifthen_s      ast_ifthen;
-typedef struct ast_ternary_s     ast_ternary;
-typedef struct ast_loop_s        ast_loop;
-typedef struct ast_call_s        ast_call;
-typedef struct ast_unary_s       ast_unary;
-typedef struct ast_return_s      ast_return;
-typedef struct ast_member_s      ast_member;
-typedef struct ast_array_index_s ast_array_index;
-typedef struct ast_breakcont_s   ast_breakcont;
-typedef struct ast_switch_s      ast_switch;
-typedef struct ast_label_s       ast_label;
-typedef struct ast_goto_s        ast_goto;
-typedef struct ast_argpipe_s     ast_argpipe;
-typedef struct ast_state_s       ast_state;
+struct ast_node;
+struct ast_expression;
+struct ast_value;
+struct ast_function;
+struct ast_block;
+struct ast_binary;
+struct ast_store;
+struct ast_binstore;
+struct ast_entfield;
+struct ast_ifthen;
+struct ast_ternary;
+struct ast_loop;
+struct ast_call;
+struct ast_unary;
+struct ast_return;
+struct ast_member;
+struct ast_array_index;
+struct ast_breakcont;
+struct ast_switch;
+struct ast_label;
+struct ast_goto;
+struct ast_argpipe;
+struct ast_state;
 
 enum {
     AST_FLAG_VARIADIC       = 1 << 0,
@@ -116,43 +93,36 @@ enum {
     TYPE_ast_state        /* 22 */
 };
 
-#define ast_istype(x, t) ( ((ast_node*)x)->nodetype == (TYPE_##t) )
-#define ast_ctx(node) (((ast_node*)(node))->context)
-#define ast_side_effects(node) (((ast_node*)(node))->side_effects)
+#define ast_istype(x, t) ( (x)->m_node_type == (TYPE_##t) )
 
-/* Node interface with common components
- */
-typedef void ast_node_delete(ast_node*);
-struct ast_node_common
+struct ast_node
 {
-    lex_ctx_t          context;
+    ast_node() = delete;
+    ast_node(lex_ctx_t, int nodetype);
+    virtual ~ast_node();
+
+    lex_ctx_t m_context;
     /* I don't feel comfortable using keywords like 'delete' as names... */
-    ast_node_delete *destroy;
-    int              nodetype;
-    /* keep: if a node contains this node, 'keep'
+    int              m_node_type;
+    /* keep_node: if a node contains this node, 'keep_node'
      * prevents its dtor from destroying this node as well.
      */
-    bool             keep;
-    bool             side_effects;
+    bool             m_keep_node;
+    bool             m_side_effects;
+
+    void propagateSideEffects(const ast_node *other);
 };
 
-#define ast_delete(x) (*( ((ast_node*)(x))->destroy ))((ast_node*)(x))
-#define ast_unref(x) do                \
-{                                      \
-    if (! (((ast_node*)(x))->keep) ) { \
-        ast_delete(x);                 \
-    }                                  \
+#define ast_unref(x) do        \
+{                              \
+    if (! (x)->m_keep_node ) { \
+        delete (x);            \
+    }                          \
 } while(0)
 
-/* Expression interface
- *
- * Any expression or block returns an ir_value, and needs
- * to know the current function.
- */
-typedef bool ast_expression_codegen(ast_expression*,
-                                    ast_function*,
-                                    bool lvalue,
-                                    ir_value**);
+enum class ast_copy_type_t { value };
+static const ast_copy_type_t ast_copy_type = ast_copy_type_t::value;
+
 /* TODO: the codegen function should take an output-type parameter
  * indicating whether a variable, type, label etc. is expected, and
  * an environment!
@@ -163,27 +133,42 @@ typedef bool ast_expression_codegen(ast_expression*,
  * type `expression`, so the ast_ident's codegen would search for
  * variables through the environment (or functions, constants...).
  */
-struct ast_expression_common
-{
-    ast_node                node;
-    ast_expression_codegen *codegen;
-    int                     vtype;
-    ast_expression         *next;
+struct ast_expression : ast_node {
+    ast_expression() = delete;
+    ast_expression(lex_ctx_t ctx, int nodetype, qc_type vtype);
+    ast_expression(lex_ctx_t ctx, int nodetype);
+    ~ast_expression();
+
+    ast_expression(ast_copy_type_t, const ast_expression&);
+    ast_expression(ast_copy_type_t, lex_ctx_t ctx, const ast_expression&);
+    ast_expression(ast_copy_type_t, int nodetype, const ast_expression&);
+    ast_expression(ast_copy_type_t, int nodetype, lex_ctx_t ctx, const ast_expression&);
+
+    static ast_expression *shallowType(lex_ctx_t ctx, qc_type vtype);
+
+    bool compareType(const ast_expression &other) const;
+    void adoptType(const ast_expression &other);
+
+    qc_type                 m_vtype = TYPE_VOID;
+    ast_expression         *m_next = nullptr;
     /* arrays get a member-count */
-    size_t                  count;
-    ast_value*             *params;
-    ast_flag_t              flags;
+    size_t                  m_count = 0;
+    std::vector<std::unique_ptr<ast_value>> m_type_params;
+
+    ast_flag_t              m_flags = 0;
     /* void foo(string...) gets varparam set as a restriction
      * for variadic parameters
      */
-    ast_expression         *varparam;
+    ast_expression         *m_varparam = nullptr;
     /* The codegen functions should store their output values
      * so we can call it multiple times without re-evaluating.
      * Store lvalue and rvalue seperately though. So that
      * ast_entfield for example can generate both if required.
      */
-    ir_value               *outl;
-    ir_value               *outr;
+    ir_value               *m_outl = nullptr;
+    ir_value               *m_outr = nullptr;
+
+    virtual bool codegen(ast_function *current, bool lvalue, ir_value **out);
 };
 
 /* Value
@@ -193,7 +178,7 @@ struct ast_expression_common
  * typedef float foo;
  * is like creating a 'float foo', foo serving as the type's name.
  */
-typedef union {
+union basic_value_t {
     qcfloat_t     vfloat;
     int           vint;
     vec3_t        vvec;
@@ -201,129 +186,133 @@ typedef union {
     int           ventity;
     ast_function *vfunc;
     ast_value    *vfield;
-} basic_value_t;
+};
 
-struct ast_value_s
+struct ast_value : ast_expression
 {
-    ast_expression        expression;
+    ast_value() = delete;
+    ast_value(lex_ctx_t ctx, const std::string &name, qc_type qctype);
+    ~ast_value();
+
+    ast_value(ast_copy_type_t, const ast_expression&, const std::string&);
+    ast_value(ast_copy_type_t, const ast_value&);
+    ast_value(ast_copy_type_t, const ast_value&, const std::string&);
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
 
-    const char *name;
-    const char *desc;
+    void addParam(ast_value*);
 
-    const char *argcounter;
+    bool generateGlobal(ir_builder*, bool isfield);
+    bool generateLocal(ir_function*, bool param);
+    bool generateAccessors(ir_builder*);
 
-    int  cvq;     /* const/var qualifier */
-    bool isfield; /* this declares a field */
-    bool isimm;   /* an immediate, not just const */
-    bool hasvalue;
-    bool inexact; /* inexact coming from folded expression */
-    basic_value_t constval;
+    std::string m_name;
+    std::string m_desc;
+
+    const char *m_argcounter = nullptr;
+
+    int m_cvq = CV_NONE;     /* const/var qualifier */
+    bool m_isfield = false; /* this declares a field */
+    bool m_isimm = false;   /* an immediate, not just const */
+    bool m_hasvalue = false;
+    bool m_inexact = false; /* inexact coming from folded expression */
+    basic_value_t m_constval;
     /* for TYPE_ARRAY we have an optional vector
      * of constants when an initializer list
      * was provided.
      */
-    basic_value_t *initlist;
+    std::vector<basic_value_t> m_initlist;
 
     /* usecount for the parser */
-    size_t uses;
+    size_t m_uses = 0;
 
-    ir_value *ir_v;
-    ir_value **ir_values;
-    size_t   ir_value_count;
+    ir_value *m_ir_v = nullptr;
+    std::vector<ir_value*> m_ir_values;
+    size_t m_ir_value_count = 0;
 
     /* ONLY for arrays in progs version up to 6 */
-    ast_value *setter;
-    ast_value *getter;
+    ast_value *m_setter = nullptr;
+    ast_value *m_getter = nullptr;
 
+    bool m_intrinsic = false; /* true if associated with intrinsic */
 
-    bool      intrinsic; /* true if associated with intrinsic */
+private:
+    bool generateGlobalFunction(ir_builder*);
+    bool generateGlobalField(ir_builder*);
+    ir_value *prepareGlobalArray(ir_builder*);
+    bool setGlobalArray();
+    bool checkArray(const ast_value &array) const;
 };
 
-ast_value* ast_value_new(lex_ctx_t ctx, const char *name, int qctype);
-ast_value* ast_value_copy(const ast_value *self);
-/* This will NOT delete an underlying ast_function */
-void ast_value_delete(ast_value*);
-
-bool ast_value_set_name(ast_value*, const char *name);
-
-/*
-bool ast_value_codegen(ast_value*, ast_function*, bool lvalue, ir_value**);
-bool ast_local_codegen(ast_value *self, ir_function *func, bool isparam);
-*/
-
-bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield);
+void ast_type_to_string(const ast_expression *e, char *buf, size_t bufsize);
 
-void ast_value_params_add(ast_value*, ast_value*);
-
-bool ast_compare_type(ast_expression *a, ast_expression *b);
-ast_expression* ast_type_copy(lex_ctx_t ctx, const ast_expression *ex);
-#define ast_type_adopt(a, b) ast_type_adopt_impl((ast_expression*)(a), (ast_expression*)(b))
-void ast_type_adopt_impl(ast_expression *self, const ast_expression *other);
-void ast_type_to_string(ast_expression *e, char *buf, size_t bufsize);
-
-typedef enum ast_binary_ref_s {
+enum ast_binary_ref {
     AST_REF_NONE  = 0,
     AST_REF_LEFT  = 1 << 1,
     AST_REF_RIGHT = 1 << 2,
     AST_REF_ALL   = (AST_REF_LEFT | AST_REF_RIGHT)
-} ast_binary_ref;
+};
 
 
 /* Binary
  *
  * A value-returning binary expression.
  */
-struct ast_binary_s
+struct ast_binary : ast_expression
 {
-    ast_expression        expression;
+    ast_binary() = delete;
+    ast_binary(lex_ctx_t ctx, int op, ast_expression *l, ast_expression *r);
+    ~ast_binary();
 
-    int             op;
-    ast_expression *left;
-    ast_expression *right;
-    ast_binary_ref  refs;
-    bool            right_first;
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    int m_op;
+    ast_expression *m_left;
+    ast_expression *m_right;
+    ast_binary_ref m_refs;
+    bool m_right_first;
 };
-ast_binary* ast_binary_new(lex_ctx_t    ctx,
-                           int        op,
-                           ast_expression *left,
-                           ast_expression *right);
 
 /* Binstore
  *
  * An assignment including a binary expression with the source as left operand.
  * Eg. a += b; is a binstore { INSTR_STORE, INSTR_ADD, a, b }
  */
-struct ast_binstore_s
+struct ast_binstore : ast_expression
 {
-    ast_expression        expression;
+    ast_binstore() = delete;
+    ast_binstore(lex_ctx_t ctx, int storeop, int mathop, ast_expression *l, ast_expression *r);
+    ~ast_binstore();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
 
-    int             opstore;
-    int             opbin;
-    ast_expression *dest;
-    ast_expression *source;
+    int m_opstore;
+    int m_opbin;
+    ast_expression *m_dest;
+    ast_expression *m_source;
     /* for &~= which uses the destination in a binary in source we can use this */
-    bool            keep_dest;
+    bool m_keep_dest;
 };
-ast_binstore* ast_binstore_new(lex_ctx_t    ctx,
-                               int        storeop,
-                               int        op,
-                               ast_expression *left,
-                               ast_expression *right);
 
 /* Unary
  *
  * Regular unary expressions: not,neg
  */
-struct ast_unary_s
+struct ast_unary : ast_expression
 {
-    ast_expression        expression;
+    ast_unary() = delete;
+    ~ast_unary();
 
-    int             op;
-    ast_expression *operand;
+    static ast_unary* make(lex_ctx_t ctx, int op, ast_expression *expr);
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    int m_op;
+    ast_expression *m_operand;
+
+private:
+    ast_unary(lex_ctx_t ctx, int op, ast_expression *expr);
 };
-ast_unary* ast_unary_new(lex_ctx_t    ctx,
-                         int        op,
-                         ast_expression *expr);
 
 /* Return
  *
@@ -331,13 +320,16 @@ ast_unary* ast_unary_new(lex_ctx_t    ctx,
  * will refuse to create further instructions.
  * This should be honored by the parser.
  */
-struct ast_return_s
+struct ast_return : ast_expression
 {
-    ast_expression        expression;
-    ast_expression *operand;
+    ast_return() = delete;
+    ast_return(lex_ctx_t ctx, ast_expression *expr);
+    ~ast_return();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    ast_expression *m_operand;
 };
-ast_return* ast_return_new(lex_ctx_t    ctx,
-                           ast_expression *expr);
 
 /* Entity-field
  *
@@ -352,34 +344,42 @@ ast_return* ast_return_new(lex_ctx_t    ctx,
  * For this we will have to extend the codegen() functions with
  * a flag saying whether or not we need an L or an R-value.
  */
-struct ast_entfield_s
+struct ast_entfield : ast_expression
 {
-    ast_expression        expression;
-    /* The entity can come from an expression of course. */
-    ast_expression *entity;
-    /* As can the field, it just must result in a value of TYPE_FIELD */
-    ast_expression *field;
+    ast_entfield() = delete;
+    ast_entfield(lex_ctx_t ctx, ast_expression *entity, ast_expression *field);
+    ast_entfield(lex_ctx_t ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype);
+    ~ast_entfield();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    // The entity can come from an expression of course.
+    ast_expression *m_entity;
+    // As can the field, it just must result in a value of TYPE_FIELD
+    ast_expression *m_field;
 };
-ast_entfield* ast_entfield_new(lex_ctx_t ctx, ast_expression *entity, ast_expression *field);
-ast_entfield* ast_entfield_new_force(lex_ctx_t ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype);
 
 /* Member access:
  *
  * For now used for vectors. If we get structs or unions
  * we can have them handled here as well.
  */
-struct ast_member_s
+struct ast_member : ast_expression
 {
-    ast_expression  expression;
-    ast_expression *owner;
-    unsigned int    field;
-    const char     *name;
-    bool            rvalue;
-};
-ast_member* ast_member_new(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const char *name);
-void ast_member_delete(ast_member*);
-bool ast_member_set_name(ast_member*, const char *name);
+    static ast_member *make(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const std::string &name);
+    ~ast_member();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    ast_expression *m_owner;
+    unsigned int m_field;
+    std::string m_name;
+    bool m_rvalue;
 
+private:
+    ast_member() = delete;
+    ast_member(lex_ctx_t ctx, ast_expression *owner, unsigned int field, const std::string &name);
+};
 
 /* Array index access:
  *
@@ -391,60 +391,77 @@ bool ast_member_set_name(ast_member*, const char *name);
  * In any case, accessing an element via a compiletime-constant index will
  * result in quick access to that variable.
  */
-struct ast_array_index_s
+struct ast_array_index : ast_expression
 {
-    ast_expression  expression;
-    ast_expression *array;
-    ast_expression *index;
+    static ast_array_index* make(lex_ctx_t ctx, ast_expression *array, ast_expression *index);
+    ~ast_array_index();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    ast_expression *m_array;
+    ast_expression *m_index;
+private:
+    ast_array_index() = delete;
+    ast_array_index(lex_ctx_t ctx, ast_expression *array, ast_expression *index);
 };
-ast_array_index* ast_array_index_new(lex_ctx_t ctx, ast_expression *array, ast_expression *index);
 
 /* Vararg pipe node:
  *
  * copy all varargs starting from a specific index
  */
-struct ast_argpipe_s
+struct ast_argpipe : ast_expression
 {
-    ast_expression  expression;
-    ast_expression *index;
+    ast_argpipe() = delete;
+    ast_argpipe(lex_ctx_t ctx, ast_expression *index);
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    ~ast_argpipe();
+    ast_expression *m_index;
 };
-ast_argpipe* ast_argpipe_new(lex_ctx_t ctx, ast_expression *index);
 
 /* Store
  *
  * Stores left<-right and returns left.
  * Specialized binary expression node
  */
-struct ast_store_s
+struct ast_store : ast_expression
 {
-    ast_expression  expression;
-    int             op;
-    ast_expression *dest;
-    ast_expression *source;
+    ast_store() = delete;
+    ast_store(lex_ctx_t ctx, int op, ast_expression *d, ast_expression *s);
+    ~ast_store();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    int m_op;
+    ast_expression *m_dest;
+    ast_expression *m_source;
 };
-ast_store* ast_store_new(lex_ctx_t ctx, int op,
-                         ast_expression *d, ast_expression *s);
 
 /* If
  *
- * A general 'if then else' statement, either side can be NULL and will
- * thus be omitted. It is an error for *both* cases to be NULL at once.
+ * A general 'if then else' statement, either side can be nullptr and will
+ * thus be omitted. It is an error for *both* cases to be nullptr at once.
  *
  * During its 'codegen' it'll be changing the ast_function's block.
  *
- * An if is also an "expression". Its codegen will put NULL into the
+ * An if is also an "expression". Its codegen will put nullptr into the
  * output field though. For ternary expressions an ast_ternary will be
  * added.
  */
-struct ast_ifthen_s
+struct ast_ifthen : ast_expression
 {
-    ast_expression  expression;
-    ast_expression *cond;
+    ast_ifthen() = delete;
+    ast_ifthen(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
+    ~ast_ifthen();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    ast_expression *m_cond;
     /* It's all just 'expressions', since an ast_block is one too. */
-    ast_expression *on_true;
-    ast_expression *on_false;
+    ast_expression *m_on_true;
+    ast_expression *m_on_false;
 };
-ast_ifthen* ast_ifthen_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
 
 /* Ternary expressions...
  *
@@ -454,20 +471,24 @@ ast_ifthen* ast_ifthen_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *
  * a PHI node.
  *
  * The other difference is that in an ast_ternary, NEITHER side
- * must be NULL, there's ALWAYS an else branch.
+ * must be nullptr, there's ALWAYS an else branch.
  *
  * This is the only ast_node beside ast_value which contains
  * an ir_value. Theoretically we don't need to remember it though.
  */
-struct ast_ternary_s
+struct ast_ternary : ast_expression
 {
-    ast_expression  expression;
-    ast_expression *cond;
+    ast_ternary() = delete;
+    ast_ternary(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
+    ~ast_ternary();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    ast_expression *m_cond;
     /* It's all just 'expressions', since an ast_block is one too. */
-    ast_expression *on_true;
-    ast_expression *on_false;
+    ast_expression *m_on_true;
+    ast_expression *m_on_false;
 };
-ast_ternary* ast_ternary_new(lex_ctx_t ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
 
 /* A general loop node
  *
@@ -492,39 +513,48 @@ continue:      // a 'continue' will jump here
     {inc};
 }
  */
-struct ast_loop_s
+struct ast_loop : ast_expression
 {
-    ast_expression  expression;
-    ast_expression *initexpr;
-    ast_expression *precond;
-    ast_expression *postcond;
-    ast_expression *increment;
-    ast_expression *body;
+    ast_loop() = delete;
+    ast_loop(lex_ctx_t ctx,
+             ast_expression *initexpr,
+             ast_expression *precond, bool pre_not,
+             ast_expression *postcond, bool post_not,
+             ast_expression *increment,
+             ast_expression *body);
+    ~ast_loop();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    ast_expression *m_initexpr;
+    ast_expression *m_precond;
+    ast_expression *m_postcond;
+    ast_expression *m_increment;
+    ast_expression *m_body;
     /* For now we allow a seperate flag on whether or not the condition
      * is supposed to be true or false.
      * That way, the parser can generate a 'while not(!x)' for `while(x)`
      * if desired, which is useful for the new -f{true,false}-empty-strings
      * flag.
      */
-    bool pre_not;
-    bool post_not;
+    bool m_pre_not;
+    bool m_post_not;
 };
-ast_loop* ast_loop_new(lex_ctx_t ctx,
-                       ast_expression *initexpr,
-                       ast_expression *precond, bool pre_not,
-                       ast_expression *postcond, bool post_not,
-                       ast_expression *increment,
-                       ast_expression *body);
 
 /* Break/Continue
  */
-struct ast_breakcont_s
+struct ast_breakcont : ast_expression
 {
-    ast_expression expression;
-    bool           is_continue;
-    unsigned int   levels;
+    ast_breakcont() = delete;
+    ast_breakcont(lex_ctx_t ctx, bool iscont, unsigned int levels);
+    ~ast_breakcont();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+
+    bool         m_is_continue;
+    unsigned int m_levels;
 };
-ast_breakcont* ast_breakcont_new(lex_ctx_t ctx, bool iscont, unsigned int levels);
 
 /* Switch Statements
  *
@@ -536,64 +566,82 @@ ast_breakcont* ast_breakcont_new(lex_ctx_t ctx, bool iscont, unsigned int levels
  * be expected from it.
  * TODO: Ticket #20
  */
-typedef struct {
-    ast_expression *value; /* #20 will replace this */
-    ast_expression *code;
-} ast_switch_case;
-struct ast_switch_s
+struct ast_switch_case {
+    ast_expression *m_value; /* #20 will replace this */
+    ast_expression *m_code;
+};
+
+struct ast_switch : ast_expression
 {
-    ast_expression   expression;
+    ast_switch() = delete;
+    ast_switch(lex_ctx_t ctx, ast_expression *op);
+    ~ast_switch();
 
-    ast_expression  *operand;
-    ast_switch_case *cases;
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    ast_expression *m_operand;
+    std::vector<ast_switch_case> m_cases;
 };
 
-ast_switch* ast_switch_new(lex_ctx_t ctx, ast_expression *op);
 
 /* Label nodes
  *
  * Introduce a label which can be used together with 'goto'
  */
-struct ast_label_s
+struct ast_label : ast_expression
 {
-    ast_expression  expression;
-    const char     *name;
-    ir_block       *irblock;
-    ast_goto      **gotos;
+    ast_label() = delete;
+    ast_label(lex_ctx_t ctx, const std::string &name, bool undefined);
+    ~ast_label();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    std::string m_name;
+    ir_block *m_irblock;
+    std::vector<ast_goto*> m_gotos;
 
     /* means it has not yet been defined */
-    bool           undefined;
-};
+    bool m_undefined;
 
-ast_label* ast_label_new(lex_ctx_t ctx, const char *name, bool undefined);
+private:
+    void registerGoto(ast_goto*);
+    friend struct ast_goto;
+};
 
 /* GOTO nodes
  *
  * Go to a label, the label node is filled in at a later point!
  */
-struct ast_goto_s
+struct ast_goto : ast_expression
 {
-    ast_expression expression;
-    const char    *name;
-    ast_label     *target;
-    ir_block      *irblock_from;
-};
+    ast_goto() = delete;
+    ast_goto(lex_ctx_t ctx, const std::string &name);
+    ~ast_goto();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    void setLabel(ast_label*);
 
-ast_goto* ast_goto_new(lex_ctx_t ctx, const char *name);
-void ast_goto_set_label(ast_goto*, ast_label*);
+    std::string m_name;
+    ast_label *m_target;
+    ir_block *m_irblock_from;
+};
 
 /* STATE node
  *
  * For frame/think state updates: void foo() [framenum, nextthink] {}
  */
-struct ast_state_s
+struct ast_state : ast_expression
 {
-    ast_expression  expression;
-    ast_expression *framenum;
-    ast_expression *nextthink;
+    ast_state() = delete;
+    ast_state(lex_ctx_t ctx, ast_expression *frame, ast_expression *think);
+    ~ast_state();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    ast_expression *m_framenum;
+    ast_expression *m_nextthink;
 };
-ast_state* ast_state_new(lex_ctx_t ctx, ast_expression *frame, ast_expression *think);
-void ast_state_delete(ast_state*);
 
 /* CALL node
  *
@@ -605,34 +653,44 @@ void ast_state_delete(ast_state*);
  * Additionally it contains a list of ast_expressions as parameters.
  * Since calls can return values, an ast_call is also an ast_expression.
  */
-struct ast_call_s
+struct ast_call : ast_expression
 {
-    ast_expression  expression;
-    ast_expression *func;
-    ast_expression **params;
-    ast_expression *va_count;
+    ast_call() = delete;
+    static ast_call *make(lex_ctx_t, ast_expression*);
+    ~ast_call();
+
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
+
+    bool checkTypes(ast_expression *this_func_va_type) const;
+
+    ast_expression *m_func;
+    std::vector<ast_expression *> m_params;
+    ast_expression *m_va_count;
+
+private:
+    ast_call(lex_ctx_t ctx, ast_expression *funcexpr);
+    bool checkVararg(ast_expression *va_type, ast_expression *exp_type) const;
 };
-ast_call* ast_call_new(lex_ctx_t ctx,
-                       ast_expression *funcexpr);
-bool ast_call_check_types(ast_call*, ast_expression *this_func_va_type);
 
 /* Blocks
  *
  */
-struct ast_block_s
+struct ast_block : ast_expression
 {
-    ast_expression   expression;
+    ast_block() = delete;
+    ast_block(lex_ctx_t ctx);
+    ~ast_block();
 
-    ast_value*      *locals;
-    ast_expression* *exprs;
-    ast_expression* *collect;
-};
-ast_block* ast_block_new(lex_ctx_t ctx);
-void ast_block_delete(ast_block*);
-void ast_block_set_type(ast_block*, ast_expression *from);
-void ast_block_collect(ast_block*, ast_expression*);
+    bool codegen(ast_function *current, bool lvalue, ir_value **out) override;
 
-bool GMQCC_WARN ast_block_add_expr(ast_block*, ast_expression*);
+    std::vector<ast_value*>      m_locals;
+    std::vector<ast_expression*> m_exprs;
+    std::vector<ast_expression*> m_collect;
+
+    void setType(const ast_expression &from);
+    bool GMQCC_WARN addExpr(ast_expression*);
+    void collect(ast_expression*);
+};
 
 /* Function
  *
@@ -643,62 +701,53 @@ bool GMQCC_WARN ast_block_add_expr(ast_block*, ast_expression*);
  * neither functions inside functions, nor lambdas, and function
  * pointers could just work with a name. However, this way could be
  * more flexible, and adds no real complexity.
+ *
+ * The destructor will NOT delete the underlying ast_value
+ *
  */
-struct ast_function_s
+struct ast_function : ast_node
 {
-    ast_node    node;
+    ast_function() = delete;
+    static ast_function *make(lex_ctx_t ctx, const std::string &name, ast_value *vtype);
+    ~ast_function();
+
+    const char* makeLabel(const char *prefix);
+    virtual bool generateFunction(ir_builder*);
 
-    ast_value  *vtype;
-    const char *name;
+    ast_value  *m_function_type = nullptr;
+    std::string m_name;
 
-    int builtin;
+    int m_builtin = 0;
 
     /* list of used-up names for statics without the count suffix */
-    char       **static_names;
+    std::vector<std::string> m_static_names;
     /* number of static variables, by convention this includes the
      * ones without the count-suffix - remember this when dealing
      * with savegames. uint instead of size_t as %zu in printf is
      * C99, so no windows support. */
-    unsigned int static_count;
+    unsigned int m_static_count = 0;
 
-    ir_function *ir_func;
-    ir_block    *curblock;
-    ir_block    **breakblocks;
-    ir_block    **continueblocks;
+    ir_function *m_ir_func = nullptr;
+    ir_block *m_curblock = nullptr;
+    std::vector<ir_block*> m_breakblocks;
+    std::vector<ir_block*> m_continueblocks;
 
-#if 0
-    /* In order for early-out logic not to go over
-     * excessive jumps, we remember their target
-     * blocks...
-     */
-    ir_block    *iftrue;
-    ir_block    *iffalse;
-#endif
-
-    size_t       labelcount;
+    size_t m_labelcount = 0;
     /* in order for thread safety - for the optional
      * channel abesed multithreading... keeping a buffer
      * here to use in ast_function_label.
      */
-    char         labelbuf[64];
+    std::vector<std::unique_ptr<ast_block>> m_blocks;
+    std::unique_ptr<ast_value> m_varargs;
+    std::unique_ptr<ast_value> m_argc;
+    ast_value *m_fixedparams = nullptr; // these use unref()
+    ast_value *m_return_value = nullptr;
 
-    ast_block* *blocks;
+private:
+    ast_function(lex_ctx_t ctx, const std::string &name, ast_value *vtype);
 
-    ast_value   *varargs;
-    ast_value   *argc;
-    ast_value   *fixedparams;
-    ast_value   *return_value;
+    char m_labelbuf[64];
 };
-ast_function* ast_function_new(lex_ctx_t ctx, const char *name, ast_value *vtype);
-/* This will NOT delete the underlying ast_value */
-void ast_function_delete(ast_function*);
-/* For "optimized" builds this can just keep returning "foo"...
- * or whatever...
- */
-const char* ast_function_label(ast_function*, const char *prefix);
-
-bool ast_function_codegen(ast_function *self, ir_builder *builder);
-bool ast_generate_accessors(ast_value *asvalue, ir_builder *ir);
 
 /*
  * If the condition creates a situation where this becomes -1 size it means there are
diff --git a/code.c b/code.c
deleted file mode 100644 (file)
index 6f040ce..0000000
--- a/code.c
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
- * Copyright (C) 2012, 2013, 2014, 2015
- *     Dale Weiler
- *     Wolfgang Bumiller
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-#include <string.h>
-#include "gmqcc.h"
-
-/*
- * We could use the old method of casting to uintptr_t then to void*
- * or qcint_t; however, it's incredibly unsafe for two reasons.
- * 1) The compilers aliasing optimization can legally make it unstable
- *    (it's undefined behaviour).
- *
- * 2) The cast itself depends on fresh storage (newly allocated in which
- *    ever function is using the cast macros), the contents of which are
- *    transferred in a way that the obligation to release storage is not
- *    propagated.
- */
-typedef union {
-    void   *enter;
-    qcint_t leave;
-} code_hash_entry_t;
-
-/* Some sanity macros */
-#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter)
-#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave)
-
-void code_push_statement(code_t *code, prog_section_statement_t *stmt_in, lex_ctx_t ctx)
-{
-    prog_section_statement_t stmt = *stmt_in;
-
-    if (OPTS_FLAG(TYPELESS_STORES)) {
-        switch (stmt.opcode) {
-            case INSTR_LOAD_S:
-            case INSTR_LOAD_ENT:
-            case INSTR_LOAD_FLD:
-            case INSTR_LOAD_FNC:
-                stmt.opcode = INSTR_LOAD_F;
-                break;
-            case INSTR_STORE_S:
-            case INSTR_STORE_ENT:
-            case INSTR_STORE_FLD:
-            case INSTR_STORE_FNC:
-                stmt.opcode = INSTR_STORE_F;
-                break;
-            case INSTR_STOREP_S:
-            case INSTR_STOREP_ENT:
-            case INSTR_STOREP_FLD:
-            case INSTR_STOREP_FNC:
-                stmt.opcode = INSTR_STOREP_F;
-                break;
-        }
-    }
-
-
-    if (OPTS_FLAG(SORT_OPERANDS)) {
-        uint16_t pair;
-
-        switch (stmt.opcode) {
-            case INSTR_MUL_F:
-            case INSTR_MUL_V:
-            case INSTR_ADD_F:
-            case INSTR_EQ_F:
-            case INSTR_EQ_S:
-            case INSTR_EQ_E:
-            case INSTR_EQ_FNC:
-            case INSTR_NE_F:
-            case INSTR_NE_V:
-            case INSTR_NE_S:
-            case INSTR_NE_E:
-            case INSTR_NE_FNC:
-            case INSTR_AND:
-            case INSTR_OR:
-            case INSTR_BITAND:
-            case INSTR_BITOR:
-                if (stmt.o1.u1 < stmt.o2.u1) {
-                    uint16_t a = stmt.o2.u1;
-                    stmt.o1.u1 = stmt.o2.u1;
-                    stmt.o2.u1 = a;
-                }
-                break;
-
-            case INSTR_MUL_VF: pair = INSTR_MUL_FV; goto case_pair_gen;
-            case INSTR_MUL_FV: pair = INSTR_MUL_VF; goto case_pair_gen;
-            case INSTR_LT:     pair = INSTR_GT;     goto case_pair_gen;
-            case INSTR_GT:     pair = INSTR_LT;     goto case_pair_gen;
-            case INSTR_LE:     pair = INSTR_GT;     goto case_pair_gen;
-            case INSTR_GE:     pair = INSTR_LE;
-
-            case_pair_gen:
-                if (stmt.o1.u1 < stmt.o2.u1) {
-                    uint16_t x  = stmt.o1.u1;
-                    stmt.o1.u1  = stmt.o2.u1;
-                    stmt.o2.u1  = x;
-                    stmt.opcode = pair;
-                }
-                break;
-        }
-    }
-
-    vec_push(code->statements, stmt);
-    vec_push(code->linenums,   (int)ctx.line);
-    vec_push(code->columnnums, (int)ctx.column);
-}
-
-void code_pop_statement(code_t *code)
-{
-    vec_pop(code->statements);
-    vec_pop(code->linenums);
-    vec_pop(code->columnnums);
-}
-
-code_t *code_init() {
-    static lex_ctx_t                empty_ctx       = {0, 0, 0};
-    static prog_section_function_t  empty_function  = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
-    static prog_section_statement_t empty_statement = {0,{0},{0},{0}};
-    static prog_section_def_t       empty_def       = {0, 0, 0};
-
-    code_t *code       = (code_t*)mem_a(sizeof(code_t));
-    int     i          = 0;
-
-    memset(code, 0, sizeof(code_t));
-    code->entfields    = 0;
-    code->string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
-
-    /*
-     * The way progs.dat is suppose to work is odd, there needs to be
-     * some null (empty) statements, functions, and 28 globals
-     */
-    for(; i < 28; i++)
-        vec_push(code->globals, 0);
-
-    vec_push(code->chars, '\0');
-    vec_push(code->functions,  empty_function);
-
-    code_push_statement(code, &empty_statement, empty_ctx);
-
-    vec_push(code->defs,    empty_def);
-    vec_push(code->fields,  empty_def);
-
-    return code;
-}
-
-void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
-
-uint32_t code_genstring(code_t *code, const char *str) {
-    size_t            hash;
-    code_hash_entry_t existing;
-
-    if (!str)
-        return 0;
-
-    if (!*str) {
-        if (!code->string_cached_empty) {
-            code->string_cached_empty = vec_size(code->chars);
-            vec_push(code->chars, 0);
-        }
-        return code->string_cached_empty;
-    }
-
-    if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
-        hash                      = ((unsigned char*)str)[strlen(str)-1];
-        CODE_HASH_ENTER(existing) = code_util_str_htgeth(code->string_cache, str, hash);
-    } else {
-        hash                      = util_hthash(code->string_cache, str);
-        CODE_HASH_ENTER(existing) = util_htgeth(code->string_cache, str, hash);
-    }
-
-    if (CODE_HASH_ENTER(existing))
-        return CODE_HASH_LEAVE(existing);
-
-    CODE_HASH_LEAVE(existing) = vec_size(code->chars);
-    vec_append(code->chars, strlen(str)+1, str);
-
-    util_htseth(code->string_cache, str, hash, CODE_HASH_ENTER(existing));
-    return CODE_HASH_LEAVE(existing);
-}
-
-qcint_t code_alloc_field (code_t *code, size_t qcsize)
-{
-    qcint_t pos = (qcint_t)code->entfields;
-    code->entfields += qcsize;
-    return pos;
-}
-
-static size_t code_size_generic(code_t *code, prog_header_t *code_header, bool lno) {
-    size_t size = 0;
-    if (lno) {
-        size += 4;  /* LNOF */
-        size += sizeof(uint32_t); /* version */
-        size += sizeof(code_header->defs.length);
-        size += sizeof(code_header->globals.length);
-        size += sizeof(code_header->fields.length);
-        size += sizeof(code_header->statements.length);
-        size += sizeof(code->linenums[0])   * vec_size(code->linenums);
-        size += sizeof(code->columnnums[0]) * vec_size(code->columnnums);
-    } else {
-        size += sizeof(prog_header_t);
-        size += sizeof(prog_section_statement_t) * vec_size(code->statements);
-        size += sizeof(prog_section_def_t)       * vec_size(code->defs);
-        size += sizeof(prog_section_field_t)     * vec_size(code->fields);
-        size += sizeof(prog_section_function_t)  * vec_size(code->functions);
-        size += sizeof(int32_t)                  * vec_size(code->globals);
-        size += 1                                * vec_size(code->chars);
-    }
-    return size;
-}
-
-#define code_size_binary(C, H) code_size_generic((C), (H), false)
-#define code_size_debug(C, H)  code_size_generic((C), (H), true)
-
-static void code_create_header(code_t *code, prog_header_t *code_header, const char *filename, const char *lnofile) {
-    size_t i;
-
-    code_header->statements.offset = sizeof(prog_header_t);
-    code_header->statements.length = vec_size(code->statements);
-    code_header->defs.offset       = code_header->statements.offset + (sizeof(prog_section_statement_t) * vec_size(code->statements));
-    code_header->defs.length       = vec_size(code->defs);
-    code_header->fields.offset     = code_header->defs.offset       + (sizeof(prog_section_def_t)       * vec_size(code->defs));
-    code_header->fields.length     = vec_size(code->fields);
-    code_header->functions.offset  = code_header->fields.offset     + (sizeof(prog_section_field_t)     * vec_size(code->fields));
-    code_header->functions.length  = vec_size(code->functions);
-    code_header->globals.offset    = code_header->functions.offset  + (sizeof(prog_section_function_t)  * vec_size(code->functions));
-    code_header->globals.length    = vec_size(code->globals);
-    code_header->strings.offset    = code_header->globals.offset    + (sizeof(int32_t)                  * vec_size(code->globals));
-    code_header->strings.length    = vec_size(code->chars);
-    code_header->version           = 6;
-    code_header->skip              = 0;
-
-    if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
-        code_header->crc16         = OPTS_OPTION_U16(OPTION_FORCED_CRC);
-    else
-        code_header->crc16         = code->crc;
-    code_header->entfield          = code->entfields;
-
-    if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
-        /* >= + P */
-        vec_push(code->chars, '\0'); /* > */
-        vec_push(code->chars, '\0'); /* = */
-        vec_push(code->chars, '\0'); /* P */
-    }
-
-    /* ensure all data is in LE format */
-    util_swap_header(code_header);
-
-    /*
-     * These are not part of the header but we ensure LE format here to save on duplicated
-     * code.
-     */
-
-    util_swap_statements (code->statements);
-    util_swap_defs_fields(code->defs);
-    util_swap_defs_fields(code->fields);
-    util_swap_functions  (code->functions);
-    util_swap_globals    (code->globals);
-
-    if (!OPTS_OPTION_BOOL(OPTION_QUIET)) {
-        if (lnofile)
-            con_out("writing '%s' and '%s'...\n", filename, lnofile);
-        else
-            con_out("writing '%s'\n", filename);
-    }
-
-    if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
-        !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
-    {
-        char buffer[1024];
-        con_out("\nOptimizations:\n");
-        for (i = 0; i < COUNT_OPTIMIZATIONS; ++i) {
-            if (opts_optimizationcount[i]) {
-                util_optimizationtostr(opts_opt_list[i].name, buffer, sizeof(buffer));
-                con_out(
-                    "    %s: %u\n",
-                    buffer,
-                    (unsigned int)opts_optimizationcount[i]
-                );
-            }
-        }
-    }
-}
-
-static void code_stats(const char *filename, const char *lnofile, code_t *code, prog_header_t *code_header) {
-    if (OPTS_OPTION_BOOL(OPTION_QUIET) ||
-        OPTS_OPTION_BOOL(OPTION_PP_ONLY))
-            return;
-
-    con_out("\nFile statistics:\n");
-    con_out("    dat:\n");
-    con_out("        name: %s\n",         filename);
-    con_out("        size: %u (bytes)\n", code_size_binary(code, code_header));
-    con_out("        crc:  0x%04X\n",     code->crc);
-
-    if (lnofile) {
-        con_out("    lno:\n");
-        con_out("        name: %s\n",  lnofile);
-        con_out("        size: %u (bytes)\n",  code_size_debug(code, code_header));
-    }
-
-    con_out("\n");
-}
-
-/*
- * Same principle except this one allocates memory and writes the lno(optional) and the dat file
- * directly out to allocated memory. Which is actually very useful for the future library support
- * we're going to add.
- */
-#if 0
-static bool code_write_memory(code_t *code, uint8_t **datmem, size_t *sizedat, uint8_t **lnomem, size_t *sizelno) GMQCC_UNUSED {
-    prog_header_t code_header;
-    uint32_t      offset  = 0;
-
-    if (!datmem)
-        return false;
-
-    code_create_header(code, &code_header, "<<memory>>", "<<memory>>");
-
-    #define WRITE_CHUNK(C,X,S)                                     \
-        do {                                                       \
-            memcpy((void*)(&(*C)[offset]), (const void*)(X), (S)); \
-            offset += (S);                                         \
-        } while (0)
-
-    /* Calculate size required to store entire file out to memory */
-    if (lnomem) {
-        uint32_t version = 1;
-
-        *sizelno = code_size_debug(code, &code_header);
-        *lnomem  = (uint8_t*)mem_a(*sizelno);
-
-        WRITE_CHUNK(lnomem, "LNOF",                         4);
-        WRITE_CHUNK(lnomem, &version,                       sizeof(version));
-        WRITE_CHUNK(lnomem, &code_header.defs.length,       sizeof(code_header.defs.length));
-        WRITE_CHUNK(lnomem, &code_header.globals.length,    sizeof(code_header.globals.length));
-        WRITE_CHUNK(lnomem, &code_header.fields.length,     sizeof(code_header.fields.length));
-        WRITE_CHUNK(lnomem, &code_header.statements.length, sizeof(code_header.statements.length));
-
-        /* something went terribly wrong */
-        if (offset != *sizelno) {
-            mem_d(*lnomem);
-            *sizelno = 0;
-            return false;
-        }
-        offset = 0;
-    }
-
-    /* Write out the dat */
-    *sizedat = code_size_binary(code, &code_header);
-    *datmem  = (uint8_t*)mem_a(*sizedat);
-
-    WRITE_CHUNK(datmem, &code_header,     sizeof(prog_header_t));
-    WRITE_CHUNK(datmem, code->statements, sizeof(prog_section_statement_t) * vec_size(code->statements));
-    WRITE_CHUNK(datmem, code->defs,       sizeof(prog_section_def_t)       * vec_size(code->defs));
-    WRITE_CHUNK(datmem, code->fields,     sizeof(prog_section_field_t)     * vec_size(code->fields));
-    WRITE_CHUNK(datmem, code->functions,  sizeof(prog_section_function_t)  * vec_size(code->functions));
-    WRITE_CHUNK(datmem, code->globals,    sizeof(int32_t)                  * vec_size(code->globals));
-    WRITE_CHUNK(datmem, code->chars,      1                                * vec_size(code->chars));
-
-    vec_free(code->statements);
-    vec_free(code->linenums);
-    vec_free(code->columnnums);
-    vec_free(code->defs);
-    vec_free(code->fields);
-    vec_free(code->functions);
-    vec_free(code->globals);
-    vec_free(code->chars);
-
-    util_htdel(code->string_cache);
-    mem_d(code);
-    code_stats("<<memory>>", (lnomem) ? "<<memory>>" : NULL, code, &code_header);
-    return true;
-}
-#endif /*!#if 0 reenable when ready to be used */
-#undef WRITE_CHUNK
-
-bool code_write(code_t *code, const char *filename, const char *lnofile) {
-    prog_header_t  code_header;
-    fs_file_t     *fp = NULL;
-
-    code_create_header(code, &code_header, filename, lnofile);
-
-    if (lnofile) {
-        uint32_t version = 1;
-
-        fp = fs_file_open(lnofile, "wb");
-        if (!fp)
-            return false;
-
-        util_endianswap(&version,         1,                          sizeof(version));
-        util_endianswap(code->linenums,   vec_size(code->linenums),   sizeof(code->linenums[0]));
-        util_endianswap(code->columnnums, vec_size(code->columnnums), sizeof(code->columnnums[0]));
-
-        if (fs_file_write("LNOF",                          4,                                      1,                          fp) != 1 ||
-            fs_file_write(&version,                        sizeof(version),                        1,                          fp) != 1 ||
-            fs_file_write(&code_header.defs.length,        sizeof(code_header.defs.length),        1,                          fp) != 1 ||
-            fs_file_write(&code_header.globals.length,     sizeof(code_header.globals.length),     1,                          fp) != 1 ||
-            fs_file_write(&code_header.fields.length,      sizeof(code_header.fields.length),      1,                          fp) != 1 ||
-            fs_file_write(&code_header.statements.length,  sizeof(code_header.statements.length),  1,                          fp) != 1 ||
-            fs_file_write(code->linenums,                  sizeof(code->linenums[0]),              vec_size(code->linenums),   fp) != vec_size(code->linenums) ||
-            fs_file_write(code->columnnums,                sizeof(code->columnnums[0]),            vec_size(code->columnnums), fp) != vec_size(code->columnnums))
-        {
-            con_err("failed to write lno file\n");
-        }
-
-        fs_file_close(fp);
-        fp = NULL;
-    }
-
-    fp = fs_file_open(filename, "wb");
-    if (!fp)
-        return false;
-
-    if (1                          != fs_file_write(&code_header,     sizeof(prog_header_t)           , 1                         , fp) ||
-        vec_size(code->statements) != fs_file_write(code->statements, sizeof(prog_section_statement_t), vec_size(code->statements), fp) ||
-        vec_size(code->defs)       != fs_file_write(code->defs,       sizeof(prog_section_def_t)      , vec_size(code->defs)      , fp) ||
-        vec_size(code->fields)     != fs_file_write(code->fields,     sizeof(prog_section_field_t)    , vec_size(code->fields)    , fp) ||
-        vec_size(code->functions)  != fs_file_write(code->functions,  sizeof(prog_section_function_t) , vec_size(code->functions) , fp) ||
-        vec_size(code->globals)    != fs_file_write(code->globals,    sizeof(int32_t)                 , vec_size(code->globals)   , fp) ||
-        vec_size(code->chars)      != fs_file_write(code->chars,      1                               , vec_size(code->chars)     , fp))
-    {
-        fs_file_close(fp);
-        return false;
-    }
-
-    fs_file_close(fp);
-    code_stats(filename, lnofile, code, &code_header);
-    return true;
-}
-
-void code_cleanup(code_t *code) {
-    vec_free(code->statements);
-    vec_free(code->linenums);
-    vec_free(code->columnnums);
-    vec_free(code->defs);
-    vec_free(code->fields);
-    vec_free(code->functions);
-    vec_free(code->globals);
-    vec_free(code->chars);
-
-    util_htdel(code->string_cache);
-
-    mem_d(code);
-}
diff --git a/code.cpp b/code.cpp
new file mode 100644 (file)
index 0000000..645ad25
--- /dev/null
+++ b/code.cpp
@@ -0,0 +1,348 @@
+#include <string.h>
+#include "gmqcc.h"
+
+/*
+ * We could use the old method of casting to uintptr_t then to void*
+ * or qcint_t; however, it's incredibly unsafe for two reasons.
+ * 1) The compilers aliasing optimization can legally make it unstable
+ *    (it's undefined behaviour).
+ *
+ * 2) The cast itself depends on fresh storage (newly allocated in which
+ *    ever function is using the cast macros), the contents of which are
+ *    transferred in a way that the obligation to release storage is not
+ *    propagated.
+ */
+typedef union {
+    void   *enter;
+    qcint_t leave;
+} code_hash_entry_t;
+
+/* Some sanity macros */
+#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter)
+#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave)
+
+void code_push_statement(code_t *code, prog_section_statement_t *stmt_in, lex_ctx_t ctx)
+{
+    prog_section_statement_t stmt = *stmt_in;
+
+    if (OPTS_FLAG(TYPELESS_STORES)) {
+        switch (stmt.opcode) {
+            case INSTR_LOAD_S:
+            case INSTR_LOAD_ENT:
+            case INSTR_LOAD_FLD:
+            case INSTR_LOAD_FNC:
+                stmt.opcode = INSTR_LOAD_F;
+                break;
+            case INSTR_STORE_S:
+            case INSTR_STORE_ENT:
+            case INSTR_STORE_FLD:
+            case INSTR_STORE_FNC:
+                stmt.opcode = INSTR_STORE_F;
+                break;
+            case INSTR_STOREP_S:
+            case INSTR_STOREP_ENT:
+            case INSTR_STOREP_FLD:
+            case INSTR_STOREP_FNC:
+                stmt.opcode = INSTR_STOREP_F;
+                break;
+        }
+    }
+
+
+    if (OPTS_FLAG(SORT_OPERANDS)) {
+        uint16_t pair;
+
+        switch (stmt.opcode) {
+            case INSTR_MUL_F:
+            case INSTR_MUL_V:
+            case INSTR_ADD_F:
+            case INSTR_EQ_F:
+            case INSTR_EQ_S:
+            case INSTR_EQ_E:
+            case INSTR_EQ_FNC:
+            case INSTR_NE_F:
+            case INSTR_NE_V:
+            case INSTR_NE_S:
+            case INSTR_NE_E:
+            case INSTR_NE_FNC:
+            case INSTR_AND:
+            case INSTR_OR:
+            case INSTR_BITAND:
+            case INSTR_BITOR:
+                if (stmt.o1.u1 < stmt.o2.u1) {
+                    uint16_t a = stmt.o2.u1;
+                    stmt.o1.u1 = stmt.o2.u1;
+                    stmt.o2.u1 = a;
+                }
+                break;
+
+            case INSTR_MUL_VF: pair = INSTR_MUL_FV; goto case_pair_gen;
+            case INSTR_MUL_FV: pair = INSTR_MUL_VF; goto case_pair_gen;
+            case INSTR_LT:     pair = INSTR_GT;     goto case_pair_gen;
+            case INSTR_GT:     pair = INSTR_LT;     goto case_pair_gen;
+            case INSTR_LE:     pair = INSTR_GT;     goto case_pair_gen;
+            case INSTR_GE:     pair = INSTR_LE;
+
+            case_pair_gen:
+                if (stmt.o1.u1 < stmt.o2.u1) {
+                    uint16_t x  = stmt.o1.u1;
+                    stmt.o1.u1  = stmt.o2.u1;
+                    stmt.o2.u1  = x;
+                    stmt.opcode = pair;
+                }
+                break;
+        }
+    }
+
+    code->statements.push_back(stmt);
+    code->linenums.push_back(ctx.line);
+    code->columnnums.push_back(ctx.column);
+}
+
+void code_pop_statement(code_t *code)
+{
+    code->statements.pop_back();
+    code->linenums.pop_back();
+    code->columnnums.pop_back();
+}
+
+void *code_t::operator new(std::size_t bytes) {
+  return mem_a(bytes);
+}
+
+void code_t::operator delete(void *ptr) {
+  mem_d(ptr);
+}
+
+code_t::code_t()
+{
+    static lex_ctx_t                empty_ctx       = {0, 0, 0};
+    static prog_section_function_t  empty_function  = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
+    static prog_section_statement_t empty_statement = {0,{0},{0},{0}};
+    static prog_section_def_t       empty_def       = {0, 0, 0};
+
+    string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
+
+    // The way progs.dat is suppose to work is odd, there needs to be
+    // some null (empty) statements, functions, and 28 globals
+    globals.insert(globals.begin(), 28, 0);
+
+    chars.push_back('\0');
+    functions.push_back(empty_function);
+
+    code_push_statement(this, &empty_statement, empty_ctx);
+
+    defs.push_back(empty_def);
+    fields.push_back(empty_def);
+}
+
+code_t::~code_t()
+{
+    util_htdel(string_cache);
+}
+
+void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
+
+uint32_t code_genstring(code_t *code, const char *str) {
+    size_t            hash;
+    code_hash_entry_t existing;
+
+    if (!str)
+        return 0;
+
+    if (!*str) {
+        if (!code->string_cached_empty) {
+            code->string_cached_empty = code->chars.size();
+            code->chars.push_back(0);
+        }
+        return code->string_cached_empty;
+    }
+
+    if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
+        hash                      = ((unsigned char*)str)[strlen(str)-1];
+        CODE_HASH_ENTER(existing) = code_util_str_htgeth(code->string_cache, str, hash);
+    } else {
+        hash                      = util_hthash(code->string_cache, str);
+        CODE_HASH_ENTER(existing) = util_htgeth(code->string_cache, str, hash);
+    }
+
+    if (CODE_HASH_ENTER(existing))
+        return CODE_HASH_LEAVE(existing);
+
+    CODE_HASH_LEAVE(existing) = code->chars.size();
+    code->chars.insert(code->chars.end(), str, str + strlen(str) + 1);
+
+    util_htseth(code->string_cache, str, hash, CODE_HASH_ENTER(existing));
+    return CODE_HASH_LEAVE(existing);
+}
+
+qcint_t code_alloc_field (code_t *code, size_t qcsize)
+{
+    qcint_t pos = (qcint_t)code->entfields;
+    code->entfields += qcsize;
+    return pos;
+}
+
+static size_t code_size_generic(code_t *code, prog_header_t *code_header, bool lno) {
+    size_t size = 0;
+    if (lno) {
+        size += 4;  /* LNOF */
+        size += sizeof(uint32_t); /* version */
+        size += sizeof(code_header->defs.length);
+        size += sizeof(code_header->globals.length);
+        size += sizeof(code_header->fields.length);
+        size += sizeof(code_header->statements.length);
+        size += sizeof(code->linenums[0])   * code->linenums.size();
+        size += sizeof(code->columnnums[0]) * code->columnnums.size();
+    } else {
+        size += sizeof(prog_header_t);
+        size += sizeof(prog_section_statement_t) * code->statements.size();
+        size += sizeof(prog_section_def_t)       * code->defs.size();
+        size += sizeof(prog_section_field_t)     * code->fields.size();
+        size += sizeof(prog_section_function_t)  * code->functions.size();
+        size += sizeof(int32_t)                  * code->globals.size();
+        size += 1                                * code->chars.size();
+    }
+    return size;
+}
+
+#define code_size_binary(C, H) code_size_generic((C), (H), false)
+#define code_size_debug(C, H)  code_size_generic((C), (H), true)
+
+static void code_create_header(code_t *code, prog_header_t *code_header, const char *filename, const char *lnofile) {
+    size_t i;
+
+    code_header->statements.offset = sizeof(prog_header_t);
+    code_header->statements.length = code->statements.size();
+    code_header->defs.offset       = code_header->statements.offset + (sizeof(prog_section_statement_t) * code->statements.size());
+    code_header->defs.length       = code->defs.size();
+    code_header->fields.offset     = code_header->defs.offset       + (sizeof(prog_section_def_t)       * code->defs.size());
+    code_header->fields.length     = code->fields.size();
+    code_header->functions.offset  = code_header->fields.offset     + (sizeof(prog_section_field_t)     * code->fields.size());
+    code_header->functions.length  = code->functions.size();
+    code_header->globals.offset    = code_header->functions.offset  + (sizeof(prog_section_function_t)  * code->functions.size());
+    code_header->globals.length    = code->globals.size();
+    code_header->strings.offset    = code_header->globals.offset    + (sizeof(int32_t)                  * code->globals.size());
+    code_header->strings.length    = code->chars.size();
+    code_header->version           = 6;
+    code_header->skip              = 0;
+
+    if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
+        code_header->crc16         = OPTS_OPTION_U16(OPTION_FORCED_CRC);
+    else
+        code_header->crc16         = code->crc;
+    code_header->entfield          = code->entfields;
+
+    if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
+        /* >= + P */
+        code->chars.push_back('\0'); /* > */
+        code->chars.push_back('\0'); /* = */
+        code->chars.push_back('\0'); /* P */
+    }
+
+    /* ensure all data is in LE format */
+    util_swap_header(*code_header);
+    util_swap_statements(code->statements);
+    util_swap_defs_fields(code->defs);
+    util_swap_defs_fields(code->fields);
+    util_swap_functions(code->functions);
+    util_swap_globals(code->globals);
+
+    if (!OPTS_OPTION_BOOL(OPTION_QUIET)) {
+        if (lnofile)
+            con_out("writing '%s' and '%s'...\n", filename, lnofile);
+        else
+            con_out("writing '%s'\n", filename);
+    }
+
+    if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
+        !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
+    {
+        char buffer[1024];
+        con_out("\nOptimizations:\n");
+        for (i = 0; i < COUNT_OPTIMIZATIONS; ++i) {
+            if (opts_optimizationcount[i]) {
+                util_optimizationtostr(opts_opt_list[i].name, buffer, sizeof(buffer));
+                con_out(
+                    "    %s: %u\n",
+                    buffer,
+                    (unsigned int)opts_optimizationcount[i]
+                );
+            }
+        }
+    }
+}
+
+static void code_stats(const char *filename, const char *lnofile, code_t *code, prog_header_t *code_header) {
+    if (OPTS_OPTION_BOOL(OPTION_QUIET) ||
+        OPTS_OPTION_BOOL(OPTION_PP_ONLY))
+            return;
+
+    con_out("\nFile statistics:\n");
+    con_out("    dat:\n");
+    con_out("        name: %s\n",         filename);
+    con_out("        size: %u (bytes)\n", code_size_binary(code, code_header));
+    con_out("        crc:  0x%04X\n",     code->crc);
+
+    if (lnofile) {
+        con_out("    lno:\n");
+        con_out("        name: %s\n",  lnofile);
+        con_out("        size: %u (bytes)\n",  code_size_debug(code, code_header));
+    }
+
+    con_out("\n");
+}
+
+bool code_write(code_t *code, const char *filename, const char *lnofile) {
+    prog_header_t code_header;
+    FILE *fp = nullptr;
+
+    code_create_header(code, &code_header, filename, lnofile);
+
+    if (lnofile) {
+        uint32_t version = 1;
+
+        fp = fopen(lnofile, "wb");
+        if (!fp)
+            return false;
+
+        util_endianswap(&version,             1,                       sizeof(version));
+        util_endianswap(&code->linenums[0],   code->linenums.size(),   sizeof(code->linenums[0]));
+        util_endianswap(&code->columnnums[0], code->columnnums.size(), sizeof(code->columnnums[0]));
+
+        if (fwrite("LNOF",                          4,                                      1,                          fp) != 1 ||
+            fwrite(&version,                        sizeof(version),                        1,                          fp) != 1 ||
+            fwrite(&code_header.defs.length,        sizeof(code_header.defs.length),        1,                          fp) != 1 ||
+            fwrite(&code_header.globals.length,     sizeof(code_header.globals.length),     1,                          fp) != 1 ||
+            fwrite(&code_header.fields.length,      sizeof(code_header.fields.length),      1,                          fp) != 1 ||
+            fwrite(&code_header.statements.length,  sizeof(code_header.statements.length),  1,                          fp) != 1 ||
+            fwrite(&code->linenums[0],              sizeof(code->linenums[0]),              code->linenums.size(),      fp) != code->linenums.size() ||
+            fwrite(&code->columnnums[0],            sizeof(code->columnnums[0]),            code->columnnums.size(),    fp) != code->columnnums.size())
+        {
+            con_err("failed to write lno file\n");
+        }
+
+        fclose(fp);
+        fp = nullptr;
+    }
+
+    fp = fopen(filename, "wb");
+    if (!fp)
+        return false;
+
+    if (1                       != fwrite(&code_header,         sizeof(prog_header_t)           , 1                      , fp) ||
+        code->statements.size() != fwrite(&code->statements[0], sizeof(prog