mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-26 05:31:50 +00:00
They're currently wrong since they're meant to be for specialization constants (and that whole system is currently broken anyway) but are instead raw code expressions, but progress is progress.
776 lines
22 KiB
C
776 lines
22 KiB
C
/*
|
|
spirv.c
|
|
|
|
qfcc spir-v file support
|
|
|
|
Copyright (C) 2024 Bill Currie <bill@taniwha.org>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include <spirv/unified1/GLSL.std.450.h>
|
|
|
|
#include "QF/quakeio.h"
|
|
|
|
#include "tools/qfcc/include/def.h"
|
|
#include "tools/qfcc/include/defspace.h"
|
|
#include "tools/qfcc/include/diagnostic.h"
|
|
#include "tools/qfcc/include/function.h"
|
|
#include "tools/qfcc/include/qfcc.h"
|
|
#include "tools/qfcc/include/spirv.h"
|
|
#include "tools/qfcc/include/statements.h"
|
|
#include "tools/qfcc/include/strpool.h"
|
|
#include "tools/qfcc/include/symtab.h"
|
|
#include "tools/qfcc/include/type.h"
|
|
|
|
typedef struct spirvctx_s {
|
|
defspace_t *space;
|
|
defspace_t *linkage;
|
|
defspace_t *strings;
|
|
defspace_t *names;
|
|
defspace_t *types;
|
|
defspace_t *code;
|
|
struct DARRAY_TYPE (unsigned) type_ids;
|
|
unsigned id;
|
|
} spirvctx_t;
|
|
|
|
static def_t *
|
|
spirv_new_insn (int op, int size, defspace_t *space)
|
|
{
|
|
auto def = new_def (nullptr, nullptr, space, sc_static);
|
|
def->offset = defspace_alloc_loc (space, size);
|
|
|
|
D_INT (def) = (op & SpvOpCodeMask) | (size << SpvWordCountShift);
|
|
return def;
|
|
}
|
|
|
|
static def_t *
|
|
spirv_str_insn (int op, int offs, int extra, const char *str, defspace_t *space)
|
|
{
|
|
int len = strlen (str) + 1;
|
|
int str_size = RUP(len, 4) / 4;
|
|
int size = offs + str_size + extra;
|
|
auto def = spirv_new_insn (op, size, space);
|
|
D_var_o(int, def, str_size - 1) = 0;
|
|
memcpy (&D_var_o(int, def, offs), str, len);
|
|
return def;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_id (spirvctx_t *ctx)
|
|
{
|
|
return ++ctx->id;
|
|
}
|
|
|
|
static void
|
|
spirv_Capability (SpvCapability capability, defspace_t *space)
|
|
{
|
|
auto def = spirv_new_insn (SpvOpCapability, 2, space);
|
|
D_var_o(int, def, 1) = capability;
|
|
}
|
|
|
|
static void
|
|
spirv_Extension (const char *ext, defspace_t *space)
|
|
{
|
|
spirv_str_insn (SpvOpExtension, 1, 0, ext, space);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_ExtInstImport (const char *imp, spirvctx_t *ctx)
|
|
{
|
|
auto def = spirv_str_insn (SpvOpExtInstImport, 2, 0, imp, ctx->space);
|
|
int id = spirv_id (ctx);
|
|
D_var_o(int, def, 1) = id;
|
|
return id;
|
|
}
|
|
|
|
static void
|
|
spirv_MemoryModel (SpvAddressingModel addressing, SpvMemoryModel memory,
|
|
defspace_t *space)
|
|
{
|
|
auto def = spirv_new_insn (SpvOpMemoryModel, 3, space);
|
|
D_var_o(int, def, 1) = addressing;
|
|
D_var_o(int, def, 2) = memory;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_String (const char *name, spirvctx_t *ctx)
|
|
{
|
|
auto def = spirv_str_insn (SpvOpString, 2, 0, name, ctx->strings);
|
|
int id = spirv_id (ctx);
|
|
D_var_o(int, def, 1) = id;
|
|
return id;
|
|
}
|
|
|
|
static void
|
|
spirv_Source (unsigned lang, unsigned version, unsigned srcid,
|
|
const char *src_str, spirvctx_t *ctx)
|
|
{
|
|
auto def = spirv_new_insn (SpvOpSource, 4, ctx->strings);
|
|
D_var_o(int, def, 1) = lang;
|
|
D_var_o(int, def, 2) = version;
|
|
D_var_o(int, def, 3) = srcid;
|
|
}
|
|
|
|
static void
|
|
spirv_Name (unsigned id, const char *name, spirvctx_t *ctx)
|
|
{
|
|
int len = strlen (name) + 1;
|
|
auto def = spirv_new_insn (SpvOpName, 2 + RUP(len, 4) / 4, ctx->names);
|
|
D_var_o(int, def, 1) = id;
|
|
memcpy (&D_var_o(int, def, 2), name, len);
|
|
}
|
|
|
|
static void
|
|
spirv_MemberName (unsigned id, unsigned member, const char *name,
|
|
spirvctx_t *ctx)
|
|
{
|
|
int len = strlen (name) + 1;
|
|
auto def = spirv_new_insn (SpvOpMemberName, 3 + RUP(len, 4) / 4,
|
|
ctx->names);
|
|
D_var_o(int, def, 1) = id;
|
|
D_var_o(int, def, 2) = member;
|
|
memcpy (&D_var_o(int, def, 3), name, len);
|
|
}
|
|
|
|
static unsigned type_id (const type_t *type, spirvctx_t *ctx);
|
|
|
|
static unsigned
|
|
spirv_TypeVoid (spirvctx_t *ctx)
|
|
{
|
|
auto def = spirv_new_insn (SpvOpTypeVoid, 2, ctx->types);
|
|
D_var_o(int, def, 1) = spirv_id (ctx);
|
|
return D_var_o(int, def, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeInt (unsigned bitwidth, bool is_signed, spirvctx_t *ctx)
|
|
{
|
|
auto def = spirv_new_insn (SpvOpTypeInt, 4, ctx->types);
|
|
D_var_o(int, def, 1) = spirv_id (ctx);
|
|
D_var_o(int, def, 2) = bitwidth;
|
|
D_var_o(int, def, 3) = is_signed;
|
|
return D_var_o(int, def, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeFloat (unsigned bitwidth, spirvctx_t *ctx)
|
|
{
|
|
auto def = spirv_new_insn (SpvOpTypeFloat, 3, ctx->types);
|
|
D_var_o(int, def, 1) = spirv_id (ctx);
|
|
D_var_o(int, def, 2) = bitwidth;
|
|
return D_var_o(int, def, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeVector (unsigned base, unsigned width, spirvctx_t *ctx)
|
|
{
|
|
auto def = spirv_new_insn (SpvOpTypeVector, 4, ctx->types);
|
|
D_var_o(int, def, 1) = spirv_id (ctx);
|
|
D_var_o(int, def, 2) = base;
|
|
D_var_o(int, def, 3) = width;
|
|
return D_var_o(int, def, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeMatrix (unsigned col_type, unsigned columns, spirvctx_t *ctx)
|
|
{
|
|
auto def = spirv_new_insn (SpvOpTypeMatrix, 4, ctx->types);
|
|
D_var_o(int, def, 1) = spirv_id (ctx);
|
|
D_var_o(int, def, 2) = col_type;
|
|
D_var_o(int, def, 3) = columns;
|
|
return D_var_o(int, def, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypePointer (const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
auto rtype = dereference_type (type);
|
|
unsigned rid = type_id (rtype, ctx);
|
|
|
|
auto def = spirv_new_insn (SpvOpTypePointer, 4, ctx->types);
|
|
D_var_o(int, def, 1) = spirv_id (ctx);
|
|
D_var_o(int, def, 2) = SpvStorageClassFunction;//FIXME
|
|
D_var_o(int, def, 3) = rid;
|
|
return D_var_o(int, def, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeStruct (const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
auto symtab = type->symtab;
|
|
int num_members = 0;
|
|
for (auto s = symtab->symbols; s; s = s->next) {
|
|
num_members++;
|
|
}
|
|
unsigned member_types[num_members];
|
|
num_members = 0;
|
|
for (auto s = symtab->symbols; s; s = s->next) {
|
|
member_types[num_members++] = type_id (s->type, ctx);
|
|
}
|
|
|
|
unsigned id = spirv_id (ctx);
|
|
auto def = spirv_new_insn (SpvOpTypeStruct, 2 + num_members, ctx->types);
|
|
D_var_o(int, def, 1) = id;
|
|
memcpy (&D_var_o(int, def, 2), member_types, sizeof (member_types));
|
|
|
|
spirv_Name (id, type->name + 4, ctx);
|
|
|
|
num_members = 0;
|
|
for (auto s = symtab->symbols; s; s = s->next) {
|
|
int m = num_members++;
|
|
spirv_MemberName (id, m, s->name, ctx);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeFunction (symbol_t *fsym, spirvctx_t *ctx)
|
|
{
|
|
int num_params = 0;
|
|
for (auto p = fsym->params; p; p = p->next) {
|
|
num_params++;
|
|
}
|
|
unsigned ret_type = type_id (fsym->type->func.ret_type, ctx);
|
|
|
|
unsigned param_types[num_params];
|
|
num_params = 0;
|
|
for (auto p = fsym->params; p; p = p->next) {
|
|
auto ptype = p->type;
|
|
if (p->qual != pq_const) {
|
|
ptype = pointer_type (ptype);
|
|
}
|
|
param_types[num_params++] = type_id (ptype, ctx);
|
|
}
|
|
|
|
unsigned ft_id = spirv_id (ctx);
|
|
auto def = spirv_new_insn (SpvOpTypeFunction, 3 + num_params, ctx->types);
|
|
D_var_o(int, def, 1) = ft_id;
|
|
D_var_o(int, def, 2) = ret_type;
|
|
for (int i = 0; i < num_params; i++) {
|
|
D_var_o(int, def, 3 + i) = param_types[i];
|
|
}
|
|
return ft_id;
|
|
}
|
|
|
|
static unsigned
|
|
type_id (const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
if (type->id < ctx->type_ids.size && ctx->type_ids.a[type->id]) {
|
|
return ctx->type_ids.a[type->id];
|
|
}
|
|
if (type->id >= ctx->type_ids.size) {
|
|
size_t base = ctx->type_ids.size;
|
|
DARRAY_RESIZE (&ctx->type_ids, type->id + 1);
|
|
while (base < type->id + 1) {
|
|
ctx->type_ids.a[base++] = 0;
|
|
}
|
|
}
|
|
unsigned id = 0;
|
|
if (is_void (type)) {
|
|
id = spirv_TypeVoid (ctx);
|
|
} else if (is_vector (type)) {
|
|
// spir-v doesn't allow duplicate non-aggregate types, so emit
|
|
// vector as vec3
|
|
auto vtype = vector_type (&type_float, 3);
|
|
id = type_id (vtype, ctx);
|
|
} else if (is_quaternion (type)) {
|
|
// spir-v doesn't allow duplicate non-aggregate types, so emit
|
|
// quaternion as vec4
|
|
auto qtype = vector_type (&type_float, 4);
|
|
id = type_id (qtype, ctx);
|
|
} else if (type_cols (type) > 1) {
|
|
auto ctype = vector_type (base_type (type), type_rows (type));
|
|
unsigned cid = type_id (ctype, ctx);
|
|
id = spirv_TypeMatrix (cid, type_cols (type), ctx);
|
|
} else if (type_width (type) > 1) {
|
|
auto btype = base_type (type);
|
|
unsigned bid = type_id (btype, ctx);
|
|
id = spirv_TypeVector (bid, type_width (type), ctx);
|
|
} else if (is_int (type)) {
|
|
id = spirv_TypeInt (32, true, ctx);
|
|
} else if (is_uint (type)) {
|
|
id = spirv_TypeInt (32, false, ctx);
|
|
} else if (is_long (type)) {
|
|
id = spirv_TypeInt (64, true, ctx);
|
|
} else if (is_ulong (type)) {
|
|
id = spirv_TypeInt (64, false, ctx);
|
|
} else if (is_float (type)) {
|
|
id = spirv_TypeFloat (32, ctx);
|
|
} else if (is_double (type)) {
|
|
id = spirv_TypeFloat (64, ctx);
|
|
} else if (is_ptr (type)) {
|
|
id = spirv_TypePointer (type, ctx);
|
|
} else if (is_struct (type)) {
|
|
id = spirv_TypeStruct (type, ctx);
|
|
}
|
|
if (!id) {
|
|
dstring_t *str = dstring_newstr ();
|
|
print_type_str (str, type);
|
|
internal_error (0, "can't emit type %s", str->str);
|
|
}
|
|
|
|
ctx->type_ids.a[type->id] = id;
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_Label (spirvctx_t *ctx)
|
|
{
|
|
auto def = spirv_new_insn (SpvOpLabel, 2, ctx->code);
|
|
D_var_o(int, def, 1) = spirv_id (ctx);
|
|
return D_var_o(int, def, 1);
|
|
}
|
|
|
|
static void
|
|
spirv_Unreachable (spirvctx_t *ctx)
|
|
{
|
|
spirv_new_insn (SpvOpUnreachable, 1, ctx->code);
|
|
}
|
|
|
|
static void
|
|
spirv_FunctionEnd (spirvctx_t *ctx)
|
|
{
|
|
spirv_new_insn (SpvOpFunctionEnd, 1, ctx->code);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_FunctionParameter (const char *name, const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
unsigned id = spirv_id (ctx);
|
|
auto def = spirv_new_insn (SpvOpFunctionParameter, 3, ctx->code);
|
|
D_var_o(int, def, 1) = type_id (type, ctx);
|
|
D_var_o(int, def, 2) = id;
|
|
spirv_Name (id, name, ctx);
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_function (function_t *func, spirvctx_t *ctx)
|
|
{
|
|
unsigned ft_id = spirv_TypeFunction (func->sym, ctx);
|
|
|
|
unsigned func_id = spirv_id (ctx);
|
|
auto def = spirv_new_insn (SpvOpFunction, 5, ctx->code);
|
|
D_var_o(int, def, 1) = type_id (func->type->func.ret_type, ctx);
|
|
D_var_o(int, def, 2) = func_id;
|
|
D_var_o(int, def, 3) = 0;
|
|
D_var_o(int, def, 4) = ft_id;
|
|
spirv_Name (func_id, GETSTR (func->s_name), ctx);
|
|
|
|
for (auto p = func->sym->params; p; p = p->next) {
|
|
auto ptype = p->type;
|
|
if (p->qual != pq_const) {
|
|
ptype = pointer_type (ptype);
|
|
}
|
|
spirv_FunctionParameter (p->name, ptype, ctx);
|
|
}
|
|
|
|
if (!func->sblock) {
|
|
spirv_Label (ctx);
|
|
spirv_Unreachable (ctx);
|
|
}
|
|
for (auto sblock = func->sblock; sblock; sblock = sblock->next) {
|
|
if (!sblock->id) {
|
|
sblock->id = spirv_Label (ctx);
|
|
}
|
|
spirv_Unreachable (ctx);
|
|
}
|
|
spirv_FunctionEnd (ctx);
|
|
return func_id;
|
|
}
|
|
|
|
static void
|
|
spirv_EntryPoint (unsigned func_id, const char *func_name,
|
|
SpvExecutionModel model, spirvctx_t *ctx)
|
|
{
|
|
int len = strlen (func_name) + 1;
|
|
auto def = spirv_new_insn (SpvOpEntryPoint, 3 + RUP(len, 4) / 4,
|
|
ctx->linkage);
|
|
D_var_o(int, def, 1) = model;
|
|
D_var_o(int, def, 2) = func_id;
|
|
memcpy (&D_var_o(int, def, 3), func_name, len);
|
|
}
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
unsigned id;
|
|
} extinst_t;
|
|
|
|
typedef unsigned (*spirv_expr_f) (const expr_t *e, spirvctx_t *ctx);
|
|
|
|
typedef struct {
|
|
const char *op_name;
|
|
SpvOp op;
|
|
unsigned types1;
|
|
unsigned types2;
|
|
spirv_expr_f generate;
|
|
extinst_t *extinst;
|
|
} spvop_t;
|
|
|
|
static unsigned
|
|
spirv_generate_wedge (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_generate_qmul (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_generate_qvmul (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_generate_vqmul (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#define SPV_type(t) (1<<(t))
|
|
#define SPV_SINT (SPV_type(ev_int)|SPV_type(ev_long))
|
|
#define SPV_UINT (SPV_type(ev_uint)|SPV_type(ev_ulong))
|
|
#define SPV_FLOAT (SPV_type(ev_float)|SPV_type(ev_double))
|
|
#define SPV_INT (SPV_SINT|SPV_UINT)
|
|
|
|
static extinst_t glsl_450 = {
|
|
.name = "GLSL.std.450"
|
|
};
|
|
|
|
static spvop_t spv_ops[] = {
|
|
{"or", SpvOpLogicalOr, SPV_INT, SPV_INT },
|
|
{"and", SpvOpLogicalAnd, SPV_INT, SPV_INT },
|
|
|
|
{"eq", SpvOpIEqual, SPV_INT, SPV_INT },
|
|
{"eq", SpvOpFOrdEqual, SPV_FLOAT, SPV_FLOAT },
|
|
{"eq", SpvOpFOrdNotEqual, SPV_FLOAT, SPV_FLOAT },
|
|
{"ne", SpvOpINotEqual, SPV_INT, SPV_INT },
|
|
{"le", SpvOpULessThanEqual, SPV_UINT, SPV_UINT },
|
|
{"le", SpvOpSLessThanEqual, SPV_SINT, SPV_SINT },
|
|
{"le", SpvOpFOrdLessThanEqual, SPV_FLOAT, SPV_FLOAT },
|
|
{"ge", SpvOpUGreaterThanEqual, SPV_UINT, SPV_UINT },
|
|
{"ge", SpvOpSGreaterThanEqual, SPV_SINT, SPV_SINT },
|
|
{"ge", SpvOpFOrdGreaterThanEqual, SPV_FLOAT, SPV_FLOAT },
|
|
{"lt", SpvOpULessThan, SPV_UINT, SPV_UINT },
|
|
{"lt", SpvOpSLessThan, SPV_SINT, SPV_SINT },
|
|
{"lt", SpvOpFOrdLessThan, SPV_FLOAT, SPV_FLOAT },
|
|
{"gt", SpvOpUGreaterThan, SPV_UINT, SPV_UINT },
|
|
{"gt", SpvOpSGreaterThan, SPV_SINT, SPV_SINT },
|
|
{"gt", SpvOpFOrdGreaterThan, SPV_FLOAT, SPV_FLOAT },
|
|
|
|
{"sub", SpvOpSNegate, SPV_INT, 0 },
|
|
{"sub", SpvOpFNegate, SPV_FLOAT, 0 },
|
|
|
|
{"add", SpvOpIAdd, SPV_INT, SPV_INT },
|
|
{"add", SpvOpFAdd, SPV_FLOAT, SPV_FLOAT },
|
|
{"sub", SpvOpISub, SPV_INT, SPV_INT },
|
|
{"sub", SpvOpFSub, SPV_FLOAT, SPV_FLOAT },
|
|
{"mul", SpvOpIMul, SPV_INT, SPV_INT },
|
|
{"mul", SpvOpFMul, SPV_FLOAT, SPV_FLOAT },
|
|
{"div", SpvOpUDiv, SPV_UINT, SPV_UINT },
|
|
{"div", SpvOpSDiv, SPV_SINT, SPV_SINT },
|
|
{"div", SpvOpFDiv, SPV_FLOAT, SPV_FLOAT },
|
|
{"rem", SpvOpSRem, SPV_INT, SPV_INT },
|
|
{"rem", SpvOpFRem, SPV_FLOAT, SPV_FLOAT },
|
|
{"mod", SpvOpUMod, SPV_UINT, SPV_UINT },
|
|
{"mod", SpvOpSMod, SPV_SINT, SPV_SINT },
|
|
{"mod", SpvOpFMod, SPV_FLOAT, SPV_FLOAT },
|
|
|
|
{"bitor", SpvOpBitwiseOr, SPV_INT, SPV_INT },
|
|
{"bitxor", SpvOpBitwiseXor, SPV_INT, SPV_INT },
|
|
{"bitand", SpvOpBitwiseAnd, SPV_INT, SPV_INT },
|
|
|
|
{"bitnot", SpvOpNot, SPV_INT, 0 },
|
|
{"not", SpvOpLogicalNot, SPV_INT, 0 },
|
|
|
|
{"shl", SpvOpShiftLeftLogical, SPV_INT, SPV_INT },
|
|
{"shr", SpvOpShiftRightLogical, SPV_UINT, SPV_UINT },
|
|
{"shr", SpvOpShiftRightArithmetic, SPV_SINT, SPV_SINT },
|
|
|
|
{"dot", SpvOpDot, SPV_FLOAT, SPV_FLOAT },
|
|
{"scale", SpvOpVectorTimesScalar, SPV_FLOAT, SPV_FLOAT },
|
|
|
|
{"cross", GLSLstd450Cross, SPV_FLOAT, SPV_FLOAT,
|
|
.extinst = &glsl_450 },
|
|
{"wedge", .types1 = SPV_FLOAT, .types2 = SPV_FLOAT,
|
|
.generate = spirv_generate_wedge, .extinst = &glsl_450 },
|
|
{"qmul", .types1 = SPV_FLOAT, .types2 = SPV_FLOAT,
|
|
.generate = spirv_generate_qmul, .extinst = &glsl_450 },
|
|
{"qvmul", .types1 = SPV_FLOAT, .types2 = SPV_FLOAT,
|
|
.generate = spirv_generate_qvmul, .extinst = &glsl_450 },
|
|
{"vqmul", .types1 = SPV_FLOAT, .types2 = SPV_FLOAT,
|
|
.generate = spirv_generate_vqmul, .extinst = &glsl_450 },
|
|
};
|
|
|
|
static const spvop_t *
|
|
spirv_find_op (const char *op_name, etype_t type1, etype_t type2)
|
|
{
|
|
constexpr int num_ops = sizeof (spv_ops) / sizeof (spv_ops[0]);
|
|
for (int i = 0; i < num_ops; i++) {
|
|
if (strcmp (spv_ops[i].op_name, op_name) == 0
|
|
&& spv_ops[i].types1 & SPV_type(type1)
|
|
&& ((!spv_ops[i].types2 && type2 == ev_void)
|
|
|| (spv_ops[i].types2
|
|
&& (spv_ops[i].types2 & SPV_type(type2))))) {
|
|
return &spv_ops[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static unsigned spirv_emit_expr (const expr_t *e, spirvctx_t *ctx);
|
|
|
|
static unsigned
|
|
spirv_uexpr (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
if (e->expr.op == '+') {
|
|
return spirv_emit_expr (e->expr.e1, ctx);
|
|
}
|
|
|
|
auto op_name = convert_op (e->expr.op);
|
|
if (!op_name) {
|
|
internal_error (e, "unexpected unary op: %d\n", e->expr.op);
|
|
}
|
|
auto t = get_type (e->expr.e1);
|
|
auto spv_op = spirv_find_op (op_name, t->type, 0);
|
|
if (!spv_op) {
|
|
internal_error (e, "unexpected unary op_name: %s %s\n", op_name,
|
|
pr_type_name[t->type]);
|
|
}
|
|
if (!spv_op->op) {
|
|
internal_error (e, "unimplemented op: %s %s\n", op_name,
|
|
pr_type_name[t->type]);
|
|
}
|
|
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;
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_expr (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
auto op_name = convert_op (e->expr.op);
|
|
if (!op_name) {
|
|
internal_error (e, "unexpected binary op: %d\n", e->expr.op);
|
|
}
|
|
auto t1 = get_type (e->expr.e1);
|
|
auto t2 = get_type (e->expr.e1);
|
|
auto spv_op = spirv_find_op (op_name, t1->type, t2->type);
|
|
if (!spv_op) {
|
|
internal_error (e, "unexpected binary op_name: %s %s %s\n", op_name,
|
|
pr_type_name[t1->type],
|
|
pr_type_name[t2->type]);
|
|
}
|
|
if (!spv_op->op) {
|
|
internal_error (e, "unimplemented op: %s %s %s\n", op_name,
|
|
pr_type_name[t1->type],
|
|
pr_type_name[t2->type]);
|
|
}
|
|
unsigned bid1 = spirv_emit_expr (e->expr.e1, 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;
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_value (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
auto value = e->value;
|
|
if (!value->id) {
|
|
unsigned tid = type_id (value->type, ctx);
|
|
unsigned op = SpvOpConstant;
|
|
int val_size = 1;
|
|
pr_ulong_t val = 0;
|
|
if (is_bool (value->type)) {
|
|
op = value->int_val ? SpvOpConstantTrue : SpvOpConstantFalse;
|
|
val_size = 0;
|
|
} else {
|
|
if (type_size (value->type) == 1) {
|
|
val = value->uint_val;
|
|
val_size = 1;
|
|
} else if (type_size (value->type) == 2) {
|
|
val = value->ulong_val;
|
|
val_size = 2;
|
|
} else {
|
|
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 (val_size > 1) {
|
|
D_var_o(int, def, 4) = val >> 32;
|
|
}
|
|
}
|
|
return value->id;
|
|
}
|
|
|
|
static unsigned
|
|
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_value] = spirv_value,
|
|
};
|
|
|
|
if (e->type >= ex_count) {
|
|
internal_error (e, "bad sub-expression type: %d", e->type);
|
|
}
|
|
if (!funcs[e->type]) {
|
|
internal_error (e, "unexpected sub-expression type: %s",
|
|
expr_names[e->type]);
|
|
}
|
|
|
|
if (!e->id) {
|
|
unsigned id = funcs[e->type] (e, ctx);
|
|
//FIXME const cast (store elsewhere)
|
|
((expr_t *) e)->id = id;
|
|
}
|
|
return e->id;
|
|
}
|
|
|
|
bool
|
|
spirv_write (struct pr_info_s *pr, const char *filename)
|
|
{
|
|
spirvctx_t ctx = {
|
|
.space = defspace_new (ds_backed),
|
|
.linkage = defspace_new (ds_backed),
|
|
.strings = defspace_new (ds_backed),
|
|
.names = defspace_new (ds_backed),
|
|
.types = defspace_new (ds_backed),
|
|
.code = defspace_new (ds_backed),
|
|
.type_ids = DARRAY_STATIC_INIT (64),
|
|
.id = 0,
|
|
};
|
|
auto header = spirv_new_insn (0, 5, ctx.space);
|
|
D_var_o(int, header, 0) = SpvMagicNumber;
|
|
D_var_o(int, header, 1) = SpvVersion;
|
|
D_var_o(int, header, 2) = 0; // FIXME get a magic number for QFCC
|
|
D_var_o(int, header, 3) = 0; // Filled in later
|
|
D_var_o(int, header, 4) = 0; // Reserved
|
|
|
|
for (auto cap = pr->module->capabilities.head; cap; cap = cap->next) {
|
|
spirv_Capability (expr_uint (cap->expr), ctx.space);
|
|
}
|
|
for (auto ext = pr->module->extensions.head; ext; ext = ext->next) {
|
|
spirv_Extension (expr_string (ext->expr), ctx.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), ctx.space);
|
|
|
|
for (auto sym = pr->symtab->symbols; sym; sym = sym->next) {
|
|
if (sym->sy_type == sy_expr && is_constexpr (sym->expr)) {
|
|
puts (sym->name);
|
|
spirv_emit_expr (sym->expr, &ctx);
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (strcmp ("main", func->o_name) == 0) {
|
|
auto model = SpvExecutionModelVertex;//FIXME
|
|
spirv_EntryPoint (func_id, func->o_name, model, &ctx);
|
|
}
|
|
}
|
|
|
|
auto main_sym = symtab_lookup (pr->symtab, "main");
|
|
if (main_sym && main_sym->sy_type == sy_func) {
|
|
}
|
|
|
|
D_var_o(int, header, 3) = ctx.id + 1;
|
|
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.types->data, ctx.types->size);
|
|
defspace_add_data (ctx.space, ctx.code->data, ctx.code->size);
|
|
|
|
QFile *file = Qopen (filename, "wb");
|
|
Qwrite (file, ctx.space->data, ctx.space->size * sizeof (pr_type_t));
|
|
Qclose (file);
|
|
return false;
|
|
}
|
|
|
|
void
|
|
spirv_add_capability (module_t *module, SpvCapability capability)
|
|
{
|
|
auto cap = new_uint_expr (capability);
|
|
list_append (&module->capabilities, cap);
|
|
}
|
|
|
|
void
|
|
spirv_add_extension (module_t *module, const char *extension)
|
|
{
|
|
auto ext = new_string_expr (extension);
|
|
list_append (&module->extensions, ext);
|
|
}
|
|
|
|
void
|
|
spirv_add_extinst_import (module_t *module, const char *import)
|
|
{
|
|
auto imp = new_string_expr (import);
|
|
list_append (&module->extinst_imports, imp);
|
|
}
|
|
|
|
void
|
|
spirv_set_addressing_model (module_t *module, SpvAddressingModel model)
|
|
{
|
|
module->addressing_model = new_uint_expr (model);
|
|
}
|
|
|
|
void
|
|
spirv_set_memory_model (module_t *module, SpvMemoryModel model)
|
|
{
|
|
module->memory_model = new_uint_expr (model);
|
|
}
|