mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-04-11 20:03:11 +00:00
[qfcc] Implement intrinsic functions in spir-v
Only a tiny handful of glsl functions are implemented (dot, cross, sqrt), but the system works, both via generics and regular overloads.
This commit is contained in:
parent
b9ed0dcc64
commit
1e5d500f8b
10 changed files with 174 additions and 46 deletions
|
@ -974,6 +974,8 @@ expr_t *new_select_expr (bool not, const expr_t *test,
|
|||
const expr_t *true_body,
|
||||
const expr_t *els, const expr_t *false_body);
|
||||
|
||||
expr_t *new_intrinsic_expr (const expr_t *expr_list);
|
||||
|
||||
/** Create an expression of the correct type that references the specified
|
||||
parameter slot.
|
||||
|
||||
|
|
|
@ -169,6 +169,7 @@ typedef struct metafunc_s {
|
|||
rua_loc_t loc; ///< source location of the function
|
||||
mf_type_e meta_type; ///< is this function overloaded
|
||||
function_t *func;
|
||||
const expr_t *expr; ///< inline or intrinsic
|
||||
} metafunc_t;
|
||||
|
||||
extern function_t *current_func;
|
||||
|
@ -219,6 +220,7 @@ function_t *build_code_function (symbol_t *fsym,
|
|||
function_t *build_builtin_function (symbol_t *sym,
|
||||
const expr_t *bi_val, int far,
|
||||
enum storage_class_e storage);
|
||||
void build_intrinsic_function (specifier_t spec, const expr_t *intrinsic);
|
||||
void emit_function (function_t *f, expr_t *e);
|
||||
void clear_functions (void);
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ typedef struct entrypoint_s {
|
|||
typedef struct module_s {
|
||||
ex_list_t capabilities;
|
||||
ex_list_t extensions;
|
||||
ex_list_t extinst_imports;
|
||||
symtab_t *extinst_imports;
|
||||
const expr_t *addressing_model;
|
||||
const expr_t *memory_model;
|
||||
struct DARRAY_TYPE (symbol_t *) interface_syms;
|
||||
|
|
|
@ -409,10 +409,14 @@ print_intrinsic (dstring_t *dstr, const expr_t *e, int level, int id,
|
|||
for (auto l = e->intrinsic.operands.head; l; l = l->next) {
|
||||
_print_expr (dstr, l->expr, level, id, next);
|
||||
}
|
||||
dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"op\"];\n", indent, "", e,
|
||||
e->intrinsic.opcode);
|
||||
for (auto l = e->intrinsic.operands.head; l; l = l->next) {
|
||||
dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"b\"];\n", indent, "", e,
|
||||
dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e,
|
||||
l->expr);
|
||||
}
|
||||
dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e,
|
||||
"@intrinsic", e->loc.line);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -2268,6 +2268,25 @@ new_select_expr (bool not, const expr_t *test,
|
|||
return select;
|
||||
}
|
||||
|
||||
expr_t *
|
||||
new_intrinsic_expr (const expr_t *expr_list)
|
||||
{
|
||||
auto intrinsic = new_expr ();
|
||||
intrinsic->type = ex_intrinsic;
|
||||
|
||||
if (expr_list) {
|
||||
int count = list_count (&expr_list->list);
|
||||
const expr_t *exprs[count + 1];
|
||||
list_scatter (&expr_list->list, exprs);
|
||||
|
||||
intrinsic->intrinsic = (ex_intrinsic_t) {
|
||||
.opcode = exprs[0],
|
||||
};
|
||||
list_gather (&intrinsic->intrinsic.operands, exprs + 1, count - 1);
|
||||
}
|
||||
return intrinsic;
|
||||
}
|
||||
|
||||
expr_t *
|
||||
append_decl (expr_t *decl, symbol_t *sym, const expr_t *init)
|
||||
{
|
||||
|
|
|
@ -279,7 +279,6 @@ build_function_call (const expr_t *fexpr, const type_t *ftype,
|
|||
const expr_t *args)
|
||||
{
|
||||
int param_count = 0;
|
||||
expr_t *call;
|
||||
const expr_t *err = 0;
|
||||
|
||||
int arg_count = args ? list_count (&args->list) : 0;
|
||||
|
@ -302,20 +301,39 @@ build_function_call (const expr_t *fexpr, const type_t *ftype,
|
|||
return err;
|
||||
}
|
||||
|
||||
expr_t *call = nullptr;
|
||||
scoped_src_loc (fexpr);
|
||||
call = new_block_expr (0);
|
||||
call->block.is_call = 1;
|
||||
int num_args = 0;
|
||||
const expr_t *arg_exprs[arg_count + 1][2];
|
||||
auto arg_list = build_args (arg_exprs, &num_args, arguments, arg_types,
|
||||
arg_count, param_count, ftype);
|
||||
for (int i = 0; i < num_args; i++) {
|
||||
scoped_src_loc (arg_exprs[i][0]);
|
||||
auto assign = assign_expr (arg_exprs[i][1], arg_exprs[i][0]);
|
||||
append_expr (call, assign);
|
||||
if (fexpr->type == ex_symbol && fexpr->symbol->sy_type == sy_func
|
||||
&& fexpr->symbol->metafunc->expr) {
|
||||
auto expr = fexpr->symbol->metafunc->expr;
|
||||
if (expr->type == ex_intrinsic) {
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
if (is_reference (get_type (arguments[i]))) {
|
||||
arguments[i] = pointer_deref (arguments[i]);
|
||||
}
|
||||
}
|
||||
call = new_intrinsic_expr (nullptr);
|
||||
call->intrinsic.opcode = expr->intrinsic.opcode;
|
||||
call->intrinsic.res_type = ftype->func.ret_type;
|
||||
list_append_list (&call->intrinsic.operands,
|
||||
&expr->intrinsic.operands);
|
||||
list_gather (&call->intrinsic.operands, arguments, arg_count);
|
||||
}
|
||||
} else {
|
||||
call = new_block_expr (0);
|
||||
call->block.is_call = 1;
|
||||
int num_args = 0;
|
||||
const expr_t *arg_exprs[arg_count + 1][2];
|
||||
auto arg_list = build_args (arg_exprs, &num_args, arguments, arg_types,
|
||||
arg_count, param_count, ftype);
|
||||
for (int i = 0; i < num_args; i++) {
|
||||
scoped_src_loc (arg_exprs[i][0]);
|
||||
auto assign = assign_expr (arg_exprs[i][1], arg_exprs[i][0]);
|
||||
append_expr (call, assign);
|
||||
}
|
||||
auto ret_type = ftype->func.ret_type;
|
||||
call->block.result = new_call_expr (fexpr, arg_list, ret_type);
|
||||
}
|
||||
auto ret_type = ftype->func.ret_type;
|
||||
call->block.result = new_call_expr (fexpr, arg_list, ret_type);
|
||||
return call;
|
||||
}
|
||||
|
||||
|
|
|
@ -847,7 +847,7 @@ function_symbol (specifier_t spec)
|
|||
if (genfunc) {
|
||||
add_generic_function (genfunc);
|
||||
|
||||
ALLOC (1024, metafunc_t, metafuncs, func);
|
||||
func = new_metafunc ();
|
||||
*func = (metafunc_t) {
|
||||
.name = save_string (name),
|
||||
.full_name = name,
|
||||
|
@ -897,7 +897,7 @@ find_function (const expr_t *fexpr, const expr_t *params)
|
|||
}
|
||||
|
||||
int num_params = params ? list_count (¶ms->list) : 0;
|
||||
const expr_t *args[num_params + 1];
|
||||
const expr_t *args[num_params + 1] = {};
|
||||
if (params) {
|
||||
list_scatter_rev (¶ms->list, args);
|
||||
}
|
||||
|
@ -1240,6 +1240,21 @@ build_builtin_function (symbol_t *sym, const expr_t *bi_val, int far,
|
|||
return func;
|
||||
}
|
||||
|
||||
void
|
||||
build_intrinsic_function (specifier_t spec, const expr_t *intrinsic)
|
||||
{
|
||||
auto sym = function_symbol (spec);
|
||||
if (sym->metafunc->expr || sym->metafunc->func) {
|
||||
error (intrinsic, "%s already defined", sym->name);
|
||||
return;
|
||||
}
|
||||
if (sym->type->func.num_params < 0) {
|
||||
error (intrinsic, "intrinsic functions cannot be variadic");
|
||||
return;
|
||||
}
|
||||
sym->metafunc->expr = intrinsic;
|
||||
}
|
||||
|
||||
void
|
||||
clear_functions (void)
|
||||
{
|
||||
|
|
|
@ -337,6 +337,9 @@ static gentype_t genDType = {
|
|||
};
|
||||
#endif
|
||||
|
||||
#define SPV(op) "@intrinsic(" #op ")"
|
||||
#define GLSL(op) "@intrinsic(12, \"GLSL.std.450\", " #op ")"
|
||||
|
||||
static const char *glsl_general_functions =
|
||||
SRC_LINE
|
||||
"#define out @out" "\n"
|
||||
|
@ -377,8 +380,8 @@ SRC_LINE
|
|||
"genFType log(genFType x);" "\n"
|
||||
"genFType exp2(genFType x);" "\n"
|
||||
"genFType log2(genFType x);" "\n"
|
||||
"genFType sqrt(genFType x);" "\n"
|
||||
"genDType sqrt(genDType x);" "\n"
|
||||
"genFType sqrt(genFType x) = " GLSL(31) ";" "\n"
|
||||
"genDType sqrt(genDType x) = " GLSL(31) ";" "\n"
|
||||
"genFType inversesqrt(genFType x);" "\n"
|
||||
"genDType inversesqrt(genDType x);" "\n"
|
||||
|
||||
|
@ -485,10 +488,10 @@ SRC_LINE
|
|||
"double length(genDType x);" "\n"
|
||||
"float distance(genFType p0, genFType p1);" "\n"
|
||||
"double distance(genDType p0, genDType p1);" "\n"
|
||||
"float dot(genFType x, genFType y);" "\n"
|
||||
"double dot(genDType x, genDType y);" "\n"
|
||||
"@overload vec3 cross(vec3 x, vec3 y);" "\n"
|
||||
"@overload dvec3 cross(dvec3 x, dvec3 y);" "\n"
|
||||
"float dot(genFType x, genFType y) = " SPV(148) ";" "\n"
|
||||
"double dot(genDType x, genDType y) = " SPV(148) ";" "\n"
|
||||
"@overload vec3 cross(vec3 x, vec3 y) = " GLSL(68) ";" "\n"
|
||||
"@overload dvec3 cross(dvec3 x, dvec3 y) = " GLSL(68) ";" "\n"
|
||||
"genFType normalize(genFType x);" "\n"
|
||||
"genDType normalize(genDType x);" "\n"
|
||||
"genFType faceforward(genFType N, genFType I, genFType Nref);" "\n"
|
||||
|
|
|
@ -157,7 +157,7 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc);
|
|||
%token NIL GOTO SWITCH CASE DEFAULT ENUM ALGEBRA
|
||||
%token ARGS TYPEDEF EXTERN STATIC SYSTEM OVERLOAD NOT ATTRIBUTE
|
||||
%token <op> STRUCT
|
||||
%token HANDLE
|
||||
%token HANDLE INTRINSIC
|
||||
%token <spec> TYPE_SPEC TYPE_NAME TYPE_QUAL
|
||||
%token <spec> OBJECT_NAME
|
||||
%token CLASS DEFS ENCODE END IMPLEMENTATION INTERFACE PRIVATE
|
||||
|
@ -217,6 +217,7 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc);
|
|||
%type <element> element
|
||||
%type <expr> method_optional_state_expr optional_state_expr
|
||||
%type <expr> texpr vector_expr
|
||||
%type <expr> intrinsic
|
||||
%type <expr> statement
|
||||
%type <mut_expr> statements compound_statement compound_statement_ns
|
||||
%type <expr> else bool_label break_label continue_label
|
||||
|
@ -814,6 +815,11 @@ qc_nocode_func
|
|||
symbol_t *sym = function_symbol (spec);
|
||||
build_builtin_function (sym, bi_val, 0, spec.storage);
|
||||
}
|
||||
| identifier '=' intrinsic
|
||||
{
|
||||
specifier_t spec = qc_set_symbol ($<spec>0, $1);
|
||||
build_intrinsic_function (spec, $3);
|
||||
}
|
||||
| identifier '=' expr
|
||||
{
|
||||
specifier_t spec = qc_set_symbol ($<spec>0, $1);
|
||||
|
@ -1171,6 +1177,15 @@ function_body
|
|||
symbol_t *sym = function_symbol (spec);
|
||||
build_builtin_function (sym, bi_val, 0, spec.storage);
|
||||
}
|
||||
| '=' intrinsic
|
||||
{
|
||||
specifier_t spec = $<spec>0;
|
||||
build_intrinsic_function (spec, $2);
|
||||
}
|
||||
;
|
||||
|
||||
intrinsic
|
||||
: INTRINSIC '(' expr_list ')' { $$ = new_intrinsic_expr ($3); }
|
||||
;
|
||||
|
||||
storage_class
|
||||
|
@ -2948,6 +2963,7 @@ static keyword_t keywords[] = {
|
|||
{"@overload", QC_OVERLOAD, },
|
||||
{"@attribute", QC_ATTRIBUTE, },
|
||||
{"@handle", QC_HANDLE, },
|
||||
{"@intrinsic", QC_INTRINSIC, },
|
||||
};
|
||||
|
||||
static int
|
||||
|
|
|
@ -166,13 +166,32 @@ spirv_Extension (const char *ext, defspace_t *space)
|
|||
static unsigned
|
||||
spirv_ExtInstImport (const char *imp, spirvctx_t *ctx)
|
||||
{
|
||||
auto strings = ctx->module->strings;
|
||||
auto insn = spirv_str_insn (SpvOpExtInstImport, 2, 0, imp, strings);
|
||||
auto space = ctx->module->extinst_imports->space;
|
||||
auto insn = spirv_str_insn (SpvOpExtInstImport, 2, 0, imp, space);
|
||||
int id = spirv_id (ctx);
|
||||
INSN (insn, 1) = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
spirv_extinst_import (module_t *module, const char *import, spirvctx_t *ctx)
|
||||
{
|
||||
if (!module->extinst_imports) {
|
||||
module->extinst_imports = new_symtab (nullptr, stab_enum);
|
||||
module->extinst_imports->space = defspace_new (ds_backed);
|
||||
}
|
||||
auto imp = symtab_lookup (module->extinst_imports, import);
|
||||
if (!imp) {
|
||||
imp = new_symbol (import);
|
||||
imp->sy_type = sy_offset;
|
||||
symtab_addsymbol (module->extinst_imports, imp);
|
||||
}
|
||||
if (ctx && !imp->offset) {
|
||||
imp->offset = spirv_ExtInstImport (import, ctx);
|
||||
}
|
||||
return imp->offset;
|
||||
}
|
||||
|
||||
static void
|
||||
spirv_MemoryModel (SpvAddressingModel addressing, SpvMemoryModel memory,
|
||||
defspace_t *space)
|
||||
|
@ -1579,6 +1598,36 @@ spirv_select (const expr_t *e, spirvctx_t *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
spirv_intrinsic (const expr_t *e, spirvctx_t *ctx)
|
||||
{
|
||||
auto intr = e->intrinsic;
|
||||
unsigned op = expr_integral (intr.opcode);
|
||||
|
||||
int count = list_count (&intr.operands);
|
||||
int start = 0;
|
||||
const expr_t *operands[count + 1] = {};
|
||||
unsigned op_ids[count + 1] = {};
|
||||
list_scatter (&intr.operands, operands);
|
||||
if (op == SpvOpExtInst) {
|
||||
auto set = expr_string (operands[0]);
|
||||
op_ids[0] = spirv_extinst_import (ctx->module, set, ctx);
|
||||
op_ids[1] = expr_integral (operands[1]);
|
||||
start = 2;
|
||||
}
|
||||
for (int i = start; i < count; i++) {
|
||||
op_ids[i] = spirv_emit_expr (operands[i], ctx);
|
||||
}
|
||||
unsigned tid = type_id (intr.res_type, ctx);
|
||||
unsigned id = spirv_id (ctx);
|
||||
|
||||
auto insn = spirv_new_insn (op, 3 + count, ctx->code_space);
|
||||
INSN (insn, 1) = tid;
|
||||
INSN (insn, 2) = id;
|
||||
memcpy (&INSN (insn, 3), op_ids, count * sizeof (op_ids[0]));
|
||||
return id;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
spirv_emit_expr (const expr_t *e, spirvctx_t *ctx)
|
||||
{
|
||||
|
@ -1601,6 +1650,7 @@ spirv_emit_expr (const expr_t *e, spirvctx_t *ctx)
|
|||
[ex_array] = spirv_field_array,
|
||||
[ex_loop] = spirv_loop,
|
||||
[ex_select] = spirv_select,
|
||||
[ex_intrinsic] = spirv_intrinsic,
|
||||
};
|
||||
|
||||
if (e->type >= ex_count) {
|
||||
|
@ -1649,23 +1699,6 @@ spirv_write (struct pr_info_s *pr, const char *filename)
|
|||
INSN (header, 3) = 0; // Filled in later
|
||||
INSN (header, 4) = 0; // Reserved
|
||||
|
||||
for (auto cap = pr->module->capabilities.head; cap; cap = cap->next) {
|
||||
spirv_Capability (expr_uint (cap->expr), space);
|
||||
}
|
||||
for (auto ext = pr->module->extensions.head; ext; ext = ext->next) {
|
||||
spirv_Extension (expr_string (ext->expr), space);
|
||||
}
|
||||
for (auto imp = pr->module->extinst_imports.head; imp; imp = imp->next) {
|
||||
//FIXME need to store id where it can be used for instructions
|
||||
spirv_ExtInstImport (expr_string (imp->expr), &ctx);
|
||||
}
|
||||
|
||||
spirv_MemoryModel (expr_uint (pr->module->addressing_model),
|
||||
expr_uint (pr->module->memory_model), space);
|
||||
|
||||
|
||||
auto srcid = spirv_String (pr->src_name, &ctx);
|
||||
spirv_Source (0, 1, srcid, nullptr, &ctx);
|
||||
for (auto func = pr->func_head; func; func = func->next)
|
||||
{
|
||||
auto func_id = spirv_function (func, &ctx);
|
||||
|
@ -1676,11 +1709,28 @@ spirv_write (struct pr_info_s *pr, const char *filename)
|
|||
}
|
||||
}
|
||||
|
||||
auto mod = pr->module;
|
||||
for (auto cap = pr->module->capabilities.head; cap; cap = cap->next) {
|
||||
spirv_Capability (expr_uint (cap->expr), space);
|
||||
}
|
||||
for (auto ext = pr->module->extensions.head; ext; ext = ext->next) {
|
||||
spirv_Extension (expr_string (ext->expr), space);
|
||||
}
|
||||
if (mod->extinst_imports) {
|
||||
ADD_DATA (space, mod->extinst_imports->space);
|
||||
}
|
||||
|
||||
spirv_MemoryModel (expr_uint (pr->module->addressing_model),
|
||||
expr_uint (pr->module->memory_model), space);
|
||||
|
||||
|
||||
auto srcid = spirv_String (pr->src_name, &ctx);
|
||||
spirv_Source (0, 1, srcid, nullptr, &ctx);
|
||||
|
||||
auto main_sym = symtab_lookup (pr->symtab, "main");
|
||||
if (main_sym && main_sym->sy_type == sy_func) {
|
||||
}
|
||||
|
||||
auto mod = pr->module;
|
||||
INSN (header, 3) = ctx.id + 1;
|
||||
ADD_DATA (space, mod->entry_points);
|
||||
ADD_DATA (space, mod->exec_modes);
|
||||
|
@ -1715,8 +1765,7 @@ spirv_add_extension (module_t *module, const char *extension)
|
|||
void
|
||||
spirv_add_extinst_import (module_t *module, const char *import)
|
||||
{
|
||||
auto imp = new_string_expr (import);
|
||||
list_append (&module->extinst_imports, imp);
|
||||
spirv_extinst_import (module, import, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in a new issue