[qfcc] Support the idea of constexpr

It's nowhere near complete, but unary and binary expressions that are
marked as constant will not be subject to constant folding. This is
necessary for proper support of specialization constants.
This commit is contained in:
Bill Currie 2024-09-22 12:28:37 +09:00
parent e7710bb7ac
commit 8099c4cf8e
4 changed files with 72 additions and 4 deletions

View file

@ -63,6 +63,7 @@ typedef struct ex_expr_s {
bool commutative; ///< e1 and e2 can be swapped
bool anticommute; ///< e1 and e2 can be swapped with negation
bool associative; ///< a op (b op c) == (a op b) op c
bool constant; ///< constant that has/will not been folded
const type_t *type; ///< the type of the result of this expression
const expr_t *e1; ///< left side of binary, sole of unary
const expr_t *e2; ///< right side of binary, null for unary
@ -758,11 +759,20 @@ bool is_error (const expr_t *e) __attribute__((pure));
/** Check if the expression refers to a constant value.
This does not included computed constants that have not been folded.
\param e The expression to check.
\return True if the expression is constant.
*/
int is_constant (const expr_t *e) __attribute__((pure));
/** Check if the expression refers to a constant expression or value.
\param e The expression to check.
\return True if the expression is constant.
*/
bool is_constexpr (const expr_t *e) __attribute__((pure));
/** Check if the expression refers to a variable.
\param e The expression to check.

View file

@ -568,6 +568,7 @@ new_binary_expr (int op, const expr_t *e1, const expr_t *e2)
expr_t *e = new_expr ();
e->type = ex_expr;
e->nodag = e1->nodag | e2->nodag;
e->expr.constant = is_constexpr (e1) && is_constexpr (e2);
e->expr.op = op;
e->expr.e1 = e1;
e->expr.e2 = e2;
@ -596,12 +597,13 @@ new_unary_expr (int op, const expr_t *e1)
{
if (e1 && e1->type == ex_error) {
internal_error (e1, "error expr in new_binary_expr");
internal_error (e1, "error expr in new_unary_expr");
}
expr_t *e = new_expr ();
e->type = ex_uexpr;
e->nodag = e1->nodag;
e->expr.constant = is_constexpr (e1);
e->expr.op = op;
e->expr.e1 = e1;
return e;
@ -970,6 +972,18 @@ is_constant (const expr_t *e)
return 0;
}
bool
is_constexpr (const expr_t *e)
{
while (e->type == ex_alias) {
e = e->alias.expr;
}
if ((e->type == ex_expr || e->type == ex_uexpr) && e->expr.constant) {
return true;
}
return is_constant (e);
}
int
is_variable (const expr_t *e)
{

View file

@ -45,6 +45,9 @@ void
glsl_parse_declaration (specifier_t spec, symbol_t *sym, const type_t *array,
const expr_t *init, symtab_t *symtab)
{
if (sym->type) {
internal_error (0, "unexected typed symbol");
}
if (array) {
spec.type = append_type (array, spec.type);
spec.type = find_type (spec.type);
@ -66,8 +69,41 @@ glsl_parse_declaration (specifier_t spec, symbol_t *sym, const type_t *array,
} else {
spec.sym = sym;
if (spec.sym) {
if (spec.is_const && !init) {
error (0, "uninitialized const %s", sym->name);
init = new_zero_expr (spec.type);
}
if (spec.is_const && init->type != ex_compound) {
auto type = get_type (init);
if (type != spec.type && type_assignable (spec.type, type)) {
if (!init->implicit && !type_promotes (spec.type, type)) {
warning (init, "initialization of %s with %s"
" (use a cast)\n)",
get_type_string (spec.type),
get_type_string (type));
}
init = cast_expr (spec.type, init);
}
if (get_type (init) != spec.type) {
error (init, "type mismatch in initializer");
init = new_zero_expr (spec.type);
}
if (!is_constexpr (init)) {
error (init, "non-constant initializer");
init = new_zero_expr (spec.type);
}
sym->type = spec.type;
sym->sy_type = sy_expr;
sym->expr = init;
auto s = symtab_lookup (symtab, sym->name);
if (s && s->table == symtab) {
error (0, "%s redeclared", sym->name);
}
symtab_addsymbol (symtab, sym);
} else {
spec.sym = declare_symbol (spec, init, symtab);
}
}
glsl_apply_attributes (attributes, spec);
}
}

View file

@ -60,6 +60,14 @@ glsl_layout_location (specifier_t spec, const expr_t *qual_name,
get_value_string (val->value));
}
static void
glsl_layout_constant_id (specifier_t spec, const expr_t *qual_name,
const expr_t *val)
{
notice (qual_name, "%s %s", expr_string (qual_name),
get_value_string (val->value));
}
static void
glsl_layout_binding (specifier_t spec, const expr_t *qual_name,
const expr_t *val)
@ -466,7 +474,7 @@ static layout_qual_t layout_qualifiers[] = {
},
{ .name = "constant_id",
.apply = E(nullptr),
.apply = E(glsl_layout_constant_id),
.obj_mask = D(var),
.var_type = V(scalar),
},
@ -765,7 +773,7 @@ layout_check_qualifier (const layout_qual_t *qual, specifier_t spec)
&& qual->var_type != var_type) {
return false;
}
if (!(qual->if_mask & if_mask)) {
if (qual->if_mask != if_mask && !(qual->if_mask & if_mask)) {
return false;
}
if (qual->stage_filter) {