Merge branch 'master' into cooking
authorWolfgang Bumiller <wry.git@bumiller.com>
Mon, 30 Sep 2013 13:02:03 +0000 (15:02 +0200)
committerWolfgang Bumiller <wry.git@bumiller.com>
Mon, 30 Sep 2013 13:02:03 +0000 (15:02 +0200)
29 files changed:
TODO
ast.c
code.c
conout.c
distro/archbsd/this/Makefile [new file with mode: 0644]
distro/archlinux/this/Makefile
distro/fedora/gmqcc.spec
doc/gmqcc.1
exec.c
fold.c
gmqcc.h
gmqcc.ini.example
intrin.c
ir.c
lexer.c
lexer.h
main.c
misc/check-doc.sh
misc/check-proj.sh [new file with mode: 0755]
misc/nexuiz_export.sh [new file with mode: 0755]
misc/xonotic_export.sh [new file with mode: 0755]
opts.c
opts.def
parser.c
parser.h
test.c
tests/vec_ops.qc
tests/vec_ops.tmpl
util.c

diff --git a/TODO b/TODO
index 94163afefcf65b50881f672f3e5735a7e42b128f..8652ed228b22b6dbe660b14f53fb12a1fb661ad7 100644 (file)
--- a/TODO
+++ b/TODO
@@ -29,21 +29,10 @@ Optimizations:
             Which can be replaced with calls to a shared subroutine. To
             reduce duplicated code. (Size optimization)
 
-    The following are optimizations that can be implemented anywhere, ideally
-    these are functional language optimizations.
-
-        Removing Recursion:
-            Tail recursive algorithms can be converted to iteration, which
-            does not have to have call overhead.
-
-
 Language Features:
     The following are language features that we'd like to see implemented in the
     future.
 
-    Enumerations:
-        Like C
-
     AST Macros:
         Macros with sanity.  Not textual substiution.
 
@@ -153,5 +142,3 @@ Testsuite:
 Assembler:
     Possibly support for a future assembler for QCASM.  But we're not
     entirely sure if it makes sense.
-
-
diff --git a/ast.c b/ast.c
index 2f1566bfc5700a26f7fb51f923b46c0a7ecdc700..e1f86a65e9b5558d1d4a71989b79e60ab85c973d 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -515,15 +515,35 @@ ast_unary* ast_unary_new(lex_ctx_t ctx, int op,
     ast_instantiate(ast_unary, ctx, ast_unary_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_unary_codegen);
 
-    self->op = op;
+    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;
+        ast_unary *cur  = (ast_unary*)expr;
+
+        /* Handle for double negation */
+        if (cur->op == op && (op >= VINSTR_NEG_F && op <= VINSTR_NEG_V))
+            prev = cur;
+
+        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) {
+    if (op >= INSTR_NOT_F  && op <= INSTR_NOT_FNC) {
         self->expression.vtype = TYPE_FLOAT;
-    } else
+    } else if (op >= VINSTR_NEG_F && op <= VINSTR_NEG_V) {
+        self->expression.vtype = TYPE_FLOAT;
+    } else {
         compile_error(ctx, "cannot determine type of unary operation %s", util_instr_str[op]);
+    }
 
     return self;
 }
@@ -1877,6 +1897,17 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
     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
@@ -1922,7 +1953,7 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
     for (i = 0; i < vec_size(self->exprs); ++i)
     {
         ast_expression_codegen *gen;
-        if (func->curblock->final && !ast_istype(self->exprs[i], ast_label)) {
+        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;
@@ -2571,8 +2602,8 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
     /* 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 if */
-    if ((fold = fold_cond(condval, func, self)) != -1)
+    /* try constant folding away the condition */
+    if ((fold = fold_cond_ifthen(condval, func, self)) != -1)
         return fold;
 
     if (self->on_true) {
@@ -2655,6 +2686,7 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
     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)
@@ -2679,6 +2711,10 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
         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)
diff --git a/code.c b/code.c
index f3d60fffc40555de1c7f2f1abcd2306a53b8f7d9..5703e5098b60131251ba96ec7d34df62efaa2e11 100644 (file)
--- a/code.c
+++ b/code.c
@@ -253,8 +253,6 @@ static void code_create_header(code_t *code, prog_header_t *code_header, const c
     code_header->entfield          = code->entfields;
 
     if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
-        util_debug("GEN", "Patching stringtable for -fdarkplaces-stringtablebug\n");
-
         /* >= + P */
         vec_push(code->chars, '\0'); /* > */
         vec_push(code->chars, '\0'); /* = */
@@ -403,8 +401,7 @@ static bool code_write_memory(code_t *code, uint8_t **datmem, size_t *sizedat, u
 
 bool code_write(code_t *code, const char *filename, const char *lnofile) {
     prog_header_t  code_header;
-    FILE          *fp           = NULL;
-    size_t         it           = 2;
+    FILE          *fp = NULL;
 
     code_create_header(code, &code_header, filename, lnofile);
 
@@ -451,60 +448,6 @@ bool code_write(code_t *code, const char *filename, const char *lnofile) {
         return false;
     }
 
-    util_debug("GEN","HEADER:\n");
-    util_debug("GEN","    version:    = %d\n", code_header.version );
-    util_debug("GEN","    crc16:      = %d\n", code_header.crc16   );
-    util_debug("GEN","    entfield:   = %d\n", code_header.entfield);
-    util_debug("GEN","    statements  = {.offset = % 8d, .length = % 8d}\n", code_header.statements.offset, code_header.statements.length);
-    util_debug("GEN","    defs        = {.offset = % 8d, .length = % 8d}\n", code_header.defs      .offset, code_header.defs      .length);
-    util_debug("GEN","    fields      = {.offset = % 8d, .length = % 8d}\n", code_header.fields    .offset, code_header.fields    .length);
-    util_debug("GEN","    functions   = {.offset = % 8d, .length = % 8d}\n", code_header.functions .offset, code_header.functions .length);
-    util_debug("GEN","    globals     = {.offset = % 8d, .length = % 8d}\n", code_header.globals   .offset, code_header.globals   .length);
-    util_debug("GEN","    strings     = {.offset = % 8d, .length = % 8d}\n", code_header.strings   .offset, code_header.strings   .length);
-
-    /* FUNCTIONS */
-    util_debug("GEN", "FUNCTIONS:\n");
-    for (; it < vec_size(code->functions); it++) {
-        size_t j = code->functions[it].entry;
-        util_debug("GEN", "    {.entry =% 5d, .firstlocal =% 5d, .locals =% 5d, .profile =% 5d, .name =% 5d, .file =% 5d, .nargs =% 5d, .argsize ={%d,%d,%d,%d,%d,%d,%d,%d} }\n",
-            code->functions[it].entry,
-            code->functions[it].firstlocal,
-            code->functions[it].locals,
-            code->functions[it].profile,
-            code->functions[it].name,
-            code->functions[it].file,
-            code->functions[it].nargs,
-            code->functions[it].argsize[0],
-            code->functions[it].argsize[1],
-            code->functions[it].argsize[2],
-            code->functions[it].argsize[3],
-            code->functions[it].argsize[4],
-            code->functions[it].argsize[5],
-            code->functions[it].argsize[6],
-            code->functions[it].argsize[7]
-
-        );
-        util_debug("GEN", "    NAME: %s\n", &code->chars[code->functions[it].name]);
-        /* Internal functions have no code */
-        if (code->functions[it].entry >= 0) {
-            util_debug("GEN", "    CODE:\n");
-            for (;;) {
-                if (code->statements[j].opcode != INSTR_DONE)
-                    util_debug("GEN", "        %-12s {% 5i,% 5i,% 5i}\n",
-                        util_instr_str[code->statements[j].opcode],
-                        code->statements[j].o1.s1,
-                        code->statements[j].o2.s1,
-                        code->statements[j].o3.s1
-                    );
-                else {
-                    util_debug("GEN", "        DONE  {0x00000,0x00000,0x00000}\n");
-                    break;
-                }
-                j++;
-            }
-        }
-    }
-
     fs_file_close(fp);
     code_stats(filename, lnofile, code, &code_header);
     return true;
index aedff4da20b32e036fefd64ef4e3e0085d365546..63391d6a86f541efe9ad9f3f0b20af4a5a3f910e 100644 (file)
--- a/conout.c
+++ b/conout.c
@@ -326,7 +326,6 @@ int con_out(const char *fmt, ...) {
     return   ln;
 }
 
-#ifndef QCVM_EXECUTOR
 /*
  * Utility console message writes for lexer contexts.  These will allow
  * for reporting of file:line based on lexer context, These are used
@@ -379,6 +378,7 @@ void con_cprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg,
     va_end  (va);
 }
 
+#ifndef QCVM_EXECUTOR
 /* General error interface */
 size_t compile_errors   = 0;
 size_t compile_warnings = 0;
diff --git a/distro/archbsd/this/Makefile b/distro/archbsd/this/Makefile
new file mode 100644 (file)
index 0000000..afe5514
--- /dev/null
@@ -0,0 +1,4 @@
+all:
+       $(MAKE) -f ../../archlinux/this/Makefile \
+         LIBC_DEPEND=libc \
+         DESTDIR=distro/archbsd/this
index fa863d100aa77a62c2664189637bcbad881d1d11..6efdc00cc33f270ade32095552fd0c3528d0efef 100644 (file)
@@ -9,9 +9,10 @@ CARCH   := $(shell uname -m)
 PKGDIR  := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)-$(CARCH)
 PKG     := $(PKGDIR).pkg.tar.xz
 PKGINFO := $(PKGDIR)/.PKGINFO
-DESTDIR := distro/archlinux/this/$(PKGDIR)
+DESTDIR := distro/archlinux/this
 CFLAGS  :=
 
+LIBC_DEPEND := glibc
 
 ifneq (, $(findstring i686, $(CARCH)))
        CFLAGS += -m32
@@ -21,7 +22,7 @@ endif
 base:
        $(MAKE) -C $(BASEDIR) clean
        CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
-         $(MAKE) -C $(BASEDIR) "DESTDIR=$(DESTDIR)" "PREFIX=$(PREFIX)" install
+         $(MAKE) -C $(BASEDIR) "DESTDIR=$(DESTDIR)/$(PKGDIR)" "PREFIX=$(PREFIX)" install
        @echo "pkgname = gmqcc" > $(PKGINFO)
        @echo "pkgver = $(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)" >> $(PKGINFO)
        @echo "pkgdesc = An Improved Quake C Compiler" >> $(PKGINFO)
@@ -32,7 +33,7 @@ base:
        @echo "arch = $(CARCH)" >> $(PKGINFO)
        @echo "license = MIT" >> $(PKGINFO)
        @echo "conflict = gmqcc" >> $(PKGINFO)
-       @echo "depend = glibc" >> $(PKGINFO)
+       @echo "depend = $(LIBC_DEPEND)" >> $(PKGINFO)
        @echo "makepkgopt = strip" >> $(PKGINFO)
        @echo "makepkgopt = docs" >> $(PKGINFO)
        @echo "makepkgopt = libtool" >> $(PKGINFO)
@@ -40,7 +41,11 @@ base:
        @echo "makepkgopt = zipman" >> $(PKGINFO)
        @echo "makepkgopt = purge" >> $(PKGINFO)
        @echo "makepkgopt = !upx" >> $(PKGINFO)
-       @tar -cJvf $(PKG) -C $(PKGDIR)/ .PKGINFO usr/
+       @tar -C $(PKGDIR) -czf $(PKGDIR)/.MTREE \
+           --format=mtree \
+           --options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' \
+           .PKGINFO usr/
+       @tar -cJvf $(PKG) -C $(PKGDIR)/ .PKGINFO .MTREE usr/
        @rm -rf $(PKGDIR)
 
 clean:
index 78ea18d63f92c1145e179abc982f77628782b13b..9157bb4ba5e20c93a8fe69180b95d38b0bca2721 100644 (file)
@@ -1,12 +1,10 @@
 Name:           gmqcc
-Version:        0.2.9
-Release:        1%{?dist}
+Version:        0.3.0
+Release:        2%{?dist}
 Summary:        Improved Quake C Compiler
 License:        MIT
 URL:            http://graphitemaster.github.io/gmqcc/
 Source0:        https://github.com/graphitemaster/%{name}/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz
-# Downstream patch. TODO: drop it in 0.3.0 release.
-Patch0:         build_fix.patch
 
 %description
 Modern written-from-scratch compiler for the QuakeC language with
@@ -21,13 +19,34 @@ as gmqcc or fteqcc. It provides a small set of built-in functions, and
 by default executes the main function if there is one. Some options
 useful for debugging are available as well.
 
-# TODO: add new package gmqpak after 0.3.0 release
+%package -n gmqpak
+Summary:        Standalone Quake PAK file utility
+
+%description -n gmqpak
+Standalone Quake PAK file utility supporting the extraction of files,
+directories, or whole PAKs, as well as the opposite (creation of PAK files).
 
 %prep
 %setup -q
-%patch0 -p1
 echo '#!/bin/sh' > ./configure
-chmod +x ./configure 
+chmod +x ./configure
+
+# and for all for all of those switches they increase the runtime of the compile
+# making compiles of code slower
+
+# we don't need compiel time buffer protection, we test with clangs address
+# sanatizer and valgrind before releases
+%global optflags %(echo %{optflags} | sed 's/-D_FORTIFY_SOURCE=2 //')
+# there is no exceptions in C
+%global optflags %(echo %{optflags} | sed 's/-fexceptions //')
+# same with clangs address sanatizer and valgrind testing
+%global optflags %(echo %{optflags} | sed 's/-fstack-protector-strong //')
+# buffer overflow protection is unrequired since most (if not all) allocations
+# happen dynamically and we have our own memory allocator which checks this
+# (with valgrind integration), also clangs address santatizer cathes it as
+# for grecord-gcc-switches, that just adds pointless information to the binary
+# increasing it size
+%global optflags %(echo %{optflags} | sed 's/--param=ssp-buffer-size=4 //')
 
 %build
 %configure
@@ -42,14 +61,25 @@ make check
 %files
 %doc LICENSE README AUTHORS CHANGES TODO
 %doc gmqcc.ini.example
-%doc %{_mandir}/man1/gmqcc.1.gz
+%{_mandir}/man1/gmqcc.1.gz
 %{_bindir}/gmqcc
 
 %files -n qcvm
 %doc LICENSE README AUTHORS CHANGES TODO
-%doc %{_mandir}/man1/qcvm.1.gz
+%{_mandir}/man1/qcvm.1.gz
 %{_bindir}/qcvm
 
+%files -n gmqpak
+%doc LICENSE README AUTHORS CHANGES TODO
+%{_mandir}/man1/gmqpak.1.gz
+%{_bindir}/gmqpak
+
 %changelog
+* Thu Sep 26 2013 Igor Gnatenko <i.gnatenko.brain@gmail.com> - 0.3.0-2
+- Optimizing compile flags
+
+* Fri Sep 20 2013 Igor Gnatenko <i.gnatenko.brain@gmail.com> - 0.3.0-1
+- Update to 0.3.0 (improved new package: gmqpak)
+
 * Sat Jul 27 2013 Igor Gnatenko <i.gnatenko.brain@gmail.com> - 0.2.9-1
 - Initial release
index a940f4235a6541478aecf48c1aae7d5d2559f39d..c825a417b5d25a05cd3a0c51c534cc35074634df 100644 (file)
@@ -327,10 +327,15 @@ it can happen that incompatible types are passed to functions. This
 enables several warnings when static typechecking cannot guarantee
 consistent behavior.
 .It Fl W Ns Cm breakdef
-When compiling original id1 QC, there is a definition for `break`
+When compiling original id1 QC there is a definition for `break`
 which conflicts with the 'break' keyword in GMQCC. Enabling this
-warning will print a warning when the definition occurs. The
-definition is ignored for both cases.
+will print a warning when the definition occurs. The definition is
+ignored for both cases.
+.It Fl W Ns Cm const-overwrite
+When compiling original QuakeWorld QC there are instances where
+code overwrites constants. This is considered an error, however
+for QuakeWorld to compile it needs to be treated as a warning
+instead, as such this warning only works when -std=qcc.
 .El
 .Sh COMPILE FLAGS
 .Bl -tag -width Ds
diff --git a/exec.c b/exec.c
index 1856248bcab4a88ed50725ea9df53b82766580d4..c4de8addec619b8d9521a7925d258d17907abcae 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -30,7 +30,6 @@
 
 #include "gmqcc.h"
 
-opts_cmd_t   opts; /* command line options */
 static void loaderror(const char *fmt, ...)
 {
     int     err = errno;
@@ -1256,7 +1255,7 @@ void prog_disasm_function(qc_program_t *prog, size_t id) {
 #   define FLOAT_IS_TRUE_FOR_INT(x) ( (x) & 0x7FFFFFFF )
 #endif
 
-while (1) {
+while (prog->vmerror == 0) {
     prog_section_function_t  *newf;
     qcany_t          *ed;
     qcany_t          *ptr;
diff --git a/fold.c b/fold.c
index f8a143ceb7f7f4ab772c99323d7a96336965bf84..d51b7652dcc9b48efdd2bf42c0befe73d6c13503 100644 (file)
--- a/fold.c
+++ b/fold.c
@@ -163,6 +163,14 @@ static GMQCC_INLINE bool vec3_pbool(vec3_t a) {
     return (a.x && a.y && a.z);
 }
 
+static GMQCC_INLINE vec3_t vec3_cross(vec3_t a, vec3_t b) {
+    vec3_t out;
+    out.x = a.y * b.z - a.z * b.y;
+    out.y = a.z * b.x - a.x * b.z;
+    out.z = a.x * b.y - a.y * b.x;
+    return out;
+}
+
 static lex_ctx_t fold_ctx(fold_t *fold) {
     lex_ctx_t ctx;
     if (fold->parser->lex)
@@ -201,11 +209,25 @@ static GMQCC_INLINE bool fold_immediate_true(fold_t *fold, ast_value *v) {
                 ((ast_expression*)(X))->vtype != TYPE_FUNCTION)
 
 #define fold_can_2(X, Y) (fold_can_1(X) && fold_can_1(Y))
+#define fold_can_div(X) (fold_immvalue_float(X) != 0.0f)
 
 #define fold_immvalue_float(E)  ((E)->constval.vfloat)
 #define fold_immvalue_vector(E) ((E)->constval.vvec)
 #define fold_immvalue_string(E) ((E)->constval.vstring)
 
+#ifdef INFINITY
+#   define fold_infinity_float  INFINITY
+#else
+#   define fold_infinity_float  (1.0 / 0.0)
+#endif /*! INFINITY */
+
+#define fold_infinity_vector \
+    vec3_create(             \
+        fold_infinity_float, \
+        fold_infinity_float, \
+        fold_infinity_float  \
+    )
+
 fold_t *fold_init(parser_t *parser) {
     fold_t *fold                 = (fold_t*)mem_a(sizeof(fold_t));
     fold->parser                 = parser;
@@ -222,9 +244,11 @@ fold_t *fold_init(parser_t *parser) {
     (void)fold_constgen_float (fold,  0.0f);
     (void)fold_constgen_float (fold,  1.0f);
     (void)fold_constgen_float (fold, -1.0f);
+    (void)fold_constgen_float (fold,  fold_infinity_float); /* +inf */
 
     (void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.0f));
     (void)fold_constgen_vector(fold, vec3_create(-1.0f, -1.0f, -1.0f));
+    (void)fold_constgen_vector(fold, fold_infinity_vector); /* +inf */
 
     return fold;
 }
@@ -362,7 +386,7 @@ static GMQCC_INLINE ast_expression *fold_op_mul_vec(fold_t *fold, vec3_t vec, as
         out                        = (ast_expression*)ast_member_new(fold_ctx(fold), (ast_expression*)sel, set[0]-'x', NULL);
         out->node.keep             = false;
         ((ast_member*)out)->rvalue = true;
-        if (x != -1)
+        if (x != -1.0f)
             return (ast_expression*)ast_binary_new(fold_ctx(fold), INSTR_MUL_F, fold_constgen_float(fold, x), out);
     }
     return NULL;
@@ -454,12 +478,28 @@ static GMQCC_INLINE ast_expression *fold_op_mul(fold_t *fold, ast_value *a, ast_
 
 static GMQCC_INLINE ast_expression *fold_op_div(fold_t *fold, ast_value *a, ast_value *b) {
     if (isfloat(a)) {
-        if (fold_can_2(a, b))
-            return fold_constgen_float(fold, fold_immvalue_float(a) / fold_immvalue_float(b));
+        if (fold_can_2(a, b)) {
+            if (fold_can_div(b))
+                return fold_constgen_float(fold, fold_immvalue_float(a) / fold_immvalue_float(b));
+            else
+                return (ast_expression*)fold->imm_float[3]; /* inf */
+        } else if (fold_can_1(b)) {
+            return (ast_expression*)ast_binary_new(
+                fold_ctx(fold),
+                INSTR_MUL_F,
+                (ast_expression*)a,
+                fold_constgen_float(fold, 1.0f / fold_immvalue_float(b))
+            );
+        }
     } else if (isvector(a)) {
-        if (fold_can_2(a, b))
-            return fold_constgen_vector(fold, vec3_mulvf(fold_immvalue_vector(a), 1.0f / fold_immvalue_float(b)));
-        else {
+        if (fold_can_2(a, b)) {
+            if (fold_can_div(b)) {
+                return fold_constgen_vector(fold, vec3_mulvf(fold_immvalue_vector(a), 1.0f / fold_immvalue_float(b)));
+            }
+            else {
+                return (ast_expression*)fold->imm_vector[2]; /* inf */
+            }
+        } else {
             return (ast_expression*)ast_binary_new(
                 fold_ctx(fold),
                 INSTR_MUL_VF,
@@ -479,8 +519,12 @@ static GMQCC_INLINE ast_expression *fold_op_div(fold_t *fold, ast_value *a, ast_
 }
 
 static GMQCC_INLINE ast_expression *fold_op_mod(fold_t *fold, ast_value *a, ast_value *b) {
-    if (fold_can_2(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) % ((qcint_t)fold_immvalue_float(b))));
+    if (fold_can_2(a, b)) {
+        if (fold_can_div(b))
+            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) % ((qcint_t)fold_immvalue_float(b))));
+        else
+            return (ast_expression*)fold->imm_float[3]; /* inf */
+    }
     return NULL;
 }
 
@@ -610,6 +654,12 @@ static GMQCC_INLINE ast_expression *fold_op_bnot(fold_t *fold, ast_value *a) {
     return NULL;
 }
 
+static GMQCC_INLINE ast_expression *fold_op_cross(fold_t *fold, ast_value *a, ast_value *b) {
+    if (fold_can_2(a, b))
+        return fold_constgen_vector(fold, vec3_cross(fold_immvalue_vector(a), fold_immvalue_vector(b)));
+    return NULL;
+}
+
 ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **opexprs) {
     ast_value      *a = (ast_value*)opexprs[0];
     ast_value      *b = (ast_value*)opexprs[1];
@@ -667,69 +717,56 @@ ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **op
         fold_op_case(2, ('!', '='),    cmp,    (fold, a, b, true));
         fold_op_case(2, ('=', '='),    cmp,    (fold, a, b, false));
         fold_op_case(2, ('~', 'P'),    bnot,   (fold, a));
+        fold_op_case(2, ('>', '<'),    cross,  (fold, a, b));
     }
     #undef fold_op_case
     compile_error(fold_ctx(fold), "internal error: attempted to constant-fold for unsupported operator");
     return NULL;
 }
 
-#define expect(X)                                                                                        \
-    do {                                                                                                 \
-        if (vec_size(params) != (X)) {                                                                   \
-            compile_error(                                                                               \
-                fold_ctx(fold),                                                                          \
-                "internal error: attempted to constant-fold with invalid paramaters for intrinsic `%s`", \
-                intrin                                                                                   \
-            );                                                                                           \
-            return NULL;                                                                                 \
-        }                                                                                                \
-    } while (0)
-
-ast_expression *fold_intrin(fold_t *fold, const char *intrin, ast_expression **params) {
-    if (!fold)   return NULL;
-    if (!intrin) return NULL;
-
-    if (!strcmp(intrin, "__builtin_exp")) {
-        expect(1);
-        ++opts_optimizationcount[OPTIM_CONST_FOLD];
-        return fold_constgen_float(fold, exp(fold_immvalue_float((ast_value*)params[0])));
-    }
+/*
+ * Constant folding for compiler intrinsics, simaler approach to operator
+ * folding, primarly: individual functions for each intrinsics to fold,
+ * and a generic selection function.
+ */
+static GMQCC_INLINE ast_expression *fold_intrin_mod(fold_t *fold, ast_value *lhs, ast_value *rhs) {
+    return fold_constgen_float(
+                fold,
+                fmodf(
+                    fold_immvalue_float(lhs),
+                    fold_immvalue_float(rhs)
+                )
+            );
+}
 
-    if (!strcmp(intrin, "__builtin_mod")) {
-        expect(2);
-        ++opts_optimizationcount[OPTIM_CONST_FOLD];
-        return fold_constgen_float(
-                    fold,
-                    fmodf(
-                        fold_immvalue_float((ast_value*)params[0]),
-                        fold_immvalue_float((ast_value*)params[1])
-                    )
-                );
-    }
+static GMQCC_INLINE ast_expression *fold_intrin_pow(fold_t *fold, ast_value *lhs, ast_value *rhs) {
+    return fold_constgen_float(
+                fold,
+                powf(
+                    fold_immvalue_float(lhs),
+                    fold_immvalue_float(rhs)
+                )
+            );
+}
 
-    if (!strcmp(intrin, "__builtin_pow")) {
-        expect(2);
-        ++opts_optimizationcount[OPTIM_CONST_FOLD];
-        return fold_constgen_float(
-                    fold,
-                    powf(
-                        fold_immvalue_float((ast_value*)params[0]),
-                        fold_immvalue_float((ast_value*)params[1])
-                    )
-                );
-    }
+static GMQCC_INLINE ast_expression *fold_intrin_exp(fold_t *fold, ast_value *value) {
+    return fold_constgen_float(fold, exp(fold_immvalue_float(value)));
+}
 
-    if (!strcmp(intrin, "__builtin_isnan")) {
-        expect(1);
-        ++opts_optimizationcount[OPTIM_CONST_FOLD];
-        return fold_constgen_float(fold, isnan(fold_immvalue_float((ast_value*)params[0])) != 0.0f);
-    }
+static GMQCC_INLINE ast_expression *fold_intrin_isnan(fold_t *fold, ast_value *value) {
+    return fold_constgen_float(fold, isnan(fold_immvalue_float(value)) != 0.0f);
+}
 
-    if (!strcmp(intrin, "__builtin_fabs")) {
-        expect(1);
-        ++opts_optimizationcount[OPTIM_CONST_FOLD];
-        return fold_constgen_float(fold, fabs(fold_immvalue_float((ast_value*)params[0])));
-    }
+static GMQCC_INLINE ast_expression *fold_intrin_fabs(fold_t *fold, ast_value *value) {
+    return fold_constgen_float(fold, fabs(fold_immvalue_float(value)));
+}
+
+ast_expression *fold_intrin(fold_t *fold, const char *intrin, ast_expression **arg) {
+    if (!strcmp(intrin, "mod"))   return fold_intrin_mod  (fold, (ast_value*)arg[0], (ast_value*)arg[1]);
+    if (!strcmp(intrin, "pow"))   return fold_intrin_pow  (fold, (ast_value*)arg[0], (ast_value*)arg[1]);
+    if (!strcmp(intrin, "exp"))   return fold_intrin_exp  (fold, (ast_value*)arg[0]);
+    if (!strcmp(intrin, "isnan")) return fold_intrin_isnan(fold, (ast_value*)arg[0]);
+    if (!strcmp(intrin, "fabs"))  return fold_intrin_fabs (fold, (ast_value*)arg[0]);
 
     return NULL;
 }
@@ -760,7 +797,7 @@ ast_expression *fold_intrin(fold_t *fold, const char *intrin, ast_expression **p
 /*#define fold_can_2(X,Y)         (fold_can_1(X) && fold_can_1(Y))*/
 
 
-int fold_cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
+static GMQCC_INLINE int fold_cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
     if (isfloat(condval) && fold_can_1(condval) && OPTS_OPTIMIZATION(OPTIM_CONST_FOLD_DCE)) {
         ast_expression_codegen *cgen;
         ir_block               *elide;
@@ -794,3 +831,11 @@ int fold_cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
     }
     return -1; /* nothing done */
 }
+
+int fold_cond_ternary(ir_value *condval, ast_function *func, ast_ternary *branch) {
+    return fold_cond(condval, func, (ast_ifthen*)branch);
+}
+
+int fold_cond_ifthen(ir_value *condval, ast_function *func, ast_ifthen *branch) {
+    return fold_cond(condval, func, branch);
+}
diff --git a/gmqcc.h b/gmqcc.h
index 86b0bffd6e861280294df032f14853d035aeda3c..9848f20bbc19067d69cc0043e523fb7a4abe3236 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -332,7 +332,6 @@ void *stat_mem_allocate  (size_t, size_t, const char *);
 bool  util_filexists     (const char *);
 bool  util_strupper      (const char *);
 bool  util_strdigit      (const char *);
-void  util_debug         (const char *, const char *, ...);
 void  util_endianswap    (void *,  size_t, unsigned int);
 
 size_t util_strtocmd         (const char *, char *, size_t);
@@ -713,6 +712,7 @@ enum {
     VINSTR_PHI,
     VINSTR_JUMP,
     VINSTR_COND,
+
     /* A never returning CALL.
      * Creating this causes IR blocks to be marked as 'final'.
      * No-Return-Call
@@ -726,7 +726,10 @@ enum {
     VINSTR_BITOR_VF,
     VINSTR_BITXOR,
     VINSTR_BITXOR_V,
-    VINSTR_BITXOR_VF /* BITXOR_VF must be the last emulated bitop */
+    VINSTR_BITXOR_VF,
+    VINSTR_CROSS,
+    VINSTR_NEG_F,
+    VINSTR_NEG_V
 };
 
 /* TODO: elide */
index 9dbb6fbbc91d2a74ab4edf9c1b484e6646fb2064..474fe3c52dc95a2311abfd9c443ab6937827b679 100644 (file)
@@ -31,7 +31,7 @@
     #write a ticket.
 
     FTEPP = true
-    
+
 
     #Enable some predefined macros. This only works in combination
     #with '-fftepp' and is currently not included by '-std=fteqcc'.
     UNSAFE_TYPES = true
 
 
-    #When compiling original id1 QC, there is a definition for `break`
+    #When compiling original id1 QC there is a definition for `break`
     #which conflicts with the 'break' keyword in GMQCC. Enabling this
-    #warning will print a warning when the definition occurs. The
-    #definition is ignored for both cases.
+    #print a warning when the definition occurs. The definition is
+    #ignored for both cases.
 
     BREAKDEF = true
 
+
+    #When compiling original QuakeWorld QC there are instances where
+    #code overwrites constants. This is considered an error, however
+    #for QuakeWorld to compile it needs to be treated as a warning
+    #instead, as such this warning only works when -std=qcc.
+
+    CONST_OVERWRITE = true
+
+
+
 [optimizations]
     #Some general peephole optimizations. For instance the code `a = b
     #+ c` typically generates 2 instructions, an ADD and a STORE. This
     OVERLAP_LOCALS = true
 
 
-
     #This promotes locally declared variables to "temps". Meaning when
     #a temporary result of an operation has to be stored somewhere, a
     #local variable which is not 'alive' at that point can be used to
 
     LOCAL_TEMPS = true
 
+
     #Causes temporary values which do not need to be backed up on a
     #CALL to not be stored in the function's locals-area. With this, a
     #CALL to a function may need to back up fewer values and thus exe‐
 
     CONST_FOLD_DCE = true
 
+
     #For constant expressions we can fold them to immediate values.
     #this option cannot be disabled or enabled, the compiler forces
     #it to stay enabled by ignoring the value entierly. There are
index 7e7e69e4a204022cfd3bb966f5983515ee5237c4..d713df0f4000f0786353ea4d5de3239073986916 100644 (file)
--- a/intrin.c
+++ b/intrin.c
@@ -409,12 +409,12 @@ ast_expression *intrin_debug_typestring(intrin_t *intrin) {
 }
 
 static const intrin_func_t intrinsics[] = {
-    {&intrin_exp,              "__builtin_exp",              "exp"},
-    {&intrin_mod,              "__builtin_mod",              "mod"},
-    {&intrin_pow,              "__builtin_pow",              "pow"},
-    {&intrin_isnan,            "__builtin_isnan",            "isnan"},
-    {&intrin_fabs,             "__builtin_fabs",             "fabs"},
-    {&intrin_debug_typestring, "__builtin_debug_typestring", ""}
+    {&intrin_exp,              "__builtin_exp",              "exp",   1},
+    {&intrin_mod,              "__builtin_mod",              "mod",   2},
+    {&intrin_pow,              "__builtin_pow",              "pow",   2},
+    {&intrin_isnan,            "__builtin_isnan",            "isnan", 1},
+    {&intrin_fabs,             "__builtin_fabs",             "fabs",  1},
+    {&intrin_debug_typestring, "__builtin_debug_typestring", "",      0}
 };
 
 static void intrin_error(intrin_t *intrin, const char *fmt, ...) {
@@ -447,9 +447,14 @@ ast_expression *intrin_fold(intrin_t *intrin, ast_value *value, ast_expression *
     if (!value || !value->name)
         return NULL;
 
-    for (i = 0; i < vec_size(intrin->intrinsics); i++)
-        if (!strcmp(value->name, intrin->intrinsics[i].name))
-            return fold_intrin(intrin->fold, value->name, exprs);
+    for (i = 0; i < vec_size(intrin->intrinsics); i++) {
+        if (!strcmp(value->name, intrin->intrinsics[i].name)) {
+            if (intrin->intrinsics[i].args != vec_size(exprs))
+                return NULL;
+            /* +10 to skip the "__builtin_" substring in the string */
+            return fold_intrin(intrin->fold, value->name + 10, exprs);
+        }
+    }
 
     return NULL;
 }
diff --git a/ir.c b/ir.c
index 3efad3da81164b9064c882e5c11179ace81e9463..f33e48c54ee64d601cdbbb9f17eb1cc0c48eb126 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -248,7 +248,7 @@ static void irerror(lex_ctx_t ctx, const char *msg, ...)
     va_end(ap);
 }
 
-static bool irwarning(lex_ctx_t ctx, int warntype, const char *fmt, ...)
+static bool GMQCC_WARN irwarning(lex_ctx_t ctx, int warntype, const char *fmt, ...)
 {
     bool    r;
     va_list ap;
@@ -613,7 +613,7 @@ static bool instr_is_operation(uint16_t op)
              (op >= INSTR_NOT_F  && op <= INSTR_NOT_FNC) ||
              (op >= INSTR_AND    && op <= INSTR_BITOR) ||
              (op >= INSTR_CALL0  && op <= INSTR_CALL8) ||
-             (op >= VINSTR_BITAND_V && op <= VINSTR_BITXOR_VF) );
+             (op >= VINSTR_BITAND_V && op <= VINSTR_CROSS) );
 }
 
 static bool ir_function_pass_peephole(ir_function *self)
@@ -1815,6 +1815,7 @@ ir_value* ir_block_create_binop(ir_block *self, lex_ctx_t ctx,
         case VINSTR_BITAND_VF:
         case VINSTR_BITOR_VF:
         case VINSTR_BITXOR_VF:
+        case VINSTR_CROSS:
 #if 0
         case INSTR_DIV_VF:
         case INSTR_MUL_IV:
@@ -1877,16 +1878,20 @@ ir_value* ir_block_create_unary(ir_block *self, lex_ctx_t ctx,
         case INSTR_NOT_V:
         case INSTR_NOT_S:
         case INSTR_NOT_ENT:
-        case INSTR_NOT_FNC:
-#if 0
-        case INSTR_NOT_I:
-#endif
+        case INSTR_NOT_FNC: /*
+        case INSTR_NOT_I:   */
             ot = TYPE_FLOAT;
             break;
-        /* QC doesn't have other unary operations. We expect extensions to fill
-         * the above list, otherwise we assume out-type = in-type, eg for an
-         * unary minus
+
+        /*
+         * Negation for virtual instructions is emulated with 0-value. Thankfully
+         * the operand for 0 already exists so we just source it from here.
          */
+        case VINSTR_NEG_F:
+            return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_F, NULL, operand, ot);
+        case VINSTR_NEG_V:
+            return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, NULL, operand, ot);
+
         default:
             ot = operand->vtype;
             break;
@@ -2518,7 +2523,8 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
             instr->opcode == VINSTR_BITOR_VF ||
             instr->opcode == VINSTR_BITXOR ||
             instr->opcode == VINSTR_BITXOR_VF ||
-            instr->opcode == VINSTR_BITXOR_V)
+            instr->opcode == VINSTR_BITXOR_V ||
+            instr->opcode == VINSTR_CROSS)
         {
             value = instr->_ops[2];
             /* the float source will get an additional lifetime */
@@ -2532,7 +2538,8 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
             instr->opcode == INSTR_LOAD_V ||
             instr->opcode == VINSTR_BITXOR ||
             instr->opcode == VINSTR_BITXOR_VF ||
-            instr->opcode == VINSTR_BITXOR_V)
+            instr->opcode == VINSTR_BITXOR_V ||
+            instr->opcode == VINSTR_CROSS)
         {
             value = instr->_ops[1];
             /* the float source will get an additional lifetime */
@@ -2960,6 +2967,28 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
             continue;
         }
 
+        if (instr->opcode == VINSTR_CROSS) {
+            stmt.opcode = INSTR_MUL_F;
+            for (j = 0; j < 3; ++j) {
+                stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + (j + 1) % 3;
+                stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + (j + 2) % 3;
+                stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]) + j;
+                code_push_statement(code, &stmt, instr->context);
+                stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + (j + 2) % 3;
+                stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + (j + 1) % 3;
+                stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]) + j;
+                code_push_statement(code, &stmt, instr->context);
+            }
+            stmt.opcode = INSTR_SUB_V;
+            stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]);
+            stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]);
+            stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]);
+            code_push_statement(code, &stmt, instr->context);
+
+            /* instruction generated */
+            continue;
+        }
+
         if (instr->opcode == VINSTR_COND) {
             ontrue  = instr->bops[0];
             onfalse = instr->bops[1];
@@ -3173,7 +3202,6 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
                 continue;
             }
         }
-
         code_push_statement(code, &stmt, instr->context);
     }
     return true;
@@ -3454,8 +3482,16 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global)
     irfun = global->constval.vfunc;
     if (!irfun) {
         if (global->cvq == CV_NONE) {
-            irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER,
-                      "function `%s` has no body and in QC implicitly becomes a function-pointer", global->name);
+            if (irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER,
+                          "function `%s` has no body and in QC implicitly becomes a function-pointer",
+                          global->name))
+            {
+                /* Not bailing out just now. If this happens a lot you don't want to have
+                 * to rerun gmqcc for each such function.
+                 */
+
+                /* return false; */
+            }
         }
         /* this was a function pointer, don't generate code for those */
         return true;
@@ -3623,9 +3659,12 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool isloc
             /* TODO: same as above but for entity-fields rather than globsl
              */
         }
-        else
-            irwarning(global->context, WARN_VOID_VARIABLES, "unrecognized variable of type void `%s`",
-                      global->name);
+        else if(irwarning(global->context, WARN_VOID_VARIABLES, "unrecognized variable of type void `%s`",
+                          global->name))
+        {
+            /* Not bailing out */
+            /* return false; */
+        }
         /* I'd argue setting it to 0 is sufficient, but maybe some depend on knowing how far
          * the system fields actually go? Though the engine knows this anyway...
          * Maybe this could be an -foption
@@ -3959,6 +3998,7 @@ static const char *qc_opname(int op)
         case VINSTR_BITAND_VF: return "BITAND_VF";
         case VINSTR_BITOR_VF:  return "BITOR_VF";
         case VINSTR_BITXOR_VF: return "BITXOR_VF";
+        case VINSTR_CROSS:     return "CROSS";
         default:               return "<UNK>";
     }
 }
diff --git a/lexer.c b/lexer.c
index af7819dd802016e0870843c5b6b85ded5d5efc59..7642d261b89b4b3b9eae3e8fa6c4c1ec0751b96e 100644 (file)
--- a/lexer.c
+++ b/lexer.c
@@ -1306,7 +1306,7 @@ int lex_do(lex_file *lex)
     }
 
     if (ch == '+' || ch == '-' || /* ++, --, +=, -=  and -> as well! */
-        ch == '>' || ch == '<' || /* <<, >>, <=, >=                  */
+        ch == '>' || ch == '<' || /* <<, >>, <=, >=  and >< as well! */
         ch == '=' || ch == '!' || /* <=>, ==, !=                     */
         ch == '&' || ch == '|' || /* &&, ||, &=, |=                  */
         ch == '~' || ch == '^'    /* ~=, ~, ^                        */
@@ -1314,7 +1314,9 @@ int lex_do(lex_file *lex)
         lex_tokench(lex, ch);
 
         nextch = lex_getch(lex);
-        if ((nextch == '=' && ch != '<') || (nextch == ch && ch != '!')) {
+        if ((nextch == '=' && ch != '<') ||
+            (nextch == ch  && ch != '!') ||
+            (nextch == '<' && ch == '>')) {
             lex_tokench(lex, nextch);
         } else if (ch == '<' && nextch == '=') {
             lex_tokench(lex, nextch);
@@ -1517,6 +1519,6 @@ int lex_do(lex_file *lex)
         return (lex->tok.ttype = ch);
     }
 
-    lexerror(lex, "unknown token: `%s`", lex->tok.value);
+    lexerror(lex, "unknown token: `%c`", ch);
     return (lex->tok.ttype = TOKEN_ERROR);
 }
diff --git a/lexer.h b/lexer.h
index 48ab4b6e17b0a3b7af2aea2d6cbbb66af28ffee7..24e29ddee4080659be406da5f073ef041ecdb115 100644 (file)
--- a/lexer.h
+++ b/lexer.h
@@ -196,6 +196,7 @@ static const oper_info c_operators[] = {
     { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0,         true},
     { "/",   2, opid1('/'),         ASSOC_LEFT,  13, 0,         true},
     { "%",   2, opid1('%'),         ASSOC_LEFT,  13, 0,         true},
+    { "><",  2, opid2('>','<'),     ASSOC_LEFT,  13, 0,         true},
 
     { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0,         true},
     { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0,         true},
diff --git a/main.c b/main.c
index eab582dc5cae10d3b19db19c697c7130a870f7ee..9a76706b06653412f017945dde44aa891ccdc06e 100644 (file)
--- a/main.c
+++ b/main.c
@@ -234,6 +234,10 @@ static bool options_parse(int argc, char **argv) {
                 OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = (uint16_t)strtol(memdumpcols, NULL, 10);
                 continue;
             }
+            if (options_long_gcc("progsrc", &argc, &argv, &argarg)) {
+                OPTS_OPTION_STR(OPTION_PROGSRC) = argarg;
+                continue;
+            }
 
             /* show defaults (like pathscale) */
             if (!strcmp(argv[0]+1, "show-defaults")) {
@@ -650,8 +654,6 @@ int main(int argc, char **argv) {
         }
     }
 
-    util_debug("COM", "starting ...\n");
-
     /* add macros */
     if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
         for (itr = 0; itr < vec_size(ppems); itr++) {
@@ -672,9 +674,9 @@ int main(int argc, char **argv) {
 
         progs_src = true;
 
-        src = fs_file_open("progs.src", "rb");
+        src = fs_file_open(OPTS_OPTION_STR(OPTION_PROGSRC), "rb");
         if (!src) {
-            con_err("failed to open `progs.src` for reading\n");
+            con_err("failed to open `%s` for reading\n", OPTS_OPTION_STR(OPTION_PROGSRC));
             retval = 1;
             goto cleanup;
         }
@@ -772,7 +774,6 @@ int main(int argc, char **argv) {
     }
 
 cleanup:
-    util_debug("COM", "cleaning ...\n");
     if (ftepp)
         ftepp_finish(ftepp);
     con_close();
@@ -789,5 +790,7 @@ cleanup:
     lex_cleanup();
     stat_info();
 
+    if (!retval && compile_errors)
+        retval = 1;
     return retval;
 }
index e5ffdee295789318ff33238c8b26df5e0eae0b5c..da0c4cf0370638fb7d8d60ab31b2247c9e426a75 100755 (executable)
@@ -42,4 +42,10 @@ check_opt FLAGS f
 check_opt WARNS W
 check_opt OPTIMIZATIONS O
 
-for i in doc/*.1; do mandoc -Tlint -Wall "$i"; done
+# TODO: linux version
+if [ "$(uname -s)" != "Linux" ]; then
+    for i in doc/*.1;
+    do
+        mandoc -Tlint -Wall "$i";
+    done
+fi
diff --git a/misc/check-proj.sh b/misc/check-proj.sh
new file mode 100755 (executable)
index 0000000..36b3774
--- /dev/null
@@ -0,0 +1,138 @@
+#!/bin/sh
+
+host="gmqcc.qc.to"
+location="$host/files"
+list="$location/files"
+hashes="$location/hashes"
+options="$location/options"
+
+#download required things
+download_list=$(wget -qO- ${list})
+download_hashes=$(wget -qO- ${hashes})
+download_options=$(wget -qO- ${options})
+
+download() {
+    local old="$PWD"
+    cd ~/.gmqcc/testsuite
+    echo "$download_list" | while read -r line
+    do
+        echo "downloading $line ..."
+        wget -q "${location}/$line"
+    done
+
+    echo "$download_hashes" > ~/.gmqcc/testsuite/hashes
+    echo "$download_options" > ~/.gmqcc/testsuite/options
+
+    cd "$old"
+}
+
+if [ -z "$download_list" -o -z "$download_hashes" -o -z "$download_options" ]; then
+    echo "failed to download required information to check projects."
+
+    if [ "$(ping -q -c1 "${host}")" ]; then
+        echo "host ${host} seems to be up but missing required files."
+        echo "please file bug report at: github.com/graphitemaster/gmqcc"
+    else
+        echo "host ${host} seems to be down, please try again later."
+    fi
+
+    echo "aborting"
+    exit 1
+fi
+
+# we have existing contents around
+if [ -f ~/.gmqcc/testsuite/hashes -a -f ~/.gmqcc/testsuite/options ]; then
+    echo "$download_hashes" > /tmp/gmqcc_download_hashes
+    echo "$download_options" > /tmp/gmqcc_download_options
+
+    diff -u ~/.gmqcc/testsuite/hashes /tmp/gmqcc_download_hashes > /dev/null
+    check_hash=$?
+    diff -u ~/.gmqcc/testsuite/options /tmp/gmqcc_download_options > /dev/null
+    check_opts=$?
+
+    if [ $check_hash -ne 0 -o $check_opts -ne 0 ]; then
+        echo "consistency errors in hashes (possible update), obtaining fresh contents"
+        rm -rf ~/.gmqcc/testsuite/projects
+        rm ~/.gmqcc/testsuite/*.zip
+
+        download
+    fi
+else
+    # do we even have the directory
+    echo "preparing project testsuite for the first time"
+    if [ ! -d ~/.gmqcc/testsuite ]; then
+        mkdir -p ~/.gmqcc/testsuite
+    fi
+
+    download
+fi
+
+if [ ! -d ~/.gmqcc/testsuite/projects ]; then
+    mkdir -p ~/.gmqcc/testsuite/projects
+    old="$PWD"
+    cd ~/.gmqcc/testsuite/projects
+    echo "$(ls ../ | cat | grep -v '^hashes$' | grep -v '^projects$' | grep -v '^options$')" | while read -r line
+    do
+        echo "extracting project $line"
+        mkdir "$(echo "$line" | sed 's/\(.*\)\..*/\1/')"
+        unzip -qq "../$line" -d $(echo "$line" | sed 's/\(.*\)\..*/\1/')
+    done
+    cd "$old"
+else
+    echo "previous state exists, using it"
+fi
+
+# compile projects in those directories
+gmqcc_bin="gmqcc"
+env -i type gmqcc 1>/dev/null 2>&1 || {
+    if [ -f ../gmqcc ]; then
+        echo "previous build of gmqcc exists, using it"
+        gmqcc_bin="$(pwd)/../gmqcc"
+    elif [ -f ./gmqcc ]; then
+        echo "previous build of gmqcc exists, using it"
+        gmqcc_bin="$(pwd)/gmqcc"
+    else
+        echo "gmqcc not installed and previous build doesn't exist"
+        echo "please run make, or make install"
+        exit 1
+    fi
+}
+
+end_dir="$PWD"
+cd ~/.gmqcc/testsuite/projects
+start="$PWD"
+find . -maxdepth 1 -mindepth 1 -type d | while read -r line
+do
+    line="${line#./}"
+    echo -n "compiling $line... "
+    cd "${start}/${line}"
+
+    # does the project have multiple subprojects?
+    if [ -f dirs ]; then
+        echo ""
+        cat dirs | while read -r dir
+        do
+            # change to subproject
+            echo -n "    compiling $dir... "
+            old="$PWD"
+            cd "$dir"
+            "$gmqcc_bin" $(cat ../../../options | grep "$line:" | awk '{print substr($0, index($0, $2))}') > /dev/null 2>&1
+            if [ $? -ne 0 ]; then
+                echo "error"
+            else
+                echo "success"
+            fi
+            cd "$old"
+        done
+    # nope only one project
+    else
+        "$gmqcc_bin" $(cat ../../options | grep "$line:" | awk '{print substr($0, index($0, $2))}') > /dev/null 2>&1
+        if [ $? -ne 0 ]; then
+            echo "error"
+        else
+            echo "success"
+        fi
+    fi
+done
+
+cd "$end_dir"
diff --git a/misc/nexuiz_export.sh b/misc/nexuiz_export.sh
new file mode 100755 (executable)
index 0000000..a07a7af
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+if [ ! -d qcsrc ]; then
+    echo "failed to find qcsrc directory in $(pwd), please run this script"
+    echo "from nexuiz data directory"
+    exit 1
+else
+    # ensure this is actually a xonotic repo
+    pushd qcsrc > /dev/null
+    if [ ! -d client -o ! -d common -o ! -d menu -o ! -d server -o ! -d warpzonelib ]; then
+        echo "this doesnt look like a nexuiz source tree, aborting"
+        popd > /dev/null
+        exit 1
+    fi
+fi
+
+echo -n "removing redundant files ..."
+rm -f nexuiz.ncb
+rm -f nexuiz.sln
+rm -f nexuiz.suo
+rm -f nexuiz.vcproj
+rm -f nexuiz.vcproj.user
+echo "complete"
+
+echo -n "creating projects ..."
+echo "client" >  dirs
+echo "server" >> dirs
+echo "menu"   >> dirs
+
+echo "complete"
+
+echo -n "creating zip archive ..."
+zip -r -9 ../nexuiz.zip * > /dev/null
+echo "complete"
+
+popd > /dev/null
+echo "finished!"
diff --git a/misc/xonotic_export.sh b/misc/xonotic_export.sh
new file mode 100755 (executable)
index 0000000..5cd3921
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+if [ ! -d qcsrc ]; then
+    echo "failed to find qcsrc directory in $(pwd), please run this script"
+    echo "from xonotic-data.pk3dir"
+    exit 1
+else
+    # ensure this is actually a xonotic repo
+    pushd qcsrc > /dev/null
+    if [ ! -d client -o ! -d common -o ! -d dpdefs -o ! -d menu -o ! -d server -o ! -d warpzonelib ]; then
+        echo "this doesnt look like a xonotic source tree, aborting"
+        popd > /dev/null
+        exit 1
+    fi
+fi
+
+# force reset and update
+git rev-parse
+if [ $? -ne 0 ]; then
+    echo "not a git directory, continuing without rebase"
+else
+    echo -n "resetting git state and updating ... "
+    git reset --hard HEAD > /dev/null 2>&1
+    git pull > /dev/null 2>&1
+    echo "complete"
+fi
+
+echo -n "generate precache for csqc ..."
+./collect-precache.sh > /dev/null 2>&1
+echo "complete"
+
+echo -n "removing redundant files ..."
+rm -f Makefile
+rm -f autocvarize-update.sh
+rm -f autocvarize.pl
+rm -f collect-precache.sh
+rm -f fteqcc-bugs.qc
+rm -f i18n-badwords.txt
+rm -f i18n-guide.txt
+echo "complete"
+
+echo -n "creating projects ..."
+echo "client" >  dirs
+echo "server" >> dirs
+echo "menu"   >> dirs
+
+echo "complete"
+
+echo -n "creating zip archive ..."
+zip -r -9 ../xonotic.zip * > /dev/null
+echo "complete"
+
+popd > /dev/null
+echo "finished!"
diff --git a/opts.c b/opts.c
index 4f6660e5c1abc8c86d0629e15f181d6143719755..9aa8f633eff1c4ba2b901ae4d76b1d07fcee7a85 100644 (file)
--- a/opts.c
+++ b/opts.c
@@ -60,6 +60,7 @@ opts_cmd_t   opts; /* command line options */
 static void opts_setdefault(void) {
     memset(&opts, 0, sizeof(opts_cmd_t));
     OPTS_OPTION_BOOL(OPTION_CORRECTION) = true;
+    OPTS_OPTION_STR(OPTION_PROGSRC) = "progs.src";
 
     /* warnings */
     opts_set(opts.warn,  WARN_UNUSED_VARIABLE,           true);
@@ -89,6 +90,7 @@ static void opts_setdefault(void) {
     opts_set(opts.warn,  WARN_UNINITIALIZED_CONSTANT,    true);
     opts_set(opts.warn,  WARN_DEPRECATED,                true);
     opts_set(opts.warn,  WARN_PARENTHESIS,               true);
+    opts_set(opts.warn,  WARN_CONST_OVERWRITE,           true);
 
     /* flags */
     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,           true);
index 977272a270e79064edff0bcd58298da11904b7f4..f4d01e631ea2c163fb8fd6f5223009728ed9b1ba 100644 (file)
--- a/opts.def
+++ b/opts.def
@@ -94,6 +94,7 @@
     GMQCC_DEFINE_FLAG(PARENTHESIS)
     GMQCC_DEFINE_FLAG(UNSAFE_TYPES)
     GMQCC_DEFINE_FLAG(BREAKDEF)
+    GMQCC_DEFINE_FLAG(CONST_OVERWRITE)
 #endif
 
 #ifdef GMQCC_TYPE_OPTIMIZATIONS
     GMQCC_DEFINE_FLAG(ADD_INFO)
     GMQCC_DEFINE_FLAG(CORRECTION)
     GMQCC_DEFINE_FLAG(STATISTICS)
+    GMQCC_DEFINE_FLAG(PROGSRC)
 #endif
 
 /* some cleanup so we don't have to */
index 1cd74dd8ef49c9141e005abccc4280b376869d0c..15bb921fb30d9c67039cb1a710e1e7cd3b1b54a8 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -282,6 +282,29 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     return true;
 }
 
+static bool check_write_to(lex_ctx_t ctx, ast_expression *expr)
+{
+    if (ast_istype(expr, ast_value)) {
+        ast_value *val = (ast_value*)expr;
+        if (val->cvq == CV_CONST) {
+            if (val->name[0] == '#') {
+                compile_error(ctx, "invalid assignment to a literal constant");
+                return false;
+            }
+            /*
+             * To work around quakeworld we must elide the error and make it
+             * a warning instead.
+             */
+            if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC)
+                compile_error(ctx, "assignment to constant `%s`", val->name);
+            else
+                (void)!compile_warning(ctx, WARN_CONST_OVERWRITE, "assignment to constant `%s`", val->name);
+            return false;
+        }
+    }
+    return true;
+}
+
 static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 {
     const oper_info *op;
@@ -289,7 +312,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     ast_expression *out = NULL;
     ast_expression *exprs[3];
     ast_block      *blocks[3];
-    ast_value      *asvalue[3];
     ast_binstore   *asbinstore;
     size_t i, assignop, addop, subop;
     qcint_t  generated_op = 0;
@@ -311,8 +333,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     ctx = vec_last(sy->ops).ctx;
 
     if (vec_size(sy->out) < op->operands) {
-        compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out),
-                      op->op, (int)op->id);
+        if (op->flags & OP_PREFIX)
+            compile_error(ctx, "expected expression after unary operator `%s`", op->op, (int)op->id);
+        else /* this should have errored previously already */
+            compile_error(ctx, "expected expression after operator `%s`", op->op, (int)op->id);
         return false;
     }
 
@@ -326,7 +350,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     for (i = 0; i < op->operands; ++i) {
         exprs[i]  = sy->out[vec_size(sy->out)+i].out;
         blocks[i] = sy->out[vec_size(sy->out)+i].block;
-        asvalue[i] = (ast_value*)exprs[i];
 
         if (exprs[i]->vtype == TYPE_NOEXPR &&
             !(i != 0 && op->id == opid2('?',':')) &&
@@ -442,24 +465,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             out = exprs[0];
             break;
         case opid2('-','P'):
-            if (!(out = fold_op(parser->fold, op, exprs))) {
-                switch (exprs[0]->vtype) {
-                    case TYPE_FLOAT:
-                        out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F,
-                                                                  (ast_expression*)parser->fold->imm_float[0],
-                                                                  exprs[0]);
-                        break;
-                    case TYPE_VECTOR:
-                        out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V,
-                                                                  (ast_expression*)parser->fold->imm_vector[0],
-                                                                  exprs[0]);
-                        break;
-                    default:
-                    compile_error(ctx, "invalid types used in expression: cannot negate type %s",
+            if ((out = fold_op(parser->fold, op, exprs)))
+                break;
+            if (exprs[0]->vtype != TYPE_FLOAT &&
+                exprs[0]->vtype != TYPE_VECTOR) {
+                    compile_error(ctx, "invalid types used in unary expression: cannot negate type %s",
                                   type_name[exprs[0]->vtype]);
-                    return false;
-                }
+                return false;
             }
+            out = (ast_expression*)ast_unary_new(ctx, (VINSTR_NEG_F-TYPE_FLOAT) + exprs[0]->vtype, exprs[0]);
             break;
 
         case opid2('!','P'):
@@ -747,6 +761,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             }
             break;
 
+        case opid2('>', '<'):
+            if (NotSameType(TYPE_VECTOR)) {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+                compile_error(ctx, "invalid types used in cross product: %s and %s",
+                    ty1, ty2);
+                return false;
+            }
+
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                out = (ast_expression*)ast_binary_new(
+                        parser_ctx(parser),
+                        VINSTR_CROSS,
+                        exprs[0],
+                        exprs[1]
+                );
+            }
+
+            break;
+
         case opid3('<','=','>'): /* -1, 0, or 1 */
             if (NotSameType(TYPE_FLOAT)) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
@@ -879,9 +913,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                 }
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]);
             break;
         case opid3('+','+','P'):
@@ -896,9 +928,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 addop = INSTR_ADD_F;
             else
                 addop = INSTR_SUB_F;
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ast_ctx(exprs[0]), exprs[0]);
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
                                                         exprs[0],
@@ -924,9 +954,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 addop = INSTR_SUB_F;
                 subop = INSTR_ADD_F;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ast_ctx(exprs[0]), exprs[0]);
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
                                                         exprs[0],
@@ -953,9 +981,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (ast_istype(exprs[0], ast_entfield))
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
@@ -990,9 +1016,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (ast_istype(exprs[0], ast_entfield))
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
@@ -1036,9 +1060,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (ast_istype(exprs[0], ast_entfield))
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
@@ -1074,9 +1096,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 out = (ast_expression*)ast_binary_new(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]);
             if (!out)
                 return false;
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (exprs[0]->vtype == TYPE_FLOAT)
                 asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
             else
@@ -1664,7 +1684,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 }
             }
             if (o == operator_count) {
-                compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser));
+                compile_error(parser_ctx(parser), "unexpected operator: %s", parser_tokval(parser));
                 goto onerr;
             }
             /* found an operator */
@@ -1898,7 +1918,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
 
     parser->lex->flags.noops = true;
     if (vec_size(sy.out) != 1) {
-        parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out));
+        parseerror(parser, "expression expected");
         expr = NULL;
     } else
         expr = sy.out[0].out;
@@ -3270,7 +3290,13 @@ static bool parse_pragma_do(parser_t *parser)
     else
     {
         (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser));
-        return false;
+
+        /* skip to eol */
+        while (!parse_eol(parser)) {
+            parser_next(parser);
+        }
+
+        return true;
     }
 
     return true;
@@ -4657,6 +4683,11 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
  * The base type makes up every bit of type information which comes *before* the
  * variable name.
  *
+ * NOTE: The value must either be named, have a NULL name, or a name starting
+ *       with '<'. In the first case, this will be the actual variable or type
+ *       name, in the other cases it is assumed that the name will appear
+ *       later, and an error is generated otherwise.
+ *
  * The following will be parsed in its entirety:
  *     void() foo()
  * The 'basetype' in this case is 'void()'
@@ -4817,6 +4848,13 @@ static bool parse_typedef(parser_t *parser)
     if (!typevar)
         return false;
 
+    /* while parsing types, the ast_value's get named '<something>' */
+    if (!typevar->name || typevar->name[0] == '<') {
+        parseerror(parser, "missing name in typedef");
+        ast_delete(typevar);
+        return false;
+    }
+
     if ( (old = parser_find_var(parser, typevar->name)) ) {
         parseerror(parser, "cannot define a type with the same name as a variable: %s\n"
                    " -> `%s` has been declared here: %s:%i",
@@ -4982,6 +5020,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         return false;
     }
 
+    /* while parsing types, the ast_value's get named '<something>' */
+    if (!var->name || var->name[0] == '<') {
+        parseerror(parser, "declaration does not declare anything");
+        if (basetype)
+            ast_delete(basetype);
+        return false;
+    }
+
     while (true) {
         proto = NULL;
         wasarray = false;
@@ -5180,6 +5226,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 retval = false;
                 goto cleanup;
             }
+            /* doing this here as the above is just for a single scope */
             old = parser_find_local(parser, var->name, 0, &isparam);
             if (old && isparam) {
                 if (parsewarning(parser, WARN_LOCAL_SHADOWS,
@@ -5193,7 +5240,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
                     ast_delete(var);
                     if (ast_istype(old, ast_value))
-                        var = (ast_value*)old;
+                        var = proto = (ast_value*)old;
                     else {
                         var = NULL;
                         goto skipvar;
@@ -5252,7 +5299,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             return false;
                         }
 
-                        if (var->expression.vtype != find->vtype) {
+                        if (!ast_compare_type((ast_expression*)var, find)) {
                             char ty1[1024];
                             char ty2[1024];
 
index d4c8dddd5e65c80c2fa70de221ddd6d9123c1c39..54f93b29acae7d9f848bd118ba1341c55a54f941 100644 (file)
--- a/parser.h
+++ b/parser.h
@@ -43,6 +43,7 @@ typedef struct {
     ast_expression *(*intrin)(intrin_t *);
     const char       *name;
     const char       *alias;
+    size_t            args;
 } intrin_func_t;
 
 struct intrin_s {
@@ -132,7 +133,8 @@ bool            fold_generate       (fold_t *, ir_builder *);
 ast_expression *fold_op             (fold_t *, const oper_info *, ast_expression **);
 ast_expression *fold_intrin         (fold_t *, const char      *, ast_expression **);
 
-int             fold_cond           (ir_value *, ast_function *, ast_ifthen *);
+int             fold_cond_ifthen    (ir_value *, ast_function *, ast_ifthen  *);
+int             fold_cond_ternary   (ir_value *, ast_function *, ast_ternary *);
 
 /* intrin.c */
 intrin_t       *intrin_init            (parser_t *parser);
diff --git a/test.c b/test.c
index 4c149316acb90689010e4918d9911a8d13f74e6c..fdf3777f4f0af16363a5f6170ab2749742240f16 100644 (file)
--- a/test.c
+++ b/test.c
@@ -736,7 +736,6 @@ static bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
                 char            *qcflags = NULL;
                 task_t           task;
 
-                util_debug("TEST", "compiling task template: %s/%s\n", directories[i], files->d_name);
                 found ++;
                 if (!tmpl) {
                     con_err("error compiling task template: %s\n", files->d_name);
@@ -847,8 +846,6 @@ static bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
                     continue;
                 }
 
-                util_debug("TEST", "executing test: `%s` [%s]\n", tmpl->description, buf);
-
                 /*
                  * Open up some file desciptors for logging the stdout/stderr
                  * to our own.
@@ -876,11 +873,6 @@ static bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
     }
     vec_free(directories);
 
-    util_debug("TEST", "compiled %d task template files out of %d\n",
-        vec_size(task_tasks),
-        found
-    );
-
     return success;
 }
 
@@ -903,8 +895,6 @@ static void task_precleanup(const char *curdir) {
             util_snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name);
             if (remove(buffer))
                 con_err("error removing temporary file: %s\n", buffer);
-            else
-                util_debug("TEST", "removed temporary file: %s\n", buffer);
         }
     }
 
@@ -934,12 +924,8 @@ static void task_destroy(void) {
         if (task_tasks[i].compiled || !strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) {
             if (remove(task_tasks[i].stdoutlogfile))
                 con_err("error removing stdout log file: %s\n", task_tasks[i].stdoutlogfile);
-            else
-                util_debug("TEST", "removed stdout log file: %s\n", task_tasks[i].stdoutlogfile);
             if (remove(task_tasks[i].stderrlogfile))
                 con_err("error removing stderr log file: %s\n", task_tasks[i].stderrlogfile);
-            else
-                util_debug("TEST", "removed stderr log file: %s\n", task_tasks[i].stderrlogfile);
 
             (void)!remove(task_tasks[i].tmpl->tempfilename);
         }
@@ -987,11 +973,6 @@ static bool task_trymatch(size_t i, char ***line) {
             );
         }
 
-        util_debug("TEST", "executing qcvm: `%s` [%s]\n",
-            tmpl->description,
-            buffer
-        );
-
         execute = popen(buffer, "r");
         if (!execute)
             return false;
@@ -1138,7 +1119,6 @@ static size_t task_schedualize(size_t *pad) {
 
         con_out("test #%u %*s", i + 1, strlen(space[0]) - strlen(space[1]), "");
 
-        util_debug("TEST", "executing task: %d: %s\n", i, task_tasks[i].tmpl->description);
         /*
          * Generate a task from thin air if it requires execution in
          * the QCVM.
index 509dc3a6a1c36a42fbf532ad1b63b2a44c4ce641..d06d5799dd0e39819756ffe048010e1d997ce34d 100644 (file)
@@ -8,4 +8,5 @@ void main(vector v) {
     print(vtos(v & 16), "\n");
     print(vtos(v | '25 42 51'), "\n");
     print(vtos(v & '25 42 51'), "\n");
+    print(vtos(v >< '3 2 1'));
 }
index 79432325d3b52ae2032fca3d7a300074245c76c1..738ec46ba97a44116005c235d19a379b25958b88 100644 (file)
@@ -1,7 +1,7 @@
 I: vec_ops.qc
 D: some additional vector operations
 T: -execute
-C: -std=fteqcc
+C: -std=gmqcc
 E: -vector "8 16 32"
 M: '8 16 32'
 M: '4 8 16'
@@ -11,3 +11,4 @@ M: '20 24 16'
 M: '0 0 16'
 M: '29 42 51'
 M: '0 8 16'
+M: '-24 44 -16'
diff --git a/util.c b/util.c
index 6a701e4557a1f4a8943453802445128bfcc350d9..9b32e48394806022e2ab2f0e9c7446aeb211cae1 100644 (file)
--- a/util.c
+++ b/util.c
@@ -54,20 +54,6 @@ const char *util_instr_str[VINSTR_END] = {
     "BITAND",     "BITOR"
 };
 
-void util_debug(const char *area, const char *ms, ...) {
-    va_list  va;
-    if (!OPTS_OPTION_BOOL(OPTION_DEBUG))
-        return;
-
-    if (!strcmp(area, "MEM") && !OPTS_OPTION_BOOL(OPTION_MEMCHK))
-        return;
-
-    va_start(va, ms);
-    con_out ("[%s] ", area);
-    con_vout(ms, va);
-    va_end  (va);
-}
-
 /*
  * only required if big endian .. otherwise no need to swap
  * data.