From e006aa82388218f0f18e93087217d1aa0c2cee83 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Sun, 14 Jan 2018 09:32:53 +0100 Subject: [PATCH 01/16] Revert "search for funciton param first before function locals, this fixes #163" This reverts commit 3cf2c52fce792af3e5cc5578b336de83217e625d. --- parser.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/parser.cpp b/parser.cpp index 9bfd751..1404273 100644 --- a/parser.cpp +++ b/parser.cpp @@ -135,22 +135,17 @@ static ast_expression* parser_find_local(parser_t *parser, const char *name, siz { size_t i, hash; ast_expression *e; - ast_expression *p; hash = util_hthash(parser->htglobals, name); *isparam = false; - p = parser_find_param(parser, name); - if (p) { - *isparam = true; - return p; - } for (i = parser->variables.size(); i > upto;) { --i; if ( (e = (ast_expression*)util_htgeth(parser->variables[i], name, hash)) ) return e; } - return NULL; + *isparam = true; + return parser_find_param(parser, name); } static ast_expression* parser_find_local(parser_t *parser, const std::string &name, size_t upto, bool *isparam) { -- 2.39.2 From e920766b10ebed3fc7fad0373aab6dde507e2341 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Sun, 14 Jan 2018 09:31:45 +0100 Subject: [PATCH 02/16] Make parser_find_local only actually search locals Fixes #163 Signed-off-by: Wolfgang Bumiller --- parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.cpp b/parser.cpp index 1404273..c795e40 100644 --- a/parser.cpp +++ b/parser.cpp @@ -156,7 +156,7 @@ static ast_expression* parser_find_var(parser_t *parser, const char *name) { bool dummy; ast_expression *v; - v = parser_find_local(parser, name, 0, &dummy); + v = parser_find_local(parser, name, PARSER_HT_LOCALS, &dummy); if (!v) v = parser_find_global(parser, name); return v; } -- 2.39.2 From f84c8ea62922f20b13e54916fac849ade0886b66 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Sun, 14 Jan 2018 09:10:29 +0100 Subject: [PATCH 03/16] add variable search order test Signed-off-by: Wolfgang Bumiller --- tests/var-search-order.qc | 18 ++++++++++++++++++ tests/var-search-order.tmpl | 8 ++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/var-search-order.qc create mode 100644 tests/var-search-order.tmpl diff --git a/tests/var-search-order.qc b/tests/var-search-order.qc new file mode 100644 index 0000000..50b6746 --- /dev/null +++ b/tests/var-search-order.qc @@ -0,0 +1,18 @@ +float a = 1000; +float b = 1001; +float c = 1002; + +void test(float b) { + float c = 3002; + print(ftos(a), "\n"); + print(ftos(b), "\n"); + print(ftos(c), "\n"); + { + float b = 4001; + print(ftos(b), "\n"); + } +} + +void main() { + test(2001); +} diff --git a/tests/var-search-order.tmpl b/tests/var-search-order.tmpl new file mode 100644 index 0000000..db3a4bf --- /dev/null +++ b/tests/var-search-order.tmpl @@ -0,0 +1,8 @@ +I: var-search-order.qc +D: test variable search order +T: -execute +C: -std=gmqcc +M: 1000 +M: 2001 +M: 3002 +M: 4001 -- 2.39.2 From 97a74eb677071f3a73a07f940cc5c6ea1c9b2e4b Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Sun, 14 Jan 2018 10:58:29 +0100 Subject: [PATCH 04/16] catch broken vector member access These kinds of expressions currently cannot be handled without pionter support in the qcvm without scanning the ast from within ast_member::codegen for an assignments as seen in the added test case. This change makes code like that return a pointer type which will cause an error that we did not get a vector or field back. With pointer support this pointer could actually be used instead. So at least it shouldn't silently produce broken code anymore. Signed-off-by: Wolfgang Bumiller --- ast.cpp | 18 ++++++++++++++---- tests/vecfields-broken.tmpl | 4 ++++ tests/vecfields.qc | 4 ++++ 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 tests/vecfields-broken.tmpl diff --git a/ast.cpp b/ast.cpp index cbc5db9..1ac0a44 100644 --- a/ast.cpp +++ b/ast.cpp @@ -1932,6 +1932,7 @@ bool ast_binstore::codegen(ast_function *func, bool lvalue, ir_value **out) if (!idx->codegen(func, false, &iridx)) return false; } + if (!m_dest->codegen(func, false, &leftr)) return false; @@ -2153,19 +2154,28 @@ bool ast_member::codegen(ast_function *func, bool lvalue, ir_value **out) else m_outr = *out; return (*out != nullptr); - } else { - if (!m_owner->codegen(func, false, &vec)) - return false; } + // Vector member access + if (!m_owner->codegen(func, lvalue, &vec)) + return false; + if (vec->m_vtype != TYPE_VECTOR && !(vec->m_vtype == TYPE_FIELD && m_owner->m_next->m_vtype == TYPE_VECTOR)) { + compile_error(m_context, "vector member produced neither vector nor field"); return false; } *out = vec->vectorMember(m_field); - m_outl = *out; + if (!*out) { + compile_error(m_context, "internal error: failed to create vector member access"); + return false; + } + if (lvalue) + m_outl = *out; + else + m_outr = *out; return (*out != nullptr); } diff --git a/tests/vecfields-broken.tmpl b/tests/vecfields-broken.tmpl new file mode 100644 index 0000000..59f968b --- /dev/null +++ b/tests/vecfields-broken.tmpl @@ -0,0 +1,4 @@ +I: vecfields.qc +D: vector field member accesses +T: -fail +C: -std=gmqcc -fftepp -DBROKEN_ACCESS diff --git a/tests/vecfields.qc b/tests/vecfields.qc index 6cc053a..815de98 100644 --- a/tests/vecfields.qc +++ b/tests/vecfields.qc @@ -10,4 +10,8 @@ void main() { e.v1 = '1 2 3'; print(ftos(set(e, 42)), " => "); print(vtos(e.v1), "\n"); + +#ifdef BROKEN_ACCESS + (e.v1 = '0 0 0').x += 1; +#endif } -- 2.39.2 From 9a21c638fab848862c5c421f2f80c331d56dda3d Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Sat, 5 May 2018 15:38:12 -0400 Subject: [PATCH 05/16] error if a function is called from global scope opposed to crashing --- parser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/parser.cpp b/parser.cpp index c795e40..cc2b358 100644 --- a/parser.cpp +++ b/parser.cpp @@ -1188,6 +1188,12 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) static bool parser_close_call(parser_t *parser, shunt *sy) { + if (!parser->function) + { + parseerror(parser, "cannot call functions from global scope"); + return false; + } + /* was a function call */ ast_expression *fun; ast_value *funval = nullptr; -- 2.39.2 From dac058107a33615a9279ed0ab73dadfc2fea5fd1 Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Sat, 5 May 2018 15:44:21 -0400 Subject: [PATCH 06/16] Delete .travis.yml --- .travis.yml | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 830f373..0000000 --- a/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -language: cpp - -compiler: - - gcc - - clang - -before_install: - # g++4.8.1 - - if [ "$CXX" == "g++" ]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi - # clang 3.4 - - if [ "$CXX" == "clang++" ]; then sudo add-apt-repository -y ppa:h-rayflood/llvm; fi - - sudo apt-get update -qq - -install: - # g++4.8.1 - - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi - - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8"; fi - # clang 3.4 - - if [ "$CXX" == "clang++" ]; then sudo apt-get install --allow-unauthenticated -qq clang-3.4; fi - - if [ "$CXX" == "clang++" ]; then export CXX="clang++-3.4"; fi - -script: - - make test - -notifications: - irc: - channels: - - "irc.freenode.org#gmqcc" - template: - - "[%{commit} : %{author}] %{message}" - - "%{build_url}" - skip_join: true -- 2.39.2 From 092067482fddeccf1b3e42ff09a046f6555cd11e Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Wed, 9 May 2018 21:18:37 -0400 Subject: [PATCH 07/16] added -fdefault-eraseable which is the same as adding [[eraseable]] to all definitions instead the opposite behavior can be controlled with [[noerase]] attribute --- ast.cpp | 12 +++++++----- ast.h | 13 +++++++------ doc/gmqcc.1 | 7 +++++++ gmqcc.ini.example | 8 ++++++++ opts.def | 1 + parser.cpp | 5 ++--- 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/ast.cpp b/ast.cpp index 1ac0a44..7b444f8 100644 --- a/ast.cpp +++ b/ast.cpp @@ -37,6 +37,8 @@ ast_expression::ast_expression(lex_ctx_t ctx, int nodetype, qc_type type) { if (OPTS_OPTION_BOOL(OPTION_COVERAGE)) m_flags |= AST_FLAG_BLOCK_COVERAGE; + if (OPTS_FLAG(DEFAULT_ERASEABLE)) + m_flags |= AST_FLAG_ERASEABLE; } ast_expression::ast_expression(lex_ctx_t ctx, int nodetype) : ast_expression(ctx, nodetype, TYPE_VOID) @@ -1130,7 +1132,7 @@ bool ast_value::generateGlobal(ir_builder *ir, bool isfield) if (m_flags & AST_FLAG_INCLUDE_DEF) m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF; - if (m_flags & AST_FLAG_ERASEABLE) + if (m_flags & AST_FLAG_ERASEABLE && !(m_flags & AST_FLAG_NOERASE)) m_ir_v->m_flags |= IR_FLAG_ERASABLE; if (m_flags & AST_FLAG_NOREF) m_ir_v->m_flags |= IR_FLAG_NOREF; @@ -1194,7 +1196,7 @@ bool ast_value::generateGlobalFunction(ir_builder *ir) m_ir_v = func->m_value; if (m_flags & AST_FLAG_INCLUDE_DEF) m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF; - if (m_flags & AST_FLAG_ERASEABLE) + if (m_flags & AST_FLAG_ERASEABLE && !(m_flags & AST_FLAG_NOERASE)) m_ir_v->m_flags |= IR_FLAG_ERASABLE; if (m_flags & AST_FLAG_BLOCK_COVERAGE) func->m_flags |= IR_FLAG_BLOCK_COVERAGE; @@ -1236,7 +1238,7 @@ bool ast_value::generateGlobalField(ir_builder *ir) if (m_flags & AST_FLAG_INCLUDE_DEF) m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF; - if (m_flags & AST_FLAG_ERASEABLE) + if (m_flags & AST_FLAG_ERASEABLE && !(m_flags & AST_FLAG_NOERASE)) m_ir_v->m_flags |= IR_FLAG_ERASABLE; if (m_flags & AST_FLAG_NOREF) m_ir_v->m_flags |= IR_FLAG_NOREF; @@ -1272,7 +1274,7 @@ bool ast_value::generateGlobalField(ir_builder *ir) m_ir_v = v; if (m_flags & AST_FLAG_INCLUDE_DEF) m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF; - if (m_flags & AST_FLAG_ERASEABLE) + if (m_flags & AST_FLAG_ERASEABLE && !(m_flags & AST_FLAG_NOERASE)) m_ir_v->m_flags |= IR_FLAG_ERASABLE; if (m_flags & AST_FLAG_NOREF) m_ir_v->m_flags |= IR_FLAG_NOREF; @@ -1305,7 +1307,7 @@ ir_value *ast_value::prepareGlobalArray(ir_builder *ir) if (m_flags & AST_FLAG_INCLUDE_DEF) v->m_flags |= IR_FLAG_INCLUDE_DEF; - if (m_flags & AST_FLAG_ERASEABLE) + if (m_flags & AST_FLAG_ERASEABLE && !(m_flags & AST_FLAG_NOERASE)) v->m_flags |= IR_FLAG_ERASABLE; if (m_flags & AST_FLAG_NOREF) v->m_flags |= IR_FLAG_NOREF; diff --git a/ast.h b/ast.h index 04fa4d8..2459729 100644 --- a/ast.h +++ b/ast.h @@ -43,14 +43,15 @@ enum { AST_FLAG_IS_VARARG = 1 << 6, AST_FLAG_ALIAS = 1 << 7, AST_FLAG_ERASEABLE = 1 << 8, - AST_FLAG_ACCUMULATE = 1 << 9, + AST_FLAG_NOERASE = 1 << 9, /* Never allow it to be erased, even if ERASEABLE is present */ + AST_FLAG_ACCUMULATE = 1 << 10, /* An array declared as [] * so that the size is taken from the initializer */ - AST_FLAG_ARRAY_INIT = 1 << 10, + AST_FLAG_ARRAY_INIT = 1 << 11, - AST_FLAG_FINAL_DECL = 1 << 11, + AST_FLAG_FINAL_DECL = 1 << 12, /* Several coverage options * AST_FLAG_COVERAGE means there was an explicit [[coverage]] attribute, @@ -59,14 +60,14 @@ enum { * In the future there might be more options like tracking variable access * by creating get/set wrapper functions. */ - AST_FLAG_COVERAGE = 1 << 12, - AST_FLAG_BLOCK_COVERAGE = 1 << 13, + AST_FLAG_COVERAGE = 1 << 13, + AST_FLAG_BLOCK_COVERAGE = 1 << 14, /* * Propagates norefness to the IR so the unused (read/write) check can be * more intelligently done. */ - AST_FLAG_NOREF = 1 << 14, + AST_FLAG_NOREF = 1 << 15, AST_FLAG_LAST, AST_FLAG_TYPE_MASK = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN), diff --git a/doc/gmqcc.1 b/doc/gmqcc.1 index 510e754..f392cc3 100644 --- a/doc/gmqcc.1 +++ b/doc/gmqcc.1 @@ -628,6 +628,13 @@ after all limited to 64k. There's at least one known codebase where this lowers the number of globals from over 80k down to around 3k. In other code bases it doesn't reduce the globals at all but only increases code size. Just try it and see whether it helps you. +.It Fl f Ns Cm default-eraseable +Force all expressions to be "eraseable" which permits the compiler to +remove unused functions, variables and statements. This is equivlant to +putting [[eraseable]] on all definitions. This is dangerous as it breaks +auto cvars, definitions for functions the engine may be looking for and +translatable strings. Instead, you can mark a definition with [[noerase]] +to prevent this from happening. .El .Sh OPTIMIZATIONS .Bl -tag -width Ds diff --git a/gmqcc.ini.example b/gmqcc.ini.example index 5c1ad37..2b4920c 100644 --- a/gmqcc.ini.example +++ b/gmqcc.ini.example @@ -346,6 +346,14 @@ #expense of additional instructions. SPLIT_VECTOR_PARAMETERS = false + #Force all expressions to be "eraseable" which permits the compiler + #to remove unused functions, variables and statements. This is + #equivlant to putting [[eraseable]] on all definitions. This is + #dangerous as it breaks auto cvars, definitions for functions the + #engine may be looking for and translatable strings. Instead, you + #can mark a definition with [[noerase]] to prevent this from happening. + DEFAULT_ERASEABLE = false + [warnings] #Generate a warning about variables which are declared but never #used. This can be avoided by adding the ‘noref’ keyword in front diff --git a/opts.def b/opts.def index 2dc4fb7..5addc3c 100644 --- a/opts.def +++ b/opts.def @@ -37,6 +37,7 @@ GMQCC_DEFINE_FLAG(EMULATE_STATE) GMQCC_DEFINE_FLAG(ARITHMETIC_EXCEPTIONS) GMQCC_DEFINE_FLAG(SPLIT_VECTOR_PARAMETERS) + GMQCC_DEFINE_FLAG(DEFAULT_ERASEABLE) #endif /* warning flags */ diff --git a/parser.cpp b/parser.cpp index cc2b358..9345760 100644 --- a/parser.cpp +++ b/parser.cpp @@ -2763,6 +2763,7 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * { "noreturn", AST_FLAG_NORETURN }, { "inline", AST_FLAG_INLINE }, { "eraseable", AST_FLAG_ERASEABLE }, + { "noerase", AST_FLAG_NOERASE }, { "accumulate", AST_FLAG_ACCUMULATE }, { "last", AST_FLAG_FINAL_DECL } }; @@ -2796,7 +2797,6 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * if (i != GMQCC_ARRAY_COUNT(attributes)) goto leave; - if (!strcmp(parser_tokval(parser), "noref")) { had_noref = true; if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { @@ -5197,8 +5197,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield * store the vstring back to var for alias and * deprecation messages. */ - if (var->m_flags & AST_FLAG_DEPRECATED || - var->m_flags & AST_FLAG_ALIAS) + if (var->m_flags & AST_FLAG_DEPRECATED || var->m_flags & AST_FLAG_ALIAS) var->m_desc = vstring; if (parser_find_global(parser, var->m_name) && var->m_flags & AST_FLAG_ALIAS) { -- 2.39.2 From 0904a1ceb7bc4da1adde4604e4c1e207adc48851 Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Sat, 1 Sep 2018 00:48:18 -0400 Subject: [PATCH 08/16] fixes for progs.src --- Makefile | 2 ++ main.cpp | 63 +++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 3566bdc..f227d36 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,8 @@ CXXFLAGS = \ -std=c++11 \ -Wall \ -Wextra \ + -Wno-parentheses \ + -Wno-class-memaccess \ -fno-exceptions \ -fno-rtti \ -MD \ diff --git a/main.cpp b/main.cpp index 76c1d31..1080f8f 100644 --- a/main.cpp +++ b/main.cpp @@ -117,7 +117,7 @@ static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, cha return options_long_witharg_all(optname, argc_, argv_, out, 1, false); } -static bool options_parse(int argc, char **argv) { +static bool options_parse(int argc, char **argv, bool *has_progs_src) { bool argend = false; size_t itr; char buffer[1024]; @@ -202,6 +202,7 @@ static bool options_parse(int argc, char **argv) { } if (options_long_gcc("progsrc", &argc, &argv, &argarg)) { OPTS_OPTION_STR(OPTION_PROGSRC) = argarg; + *has_progs_src = true; continue; } @@ -434,6 +435,9 @@ static bool options_parse(int argc, char **argv) { } item.filename = argarg; vec_push(items, item); + if (item.type == TYPE_SRC) { + *has_progs_src = true; + } break; case '-': @@ -515,13 +519,13 @@ static bool progs_nextline(char **out, size_t *alen, FILE *src) { } int main(int argc, char **argv) { - size_t itr; - int retval = 0; - bool operators_free = false; - bool progs_src = false; - FILE *outfile = nullptr; - parser_t *parser = nullptr; - ftepp_t *ftepp = nullptr; + size_t itr; + int retval = 0; + bool operators_free = false; + bool has_progs_src = false; + FILE *outfile = nullptr; + parser_t *parser = nullptr; + ftepp_t *ftepp = nullptr; app_name = argv[0]; con_init (); @@ -529,7 +533,7 @@ int main(int argc, char **argv) { util_seed(time(0)); - if (!options_parse(argc, argv)) { + if (!options_parse(argc, argv, &has_progs_src)) { return usage(); } @@ -623,13 +627,19 @@ int main(int argc, char **argv) { } } - if (!vec_size(items)) { - FILE *src; - char *line = nullptr; - size_t linelen = 0; - bool hasline = false; + if (!vec_size(items) && !has_progs_src) { + FILE *fp = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb"); + if (fp) { + has_progs_src = true; + } + fclose(fp); + } - progs_src = true; + if (has_progs_src) { + FILE *src; + char *line = nullptr; + size_t linelen = 0; + bool has_first_line = false; src = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb"); if (!src) { @@ -641,16 +651,19 @@ int main(int argc, char **argv) { while (progs_nextline(&line, &linelen, src)) { argitem item; - if (!line[0] || (line[0] == '/' && line[1] == '/')) + if (!line[0] || (line[0] == '/' && line[1] == '/')) { continue; + } - if (hasline) { + if (has_first_line) { item.filename = util_strdup(line); - item.type = TYPE_QC; + item.type = TYPE_QC; vec_push(items, item); - } else if (!opts_output_wasset) { - OPTS_OPTION_DUP(OPTION_OUTPUT) = util_strdup(line); - hasline = true; + } else { + if (!opts_output_wasset) { + OPTS_OPTION_DUP(OPTION_OUTPUT) = util_strdup(line); + } + has_first_line = true; } } @@ -662,7 +675,7 @@ int main(int argc, char **argv) { if (!OPTS_OPTION_BOOL(OPTION_QUIET) && !OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { - con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual")); + con_out("Mode: %s\n", (has_progs_src ? "progs.src" : "manual")); con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items)); } @@ -678,6 +691,10 @@ int main(int argc, char **argv) { ("unknown")))))); } + if (items[itr].type == TYPE_SRC) { + continue; + } + if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { const char *out; if (!ftepp_preprocess_file(ftepp, items[itr].filename)) { @@ -713,7 +730,7 @@ int main(int argc, char **argv) { } } - if (progs_src) { + if (has_progs_src) { mem_d(items[itr].filename); items[itr].filename = nullptr; } -- 2.39.2 From 2d99ce609dfae77f31f791766f9186382b233525 Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Tue, 30 Oct 2018 17:32:21 -0400 Subject: [PATCH 09/16] fix octals --- lexer.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lexer.cpp b/lexer.cpp index 34fc71b..1007948 100644 --- a/lexer.cpp +++ b/lexer.cpp @@ -831,6 +831,7 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote) static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch) { bool ishex = false; + bool isoct = false; int ch = lastch; @@ -843,7 +844,16 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch) lex_tokench(lex, ch); ch = lex_getch(lex); - if (ch != '.' && !util_isdigit(ch)) + + if (lastch == '0' && util_isdigit(ch)) { + if (ch < '0' || ch > '7') { + lexerror(lex, "invalid octal constant"); + return (lex->tok.ttype = TOKEN_ERROR); + } + isoct = true; + } + + if (!isoct && ch != '.' && !util_isdigit(ch)) { if (lastch != '0' || ch != 'x') { @@ -898,10 +908,15 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch) lex_ungetch(lex, ch); lex_endtoken(lex); - if (lex->tok.ttype == TOKEN_FLOATCONST) + if (lex->tok.ttype == TOKEN_FLOATCONST) { lex->tok.constval.f = strtod(lex->tok.value, nullptr); - else - lex->tok.constval.i = strtol(lex->tok.value, nullptr, 0); + } else { + /* determine base for strtol */ + int base = 10; + if (ishex) base = 16; + if (isoct) base = 8; + lex->tok.constval.i = strtol(lex->tok.value, nullptr, base); + } return lex->tok.ttype; } -- 2.39.2 From 620bd76e76804b1fc1de46daa03724e23339e8bf Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Wed, 14 Nov 2018 08:43:22 -0500 Subject: [PATCH 10/16] fix __builtin_nan and add some missing builtins --- fold.cpp | 63 ++++++++++++++++++++++++++++++++++++++---------------- fold.h | 8 +++++-- intrin.cpp | 6 +++--- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/fold.cpp b/fold.cpp index 7b13db6..a982bf6 100644 --- a/fold.cpp +++ b/fold.cpp @@ -1486,29 +1486,54 @@ ast_expression *fold::intrinsic_pow(ast_value *lhs, ast_value *rhs) { ast_expression *fold::intrinsic_fabs(ast_value *a) { return constgen_float(fabsf(immvalue_float(a)), false); } +ast_expression* fold::intrinsic_nan(void) { + return constgen_float(0.0f / 0.0f, false); +} +ast_expression* fold::intrinsic_epsilon(void) { + static bool calculated = false; + static float eps = 1.0f; + if (!calculated) { + do { + eps /= 2.0f; + } while ((1.0f + (eps / 2.0f)) != 1.0f); + calculated = true; + } + return constgen_float(eps, false); +} -ast_expression *fold::intrinsic(const char *intrinsic, ast_expression **arg) { +ast_expression* fold::intrinsic_inf(void) { + return constgen_float(1.0f / 0.0f, false); +} + +ast_expression *fold::intrinsic(const char *intrinsic, size_t n_args, ast_expression **args) { ast_expression *ret = nullptr; - ast_value *a = (ast_value*)arg[0]; - ast_value *b = (ast_value*)arg[1]; - - if (!strcmp(intrinsic, "isfinite")) ret = intrinsic_isfinite(a); - if (!strcmp(intrinsic, "isinf")) ret = intrinsic_isinf(a); - if (!strcmp(intrinsic, "isnan")) ret = intrinsic_isnan(a); - if (!strcmp(intrinsic, "isnormal")) ret = intrinsic_isnormal(a); - if (!strcmp(intrinsic, "signbit")) ret = intrinsic_signbit(a); - if (!strcmp(intrinsic, "acosh")) ret = intrinsic_acosh(a); - if (!strcmp(intrinsic, "asinh")) ret = intrinsic_asinh(a); - if (!strcmp(intrinsic, "atanh")) ret = intrinsic_atanh(a); - if (!strcmp(intrinsic, "exp")) ret = intrinsic_exp(a); - if (!strcmp(intrinsic, "exp2")) ret = intrinsic_exp2(a); - if (!strcmp(intrinsic, "expm1")) ret = intrinsic_expm1(a); - if (!strcmp(intrinsic, "mod")) ret = intrinsic_mod(a, b); - if (!strcmp(intrinsic, "pow")) ret = intrinsic_pow(a, b); - if (!strcmp(intrinsic, "fabs")) ret = intrinsic_fabs(a); - if (ret) + if (n_args) { + ast_value *a = (ast_value*)args[0]; + ast_value *b = (ast_value*)args[1]; + if (!strcmp(intrinsic, "isfinite")) ret = intrinsic_isfinite(a); + if (!strcmp(intrinsic, "isinf")) ret = intrinsic_isinf(a); + if (!strcmp(intrinsic, "isnan")) ret = intrinsic_isnan(a); + if (!strcmp(intrinsic, "isnormal")) ret = intrinsic_isnormal(a); + if (!strcmp(intrinsic, "signbit")) ret = intrinsic_signbit(a); + if (!strcmp(intrinsic, "acosh")) ret = intrinsic_acosh(a); + if (!strcmp(intrinsic, "asinh")) ret = intrinsic_asinh(a); + if (!strcmp(intrinsic, "atanh")) ret = intrinsic_atanh(a); + if (!strcmp(intrinsic, "exp")) ret = intrinsic_exp(a); + if (!strcmp(intrinsic, "exp2")) ret = intrinsic_exp2(a); + if (!strcmp(intrinsic, "expm1")) ret = intrinsic_expm1(a); + if (!strcmp(intrinsic, "mod")) ret = intrinsic_mod(a, b); + if (!strcmp(intrinsic, "pow")) ret = intrinsic_pow(a, b); + if (!strcmp(intrinsic, "fabs")) ret = intrinsic_fabs(a); + } else { + if (!strcmp(intrinsic, "nan")) ret = intrinsic_nan(); + if (!strcmp(intrinsic, "epsilon")) ret = intrinsic_epsilon(); + if (!strcmp(intrinsic, "inf")) ret = intrinsic_inf(); + } + + if (ret) { ++opts_optimizationcount[OPTIM_CONST_FOLD]; + } return ret; } diff --git a/fold.h b/fold.h index 1d096e8..693231f 100644 --- a/fold.h +++ b/fold.h @@ -21,7 +21,7 @@ struct fold { bool generate(ir_builder *ir); ast_expression *op(const oper_info *info, ast_expression **opexprs); - ast_expression *intrinsic(const char *intrinsic, ast_expression **arg); + ast_expression *intrinsic(const char *intrinsic, size_t n_args, ast_expression **args); static int cond_ternary(ir_value *condval, ast_function *func, ast_ternary *branch); static int cond_ifthen(ir_value *condval, ast_function *func, ast_ifthen *branch); @@ -83,10 +83,14 @@ protected: ast_expression *intrinsic_exp(ast_value *a); ast_expression *intrinsic_exp2(ast_value *a); ast_expression *intrinsic_expm1(ast_value *a); - ast_expression *intrinsic_mod(ast_value *lhs, ast_value *rhs); ast_expression *intrinsic_pow(ast_value *lhs, ast_value *rhs); + ast_expression *intrinsic_mod(ast_value *lhs, ast_value *rhs); ast_expression *intrinsic_fabs(ast_value *a); + ast_expression* intrinsic_nan(void); + ast_expression* intrinsic_epsilon(void); + ast_expression* intrinsic_inf(void); + static qcfloat_t immvalue_float(ir_value *value); static vec3_t immvalue_vector(ir_value *value); diff --git a/intrin.cpp b/intrin.cpp index b37b50f..8053b31 100644 --- a/intrin.cpp +++ b/intrin.cpp @@ -32,8 +32,6 @@ void intrin::reg(ast_value *const value, ast_function *const func) { m_parser->globals.push_back(value); } -#define QC_POW_EPSILON 0.00001f - ast_expression *intrin::nullfunc() { ast_value *val = nullptr; ast_function *func = value(&val, nullptr, TYPE_VOID); @@ -575,6 +573,8 @@ ast_expression *intrin::expm1_() { } ast_expression *intrin::pow_() { + #define QC_POW_EPSILON 0.00001f + /* * * float pow(float base, float exp) { @@ -2005,7 +2005,7 @@ ast_expression *intrin::do_fold(ast_value *val, ast_expression **exprs) { if (val->m_name == it.name) return (vec_size(exprs) != it.args) ? nullptr - : m_fold->intrinsic(val->m_name.c_str() + kPrefixLength, exprs); + : m_fold->intrinsic(val->m_name.c_str() + kPrefixLength, it.args, exprs); } return nullptr; } -- 2.39.2 From 9c81ff263aa3f38252bdfe704e5051431e30aefa Mon Sep 17 00:00:00 2001 From: divVerent Date: Mon, 4 Feb 2019 06:14:58 -0800 Subject: [PATCH 11/16] Fix printing of floating poing values in -dumpfin. %g is not lossless for single precision floats - %.9g is (other than distinguishing NaNs, who cares). --- ir.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ir.cpp b/ir.cpp index facbc33..8388aab 100644 --- a/ir.cpp +++ b/ir.cpp @@ -4054,10 +4054,11 @@ void ir_value::dump(int (*oprintf)(const char*, ...)) const oprintf("fn:%s", m_name.c_str()); break; case TYPE_FLOAT: - oprintf("%g", m_constval.vfloat); + // %.9g is lossless for IEEE single precision. + oprintf("%.9g", m_constval.vfloat); break; case TYPE_VECTOR: - oprintf("'%g %g %g'", + oprintf("'%.9g %.9g %.9g'", m_constval.vvec.x, m_constval.vvec.y, m_constval.vvec.z); -- 2.39.2 From 031f827da5457ed969bbf66e520105f9600a1229 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Sun, 15 Sep 2019 10:06:53 +0200 Subject: [PATCH 12/16] introduce another vinstr temp Some vinstrs are currently broken when using peephole optimization as they appear as writing to a temporary ssa output before being stored into their real destination, causing the store to be optimized out, but the generated code relies on having the destination as another temporary value available. Let's just add a 2nd temp to be used in those cases. Signed-off-by: Wolfgang Bumiller --- ir.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ir.h b/ir.h index 42f6ce5..c81d1a2 100644 --- a/ir.h +++ b/ir.h @@ -249,7 +249,7 @@ ir_block* ir_function_create_block(lex_ctx_t ctx, ir_function*, const char /* builder */ #define IR_HT_SIZE 1024 -#define IR_MAX_VINSTR_TEMPS 1 +#define IR_MAX_VINSTR_TEMPS 2 struct ir_builder { ir_builder(const std::string& modulename); -- 2.39.2 From 2d4a054440e9fdf12edc202188ae4ea2e4ce90b5 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Sun, 15 Sep 2019 10:07:06 +0200 Subject: [PATCH 13/16] ir: fix generation of multi-op vinstrs Do not assume that the destination is a temporary location, as our peephole optimizer will break this. For example, the following IR code (generated via from `x ^= gety()`): (0) binst6 <- BITXOR x, call5 (0) x <- STORE_F binst6 after peephole optimization becomes: (7) x <- BITXOR x, call5 Therefore we cannot assume that the output of the virtual xor instruction can be utilized as a temporary value. BITXOR becomes `(x | y) - (x & y)`, which would wrongly be generated as: x = x | y; temp0 = x & y; x = x - temp0; While this particular case can be fixed by using temp0 first and then x, the cross-product case would not be so simple. Signed-off-by: Wolfgang Bumiller Fixes #190 --- ir.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ir.cpp b/ir.cpp index 8388aab..0f7989f 100644 --- a/ir.cpp +++ b/ir.cpp @@ -2515,7 +2515,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.opcode = INSTR_BITOR; stmt.o1.s1 = instr->_m_ops[1]->codeAddress(); stmt.o2.s1 = instr->_m_ops[2]->codeAddress(); - stmt.o3.s1 = instr->_m_ops[0]->codeAddress(); + stmt.o3.s1 = func->m_owner->m_vinstr_temp[1]->codeAddress(); code_push_statement(code, &stmt, instr->m_context); stmt.opcode = INSTR_BITAND; stmt.o1.s1 = instr->_m_ops[1]->codeAddress(); @@ -2523,7 +2523,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.o3.s1 = func->m_owner->m_vinstr_temp[0]->codeAddress(); code_push_statement(code, &stmt, instr->m_context); stmt.opcode = INSTR_SUB_F; - stmt.o1.s1 = instr->_m_ops[0]->codeAddress(); + stmt.o1.s1 = func->m_owner->m_vinstr_temp[1]->codeAddress(); stmt.o2.s1 = func->m_owner->m_vinstr_temp[0]->codeAddress(); stmt.o3.s1 = instr->_m_ops[0]->codeAddress(); code_push_statement(code, &stmt, instr->m_context); @@ -2575,7 +2575,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.opcode = INSTR_BITOR; stmt.o1.s1 = instr->_m_ops[1]->codeAddress() + j; stmt.o2.s1 = instr->_m_ops[2]->codeAddress() + j; - stmt.o3.s1 = instr->_m_ops[0]->codeAddress() + j; + stmt.o3.s1 = func->m_owner->m_vinstr_temp[1]->codeAddress() + j; code_push_statement(code, &stmt, instr->m_context); stmt.opcode = INSTR_BITAND; stmt.o1.s1 = instr->_m_ops[1]->codeAddress() + j; @@ -2584,7 +2584,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc code_push_statement(code, &stmt, instr->m_context); } stmt.opcode = INSTR_SUB_V; - stmt.o1.s1 = instr->_m_ops[0]->codeAddress(); + stmt.o1.s1 = func->m_owner->m_vinstr_temp[1]->codeAddress(); stmt.o2.s1 = func->m_owner->m_vinstr_temp[0]->codeAddress(); stmt.o3.s1 = instr->_m_ops[0]->codeAddress(); code_push_statement(code, &stmt, instr->m_context); @@ -2632,7 +2632,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc stmt.opcode = INSTR_BITOR; stmt.o1.s1 = instr->_m_ops[1]->codeAddress() + j; stmt.o2.s1 = instr->_m_ops[2]->codeAddress(); - stmt.o3.s1 = instr->_m_ops[0]->codeAddress() + j; + stmt.o3.s1 = func->m_owner->m_vinstr_temp[1]->codeAddress() + j; code_push_statement(code, &stmt, instr->m_context); stmt.opcode = INSTR_BITAND; stmt.o1.s1 = instr->_m_ops[1]->codeAddress() + j; @@ -2641,7 +2641,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc code_push_statement(code, &stmt, instr->m_context); } stmt.opcode = INSTR_SUB_V; - stmt.o1.s1 = instr->_m_ops[0]->codeAddress(); + stmt.o1.s1 = func->m_owner->m_vinstr_temp[1]->codeAddress(); stmt.o2.s1 = func->m_owner->m_vinstr_temp[0]->codeAddress(); stmt.o3.s1 = instr->_m_ops[0]->codeAddress(); code_push_statement(code, &stmt, instr->m_context); @@ -2655,7 +2655,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc for (j = 0; j < 3; ++j) { stmt.o1.s1 = instr->_m_ops[1]->codeAddress() + (j + 1) % 3; stmt.o2.s1 = instr->_m_ops[2]->codeAddress() + (j + 2) % 3; - stmt.o3.s1 = instr->_m_ops[0]->codeAddress() + j; + stmt.o3.s1 = func->m_owner->m_vinstr_temp[1]->codeAddress() + j; code_push_statement(code, &stmt, instr->m_context); stmt.o1.s1 = instr->_m_ops[1]->codeAddress() + (j + 2) % 3; stmt.o2.s1 = instr->_m_ops[2]->codeAddress() + (j + 1) % 3; @@ -2663,7 +2663,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc code_push_statement(code, &stmt, instr->m_context); } stmt.opcode = INSTR_SUB_V; - stmt.o1.s1 = instr->_m_ops[0]->codeAddress(); + stmt.o1.s1 = func->m_owner->m_vinstr_temp[1]->codeAddress(); stmt.o2.s1 = func->m_owner->m_vinstr_temp[0]->codeAddress(); stmt.o3.s1 = instr->_m_ops[0]->codeAddress(); code_push_statement(code, &stmt, instr->m_context); -- 2.39.2 From 94c2936bfad224529cf326d539a5cdac0a286183 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Sun, 15 Sep 2019 10:27:26 +0200 Subject: [PATCH 14/16] tests: xor peephole optimization regression test Signed-off-by: Wolfgang Bumiller --- tests/xor-optimized.tmpl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/xor-optimized.tmpl diff --git a/tests/xor-optimized.tmpl b/tests/xor-optimized.tmpl new file mode 100644 index 0000000..4ff8365 --- /dev/null +++ b/tests/xor-optimized.tmpl @@ -0,0 +1,21 @@ +I: xor.qc +D: test bitwise xor +T: -execute +C: -std=gmqcc -O3 +E: $null +M: 6 +M: 8 +M: commutative +M: assocative +M: inverse +M: vv: '6 8 6' +M: vf: '15 8 15' +M: vv: '6 8 6' +M: vf: '15 8 15' +M: 100:200 swapped is: 200:100 +M: '1 2 3':'4 5 6' swapped is: '4 5 6':'1 2 3' +M: '4 7 6' +M: '4 7 6' +M: '4 7 6' +M: '4 7 6' +M: '4 7 6' -- 2.39.2 From 2ce3fb0be5efe675d6910ba7a4a8165d0272f731 Mon Sep 17 00:00:00 2001 From: Martin Taibr Date: Sun, 17 Nov 2019 12:47:04 +0100 Subject: [PATCH 15/16] qc weirdness examples --- tests/_accumulate.qc | 39 +++++++++ tests/_from_wiki_typedef.qc | 17 ++++ tests/_fuck_retarded_sigs.qc | 63 ++++++++++++++ tests/_listing_fieldref.qc | 16 ++++ tests/_my_test.qc | 128 +++++++++++++++++++++++++++ tests/_string_nil.qc | 24 ++++++ tests/_tmp.qc | 18 ++++ tests/_verify_field_syntax.qc | 54 ++++++++++++ tests/_verify_fn_syntax_builtins.qc | 34 ++++++++ tests/_verify_fn_syntax_fields.qc | 129 ++++++++++++++++++++++++++++ tests/_verify_local_arrays.qc | 40 +++++++++ tests/_verify_new_qc.qc | 48 +++++++++++ 12 files changed, 610 insertions(+) create mode 100644 tests/_accumulate.qc create mode 100644 tests/_from_wiki_typedef.qc create mode 100644 tests/_fuck_retarded_sigs.qc create mode 100644 tests/_listing_fieldref.qc create mode 100644 tests/_my_test.qc create mode 100644 tests/_string_nil.qc create mode 100644 tests/_tmp.qc create mode 100644 tests/_verify_field_syntax.qc create mode 100644 tests/_verify_fn_syntax_builtins.qc create mode 100644 tests/_verify_fn_syntax_fields.qc create mode 100644 tests/_verify_local_arrays.qc create mode 100644 tests/_verify_new_qc.qc diff --git a/tests/_accumulate.qc b/tests/_accumulate.qc new file mode 100644 index 0000000..57e376c --- /dev/null +++ b/tests/_accumulate.qc @@ -0,0 +1,39 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +[[accumulate]] float f() { + print("a"); + return = 1; +} + +[[accumulate]] float f() { + print("b"); + return; +} + +[[accumulate]] float f() { + print("c"); + return 3; +} + +[[accumulate]] float f() { + print("d"); +} + +void main() { + print(ftos(f())); +} diff --git a/tests/_from_wiki_typedef.qc b/tests/_from_wiki_typedef.qc new file mode 100644 index 0000000..754edf9 --- /dev/null +++ b/tests/_from_wiki_typedef.qc @@ -0,0 +1,17 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; + +float sum3(float a, float b, float c) +{ + return a + b + c; +} + +typedef float(float, float, float) op3func_t; +var float(float a, float b, float c) f; +op3func_t g; + +void main() { + f = sum3; + g = f; + print(ftos(g(1, 2, 3)), "\n"); // prints 6 +} diff --git a/tests/_fuck_retarded_sigs.qc b/tests/_fuck_retarded_sigs.qc new file mode 100644 index 0000000..8993267 --- /dev/null +++ b/tests/_fuck_retarded_sigs.qc @@ -0,0 +1,63 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +void p() { + print("\n"); +} + +void pn(float n) = { + print(ftos(n), "\n"); +} + +// ====================================== + +void v2v() { print(ftos(5)); } +float v2f() { return 6; } +void f2v(float x) { print(ftos(x)); } +float f2f(float x) { return x; } + +void ret_v2v()() { return v2v; } +void ret_v2f()() { return v2f; } +void ret_f2v(float)() { return f2v; } +void ret_f2f(float)() { return f2f; } + +float ret()() { return v2f; } +float f(float)();// { return f2f; } + +//float()() xxx { return ret; } + +void() ret_v2f_2() { return v2f; } + +void main() { + void() local_v2v_1 = v2v; + void() local_v2v_2 = ret_v2v(); + + float() local_v2f_1 = v2f; + //float() local_v2f_2 = ret_v2f(); + + void(float) local_f2v_1 = f2v; + void(float) local_f2v_2 = ret_f2v(); + + float(float) local_f2f_1 = f2f; + //float(float) local_f2f_2 = ret_f2f(); + + pn(ret()()); + pn(f()(666)); + pn(f()(666)); + + ret_v2f_2()(); +} diff --git a/tests/_listing_fieldref.qc b/tests/_listing_fieldref.qc new file mode 100644 index 0000000..5a4868e --- /dev/null +++ b/tests/_listing_fieldref.qc @@ -0,0 +1,16 @@ +entity () spawn = #3; + +float TICK_LENGTH = 0.016; + +.float health; +.float energy; + +void regenerate_resource(entity player, .float res, float dt) { + player.(res) += 0.5 * dt; +} + +void main() { + entity player = spawn(); + regenerate_resource(player, health, TICK_LENGTH); + regenerate_resource(player, energy, TICK_LENGTH); +} diff --git a/tests/_my_test.qc b/tests/_my_test.qc new file mode 100644 index 0000000..d8a9994 --- /dev/null +++ b/tests/_my_test.qc @@ -0,0 +1,128 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +void p() { + print("\n"); +} + +void pn(float n) { + print(ftos(n), "\n"); +} + +// ====================================== + +float global_f; +.float field_f; + +float global_af[5]; +//float[5] global_af_2; NOPE + +float(float) fn_f2f; +float fn_f2f_2(float); +float fn_f2f(float) { return 0; } +float fn_f2f_2(float a) { return a; } + +.float() field_v2f_1; +.float(void) field_v2f_2; +.float(float) field_f2f; +.float fn_f2pf(float); +.float fn_f2pf(float) { return field_f; } + +float fn_f_f2f_2_f(float a, float(float wat) f2f) { + return f2f(a); +} + +float fn_f_f2f_2_f_2(float a, float f2f(float wat)) { + return f2f(a); +} + +typedef .float(float) Fn_f2pf; +var Fn_f2pf var_fn_f2pf; +//var .float(float) var_fn_f2pf_2; +//.Fn_v2pf idk; +//..float() idk2; +//Fn_v2pf fn_v2pf() = { return field_f; } +//.Fn_v2pf field_v2pf; + +typedef float(float) Fn_f2f; +typedef float ty_f; +typedef float ty_f5[5]; + +var float(float) var_fn_f2f; + +float idk() { return 5; }; +float idk2()() { return idk; } +float wtf(float, void) { return 6; } +float wtf2()(float, void) { return wtf; } + +.float sanity; +//.float(float) madness { return sanity; } +void aaaaaa() {} +.float(.float(float a) a()()()) cthulhu(.float a)(float, void, ...)(.float a(float))()()()() { return aaaaaa; } + +float unused; + +void main() { + //cthulhu(madness); + + unused = 1; + + global_f = 666; + global_af[4] = 10; + entity e = spawn(); + e.field_f = 667; + //var_fn_f2pf_2 = fn_f2pf; + //var_fn_f2pf = fn_f2pf; + //e.field_v2pf = fn_v2pf; + //e.idk(); + //idk = 6; + //idk2 = 7; + + pn(global_f); + pn(e.field_f); + p(); + + pn(fn_f2f(1)); + pn(fn_f2f_2(1)); + p(); + + e.field_f2f = fn_f2f; + pn(e.field_f2f(2)); + e.field_f2f = fn_f2f_2; + pn(e.field_f2f(2)); + p(); + + pn(e.(fn_f2pf(3))); + p(); + + .float loc_field_f = fn_f2pf(3); + pn(e.loc_field_f); + pn(e.(loc_field_f)); + p(); + + var_fn_f2f = fn_f2f_2; + pn(var_fn_f2f(4)); + p(); + + pn(fn_f_f2f_2_f(5, fn_f2f_2)); + pn(fn_f_f2f_2_f_2(5, fn_f2f_2)); + p(); + + float x, y = 1; + //pn(x); + pn(y); +} diff --git a/tests/_string_nil.qc b/tests/_string_nil.qc new file mode 100644 index 0000000..2c09d5b --- /dev/null +++ b/tests/_string_nil.qc @@ -0,0 +1,24 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +void main() { + string x; + string n = nil; + print(ftos(x == n)); + print(ftos(x == "")); + print(ftos("" == n)); +} diff --git a/tests/_tmp.qc b/tests/_tmp.qc new file mode 100644 index 0000000..52937a5 --- /dev/null +++ b/tests/_tmp.qc @@ -0,0 +1,18 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +void main() {} diff --git a/tests/_verify_field_syntax.qc b/tests/_verify_field_syntax.qc new file mode 100644 index 0000000..93ea14e --- /dev/null +++ b/tests/_verify_field_syntax.qc @@ -0,0 +1,54 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +void p() { + print("\n"); +} + +void pn(float n) { + print(ftos(n), "\n"); +} + +// ====================================== + +.float x, y; + +const .float mine, his = mine; + +void pe(entity e) { + print(ftos(e.x), ftos(e.y), ftos(e.mine), ftos(e.his), "\n"); +} + +void main() { + entity e = spawn(); + + pe(e); + .float x_bak = x; + e.x = 1; + e.y = 2; + e.mine = 3; + pn(e.x_bak); + pe(e); + x = y; + .float pf = mine; + pn(e.pf); + pe(e); + e.his = 4; + x = x_bak; + pn(e.pf); + pe(e); +} diff --git a/tests/_verify_fn_syntax_builtins.qc b/tests/_verify_fn_syntax_builtins.qc new file mode 100644 index 0000000..56578c6 --- /dev/null +++ b/tests/_verify_fn_syntax_builtins.qc @@ -0,0 +1,34 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +void p() { + print("\n"); +} + +void pn(float n) { + print(ftos(n), "\n"); +} + +// ====================================== + +float(float) sqrt1 = #13; +float sqrt2(float) = #13; + +void main() { + pn(sqrt1(4)); + pn(sqrt2(4)); +} diff --git a/tests/_verify_fn_syntax_fields.qc b/tests/_verify_fn_syntax_fields.qc new file mode 100644 index 0000000..6b4dd2e --- /dev/null +++ b/tests/_verify_fn_syntax_fields.qc @@ -0,0 +1,129 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +void p() { + print("\n"); +} + +void pn(float n) { + print(ftos(n), "\n"); +} + +// ====================================== + +// . always declares a field for all entities +// if you want to assign something to it, it's done on a per-entity basis +// .type fn_name(params) is an alias for .type(params) fn_name +// if you want a field that is a function returning a field you want something like `.(.type(...)(params) name` +// er, `.(.type(...))(params) name` I mean + +// actually: +// `.type fn_name(params)` declares functions that return fields +// `.type(params) fn_name` declare fields that are functions that return `type` + +float global_of_float; + +.float field_of_float; + +float(float f) fn_float_to_float_1; +float(float f) fn_float_to_float_1 { return f; } +float fn_float_to_float_2(float f); +float fn_float_to_float_2(float f) { return f; } + +.float fn_float_to_field_of_float(float f); +.float fn_float_to_field_of_float(float f) { return field_of_float; } +//.float(float f) fn_float_to_field_of_float_bad { return field_of_float; } +.float(float f) field_of_fn_float_to_float; + +.float(float f1) fn_float_to_field_of_fn_float_to_float(float f2); +.float(float f1) fn_float_to_field_of_fn_float_to_float(float f2) { return field_of_fn_float_to_float; } + +var .float global_of_field_of_float; + +var float(float f) global_of_fn_float_to_float_1; +var float global_of_fn_float_to_float_2(float f); + +var .float global_of_fn_float_to_field_of_float(float f); + +var .float(float f) todo1; + +var .float(float f1) todo2(float f2); + +void main() { + + entity e1 = spawn(); + + // globals + + pn(fn_float_to_float_1(10)); + + e1.(fn_float_to_field_of_float(5)) = 100; + pn(e1.field_of_float); + + e1.field_of_fn_float_to_float = fn_float_to_float_1; + pn(e1.field_of_fn_float_to_float(30)); + e1.field_of_fn_float_to_float = fn_float_to_float_2; + pn(e1.field_of_fn_float_to_float(30)); + + //pn(e1.fn_float_to_field_of_fn_float_to_float(6)(66)); + pn(e1.(fn_float_to_field_of_fn_float_to_float(6))(66)); + + global_of_field_of_float = field_of_float; + pn(e1.(global_of_field_of_float)); + + global_of_fn_float_to_float_1 = fn_float_to_float_2; + pn(global_of_fn_float_to_float_1(-10)); + + global_of_fn_float_to_float_2 = fn_float_to_float_1; + pn(global_of_fn_float_to_float_2(-10)); + + global_of_fn_float_to_field_of_float = fn_float_to_field_of_float; + pn(e1.(global_of_fn_float_to_field_of_float(-5))); + + p(); + + // locals + + .float local_of_field_of_float = field_of_float; + pn(e1.(local_of_field_of_float)); + + float(float f) local_of_fn_float_to_float_1; + local_of_fn_float_to_float_1 = fn_float_to_float_1; + pn(local_of_fn_float_to_float_1(11)); + local_of_fn_float_to_float_1 = fn_float_to_float_2; + pn(local_of_fn_float_to_float_1(11)); + + float local_of_fn_float_to_float_2(float f); + local_of_fn_float_to_float_2 = fn_float_to_float_1; + pn(local_of_fn_float_to_float_2(12)); + local_of_fn_float_to_float_2 = local_of_fn_float_to_float_1; + pn(local_of_fn_float_to_float_2(12)); + + .float local_of_fn_float_to_field_of_float(float f); + //local_of_fn_float_to_field_of_float = field_of_fn_float_to_float; // cannot assign .float(float) to .float(float) + local_of_fn_float_to_field_of_float = fn_float_to_field_of_float; + pn(e1.(local_of_fn_float_to_field_of_float(7))); + + .float(float f) local_of_field_of_fn_float_to_float; + local_of_field_of_fn_float_to_float = field_of_fn_float_to_float; + //local_of_field_of_fn_float_to_float = fn_float_to_field_of_float; // cannot assign .float(float) to .float(float) + pn(e1.local_of_field_of_fn_float_to_float(13)); + + .float(float f1) local_of_fn_float_to_field_of_fn_float_to_float(float f2); + local_of_fn_float_to_field_of_fn_float_to_float = fn_float_to_field_of_fn_float_to_float; + pn(e1.(local_of_fn_float_to_field_of_fn_float_to_float(8))(14)); +} diff --git a/tests/_verify_local_arrays.qc b/tests/_verify_local_arrays.qc new file mode 100644 index 0000000..c670b25 --- /dev/null +++ b/tests/_verify_local_arrays.qc @@ -0,0 +1,40 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +void p() { + print("\n"); +} + +void pn(float n) { + print(ftos(n), "\n"); +} + +// ====================================== + +void f(float x) { + if (x == 0) return; + + float xs[2]; + xs[0] = x; + pn(xs[0]); + f(x - 1); + pn(xs[0]); +} + +void main() { + f(3); +} diff --git a/tests/_verify_new_qc.qc b/tests/_verify_new_qc.qc new file mode 100644 index 0000000..97e87a4 --- /dev/null +++ b/tests/_verify_new_qc.qc @@ -0,0 +1,48 @@ +void (string str, ...) print = #1; +string (float val) ftos = #2; +entity () spawn = #3; +void (entity ent) kill = #4; +string (vector vec) vtos = #5; +void (string str) error = #6; +float (vector vec) vlen = #7; +string (entity ent) etos = #8; +float (string str) stof = #9; +string (...) strcat = #10; +float (string str1, string str2) strcmp = #11; +vector (vector vec) normalize = #12; +float (float val) sqrt = #13; +float (float val) floor = #14; +float (float val1, float val2) pow = #15; +vector (string str) stov = #16; + +void p() { + print("\n"); +} + +void pn(float n) { + print(ftos(n), "\n"); +} + +// ====================================== + +// note: the comments are from wiki, they're wrong +// https://gitlab.com/xonotic/xonotic/wikis/NewQC + +float global; // global variable +//float .foo; // entity field + +.float field; // fieldpointer +//.float .foo; // entity field of type fieldpointer + +float function_void_to_float(void) { return 5; } // function +//float foo*(void); // function pointer + +.float foo(void) { return field; } // function returning a fieldpointer .float +//.float foo*(void); // function pointer, returning a fieldpointer .float + +//float .foo(void); // entity field of type function returning float +//.float .foo(void); // entity field of type function returning fieldpointer + +void main() { + foo(); +} -- 2.39.2 From 82b634da032b75ef5aea42400caaa5d79f6c3f2f Mon Sep 17 00:00:00 2001 From: Martin Taibr Date: Sun, 17 Nov 2019 12:54:13 +0100 Subject: [PATCH 16/16] xon compile flags --- .gitignore | 3 +++ tests/_tmp.qc | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 478b967..13851e2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ testsuite build/ .idea/ + +out.dat +out.lno diff --git a/tests/_tmp.qc b/tests/_tmp.qc index 52937a5..ddd5196 100644 --- a/tests/_tmp.qc +++ b/tests/_tmp.qc @@ -15,4 +15,7 @@ float (float val) floor = #14; float (float val1, float val2) pow = #15; vector (string str) stov = #16; +// xon uses these flags: +// ./gmqcc -std=gmqcc -Ooverlap-locals -O3 -Wall -Wno-field-redeclared -flno -futf8 -fno-bail-on-werror -frelaxed-switch -freturn-assignments tests/_file.qc -o out.dat && ./qcvm out.dat + void main() {} -- 2.39.2