[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:
Bill Currie 2024-11-25 01:39:27 +09:00
parent b9ed0dcc64
commit 1e5d500f8b
10 changed files with 174 additions and 46 deletions

View file

@ -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.

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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)
{

View file

@ -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;
}

View file

@ -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 (&params->list) : 0;
const expr_t *args[num_params + 1];
const expr_t *args[num_params + 1] = {};
if (params) {
list_scatter_rev (&params->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)
{

View file

@ -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"

View file

@ -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

View file

@ -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