Merge branch 'arithmetic_exceptions' into cooking

Conflicts:
	doc/gmqcc.1
	gmqcc.ini.example
	opts.def
	parser.c
This commit is contained in:
Dale Weiler 2014-05-25 03:01:47 -04:00
commit 53e9ed0d96
23 changed files with 908 additions and 83 deletions

View file

@ -11,8 +11,6 @@ GITINFO :=
GITINFO != git describe --always
.endif
CFLAGS += -Wall -Wextra -Werror -Wstrict-aliasing -Wno-attributes
.if $(CC) == clang
CFLAGS += -Weverything\
-Wno-padded\
@ -22,6 +20,7 @@ CFLAGS += -Wall -Wextra -Werror -Wstrict-aliasing -Wno-attributes
-Wno-float-equal\
-Wno-unknown-warning-option\
-Wno-cast-align\
-Wno-assign-enum\
-pedantic-errors
.else
. if $(CC) != g++

View file

@ -4,8 +4,7 @@ UNAME ?= $(shell uname)
CYGWIN = $(findstring CYGWIN, $(UNAME))
MINGW = $(findstring MINGW, $(UNAME))
CFLAGS += -Wall -Wextra -Werror -Wstrict-aliasing -Wno-attributes
#turn on tons of warnings if clang is present
# turn on tons of warnings if clang is present
# but also turn off the STUPID ONES
ifeq ($(CC), clang)
CFLAGS += \
@ -17,6 +16,7 @@ ifeq ($(CC), clang)
-Wno-float-equal \
-Wno-unknown-warning-option \
-Wno-cast-align \
-Wno-assign-enum \
-pedantic-errors
else
ifneq ($(CC), g++)

1
ast.c
View file

@ -360,6 +360,7 @@ ast_value* ast_value_new(lex_ctx_t ctx, const char *name, int t)
self->cvq = CV_NONE;
self->hasvalue = false;
self->isimm = false;
self->inexact = false;
self->uses = 0;
memset(&self->constval, 0, sizeof(self->constval));
self->initlist = NULL;

1
ast.h
View file

@ -216,6 +216,7 @@ struct ast_value_s
bool isfield; /* this declares a field */
bool isimm; /* an immediate, not just const */
bool hasvalue;
bool inexact; /* inexact coming from folded expression */
basic_value_t constval;
/* for TYPE_ARRAY we have an optional vector
* of constants when an initializer list

View file

@ -349,6 +349,10 @@ will search its intrinsics table for something that matches that
function name by appending "__builtin_" to it. This behaviour may
be unexpected, so enabling this will produce a diagnostic when
such a function is resolved to a builtin.
.It Fl W Ns Cm inexact-compares
When comparing an inexact value such as `1.0/3.0' the result is
pathologically wrong. Enabling this will trigger a compiler warning
on such expressions.
.El
.Sh COMPILE FLAGS
.Bl -tag -width Ds
@ -587,6 +591,11 @@ Emulate OP_STATE operations in code rather than using the instruction.
The desired fps can be set via -state-fps=NUM, defaults to 10.
Specifying \-state-fps implicitly sets this flag. Defaults to off in all
standards.
.It Fl f Ns Cm arithmetic-exceptions
Turn on arithmetic exception tests in the compiler. In constant expressions
which trigger exceptions like division by zero, overflow, underflow, etc,
the following flag will produce diagnostics for what triggered that
exception.
.El
.Sh OPTIMIZATIONS
.Bl -tag -width Ds

4
exec.c
View file

@ -950,7 +950,7 @@ static void prog_main_setparams(qc_program_t *prog) {
}
}
void prog_disasm_function(qc_program_t *prog, size_t id);
static void prog_disasm_function(qc_program_t *prog, size_t id);
int main(int argc, char **argv) {
size_t i;
@ -1265,7 +1265,7 @@ int main(int argc, char **argv) {
return 0;
}
void prog_disasm_function(qc_program_t *prog, size_t id) {
static void prog_disasm_function(qc_program_t *prog, size_t id) {
prog_section_function_t *fdef = prog->functions + id;
prog_section_statement_t *st;

814
fold.c

File diff suppressed because it is too large Load diff

View file

@ -316,6 +316,12 @@
EMULATE_STATE = false
#Turn on arithmetic exception tests in the compiler. In constant expressions
#which trigger exceptions like division by zero, overflow, underflow, etc,
#the following flag will produce diagnostics for what triggered that
#exception.
ARITHMETIC_EXCEPTIONS = false
[warnings]
#Generate a warning about variables which are declared but never
#used. This can be avoided by adding the noref keyword in front
@ -573,6 +579,12 @@
BUILTINS = true
#When comparing an inexact value such as `1.0/3.0' the result is
#pathologically wrong. Enabling this will trigger a compiler warning
#on such expressions.
INEXACT_COMPARES = true
[optimizations]
#Some general peephole optimizations. For instance the code `a = b
#+ c` typically generates 2 instructions, an ADD and a STORE. This

View file

@ -5,6 +5,9 @@ BINDIR := $(PREFIX)/bin
DATADIR := $(PREFIX)/share
MANDIR := $(DATADIR)/man
# default flags
CFLAGS += -Wall -Wextra -Werror -Wstrict-aliasing -Wno-attributes -O2
# compiler
CC ?= clang
@ -101,7 +104,12 @@ SPLINTFLAGS = \
-observertrans \
-abstract \
-statictrans \
-castfcnptr
-castfcnptr \
-shiftimplementation \
-shiftnegative \
-boolcompare \
-infloops \
-sysunrecog
#always the right rule
default: all
@ -118,13 +126,16 @@ uninstall:
#style rule
STYLE_MATCH = \( -name '*.[ch]' -or -name '*.def' -or -name '*.qc' \)
# splint cannot parse the MSVC source
SPLINT_MATCH = \( -name '*.[ch]' -and ! -name 'msvc.c' -and ! -path './doc/*' \)
style:
find . -type f $(STYLE_MATCH) -exec sed -i 's/ *$$//' '{}' ';'
find . -type f $(STYLE_MATCH) -exec sed -i -e '$$a\' '{}' ';'
find . -type f $(STYLE_MATCH) -exec sed -i 's/\t/ /g' '{}' ';'
splint:
@splint $(SPLINTFLAGS) *.c *.h
@splint $(SPLINTFLAGS) `find . -type f $(SPLINT_MATCH)`
gource:
@gource $(GOURCEFLAGS)

View file

@ -422,7 +422,7 @@ static ast_expression *intrin_atanh(intrin_t *intrin) {
(ast_expression*)ast_binary_new(
intrin_ctx(intrin),
INSTR_MUL_F,
(ast_expression*)fold_constgen_float(intrin->fold, 0.5),
(ast_expression*)fold_constgen_float(intrin->fold, 0.5, false),
(ast_expression*)calllog
)
);
@ -496,7 +496,7 @@ static ast_expression *intrin_exp(intrin_t *intrin) {
intrin_ctx(intrin),
INSTR_LT,
(ast_expression*)i,
(ast_expression*)fold_constgen_float(intrin->fold, 200.0f)
(ast_expression*)fold_constgen_float(intrin->fold, 200.0f, false)
),
false,
NULL,
@ -1027,7 +1027,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
intrin_ctx(intrin),
INSTR_GT,
(ast_expression*)callfabs,
(ast_expression*)fold_constgen_float(intrin->fold, QC_POW_EPSILON)
(ast_expression*)fold_constgen_float(intrin->fold, QC_POW_EPSILON, false)
),
/* pre not */
false,
@ -1911,7 +1911,7 @@ static ast_expression *intrin_log_variant(intrin_t *intrin, const char *name, fl
vec_push(value->expression.params, arg1);
vec_push(callln->params, (ast_expression*)arg1);
vec_push(callln->params, (ast_expression*)fold_constgen_float(intrin->fold, base));
vec_push(callln->params, (ast_expression*)fold_constgen_float(intrin->fold, base, false));
vec_push(body->exprs,
(ast_expression*)ast_return_new(

1
opts.c
View file

@ -93,6 +93,7 @@ static void opts_setdefault(void) {
opts_set(opts.warn, WARN_CONST_OVERWRITE, true);
opts_set(opts.warn, WARN_DIRECTIVE_INMACRO, true);
opts_set(opts.warn, WARN_BUILTINS, true);
opts_set(opts.warn, WARN_INEXACT_COMPARES, true);
/* flags */
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);

View file

@ -57,6 +57,7 @@
GMQCC_DEFINE_FLAG(TYPELESS_STORES)
GMQCC_DEFINE_FLAG(SORT_OPERANDS)
GMQCC_DEFINE_FLAG(EMULATE_STATE)
GMQCC_DEFINE_FLAG(ARITHMETIC_EXCEPTIONS)
#endif
/* warning flags */
@ -99,6 +100,7 @@
GMQCC_DEFINE_FLAG(CONST_OVERWRITE)
GMQCC_DEFINE_FLAG(DIRECTIVE_INMACRO)
GMQCC_DEFINE_FLAG(BUILTINS)
GMQCC_DEFINE_FLAG(INEXACT_COMPARES)
#endif
#ifdef GMQCC_TYPE_OPTIMIZATIONS

View file

@ -1293,7 +1293,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
if ((fun->flags & AST_FLAG_VARIADIC) &&
!(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin))
{
call->va_count = (ast_expression*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount);
call->va_count = (ast_expression*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount, false);
}
}
@ -1548,14 +1548,14 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
return true;
}
else if (parser->tok == TOKEN_FLOATCONST) {
ast_expression *val = fold_constgen_float(parser->fold, (parser_token(parser)->constval.f));
ast_expression *val = fold_constgen_float(parser->fold, (parser_token(parser)->constval.f), false);
if (!val)
return false;
vec_push(sy->out, syexp(parser_ctx(parser), val));
return true;
}
else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(parser_token(parser)->constval.i));
ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(parser_token(parser)->constval.i), false);
if (!val)
return false;
vec_push(sy->out, syexp(parser_ctx(parser), val));
@ -4043,7 +4043,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
self_think = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think);
time_plus_1 = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F,
gbl_time, (ast_expression*)fold_constgen_float(parser->fold, frame_delta));
gbl_time, (ast_expression*)fold_constgen_float(parser->fold, frame_delta, false));
if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
if (self_frame) ast_delete(self_frame);
@ -4169,7 +4169,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
goto enderrfn;
}
func->varargs = varargs;
func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params));
func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params), false);
}
parser->function = func;
@ -4227,7 +4227,7 @@ static ast_expression *array_accessor_split(
cmp = ast_binary_new(ctx, INSTR_LT,
(ast_expression*)index,
(ast_expression*)fold_constgen_float(parser->fold, middle));
(ast_expression*)fold_constgen_float(parser->fold, middle, false));
if (!cmp) {
ast_delete(left);
ast_delete(right);
@ -4260,7 +4260,7 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast
if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
assignop = INSTR_STORE_V;
subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false));
if (!subscript)
return NULL;
@ -4326,7 +4326,7 @@ static ast_expression *array_field_setter_node(
if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
assignop = INSTR_STOREP_V;
subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false));
if (!subscript)
return NULL;
@ -4389,7 +4389,7 @@ static ast_expression *array_getter_node(parser_t *parser, ast_value *array, ast
ast_return *ret;
ast_array_index *subscript;
subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false));
if (!subscript)
return NULL;
@ -5160,6 +5160,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
bool wasarray = false;
ast_member *me[3] = { NULL, NULL, NULL };
ast_member *last_me[3] = { NULL, NULL, NULL };
if (!localblock && is_static)
parseerror(parser, "`static` qualifier is not supported in global scope");
@ -5623,6 +5624,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
}
}
}
memcpy(last_me, me, sizeof(me));
me[0] = me[1] = me[2] = NULL;
cleanvar = false;
/* Part 2.2
@ -5830,15 +5832,44 @@ skipvar:
} else {
ast_expression *cexp;
ast_value *cval;
bool folded_const = false;
cexp = parse_expression_leave(parser, true, false, false);
if (!cexp)
break;
cval = ast_istype(cexp, ast_value) ? (ast_value*)cexp : NULL;
if (!localblock || is_static) {
cval = (ast_value*)cexp;
/* deal with foldable constants: */
if (localblock &&
var->cvq == CV_CONST && cval && cval->hasvalue && cval->cvq == CV_CONST && !cval->isfield)
{
/* remove it from the current locals */
if (isvector) {
for (i = 0; i < 3; ++i) {
vec_pop(parser->_locals);
vec_pop(localblock->collect);
}
}
/* do sanity checking, this function really needs refactoring */
if (vec_last(parser->_locals) != (ast_expression*)var)
parseerror(parser, "internal error: unexpected change in local variable handling");
else
vec_pop(parser->_locals);
if (vec_last(localblock->locals) != var)
parseerror(parser, "internal error: unexpected change in local variable handling (2)");
else
vec_pop(localblock->locals);
/* push it to the to-be-generated globals */
vec_push(parser->globals, (ast_expression*)var);
if (isvector)
for (i = 0; i < 3; ++i)
vec_push(parser->globals, (ast_expression*)last_me[i]);
folded_const = true;
}
if (folded_const || !localblock || is_static) {
if (cval != parser->nil &&
(!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
(!cval || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
)
{
parseerror(parser, "initializer is non constant");
@ -5886,6 +5917,13 @@ skipvar:
vec_free(sy.argc);
var->cvq = cvq;
}
/* a constant initialized to an inexact value should be marked inexact:
* const float x = <inexact>; should propagate the inexact flag
*/
if (var->cvq == CV_CONST && var->expression.vtype == TYPE_FLOAT) {
if (cval && cval->hasvalue && cval->cvq == CV_CONST)
var->inexact = cval->inexact;
}
}
another:

View file

@ -127,7 +127,7 @@ ast_expression *parser_find_global(parser_t *parser, const char *name);
/* fold.c */
fold_t *fold_init (parser_t *);
void fold_cleanup (fold_t *);
ast_expression *fold_constgen_float (fold_t *, qcfloat_t);
ast_expression *fold_constgen_float (fold_t *, qcfloat_t, bool);
ast_expression *fold_constgen_vector(fold_t *, vec3_t);
ast_expression *fold_constgen_string(fold_t *, const char *, bool);
bool fold_generate (fold_t *, ir_builder *);

5
test.c
View file

@ -1100,6 +1100,7 @@ static size_t task_schedualize(size_t *pad) {
size_t i = 0;
size_t j = 0;
size_t failed = 0;
int status = 0;
util_snprintf(space[0], sizeof(space[0]), "%d", (int)vec_size(task_tasks));
@ -1167,7 +1168,9 @@ static size_t task_schedualize(size_t *pad) {
continue;
}
if (task_pclose(task_tasks[i].runhandles) != EXIT_SUCCESS && strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) {
status = task_pclose(task_tasks[i].runhandles);
if ((!strcmp(task_tasks[i].tmpl->proceduretype, "-fail") && status == EXIT_SUCCESS)
|| ( strcmp(task_tasks[i].tmpl->proceduretype, "-fail") && status == EXIT_FAILURE)) {
con_out("failure: `%s` %*s %*s\n",
task_tasks[i].tmpl->description,
(pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),

13
tests/arithexcept.qc Normal file
View file

@ -0,0 +1,13 @@
const float huge = 340282346638528859811704183484516925440.000000; // FLT_MAX
#ifdef DIVBYZERO
const float a = 1.0 / 0.0;
#endif
#ifdef OVERFLOW
const float a = huge * huge;
#endif
#ifdef UNDERFLOW
const float a = 1 / huge;
#endif

4
tests/arithexcept.tmpl Normal file
View file

@ -0,0 +1,4 @@
I: arithexcept.qc
D: arithmetic exceptions (divide by zero)
T: -fail
C: -std=fteqcc -farithmetic-exceptions -DDIVBYZERO

View file

@ -0,0 +1,4 @@
I: arithexcept.qc
D: arithmetic exceptions (overflow)
T: -fail
C: -std=ftqcc -farithmetic-exceptions -DOVERFLOW

View file

@ -0,0 +1,4 @@
I: arithexcept.qc
D: arithmetic exceptions (underflow)
T: -fail
C: -std=ftqcc -farithmetic-exceptions -DUNDERFLOW

7
tests/inexact-local.qc Normal file
View file

@ -0,0 +1,7 @@
void main() {
const float a = 1.0 / 3.0;
const float b = 0.33333333333;
if (a == b) {
// Should trigger warning
}
}

4
tests/inexact-local.tmpl Normal file
View file

@ -0,0 +1,4 @@
I: inexact-local.qc
D: inexact comparisons
T: -fail
C: -std=gmqcc -Winexact-compares -Wall -Werror

8
tests/inexact.qc Normal file
View file

@ -0,0 +1,8 @@
const float a = 1.0 / 3.0;
const float b = 0.33333333333;
void main() {
if (a == b) {
// Should trigger warning
}
}

4
tests/inexact.tmpl Normal file
View file

@ -0,0 +1,4 @@
I: inexact.qc
D: inexact comparisons
T: -fail
C: -std=gmqcc -Winexact-compares -Wall -Werror