From: Wolfgang Bumiller Date: Mon, 30 Sep 2013 13:02:03 +0000 (+0200) Subject: Merge branch 'master' into cooking X-Git-Tag: 0.3.5~56^2 X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=commitdiff_plain;h=fa14ca93d2b60f722d55c24214fde7ac84581ad0;hp=dfbd09334858e826a5cc4d1ca00d82461a3085cf Merge branch 'master' into cooking --- diff --git a/TODO b/TODO index 94163af..8652ed2 100644 --- 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 2f1566b..e1f86a6 100644 --- 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 f3d60ff..5703e50 100644 --- 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; diff --git a/conout.c b/conout.c index aedff4d..63391d6 100644 --- 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 index 0000000..afe5514 --- /dev/null +++ b/distro/archbsd/this/Makefile @@ -0,0 +1,4 @@ +all: + $(MAKE) -f ../../archlinux/this/Makefile \ + LIBC_DEPEND=libc \ + DESTDIR=distro/archbsd/this diff --git a/distro/archlinux/this/Makefile b/distro/archlinux/this/Makefile index fa863d1..6efdc00 100644 --- a/distro/archlinux/this/Makefile +++ b/distro/archlinux/this/Makefile @@ -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: diff --git a/distro/fedora/gmqcc.spec b/distro/fedora/gmqcc.spec index 78ea18d..9157bb4 100644 --- a/distro/fedora/gmqcc.spec +++ b/distro/fedora/gmqcc.spec @@ -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 - 0.3.0-2 +- Optimizing compile flags + +* Fri Sep 20 2013 Igor Gnatenko - 0.3.0-1 +- Update to 0.3.0 (improved new package: gmqpak) + * Sat Jul 27 2013 Igor Gnatenko - 0.2.9-1 - Initial release diff --git a/doc/gmqcc.1 b/doc/gmqcc.1 index a940f42..c825a41 100644 --- a/doc/gmqcc.1 +++ b/doc/gmqcc.1 @@ -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 1856248..c4de8ad 100644 --- 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 f8a143c..d51b765 100644 --- 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 86b0bff..9848f20 100644 --- 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 */ diff --git a/gmqcc.ini.example b/gmqcc.ini.example index 9dbb6fb..474fe3c 100644 --- a/gmqcc.ini.example +++ b/gmqcc.ini.example @@ -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'. @@ -516,13 +516,23 @@ 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 @@ -553,7 +563,6 @@ 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 @@ -563,7 +572,7 @@ 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‐ @@ -636,6 +645,7 @@ 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 diff --git a/intrin.c b/intrin.c index 7e7e69e..d713df0 100644 --- 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 3efad3d..f33e48c 100644 --- 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 ""; } } diff --git a/lexer.c b/lexer.c index af7819d..7642d26 100644 --- 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 48ab4b6..24e29dd 100644 --- 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 eab582d..9a76706 100644 --- 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; } diff --git a/misc/check-doc.sh b/misc/check-doc.sh index e5ffdee..da0c4cf 100755 --- a/misc/check-doc.sh +++ b/misc/check-doc.sh @@ -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 index 0000000..36b3774 --- /dev/null +++ b/misc/check-proj.sh @@ -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 index 0000000..a07a7af --- /dev/null +++ b/misc/nexuiz_export.sh @@ -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 index 0000000..5cd3921 --- /dev/null +++ b/misc/xonotic_export.sh @@ -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 4f6660e..9aa8f63 100644 --- 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); diff --git a/opts.def b/opts.def index 977272a..f4d01e6 100644 --- 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 @@ -129,6 +130,7 @@ 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 */ diff --git a/parser.c b/parser.c index 1cd74dd..15bb921 100644 --- 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 '' */ + 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 '' */ + 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]; diff --git a/parser.h b/parser.h index d4c8ddd..54f93b2 100644 --- 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 4c14931..fdf3777 100644 --- 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. diff --git a/tests/vec_ops.qc b/tests/vec_ops.qc index 509dc3a..d06d579 100644 --- a/tests/vec_ops.qc +++ b/tests/vec_ops.qc @@ -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')); } diff --git a/tests/vec_ops.tmpl b/tests/vec_ops.tmpl index 7943232..738ec46 100644 --- a/tests/vec_ops.tmpl +++ b/tests/vec_ops.tmpl @@ -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 6a701e4..9b32e48 100644 --- 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.