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.
Assembler:
Possibly support for a future assembler for QCASM. But we're not
entirely sure if it makes sense.
-
-
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;
}
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
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;
/* 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) {
ir_block *ontrue, *ontrue_out = NULL;
ir_block *onfalse, *onfalse_out = NULL;
ir_block *merge;
+ int fold = 0;
/* Ternary can never create an lvalue... */
if (lvalue)
return false;
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)
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'); /* = */
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);
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;
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
va_end (va);
}
+#ifndef QCVM_EXECUTOR
/* General error interface */
size_t compile_errors = 0;
size_t compile_warnings = 0;
--- /dev/null
+all:
+ $(MAKE) -f ../../archlinux/this/Makefile \
+ LIBC_DEPEND=libc \
+ DESTDIR=distro/archbsd/this
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
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)
@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)
@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:
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
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
%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
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
#include "gmqcc.h"
-opts_cmd_t opts; /* command line options */
static void loaderror(const char *fmt, ...)
{
int err = errno;
# 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;
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)
((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;
(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;
}
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;
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,
}
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;
}
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];
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;
}
/*#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;
}
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);
+}
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);
VINSTR_PHI,
VINSTR_JUMP,
VINSTR_COND,
+
/* A never returning CALL.
* Creating this causes IR blocks to be marked as 'final'.
* No-Return-Call
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 */
#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
}
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, ...) {
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;
}
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;
(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)
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:
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;
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 */
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 */
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];
continue;
}
}
-
code_push_statement(code, &stmt, instr->context);
}
return true;
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;
/* 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
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>";
}
}
}
if (ch == '+' || ch == '-' || /* ++, --, +=, -= and -> as well! */
- ch == '>' || ch == '<' || /* <<, >>, <=, >= */
+ ch == '>' || ch == '<' || /* <<, >>, <=, >= and >< as well! */
ch == '=' || ch == '!' || /* <=>, ==, != */
ch == '&' || ch == '|' || /* &&, ||, &=, |= */
ch == '~' || ch == '^' /* ~=, ~, ^ */
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);
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);
}
{ "*", 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},
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")) {
}
}
- 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++) {
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;
}
}
cleanup:
- util_debug("COM", "cleaning ...\n");
if (ftepp)
ftepp_finish(ftepp);
con_close();
lex_cleanup();
stat_info();
+ if (!retval && compile_errors)
+ retval = 1;
return retval;
}
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
--- /dev/null
+#!/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"
--- /dev/null
+#!/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!"
--- /dev/null
+#!/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!"
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);
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);
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 */
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;
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;
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;
}
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('?',':')) &&
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'):
}
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));
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'):
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],
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],
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
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
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
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
}
}
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 */
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;
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;
* 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()'
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",
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;
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,
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;
return false;
}
- if (var->expression.vtype != find->vtype) {
+ if (!ast_compare_type((ast_expression*)var, find)) {
char ty1[1024];
char ty2[1024];
ast_expression *(*intrin)(intrin_t *);
const char *name;
const char *alias;
+ size_t args;
} intrin_func_t;
struct intrin_s {
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);
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);
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.
}
vec_free(directories);
- util_debug("TEST", "compiled %d task template files out of %d\n",
- vec_size(task_tasks),
- found
- );
-
return success;
}
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);
}
}
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);
}
);
}
- util_debug("TEST", "executing qcvm: `%s` [%s]\n",
- tmpl->description,
- buffer
- );
-
execute = popen(buffer, "r");
if (!execute)
return false;
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.
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'));
}
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'
M: '0 0 16'
M: '29 42 51'
M: '0 8 16'
+M: '-24 44 -16'
"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.