X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;ds=sidebyside;f=parser.c;h=8b7cc7b80f29534d3cfcab4115e8156607af60d7;hb=8bdd0601128d2ae91e95d7990acbf61de89b40d1;hp=f53167ad8e66ae1438da62a34cc062322e509599;hpb=3688dab048f198c3ef37f7a8b2e54cd078622e7b;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index f53167a..8b7cc7b 100644 --- a/parser.c +++ b/parser.c @@ -51,6 +51,7 @@ typedef struct { ast_value *imm_float_zero; ast_value *imm_float_one; ast_value *imm_vector_zero; + ast_value *nil; size_t crc_globals; size_t crc_fields; @@ -103,7 +104,7 @@ static void parser_enterblock(parser_t *parser); static bool parser_leaveblock(parser_t *parser); static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e); static bool parse_typedef(parser_t *parser); -static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static); +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags); static ast_block* parse_block(parser_t *parser); static bool parse_block_into(parser_t *parser, ast_block *block); static bool parse_statement_or_block(parser_t *parser, ast_expression **out); @@ -759,10 +760,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) break; case opid1('*'): if (exprs[0]->expression.vtype != exprs[1]->expression.vtype && - exprs[0]->expression.vtype != TYPE_VECTOR && - exprs[0]->expression.vtype != TYPE_FLOAT && - exprs[1]->expression.vtype != TYPE_VECTOR && - exprs[1]->expression.vtype != TYPE_FLOAT) + !(exprs[0]->expression.vtype == TYPE_VECTOR && + exprs[1]->expression.vtype == TYPE_FLOAT) && + !(exprs[1]->expression.vtype == TYPE_VECTOR && + exprs[0]->expression.vtype == TYPE_FLOAT) + ) { parseerror(parser, "invalid types used in expression: cannot multiply types %s and %s", type_name[exprs[1]->expression.vtype], @@ -798,6 +800,64 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) { if (CanConstFold(exprs[0], exprs[1])) out = (ast_expression*)parser_const_float(parser, vec3_mulvv(ConstV(0), ConstV(1))); + else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[0])) { + vector vec = ConstV(0); + if (!vec.y && !vec.z) { /* 'n 0 0' * v */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.x != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.x), out); + } + else if (!vec.x && !vec.z) { /* '0 n 0' * v */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.y != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.y), out); + } + else if (!vec.x && !vec.y) { /* '0 n 0' * v */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.z != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.z), out); + } + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + } + else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[1])) { + vector vec = ConstV(1); + if (!vec.y && !vec.z) { /* v * 'n 0 0' */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.x != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.x)); + } + else if (!vec.x && !vec.z) { /* v * '0 n 0' */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.y != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.y)); + } + else if (!vec.x && !vec.y) { /* v * '0 n 0' */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.z != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.z)); + } + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + } else out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); } @@ -1013,7 +1073,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ast_type_to_string(exprs[1], ty2, sizeof(ty2)); parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); } - else if (!ast_compare_type(exprs[0], exprs[1])) { + else if (exprs[1]->expression.vtype != TYPE_NIL && + !ast_compare_type(exprs[0], exprs[1])) + { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) && @@ -2328,7 +2390,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou "current standard does not allow variable declarations in for-loop initializers")) goto onerr; } - if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, false)) + if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0)) goto onerr; } else if (parser->tok != ';') @@ -2501,14 +2563,14 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express /* returns true when it was a variable qualifier, false otherwise! * on error, cvq is set to CV_WRONG */ -static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn, bool *is_static) +static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags) { bool had_const = false; bool had_var = false; bool had_noref = false; - bool had_noreturn = false; bool had_attrib = false; bool had_static = false; + uint32_t flags = 0; *cvq = CV_NONE; for (;;) { @@ -2521,7 +2583,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo return false; } if (!strcmp(parser_tokval(parser), "noreturn")) { - had_noreturn = true; + flags |= AST_FLAG_NORETURN; if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`"); *cvq = CV_WRONG; @@ -2536,6 +2598,14 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo return false; } } + else if (!strcmp(parser_tokval(parser), "inline")) { + flags |= AST_FLAG_INLINE; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } else { /* Skip tokens until we hit a ]] */ @@ -2559,7 +2629,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo had_var = true; else if (!strcmp(parser_tokval(parser), "noref")) had_noref = true; - else if (!had_const && !had_var && !had_noref && !had_noreturn && !had_attrib && !had_static) { + else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) { return false; } else @@ -2574,8 +2644,8 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo else *cvq = CV_NONE; *noref = had_noref; - *noreturn = had_noreturn; *is_static = had_static; + *_flags = flags; return true; onerr: parseerror(parser, "parse error after variable qualifier"); @@ -2644,7 +2714,8 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * ast_switch_case swcase; int cvq; - bool noref, noreturn, is_static; + bool noref, is_static; + uint32_t qflags = 0; lex_ctx ctx = parser_ctx(parser); @@ -2690,19 +2761,19 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * if (parser->tok == TOKEN_IDENT) typevar = parser_find_typedef(parser, parser_tokval(parser), 0); if (typevar || parser->tok == TOKEN_TYPENAME) { - if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false)) { + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0)) { ast_delete(switchnode); return false; } continue; } - if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn, &is_static)) + if (parse_qualifiers(parser, true, &cvq, &noref, &is_static, &qflags)) { if (cvq == CV_WRONG) { ast_delete(switchnode); return false; } - if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn, is_static)) { + if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags)) { ast_delete(switchnode); return false; } @@ -2917,8 +2988,9 @@ static bool parse_pragma(parser_t *parser) static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases) { - bool noref, noreturn, is_static; + bool noref, is_static; int cvq = CV_NONE; + uint32_t qflags = 0; ast_value *typevar = NULL; *out = NULL; @@ -2937,15 +3009,15 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable")) return false; } - if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false)) + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0)) return false; return true; } - else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn, &is_static)) + else if (parse_qualifiers(parser, !!block, &cvq, &noref, &is_static, &qflags)) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, block, true, cvq, NULL, noref, noreturn, is_static); + return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags); } else if (parser->tok == TOKEN_KEYWORD) { @@ -4201,7 +4273,45 @@ static bool parse_typedef(parser_t *parser) return true; } -static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static) +static const char *cvq_to_str(int cvq) { + switch (cvq) { + case CV_NONE: return "none"; + case CV_VAR: return "`var`"; + case CV_CONST: return "`const`"; + default: return ""; + } +} + +static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, const ast_value *proto) +{ + bool av, ao; + if (proto->cvq != var->cvq) { + if (!(proto->cvq == CV_CONST && var->cvq == CV_NONE && + !OPTS_FLAG(INITIALIZED_NONCONSTANTS) && + parser->tok == '=')) + { + return !parsewarning(parser, WARN_DIFFERENT_QUALIFIERS, + "`%s` declared with different qualifiers: %s\n" + " -> previous declaration here: %s:%i uses %s", + var->name, cvq_to_str(var->cvq), + ast_ctx(proto).file, ast_ctx(proto).line, + cvq_to_str(proto->cvq)); + } + } + av = (var ->expression.flags & AST_FLAG_NORETURN); + ao = (proto->expression.flags & AST_FLAG_NORETURN); + if (av != ao) { + return !parsewarning(parser, WARN_DIFFERENT_ATTRIBUTES, + "`%s` declared with different attributes%s\n" + " -> previous declaration here: %s:%i", + var->name, (av ? ": noreturn" : ""), + ast_ctx(proto).file, ast_ctx(proto).line, + (ao ? ": noreturn" : "")); + } + return true; +} + +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags) { ast_value *var; ast_value *proto; @@ -4268,17 +4378,20 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } var->cvq = qualifier; - /* in a noref section we simply bump the usecount */ - if (noref || parser->noref) - var->uses++; - if (noreturn) - var->expression.flags |= AST_FLAG_NORETURN; + var->expression.flags |= qflags; /* Part 1: * check for validity: (end_sys_..., multiple-definitions, prototypes, ...) * Also: if there was a prototype, `var` will be deleted and set to `proto` which * is then filled with the previous definition and the parameter-names replaced. */ + if (!strcmp(var->name, "nil")) { + if (OPTS_FLAG(UNTYPED_NIL)) { + if (!localblock || !OPTS_FLAG(PERMISSIVE)) + parseerror(parser, "name `nil` not allowed (try -fpermissive)"); + } else + (void)!parsewarning(parser, WARN_RESERVED_NAMES, "variable name `nil` is reserved"); + } if (!localblock) { /* Deal with end_sys_ vars */ was_end = false; @@ -4356,6 +4469,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* we need the new parameter-names */ for (i = 0; i < vec_size(proto->expression.params); ++i) ast_value_set_name(proto->expression.params[i], var->expression.params[i]->name); + if (!parser_check_qualifiers(parser, var, proto)) { + retval = false; + proto = NULL; + goto cleanup; + } + proto->expression.flags |= var->expression.flags; ast_delete(var); var = proto; } @@ -4383,6 +4502,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield proto = NULL; goto cleanup; } + if (!parser_check_qualifiers(parser, var, proto)) { + retval = false; + proto = NULL; + goto cleanup; + } + proto->expression.flags |= var->expression.flags; ast_delete(var); var = proto; } @@ -4426,6 +4551,10 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } } + /* in a noref section we simply bump the usecount */ + if (noref || parser->noref) + var->uses++; + /* Part 2: * Create the global/local, and deal with vector types. */ @@ -4778,8 +4907,8 @@ static bool parser_global_statement(parser_t *parser) { int cvq = CV_WRONG; bool noref = false; - bool noreturn = false; bool is_static = false; + uint32_t qflags = 0; ast_value *istype = NULL; if (parser->tok == TOKEN_IDENT) @@ -4787,13 +4916,13 @@ static bool parser_global_statement(parser_t *parser) if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.') { - return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, false); + return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0); } - else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn, &is_static)) + else if (parse_qualifiers(parser, false, &cvq, &noref, &is_static, &qflags)) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn, is_static); + return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags); } else if (parser->tok == TOKEN_KEYWORD) { @@ -4906,6 +5035,7 @@ static parser_t *parser; bool parser_init() { + lex_ctx empty_ctx; size_t i; parser = (parser_t*)mem_a(sizeof(parser_t)); @@ -4930,6 +5060,12 @@ bool parser_init() vec_push(parser->variables, parser->htglobals = util_htnew(PARSER_HT_SIZE)); vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE)); vec_push(parser->_blocktypedefs, 0); + + empty_ctx.file = ""; + empty_ctx.line = 0; + parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL); + if (OPTS_FLAG(UNTYPED_NIL)) + util_htset(parser->htglobals, "nil", (void*)parser->nil); return true; } @@ -5127,6 +5263,14 @@ bool parser_finish(const char *output) if (!ast_istype(parser->globals[i], ast_value)) continue; asvalue = (ast_value*)(parser->globals[i]); + if (asvalue->cvq == CV_CONST && !asvalue->hasvalue) + (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_CONSTANT, + "uninitialized constant: `%s`", + asvalue->name); + else if ((asvalue->cvq == CV_NONE || asvalue->cvq == CV_CONST) && !asvalue->hasvalue) + (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_GLOBAL, + "uninitialized global: `%s`", + asvalue->name); if (!ast_generate_accessors(asvalue, ir)) { ir_builder_delete(ir); return false;