From 56f8e19f63be2a6ddc00d19b54ac77c8205713cd Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Wed, 2 Oct 2024 02:07:51 +0900 Subject: [PATCH] [qfcc] Implement specialization constants And they even pass validation (though it turns out there's a bug in glslangValidator regarding specialization constants with expressions (or possibly spirv-val, but it seems to be the former as my bug report shows signs of activity in that direction)). https://github.com/KhronosGroup/glslang/issues/3748 --- tools/qfcc/include/expr.h | 1 + tools/qfcc/include/symtab.h | 1 + tools/qfcc/include/value.h | 1 + tools/qfcc/source/glsl-layout.c | 10 ++++ tools/qfcc/source/spirv.c | 103 +++++++++++++++++++++++++++----- tools/qfcc/source/value.c | 2 +- 6 files changed, 101 insertions(+), 17 deletions(-) diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 7032a5e79..ca062f253 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -218,6 +218,7 @@ typedef struct ex_value_s { const type_t *type; etype_t lltype; unsigned id; + bool is_constexpr; union { uint8_t raw_value; ///< for memcpy const char *string_val; ///< string constant diff --git a/tools/qfcc/include/symtab.h b/tools/qfcc/include/symtab.h index fcebcd45c..0c9c3cf79 100644 --- a/tools/qfcc/include/symtab.h +++ b/tools/qfcc/include/symtab.h @@ -65,6 +65,7 @@ typedef struct symbol_s { vis_t visibility; ///< symbol visiblity. defaults to public const char *name; ///< symbol name sy_type_e sy_type; ///< symbol type + unsigned id; const struct type_s *type; ///< type of object to which symbol refers struct param_s *params; ///< the parameters if a function bool no_auto_init:1; ///< skip for non-designated initializers diff --git a/tools/qfcc/include/value.h b/tools/qfcc/include/value.h index 715956235..4a7417e29 100644 --- a/tools/qfcc/include/value.h +++ b/tools/qfcc/include/value.h @@ -43,6 +43,7 @@ struct type_s; struct pr_type_s; struct operand_s; +struct ex_value_s *new_value (void); struct ex_value_s *new_string_val (const char *string_val); struct ex_value_s *new_double_val (double double_val); struct ex_value_s *new_float_val (float float_val); diff --git a/tools/qfcc/source/glsl-layout.c b/tools/qfcc/source/glsl-layout.c index 01f366005..10c0ecc40 100644 --- a/tools/qfcc/source/glsl-layout.c +++ b/tools/qfcc/source/glsl-layout.c @@ -83,6 +83,16 @@ glsl_layout_constant_id (specifier_t spec, const expr_t *qual_name, spec.sym->sy_type = sy_expr; spec.sym->expr = expr; } + if (spec.sym->sy_type == sy_expr && spec.sym->expr->type == ex_value) { + auto value = new_value (); + *value = *spec.sym->expr->value; + value->is_constexpr = true; + spec.sym->expr = new_value_expr (value, false); + } else { + error (spec.sym->expr, + "specialization constant must be a scalar literal"); + return; + } spec.sym->is_constexpr = true; const char *name = expr_string (qual_name); set_attribute (&spec.sym->attributes, name, val); diff --git a/tools/qfcc/source/spirv.c b/tools/qfcc/source/spirv.c index 284079a04..9db031d3d 100644 --- a/tools/qfcc/source/spirv.c +++ b/tools/qfcc/source/spirv.c @@ -34,6 +34,7 @@ #include "QF/quakeio.h" +#include "tools/qfcc/include/attribute.h" #include "tools/qfcc/include/def.h" #include "tools/qfcc/include/defspace.h" #include "tools/qfcc/include/diagnostic.h" @@ -50,6 +51,7 @@ typedef struct spirvctx_s { defspace_t *linkage; defspace_t *strings; defspace_t *names; + defspace_t *annotations; defspace_t *types; defspace_t *code; struct DARRAY_TYPE (unsigned) type_ids; @@ -143,6 +145,22 @@ spirv_Name (unsigned id, const char *name, spirvctx_t *ctx) memcpy (&D_var_o(int, def, 2), name, len); } +static void +spirv_Decorate (unsigned id, SpvDecoration decoration, void *literal, + etype_t type, spirvctx_t *ctx) +{ + if (type != ev_int) { + internal_error (0, "unexpected type"); + } + int size = pr_type_size[type]; + auto def = spirv_new_insn (SpvOpDecorate, 3 + size, ctx->annotations); + D_var_o(int, def, 1) = id; + D_var_o(int, def, 2) = decoration; + if (type == ev_int) { + D_var_o(int, def, 3) = *(int *)literal; + } +} + static void spirv_MemberName (unsigned id, unsigned member, const char *name, spirvctx_t *ctx) @@ -571,11 +589,19 @@ spirv_uexpr (const expr_t *e, spirvctx_t *ctx) unsigned uid = spirv_emit_expr (e->expr.e1, ctx); unsigned tid = type_id (get_type (e), ctx); - auto def = spirv_new_insn (spv_op->op, 4, ctx->types); unsigned id; - D_var_o(int, def, 1) = tid; - D_var_o(int, def, 2) = id = spirv_id (ctx); - D_var_o(int, def, 3) = uid; + if (e->expr.constant) { + auto def = spirv_new_insn (SpvOpSpecConstantOp, 5, ctx->types); + D_var_o(int, def, 1) = tid; + D_var_o(int, def, 2) = id = spirv_id (ctx); + D_var_o(int, def, 3) = spv_op->op; + D_var_o(int, def, 4) = uid; + } else { + auto def = spirv_new_insn (spv_op->op, 4, ctx->types); + D_var_o(int, def, 1) = tid; + D_var_o(int, def, 2) = id = spirv_id (ctx); + D_var_o(int, def, 3) = uid; + } return id; } @@ -603,15 +629,48 @@ spirv_expr (const expr_t *e, spirvctx_t *ctx) unsigned bid2 = spirv_emit_expr (e->expr.e2, ctx); unsigned tid = type_id (get_type (e), ctx); - auto def = spirv_new_insn (spv_op->op, 5, ctx->types); unsigned id; - D_var_o(int, def, 1) = tid; - D_var_o(int, def, 2) = id = spirv_id (ctx); - D_var_o(int, def, 3) = bid1; - D_var_o(int, def, 4) = bid2; + if (e->expr.constant) { + auto def = spirv_new_insn (SpvOpSpecConstantOp, 6, ctx->types); + D_var_o(int, def, 1) = tid; + D_var_o(int, def, 2) = id = spirv_id (ctx); + D_var_o(int, def, 3) = spv_op->op; + D_var_o(int, def, 4) = bid1; + D_var_o(int, def, 5) = bid2; + } else { + auto def = spirv_new_insn (spv_op->op, 5, ctx->types); + D_var_o(int, def, 1) = tid; + D_var_o(int, def, 2) = id = spirv_id (ctx); + D_var_o(int, def, 3) = bid1; + D_var_o(int, def, 4) = bid2; + } return id; } +static unsigned +spirv_symbol (const expr_t *e, spirvctx_t *ctx) +{ + auto sym = e->symbol; + if (sym->id) { + return sym->id; + } + if (sym->sy_type == sy_expr) { + sym->id = spirv_emit_expr (sym->expr, ctx); + spirv_Name (sym->id, sym->name, ctx); + for (auto attr = sym->attributes; attr; attr = attr->next) { + if (strcmp (attr->name, "constant_id") == 0) { + int specid = expr_integral (attr->params); + spirv_Decorate (sym->id, SpvDecorationSpecId, + &specid, ev_int, ctx); + } + } + } else { + internal_error (e, "unexpected symbol type: %s for %s", + symtype_str (sym->sy_type), sym->name); + } + return sym->id; +} + static unsigned spirv_value (const expr_t *e, spirvctx_t *ctx) { @@ -635,14 +694,23 @@ spirv_value (const expr_t *e, spirvctx_t *ctx) internal_error (e, "not implemented"); } } - auto def = spirv_new_insn (op, 3 + val_size, ctx->types); - D_var_o(int, def, 1) = tid; - D_var_o(int, def, 2) = value->id = spirv_id (ctx); - if (val_size > 0) { - D_var_o(int, def, 3) = val; + if (value->is_constexpr) { + op += SpvOpSpecConstantTrue - SpvOpConstantTrue; } - if (val_size > 1) { - D_var_o(int, def, 4) = val >> 32; + if (op == SpvOpConstant && !val) { + auto def = spirv_new_insn (SpvOpConstantNull, 3, ctx->types); + D_var_o(int, def, 1) = tid; + D_var_o(int, def, 2) = value->id = spirv_id (ctx); + } else { + auto def = spirv_new_insn (op, 3 + val_size, ctx->types); + D_var_o(int, def, 1) = tid; + D_var_o(int, def, 2) = value->id = spirv_id (ctx); + if (val_size > 0) { + D_var_o(int, def, 3) = val; + } + if (val_size > 1) { + D_var_o(int, def, 4) = val >> 32; + } } } return value->id; @@ -654,6 +722,7 @@ spirv_emit_expr (const expr_t *e, spirvctx_t *ctx) static spirv_expr_f funcs[ex_count] = { [ex_expr] = spirv_expr, [ex_uexpr] = spirv_uexpr, + [ex_symbol] = spirv_symbol, [ex_value] = spirv_value, }; @@ -681,6 +750,7 @@ spirv_write (struct pr_info_s *pr, const char *filename) .linkage = defspace_new (ds_backed), .strings = defspace_new (ds_backed), .names = defspace_new (ds_backed), + .annotations = defspace_new (ds_backed), .types = defspace_new (ds_backed), .code = defspace_new (ds_backed), .type_ids = DARRAY_STATIC_INIT (64), @@ -733,6 +803,7 @@ spirv_write (struct pr_info_s *pr, const char *filename) defspace_add_data (ctx.space, ctx.linkage->data, ctx.linkage->size); defspace_add_data (ctx.space, ctx.strings->data, ctx.strings->size); defspace_add_data (ctx.space, ctx.names->data, ctx.names->size); + defspace_add_data (ctx.space, ctx.annotations->data, ctx.annotations->size); defspace_add_data (ctx.space, ctx.types->data, ctx.types->size); defspace_add_data (ctx.space, ctx.code->data, ctx.code->size); diff --git a/tools/qfcc/source/value.c b/tools/qfcc/source/value.c index 42e3d3ced..10fdf9a5a 100644 --- a/tools/qfcc/source/value.c +++ b/tools/qfcc/source/value.c @@ -94,7 +94,7 @@ value_compare (const void *_val1, const void *_val2, void *unused) return memcmp (&val1->raw_value, &val2->raw_value, value_size) == 0; } -static ex_value_t * +ex_value_t * new_value (void) { ex_value_t *value;