mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-25 21:21:14 +00:00
The main goal was to make it possible to give generic functions definitions (since the code would be very dependent on the actual parameter types), but will also allow for inline functions. It also helped move a lot of the back-end dependent code out of semantics processing and almost completely (if not completely) out of the parser. Possibly more importantly, it gets the dags flushing out of the parser, which means such is now shared by all front-ends. There's probably a lot of dead code in expr.c now, but that can be taken care of another time.
2074 lines
56 KiB
C
2074 lines
56 KiB
C
/*
|
|
target_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/attribute.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/glsl-lang.h"
|
|
#include "tools/qfcc/include/options.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/target.h"
|
|
#include "tools/qfcc/include/type.h"
|
|
#include "tools/qfcc/include/value.h"
|
|
|
|
#define INSN(i,o) D_var_o(int,(i),o)
|
|
#define ADD_DATA(d,s) defspace_add_data((d), (s)->data, (s)->size)
|
|
|
|
typedef struct spirvctx_s {
|
|
module_t *module;
|
|
|
|
defspace_t *decl_space;
|
|
defspace_t *code_space;
|
|
|
|
struct DARRAY_TYPE (unsigned) type_ids;
|
|
struct DARRAY_TYPE (unsigned) label_ids;
|
|
unsigned id;
|
|
} spirvctx_t;
|
|
|
|
static unsigned spirv_value (const expr_t *e, spirvctx_t *ctx);
|
|
|
|
static unsigned
|
|
spirv_id (spirvctx_t *ctx)
|
|
{
|
|
return ++ctx->id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_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];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
spirv_add_type_id (const type_t *type, unsigned id, spirvctx_t *ctx)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
ctx->type_ids.a[type->id] = id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_label_id (const ex_label_t *label, spirvctx_t *ctx)
|
|
{
|
|
if (label->id < ctx->label_ids.size && ctx->label_ids.a[label->id]) {
|
|
return ctx->label_ids.a[label->id];
|
|
}
|
|
unsigned id = spirv_id (ctx);
|
|
if (label->id >= ctx->label_ids.size) {
|
|
size_t base = ctx->label_ids.size;
|
|
DARRAY_RESIZE (&ctx->label_ids, label->id + 1);
|
|
while (base < label->id + 1) {
|
|
ctx->label_ids.a[base++] = 0;
|
|
}
|
|
}
|
|
ctx->label_ids.a[label->id] = id;
|
|
return id;
|
|
}
|
|
|
|
static bool
|
|
is_block_terminated (defspace_t *code_space)
|
|
{
|
|
def_t *last_insn = nullptr;
|
|
if (code_space->def_tail != &code_space->defs) {
|
|
last_insn = (def_t *) code_space->def_tail;
|
|
}
|
|
if (last_insn
|
|
&& ((INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpReturn
|
|
|| (INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpReturnValue
|
|
|| (INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpUnreachable
|
|
|| (INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpBranchConditional
|
|
|| (INSN(last_insn, 0) & SpvOpCodeMask) == SpvOpBranch)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static def_t *
|
|
spirv_new_insn (int op, int size, defspace_t *space)
|
|
{
|
|
auto insn = new_def (nullptr, nullptr, space, sc_static);
|
|
insn->offset = defspace_alloc_highwater (space, size);
|
|
|
|
INSN (insn, 0) = (op & SpvOpCodeMask) | (size << SpvWordCountShift);
|
|
return insn;
|
|
}
|
|
|
|
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 insn = spirv_new_insn (op, size, space);
|
|
INSN (insn, str_size - 1) = 0;
|
|
memcpy (&INSN (insn, offs), str, len);
|
|
return insn;
|
|
}
|
|
|
|
static void
|
|
spirv_Capability (SpvCapability capability, defspace_t *space)
|
|
{
|
|
auto insn = spirv_new_insn (SpvOpCapability, 2, space);
|
|
INSN (insn, 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 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)
|
|
{
|
|
auto insn = spirv_new_insn (SpvOpMemoryModel, 3, space);
|
|
INSN (insn, 1) = addressing;
|
|
INSN (insn, 2) = memory;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_String (const char *name, spirvctx_t *ctx)
|
|
{
|
|
auto strings = ctx->module->strings;
|
|
auto insn = spirv_str_insn (SpvOpString, 2, 0, name, strings);
|
|
int id = spirv_id (ctx);
|
|
INSN (insn, 1) = id;
|
|
return id;
|
|
}
|
|
|
|
static void
|
|
spirv_Source (unsigned lang, unsigned version, unsigned srcid,
|
|
const char *src_str, spirvctx_t *ctx)
|
|
{
|
|
auto strings = ctx->module->strings;
|
|
auto insn = spirv_new_insn (SpvOpSource, 4, strings);
|
|
INSN (insn, 1) = lang;
|
|
INSN (insn, 2) = version;
|
|
INSN (insn, 3) = srcid;
|
|
}
|
|
|
|
static void
|
|
spirv_Name (unsigned id, const char *name, spirvctx_t *ctx)
|
|
{
|
|
if (!name) {
|
|
name = "";
|
|
}
|
|
int len = strlen (name) + 1;
|
|
auto names = ctx->module->names;
|
|
auto insn = spirv_new_insn (SpvOpName, 2 + RUP(len, 4) / 4, names);
|
|
INSN (insn, 1) = id;
|
|
memcpy (&INSN (insn, 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 decorations = ctx->module->decorations;
|
|
auto insn = spirv_new_insn (SpvOpDecorate, 3 + size, decorations);
|
|
INSN (insn, 1) = id;
|
|
INSN (insn, 2) = decoration;
|
|
if (type == ev_int) {
|
|
INSN (insn, 3) = *(int *)literal;
|
|
}
|
|
}
|
|
|
|
static void
|
|
spirv_MemberName (unsigned id, unsigned member, const char *name,
|
|
spirvctx_t *ctx)
|
|
{
|
|
int len = strlen (name) + 1;
|
|
auto names = ctx->module->names;
|
|
auto insn = spirv_new_insn (SpvOpMemberName, 3 + RUP(len, 4) / 4, names);
|
|
INSN (insn, 1) = id;
|
|
INSN (insn, 2) = member;
|
|
memcpy (&INSN (insn, 3), name, len);
|
|
}
|
|
|
|
static unsigned type_id (const type_t *type, spirvctx_t *ctx);
|
|
|
|
static unsigned
|
|
spirv_TypeVoid (spirvctx_t *ctx)
|
|
{
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypeVoid, 2, globals);
|
|
INSN (insn, 1) = spirv_id (ctx);
|
|
return INSN (insn, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeInt (unsigned bitwidth, bool is_signed, spirvctx_t *ctx)
|
|
{
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypeInt, 4, globals);
|
|
INSN (insn, 1) = spirv_id (ctx);
|
|
INSN (insn, 2) = bitwidth;
|
|
INSN (insn, 3) = is_signed;
|
|
return INSN (insn, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeFloat (unsigned bitwidth, spirvctx_t *ctx)
|
|
{
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypeFloat, 3, globals);
|
|
INSN (insn, 1) = spirv_id (ctx);
|
|
INSN (insn, 2) = bitwidth;
|
|
return INSN (insn, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeVector (unsigned base, unsigned width, spirvctx_t *ctx)
|
|
{
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypeVector, 4, globals);
|
|
INSN (insn, 1) = spirv_id (ctx);
|
|
INSN (insn, 2) = base;
|
|
INSN (insn, 3) = width;
|
|
return INSN (insn, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeMatrix (unsigned col_type, unsigned columns, spirvctx_t *ctx)
|
|
{
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypeMatrix, 4, globals);
|
|
INSN (insn, 1) = spirv_id (ctx);
|
|
INSN (insn, 2) = col_type;
|
|
INSN (insn, 3) = columns;
|
|
return INSN (insn, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypePointer (const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
auto rtype = dereference_type (type);
|
|
unsigned rid = type_id (rtype, ctx);
|
|
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypePointer, 4, globals);
|
|
INSN (insn, 1) = spirv_id (ctx);
|
|
INSN (insn, 2) = type->fldptr.tag;
|
|
INSN (insn, 3) = rid;
|
|
return INSN (insn, 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 globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypeStruct, 2 + num_members, globals);
|
|
INSN (insn, 1) = id;
|
|
memcpy (&INSN (insn, 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_TypeArray (const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
auto ele_type = dereference_type (type);
|
|
auto count_expr = new_int_expr (type_count (type), false);
|
|
unsigned count = spirv_value (count_expr, ctx);
|
|
unsigned tid = type_id (ele_type, ctx);
|
|
unsigned id = spirv_id (ctx);
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypeArray, 4, globals);
|
|
INSN (insn, 1) = id;
|
|
INSN (insn, 2) = tid;
|
|
INSN (insn, 3) = count;
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeRuntimeArray (const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
auto ele_type = dereference_type (type);
|
|
unsigned tid = type_id (ele_type, ctx);
|
|
unsigned id = spirv_id (ctx);
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypeRuntimeArray, 3, globals);
|
|
INSN (insn, 1) = id;
|
|
INSN (insn, 2) = tid;
|
|
return id;
|
|
}
|
|
|
|
static void
|
|
spirv_mirror_bool (const type_t *type, unsigned id, spirvctx_t *ctx)
|
|
{
|
|
// This is rather hacky as spir-v supports only an abstract bool type,
|
|
// thus bool and lbool need to get the same type id
|
|
auto base = base_type (type);
|
|
int width = type_width (type);
|
|
// invert the size
|
|
if (is_bool (base)) {
|
|
base = &type_lbool;
|
|
} else {
|
|
base = &type_bool;
|
|
}
|
|
type = vector_type (base, width);
|
|
spirv_add_type_id (type, id, ctx);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_TypeBool (const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
auto globals = ctx->module->globals;
|
|
int id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpTypeBool, 2, globals);
|
|
INSN (insn, 1) = id;
|
|
spirv_mirror_bool (type, id, 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) {
|
|
if (is_void (p->type)) {
|
|
if (num_params || p->next) {
|
|
internal_error (0, "void param with other params");
|
|
}
|
|
break;
|
|
}
|
|
num_params++;
|
|
}
|
|
auto type = fsym->type;
|
|
if (spirv_type_id (type, ctx)) {
|
|
return spirv_type_id (type, ctx);
|
|
}
|
|
unsigned ret_type = type_id (fsym->type->func.ret_type, ctx);
|
|
|
|
unsigned param_types[num_params + 1];
|
|
num_params = 0;
|
|
for (auto p = fsym->params; p; p = p->next) {
|
|
auto ptype = p->type;
|
|
if (is_void (p->type)) {
|
|
break;
|
|
}
|
|
if (p->qual != pq_const) {
|
|
ptype = tagged_reference_type (SpvStorageClassFunction, ptype);
|
|
}
|
|
param_types[num_params++] = type_id (ptype, ctx);
|
|
}
|
|
|
|
unsigned ft_id = spirv_id (ctx);
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpTypeFunction, 3 + num_params, globals);
|
|
INSN (insn, 1) = ft_id;
|
|
INSN (insn, 2) = ret_type;
|
|
for (int i = 0; i < num_params; i++) {
|
|
INSN (insn, 3 + i) = param_types[i];
|
|
}
|
|
spirv_add_type_id (type, ft_id, ctx);
|
|
return ft_id;
|
|
}
|
|
|
|
static unsigned
|
|
type_id (const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
type = unalias_type (type);
|
|
if (spirv_type_id (type, ctx)) {
|
|
return spirv_type_id (type, ctx);
|
|
}
|
|
unsigned id = 0;
|
|
if (is_void (type)) {
|
|
id = spirv_TypeVoid (ctx);
|
|
} else if (is_array (type)) {
|
|
//FIXME should size be checked against something for validity?
|
|
if (type_count (type)) {
|
|
id = spirv_TypeArray (type, ctx);
|
|
} else {
|
|
id = spirv_TypeRuntimeArray (type, 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 (is_matrix (type)) {
|
|
auto ctype = column_type (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);
|
|
if (is_boolean (type)) {
|
|
spirv_mirror_bool (type, id, 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);
|
|
} else if (is_boolean (type)) {
|
|
id = spirv_TypeBool (type, ctx);
|
|
}
|
|
if (!id) {
|
|
dstring_t *str = dstring_newstr ();
|
|
print_type_str (str, type);
|
|
internal_error (0, "can't emit type %s", str->str);
|
|
}
|
|
spirv_add_type_id (type, id, ctx);
|
|
return id;
|
|
}
|
|
|
|
// put a label into decl_space. used only when starting a function
|
|
static unsigned
|
|
spirv_DeclLabel (spirvctx_t *ctx)
|
|
{
|
|
auto insn = spirv_new_insn (SpvOpLabel, 2, ctx->decl_space);
|
|
INSN (insn, 1) = spirv_id (ctx);
|
|
return INSN (insn, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_Label (spirvctx_t *ctx)
|
|
{
|
|
auto insn = spirv_new_insn (SpvOpLabel, 2, ctx->code_space);
|
|
INSN (insn, 1) = spirv_id (ctx);
|
|
return INSN (insn, 1);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_LabelId (unsigned id, spirvctx_t *ctx)
|
|
{
|
|
auto insn = spirv_new_insn (SpvOpLabel, 2, ctx->code_space);
|
|
INSN (insn, 1) = id;
|
|
return id;
|
|
}
|
|
|
|
static void
|
|
spirv_LoopMerge (unsigned merge, unsigned cont, spirvctx_t *ctx)
|
|
{
|
|
auto insn = spirv_new_insn (SpvOpLoopMerge, 4, ctx->code_space);
|
|
INSN (insn, 1) = merge;
|
|
INSN (insn, 2) = cont;
|
|
INSN (insn, 3) = SpvLoopControlMaskNone;
|
|
}
|
|
|
|
static void
|
|
spirv_SelectionMerge (unsigned merge, spirvctx_t *ctx)
|
|
{
|
|
auto insn = spirv_new_insn (SpvOpSelectionMerge, 3, ctx->code_space);
|
|
INSN (insn, 1) = merge;
|
|
INSN (insn, 2) = SpvSelectionControlMaskNone;
|
|
}
|
|
|
|
static void
|
|
spirv_Branch (unsigned label, spirvctx_t *ctx)
|
|
{
|
|
auto insn = spirv_new_insn (SpvOpBranch, 2, ctx->code_space);
|
|
INSN (insn, 1) = label;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_SplitBlockId (unsigned id, spirvctx_t *ctx)
|
|
{
|
|
spirv_Branch (id, ctx);
|
|
spirv_LabelId (id, ctx);
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_SplitBlock (spirvctx_t *ctx)
|
|
{
|
|
return spirv_SplitBlockId (spirv_id (ctx), ctx);
|
|
}
|
|
|
|
static void
|
|
spirv_BranchConditional (bool not, unsigned test, unsigned true_label,
|
|
unsigned false_label, spirvctx_t *ctx)
|
|
{
|
|
auto insn = spirv_new_insn (SpvOpBranchConditional, 4, ctx->code_space);
|
|
INSN (insn, 1) = test;
|
|
INSN (insn, 2) = not ? false_label : true_label;
|
|
INSN (insn, 3) = not ? true_label : false_label;
|
|
}
|
|
|
|
static void
|
|
spirv_Unreachable (spirvctx_t *ctx)
|
|
{
|
|
spirv_new_insn (SpvOpUnreachable, 1, ctx->code_space);
|
|
}
|
|
|
|
static void
|
|
spirv_FunctionEnd (spirvctx_t *ctx)
|
|
{
|
|
spirv_new_insn (SpvOpFunctionEnd, 1, ctx->code_space);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_FunctionParameter (const char *name, const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
unsigned id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpFunctionParameter, 3, ctx->decl_space);
|
|
INSN (insn, 1) = type_id (type, ctx);
|
|
INSN (insn, 2) = id;
|
|
spirv_Name (id, name, ctx);
|
|
return id;
|
|
}
|
|
|
|
static SpvStorageClass
|
|
spirv_storage_class (unsigned storage)
|
|
{
|
|
auto interface = glsl_iftype_from_sc (storage);
|
|
SpvStorageClass sc = 0;
|
|
if (interface < glsl_num_interfaces) {
|
|
static SpvStorageClass iface_storage[glsl_num_interfaces] = {
|
|
[glsl_in] = SpvStorageClassInput,
|
|
[glsl_out] = SpvStorageClassOutput,
|
|
[glsl_uniform] = SpvStorageClassUniform,
|
|
[glsl_buffer] = SpvStorageClassStorageBuffer,
|
|
};
|
|
sc = iface_storage[interface];
|
|
} else if (storage < sc_count) {
|
|
static SpvStorageClass sc_storage[sc_count] = {
|
|
[sc_global] = SpvStorageClassPrivate,
|
|
[sc_static] = SpvStorageClassPrivate,
|
|
[sc_local] = SpvStorageClassFunction,
|
|
[sc_inout] = SpvStorageClassFunction,
|
|
};
|
|
sc = sc_storage[storage];
|
|
}
|
|
if (!sc) {
|
|
internal_error (0, "invalid storage class: %d", storage);
|
|
}
|
|
return sc;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_variable (symbol_t *sym, spirvctx_t *ctx)
|
|
{
|
|
if (sym->sy_type != sy_var) {
|
|
internal_error (0, "unexpected variable symbol type");
|
|
}
|
|
auto space = ctx->module->globals;
|
|
auto storage = spirv_storage_class (sym->var.storage);
|
|
if (storage == SpvStorageClassFunction) {
|
|
space = ctx->decl_space;
|
|
} else {
|
|
DARRAY_APPEND (&ctx->module->interface_syms, sym);
|
|
}
|
|
auto type = sym->type;
|
|
unsigned tid = type_id (type, ctx);
|
|
unsigned id = spirv_id (ctx);
|
|
//FIXME init value
|
|
auto insn = spirv_new_insn (SpvOpVariable, 4, space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id;
|
|
INSN (insn, 3) = storage;
|
|
spirv_Name (id, sym->name, ctx);
|
|
return id;
|
|
}
|
|
|
|
static unsigned spirv_emit_expr (const expr_t *e, spirvctx_t *ctx);
|
|
|
|
static unsigned
|
|
spirv_undef (const type_t *type, spirvctx_t *ctx)
|
|
{
|
|
unsigned type_id = spirv_type_id (type, ctx);
|
|
unsigned id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpUndef, 3, ctx->module->globals);
|
|
INSN (insn, 1) = type_id;
|
|
INSN (insn, 2) = id;
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_function_ref (function_t *func, spirvctx_t *ctx)
|
|
{
|
|
unsigned func_id = func->id;
|
|
if (!func_id) {
|
|
func_id = spirv_id (ctx);
|
|
func->id = func_id;
|
|
}
|
|
return func_id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_function (function_t *func, spirvctx_t *ctx)
|
|
{
|
|
unsigned ft_id = spirv_TypeFunction (func->sym, ctx);
|
|
|
|
defspace_reset (ctx->code_space);
|
|
defspace_reset (ctx->decl_space);
|
|
|
|
auto ret_type = func->type->func.ret_type;
|
|
unsigned func_id = spirv_function_ref (func, ctx);
|
|
|
|
auto insn = spirv_new_insn (SpvOpFunction, 5, ctx->decl_space);
|
|
INSN (insn, 1) = type_id (ret_type, ctx);
|
|
INSN (insn, 2) = func_id;
|
|
INSN (insn, 3) = 0;
|
|
INSN (insn, 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 (is_void (ptype)) {
|
|
break;
|
|
}
|
|
if (p->qual != pq_const) {
|
|
ptype = tagged_reference_type (SpvStorageClassFunction, ptype);
|
|
}
|
|
unsigned pid = spirv_FunctionParameter (p->name, ptype, ctx);
|
|
if (func->parameters) {
|
|
for (auto s = func->parameters->symbols; s; s = s->next) {
|
|
if (p->name && p->name[0] && s->name && s->name[0]
|
|
&& !strcmp (s->name, p->name)) {
|
|
s->id = pid;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (func->locals) {
|
|
spirv_DeclLabel (ctx);
|
|
for (auto sym = func->locals->symbols; sym; sym = sym->next) {
|
|
sym->id = spirv_variable (sym, ctx);
|
|
}
|
|
} else {
|
|
spirv_Label (ctx);
|
|
}
|
|
int start_size = ctx->code_space->size;
|
|
if (func->exprs) {
|
|
spirv_emit_expr (func->exprs, ctx);
|
|
}
|
|
if (!is_block_terminated (ctx->code_space)) {
|
|
if (is_void (ret_type)) {
|
|
spirv_new_insn (SpvOpReturn, 1, ctx->code_space);
|
|
} else {
|
|
unsigned ret_id = spirv_undef (ret_type, ctx);
|
|
auto insn = spirv_new_insn (SpvOpReturnValue, 2, ctx->code_space);
|
|
INSN (insn, 1) = ret_id;
|
|
}
|
|
}
|
|
if (ctx->code_space->size == start_size) {
|
|
spirv_Unreachable (ctx);
|
|
}
|
|
spirv_FunctionEnd (ctx);
|
|
ADD_DATA (ctx->module->func_definitions, ctx->decl_space);
|
|
ADD_DATA (ctx->module->func_definitions, ctx->code_space);
|
|
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;
|
|
int iface_start = 3 + RUP(len, 4) / 4;
|
|
auto linkage = ctx->module->entry_points;
|
|
int count = ctx->module->interface_syms.size;
|
|
auto insn = spirv_new_insn (SpvOpEntryPoint, iface_start + count, linkage);
|
|
INSN (insn, 1) = model;
|
|
INSN (insn, 2) = func_id;
|
|
memcpy (&INSN (insn, 3), func_name, len);
|
|
for (int i = 0; i < count; i++) {
|
|
INSN (insn, iface_start + i) = ctx->module->interface_syms.a[i]->id;
|
|
}
|
|
}
|
|
|
|
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;
|
|
bool mat1;
|
|
bool mat2;
|
|
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;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_generate_matrix (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
auto mat_type = get_type (e);
|
|
int count = type_cols (mat_type);
|
|
scoped_src_loc (e);
|
|
unsigned columns[count];
|
|
auto col_type = column_type (mat_type);
|
|
auto e1 = e->expr.e1;
|
|
auto e2 = e->expr.e2;
|
|
for (int i = 0; i < count; i++) {
|
|
auto ind = new_int_expr (i, false);
|
|
auto a = new_array_expr (e1, ind);
|
|
auto b = new_array_expr (e2, ind);
|
|
a->array.type = col_type;
|
|
b->array.type = col_type;
|
|
auto c = typed_binary_expr (col_type, e->expr.op, a, b);
|
|
|
|
columns[i] = spirv_emit_expr (c, ctx);
|
|
}
|
|
|
|
int tid = type_id (mat_type, ctx);
|
|
int id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpCompositeConstruct, 3 + count,
|
|
ctx->code_space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id;
|
|
for (int i = 0; i < count; i++) {
|
|
INSN (insn, 3 + i) = columns[i];
|
|
}
|
|
return id;
|
|
}
|
|
|
|
#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)
|
|
#define SPV_PTR (SPV_type(ev_ptr))
|
|
|
|
static extinst_t glsl_450 = {
|
|
.name = "GLSL.std.450"
|
|
};
|
|
|
|
static spvop_t spv_ops[] = {
|
|
{"load", SpvOpLoad, SPV_PTR, 0 },
|
|
|
|
{"or", SpvOpLogicalOr, SPV_INT, SPV_INT },
|
|
{"and", SpvOpLogicalAnd, SPV_INT, SPV_INT },
|
|
|
|
{"eq", SpvOpIEqual, SPV_INT, SPV_INT },
|
|
{"eq", SpvOpFOrdEqual, SPV_FLOAT, SPV_FLOAT },
|
|
{"ne", 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 },
|
|
{"add", .types1 = SPV_FLOAT, .types2 = SPV_FLOAT,
|
|
.mat1 = true, .mat2 = true,
|
|
.generate = spirv_generate_matrix },
|
|
{"sub", SpvOpISub, SPV_INT, SPV_INT },
|
|
{"sub", SpvOpFSub, SPV_FLOAT, SPV_FLOAT },
|
|
{"add", .types1 = SPV_FLOAT, .types2 = SPV_FLOAT,
|
|
.mat1 = true, .mat2 = true,
|
|
.generate = spirv_generate_matrix },
|
|
{"mul", SpvOpIMul, SPV_INT, SPV_INT },
|
|
{"mul", SpvOpFMul, SPV_FLOAT, SPV_FLOAT },
|
|
{"mul", SpvOpMatrixTimesVector, SPV_FLOAT, SPV_FLOAT,
|
|
.mat1 = true, .mat2 = false },
|
|
{"mul", SpvOpVectorTimesMatrix, SPV_FLOAT, SPV_FLOAT,
|
|
.mat1 = false, .mat2 = true },
|
|
{"mul", SpvOpMatrixTimesMatrix, SPV_FLOAT, SPV_FLOAT,
|
|
.mat1 = true, .mat2 = true },
|
|
{"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 },
|
|
{"scale", SpvOpMatrixTimesScalar, SPV_FLOAT, SPV_FLOAT,
|
|
.mat1 = true, .mat2 = false },
|
|
|
|
{"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, const type_t *type1, const type_t *type2)
|
|
{
|
|
constexpr int num_ops = sizeof (spv_ops) / sizeof (spv_ops[0]);
|
|
etype_t t1 = type1->type;
|
|
etype_t t2 = type2 ? type2->type : ev_void;
|
|
bool mat1 = is_matrix (type1);
|
|
bool mat2 = is_matrix (type2);
|
|
|
|
for (int i = 0; i < num_ops; i++) {
|
|
if (strcmp (spv_ops[i].op_name, op_name) == 0
|
|
&& spv_ops[i].types1 & SPV_type(t1)
|
|
&& ((!spv_ops[i].types2 && t2 == ev_void)
|
|
|| (spv_ops[i].types2
|
|
&& (spv_ops[i].types2 & SPV_type(t2))))
|
|
&& spv_ops[i].mat1 == mat1 && spv_ops[i].mat2 == mat2) {
|
|
return &spv_ops[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_cast (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
auto src_type = get_type (e->expr.e1);
|
|
auto dst_type = e->expr.type;
|
|
SpvOp op = 0;
|
|
if (is_real (src_type)) {
|
|
if (is_unsigned (dst_type)) {
|
|
op = SpvOpConvertFToU;
|
|
} else if (is_signed (dst_type)) {
|
|
op = SpvOpConvertFToS;
|
|
} else if (is_real (dst_type)) {
|
|
op = SpvOpFConvert;
|
|
}
|
|
} else if (is_real (dst_type)) {
|
|
if (is_unsigned (src_type)) {
|
|
op = SpvOpConvertUToF;
|
|
} else if (is_signed (src_type)) {
|
|
op = SpvOpConvertSToF;
|
|
}
|
|
} else if (is_unsigned (dst_type)) {
|
|
if (type_size (base_type (dst_type))
|
|
!= type_size (base_type (src_type))) {
|
|
op = SpvOpUConvert;
|
|
}
|
|
} else if (is_signed (dst_type)) {
|
|
if (type_size (base_type (dst_type))
|
|
!= type_size (base_type (src_type))) {
|
|
op = SpvOpSConvert;
|
|
}
|
|
}
|
|
if (!op) {
|
|
internal_error (e, "unexpected type combination");
|
|
}
|
|
|
|
unsigned cid = spirv_emit_expr (e->expr.e1, ctx);
|
|
unsigned tid = type_id (dst_type, ctx);
|
|
unsigned id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (op, 4, ctx->code_space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id;
|
|
INSN (insn, 3) = cid;
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_uexpr (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
if (e->expr.op == 'C') {
|
|
return spirv_cast (e, ctx);
|
|
}
|
|
auto op_name = convert_op (e->expr.op);
|
|
if (!op_name) {
|
|
if (e->expr.op > 32 && e->expr.op < 127) {
|
|
internal_error (e, "unexpected unary op: '%c'\n", e->expr.op);
|
|
} else {
|
|
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, nullptr);
|
|
if (!spv_op) {
|
|
internal_error (e, "unexpected unary op_name: %s %s\n", op_name,
|
|
get_type_string(t));
|
|
}
|
|
if (!spv_op->op) {
|
|
internal_error (e, "unimplemented op: %s %s\n", op_name,
|
|
get_type_string(t));
|
|
}
|
|
unsigned uid = spirv_emit_expr (e->expr.e1, ctx);
|
|
|
|
unsigned tid = type_id (get_type (e), ctx);
|
|
unsigned id;
|
|
if (e->expr.constant) {
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpSpecConstantOp, 5, globals);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id = spirv_id (ctx);
|
|
INSN (insn, 3) = spv_op->op;
|
|
INSN (insn, 4) = uid;
|
|
} else {
|
|
auto insn = spirv_new_insn (spv_op->op, 4, ctx->code_space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id = spirv_id (ctx);
|
|
INSN (insn, 3) = uid;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_block (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
unsigned id = 0;
|
|
auto slist = &e->block.list;
|
|
for (auto s = slist->head; s; s = s->next) {
|
|
spirv_emit_expr (s->expr, ctx);
|
|
}
|
|
if (e->block.result) {
|
|
id = spirv_emit_expr (e->block.result, ctx);
|
|
}
|
|
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.e2);
|
|
auto spv_op = spirv_find_op (op_name, t1, t2);
|
|
if (!spv_op) {
|
|
internal_error (e, "unexpected binary op_name: %s %s %s\n", op_name,
|
|
get_type_string(t1),
|
|
get_type_string(t2));
|
|
}
|
|
if (!spv_op->op && !spv_op->generate) {
|
|
internal_error (e, "unimplemented op: %s %s %s\n", op_name,
|
|
get_type_string(t1),
|
|
get_type_string(t2));
|
|
}
|
|
if (spv_op->generate) {
|
|
return spv_op->generate (e, ctx);
|
|
}
|
|
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);
|
|
unsigned id;
|
|
if (e->expr.constant) {
|
|
auto globals = ctx->module->globals;
|
|
auto insn = spirv_new_insn (SpvOpSpecConstantOp, 6, globals);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id = spirv_id (ctx);
|
|
INSN (insn, 3) = spv_op->op;
|
|
INSN (insn, 4) = bid1;
|
|
INSN (insn, 5) = bid2;
|
|
} else {
|
|
auto insn = spirv_new_insn (spv_op->op, 5, ctx->code_space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id = spirv_id (ctx);
|
|
INSN (insn, 3) = bid1;
|
|
INSN (insn, 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 if (sym->sy_type == sy_func) {
|
|
auto func = sym->metafunc->func;
|
|
sym->id = spirv_function_ref (func, ctx);
|
|
} else if (sym->sy_type == sy_var) {
|
|
sym->id = spirv_variable (sym, ctx);
|
|
} else {
|
|
internal_error (e, "unexpected symbol type: %s for %s",
|
|
symtype_str (sym->sy_type), sym->name);
|
|
}
|
|
return sym->id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_temp (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
return 0;//FIXME don't want
|
|
}
|
|
|
|
static unsigned
|
|
spirv_vector_value (const ex_value_t *value, spirvctx_t *ctx)
|
|
{
|
|
auto base = base_type (value->type);
|
|
int width = type_width (value->type);
|
|
ex_value_t *comp_vals[width];
|
|
auto val = &value->raw_value;
|
|
if (type_size (base) < 1 || type_size (base) > 2) {
|
|
internal_error (nullptr, "invalid vector component size");
|
|
}
|
|
for (int i = 0; i < width; i++) {
|
|
comp_vals[i] = new_type_value (base, val);
|
|
val += type_size (base);
|
|
}
|
|
unsigned comp_ids[width];
|
|
for (int i = 0; i < width; i++) {
|
|
auto e = new_value_expr (comp_vals[i], false);
|
|
comp_ids[i] = spirv_value (e, ctx);
|
|
}
|
|
|
|
auto space = ctx->module->globals;
|
|
int tid = type_id (value->type, ctx);
|
|
int id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpConstantComposite, 3 + width, space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id;
|
|
for (int i = 0; i < width; i++) {
|
|
INSN (insn, 3 + i) = comp_ids[i];
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_matrix_value (const ex_value_t *value, spirvctx_t *ctx)
|
|
{
|
|
auto ctype = column_type (value->type);
|
|
int count = type_cols (value->type);
|
|
ex_value_t *columns[count];
|
|
auto val = &value->raw_value;
|
|
for (int i = 0; i < count; i++) {
|
|
columns[i] = new_type_value (ctype, val);
|
|
val += type_size (ctype);
|
|
columns[i]->id = spirv_vector_value (columns[i], ctx);
|
|
}
|
|
|
|
auto space = ctx->module->globals;
|
|
int tid = type_id (value->type, ctx);
|
|
int id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpConstantComposite, 3 + count, space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id;
|
|
for (int i = 0; i < count; i++) {
|
|
INSN (insn, 3 + i) = columns[i]->id;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_value (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
auto value = e->value;
|
|
if (!value->id) {
|
|
if (is_matrix (value->type)) {
|
|
return spirv_matrix_value (value, ctx);
|
|
}
|
|
if (is_nonscalar (value->type)) {
|
|
return spirv_vector_value (value, ctx);
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
if (value->is_constexpr) {
|
|
op += SpvOpSpecConstantTrue - SpvOpConstantTrue;
|
|
}
|
|
auto globals = ctx->module->globals;
|
|
if (op == SpvOpConstant && !val) {
|
|
auto insn = spirv_new_insn (SpvOpConstantNull, 3, globals);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = value->id = spirv_id (ctx);
|
|
} else {
|
|
auto insn = spirv_new_insn (op, 3 + val_size, globals);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = value->id = spirv_id (ctx);
|
|
if (val_size > 0) {
|
|
INSN (insn, 3) = val;
|
|
}
|
|
if (val_size > 1) {
|
|
INSN (insn, 4) = val >> 32;
|
|
}
|
|
}
|
|
}
|
|
return value->id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_compound (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
int num_ele = 0;
|
|
for (auto ele = e->compound.head; ele; ele = ele->next) {
|
|
num_ele++;
|
|
}
|
|
unsigned ele_ids[num_ele];
|
|
int ind = 0;
|
|
for (auto ele = e->compound.head; ele; ele = ele->next) {
|
|
ele_ids[ind++] = spirv_emit_expr (ele->expr, ctx);
|
|
}
|
|
|
|
auto type = e->compound.type;
|
|
int tid = type_id (type, ctx);
|
|
int id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpCompositeConstruct, 3 + num_ele,
|
|
ctx->code_space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id;
|
|
for (int i = 0; i < num_ele; i++) {
|
|
INSN (insn, 3 + i) = ele_ids[i];
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_vector (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
auto compound = vector_to_compound (e);
|
|
return spirv_compound (compound, ctx);
|
|
}
|
|
|
|
static unsigned
|
|
spirv_access_chain (const expr_t *e, spirvctx_t *ctx,
|
|
const type_t **res_type, const type_t **acc_type)
|
|
{
|
|
*res_type = get_type (e);
|
|
ex_list_t list = {};
|
|
// convert the left-branching field/array expression chain to a list
|
|
while (e->type == ex_field || e->type == ex_array) {
|
|
for (; e->type == ex_field; e = e->field.object) {
|
|
list_prepend (&list, e);
|
|
}
|
|
for (; e->type == ex_array; e = e->array.base) {
|
|
list_prepend (&list, e);
|
|
}
|
|
}
|
|
// e is now the base object of the field/array expression chain
|
|
auto base_type = get_type (e);
|
|
unsigned base_id = spirv_emit_expr (e, ctx);
|
|
int op = SpvOpCompositeExtract;
|
|
|
|
*acc_type = *res_type;
|
|
bool literal_ind = true;
|
|
if (is_pointer (base_type) || is_reference (base_type)) {
|
|
unsigned storage = base_type->fldptr.tag;
|
|
*acc_type = tagged_reference_type (storage, *res_type);
|
|
op = SpvOpAccessChain;
|
|
literal_ind = false;
|
|
}
|
|
|
|
int num_obj = list_count (&list);
|
|
const expr_t *ind_expr[num_obj];
|
|
unsigned ind_id[num_obj];
|
|
list_scatter (&list, ind_expr);
|
|
for (int i = 0; i < num_obj; i++) {
|
|
auto obj = ind_expr[i];
|
|
bool direct_ind = false;
|
|
unsigned index;
|
|
if (obj->type == ex_field) {
|
|
if (obj->field.member->type != ex_symbol) {
|
|
internal_error (obj->field.member, "not a symbol");
|
|
}
|
|
auto sym = obj->field.member->symbol;
|
|
index = sym->id;
|
|
} else if (obj->type == ex_array) {
|
|
auto ind = obj->array.index;
|
|
if (is_integral_val (ind)) {
|
|
index = expr_integral (ind);
|
|
} else {
|
|
if (is_reference (get_type (ind))) {
|
|
ind = pointer_deref (ind);
|
|
}
|
|
index = spirv_emit_expr (ind, ctx);
|
|
direct_ind = true;
|
|
}
|
|
} else {
|
|
internal_error (obj, "what the what?!?");
|
|
}
|
|
if (literal_ind || direct_ind) {
|
|
ind_id[i] = index;
|
|
} else {
|
|
auto ind = new_uint_expr (index);
|
|
ind_id[i] = spirv_emit_expr (ind, ctx);
|
|
}
|
|
}
|
|
|
|
int acc_type_id = type_id (*acc_type, ctx);
|
|
int id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (op, 4 + num_obj, ctx->code_space);
|
|
INSN (insn, 1) = acc_type_id;
|
|
INSN (insn, 2) = id;
|
|
INSN (insn, 3) = base_id;
|
|
auto field_ind = &INSN (insn, 4);
|
|
for (int i = 0; i < num_obj; i++) {
|
|
field_ind[i] = ind_id[i];
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_assign (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
unsigned src = spirv_emit_expr (e->assign.src, ctx);
|
|
unsigned dst = 0;
|
|
|
|
if (is_temp (e->assign.dst)) {
|
|
// spir-v uses SSA, so temps cannot be assigned to directly, so instead
|
|
// use the temp expression as a reference for the result id of the
|
|
// rhs of the assignment.
|
|
//FIXME const cast (store elsewhere)
|
|
((expr_t *) e->assign.dst)->id = src;
|
|
return src;
|
|
}
|
|
if (e->assign.dst->type == ex_field) {
|
|
const type_t *res_type;
|
|
const type_t *acc_type;
|
|
dst = spirv_access_chain (e->assign.dst, ctx, &res_type, &acc_type);
|
|
if (res_type == acc_type) {
|
|
internal_error (e, "assignment to temp?");
|
|
}
|
|
} else if (is_deref (e->assign.dst)) {
|
|
auto ptr = e->assign.dst->expr.e1;
|
|
dst = spirv_emit_expr (ptr, ctx);
|
|
}
|
|
|
|
if (!dst) {
|
|
internal_error (e, "invalid assignment?");
|
|
}
|
|
|
|
auto insn = spirv_new_insn (SpvOpStore, 3, ctx->code_space);
|
|
INSN (insn, 1) = dst;
|
|
INSN (insn, 2) = src;
|
|
return 0;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_call (const expr_t *call, spirvctx_t *ctx)
|
|
{
|
|
int num_args = list_count (&call->branch.args->list);
|
|
const expr_t *args[num_args + 1];
|
|
unsigned arg_ids[num_args + 1];
|
|
auto func = call->branch.target;
|
|
auto func_type = get_type (func);
|
|
auto ret_type = func_type->func.ret_type;
|
|
|
|
unsigned func_id = spirv_emit_expr (func, ctx);
|
|
unsigned ret_type_id = spirv_type_id (ret_type, ctx);
|
|
|
|
list_scatter_rev (&call->branch.args->list, args);
|
|
for (int i = 0; i < num_args; i++) {
|
|
auto a = args[i];
|
|
if (func_type->func.param_quals[i] == pq_const) {
|
|
arg_ids[i] = spirv_emit_expr (a, ctx);
|
|
} else {
|
|
auto psym = new_symbol ("param");
|
|
psym->type = tagged_reference_type (SpvStorageClassFunction, get_type (a));
|
|
psym->sy_type = sy_var;
|
|
psym->var.storage = SpvStorageClassFunction;
|
|
psym->id = spirv_variable (psym, ctx);
|
|
psym->lvalue = true;
|
|
arg_ids[i] = psym->id;
|
|
if (func_type->func.param_quals[i] != pq_out) {
|
|
if (a->type == ex_inout) {
|
|
a = a->inout.in;
|
|
}
|
|
auto pexpr = new_symbol_expr (psym);
|
|
auto assign = assign_expr (pexpr, a);
|
|
spirv_emit_expr (assign, ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpFunctionCall, 4 + num_args,
|
|
ctx->code_space);
|
|
INSN(insn, 1) = ret_type_id;
|
|
INSN(insn, 2) = id;
|
|
INSN(insn, 3) = func_id;
|
|
for (int i = 0; i < num_args; i++) {
|
|
INSN(insn, 4 + i) = arg_ids[i];
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_jump (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
auto target_label = &e->branch.target->label;
|
|
unsigned target = spirv_label_id (target_label, ctx);
|
|
spirv_Branch (target, ctx);
|
|
return 0;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_branch (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
if (e->branch.type == pr_branch_call) {
|
|
return spirv_call (e, ctx);
|
|
}
|
|
if (e->branch.type == pr_branch_jump) {
|
|
return spirv_jump (e, ctx);
|
|
}
|
|
internal_error (e, "unexpected branch");
|
|
}
|
|
|
|
static unsigned
|
|
spirv_return (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
if (e->retrn.ret_val) {
|
|
unsigned ret_id = spirv_emit_expr (e->retrn.ret_val, ctx);
|
|
auto insn = spirv_new_insn (SpvOpReturnValue, 2, ctx->code_space);
|
|
INSN (insn, 1) = ret_id;
|
|
} else {
|
|
spirv_new_insn (SpvOpReturn, 1, ctx->code_space);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_swizzle (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
int count = type_width (e->swizzle.type);
|
|
unsigned src_id = spirv_emit_expr (e->swizzle.src, ctx);
|
|
unsigned tid = type_id (e->swizzle.type, ctx);
|
|
unsigned id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpVectorShuffle, 5 + count, ctx->code_space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id;
|
|
INSN (insn, 3) = src_id;
|
|
INSN (insn, 4) = src_id;
|
|
for (int i = 0; i < count; i++) {
|
|
INSN (insn, 5 + i) = e->swizzle.source[i];
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_extend (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
auto src_type = get_type (e->extend.src);
|
|
auto res_type = e->extend.type;
|
|
auto src_base = base_type (src_type);
|
|
auto res_base = base_type (res_type);
|
|
int src_width = type_width (src_type);
|
|
int res_width = type_width (res_type);
|
|
|
|
if (res_base != src_type || res_width <= src_width) {
|
|
internal_error (e, "invalid type combination for extend");
|
|
}
|
|
unsigned sid = spirv_emit_expr (e->extend.src, ctx);
|
|
unsigned eid = sid;
|
|
static int id_counts[4][4] = {
|
|
{-1, 2, 3, 4},
|
|
{-1,-1, 2, 2},
|
|
{-1,-1,-1, 2},
|
|
{-1,-1,-1,-1},
|
|
};
|
|
int nids = id_counts[src_width - 1][res_width - 1];
|
|
if (e->extend.extend != 2
|
|
|| !(src_width == 1 || (src_width == 2 && res_width == 4))) {
|
|
int val = e->extend.extend == 3 ? -1 : e->extend.extend == 1 ? 1 : 0;
|
|
scoped_src_loc (e);
|
|
auto ext = cast_expr (src_base, new_int_expr (val, false));
|
|
eid = spirv_emit_expr (ext, ctx);
|
|
if (src_width == 2 && res_width == 4) {
|
|
nids += 1;
|
|
}
|
|
}
|
|
unsigned cids[4] = {};
|
|
int start = 0;
|
|
if (e->extend.reverse) {
|
|
if (src_width > 1) {
|
|
internal_error (e, "reverse extend for width %d not implemented",
|
|
src_width);
|
|
}
|
|
cids[0] = eid;
|
|
cids[1] = eid;
|
|
cids[2] = eid;
|
|
cids[3] = sid;
|
|
start = 4 - nids;
|
|
} else {
|
|
cids[0] = sid;
|
|
cids[1] = eid;
|
|
cids[2] = eid;
|
|
cids[3] = eid;
|
|
}
|
|
int tid = type_id (res_type, ctx);
|
|
int id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpCompositeConstruct, 3 + nids,
|
|
ctx->code_space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id;
|
|
for (int i = 0; i < nids; i++) {
|
|
INSN (insn, 3 + i) = cids[start + i];
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_cond (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
unsigned test_id = spirv_emit_expr (e->cond.test, ctx);
|
|
unsigned true_id = spirv_emit_expr (e->cond.true_expr, ctx);
|
|
unsigned false_id = spirv_emit_expr (e->cond.false_expr, ctx);
|
|
|
|
auto type = get_type (e);
|
|
unsigned tid = type_id (type, ctx);
|
|
unsigned id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpSelect, 6, ctx->code_space);
|
|
INSN (insn, 1) = tid;
|
|
INSN (insn, 2) = id;
|
|
INSN (insn, 3) = test_id;
|
|
INSN (insn, 4) = true_id;
|
|
INSN (insn, 5) = false_id;
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_field_array (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
const type_t *res_type;
|
|
const type_t *acc_type;
|
|
|
|
unsigned id = spirv_access_chain (e, ctx, &res_type, &acc_type);
|
|
|
|
if (acc_type != res_type) {
|
|
// base is a pointer or reference so load the value
|
|
unsigned ptr_id = id;
|
|
int res_type_id = type_id (res_type, ctx);
|
|
|
|
id = spirv_id (ctx);
|
|
auto insn = spirv_new_insn (SpvOpLoad, 4, ctx->code_space);
|
|
INSN (insn, 1) = res_type_id;
|
|
INSN (insn, 2) = id;
|
|
INSN (insn, 3) = ptr_id;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_loop (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
auto break_label = &e->loop.break_label->label;
|
|
auto continue_label = &e->loop.continue_label->label;
|
|
unsigned merge = spirv_label_id (break_label, ctx);
|
|
unsigned cont = spirv_label_id (continue_label, ctx);
|
|
if (e->loop.do_while) {
|
|
unsigned loop = spirv_SplitBlock (ctx);
|
|
spirv_LoopMerge (merge, cont, ctx);
|
|
spirv_SplitBlock (ctx);
|
|
spirv_emit_expr (e->loop.body, ctx);
|
|
spirv_SplitBlockId (cont, ctx);
|
|
if (e->loop.continue_body) {
|
|
spirv_emit_expr (e->loop.continue_body, ctx);
|
|
}
|
|
unsigned test = spirv_emit_expr (e->loop.test, ctx);
|
|
spirv_BranchConditional (e->loop.not, test, merge, loop, ctx);
|
|
spirv_LabelId (merge, ctx);
|
|
} else {
|
|
unsigned loop = spirv_SplitBlock (ctx);
|
|
spirv_LoopMerge (merge, cont, ctx);
|
|
spirv_SplitBlock (ctx);
|
|
unsigned body = spirv_id (ctx);
|
|
unsigned test = spirv_emit_expr (e->loop.test, ctx);
|
|
spirv_BranchConditional (e->loop.not, test, body, merge, ctx);
|
|
spirv_LabelId (body, ctx);
|
|
spirv_emit_expr (e->loop.body, ctx);
|
|
spirv_SplitBlockId (cont, ctx);
|
|
if (e->loop.continue_body) {
|
|
spirv_emit_expr (e->loop.continue_body, ctx);
|
|
}
|
|
spirv_Branch (loop, ctx);
|
|
spirv_LabelId (merge, ctx);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned
|
|
spirv_select (const expr_t *e, spirvctx_t *ctx)
|
|
{
|
|
unsigned merge = spirv_id (ctx);
|
|
unsigned true_label = spirv_id (ctx);
|
|
unsigned false_label = merge;
|
|
if (e->select.false_body) {
|
|
false_label = spirv_id (ctx);
|
|
}
|
|
unsigned test = spirv_emit_expr (e->select.test, ctx);
|
|
spirv_SelectionMerge (merge, ctx);
|
|
spirv_BranchConditional (e->select.not, test, true_label, false_label, ctx);
|
|
spirv_LabelId (true_label, ctx);
|
|
spirv_emit_expr (e->select.true_body, ctx);
|
|
if (!is_block_terminated (ctx->code_space)) {
|
|
spirv_Branch (merge, ctx);
|
|
}
|
|
if (e->select.false_body) {
|
|
spirv_LabelId (false_label, ctx);
|
|
spirv_emit_expr (e->select.false_body, ctx);
|
|
if (!is_block_terminated (ctx->code_space)) {
|
|
spirv_Branch (merge, ctx);
|
|
}
|
|
}
|
|
spirv_LabelId (merge, 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)
|
|
{
|
|
static spirv_expr_f funcs[ex_count] = {
|
|
[ex_block] = spirv_block,
|
|
[ex_expr] = spirv_expr,
|
|
[ex_uexpr] = spirv_uexpr,
|
|
[ex_symbol] = spirv_symbol,
|
|
[ex_temp] = spirv_temp,//FIXME don't want
|
|
[ex_value] = spirv_value,
|
|
[ex_vector] = spirv_vector,
|
|
[ex_compound] = spirv_compound,
|
|
[ex_assign] = spirv_assign,
|
|
[ex_branch] = spirv_branch,
|
|
[ex_return] = spirv_return,
|
|
[ex_swizzle] = spirv_swizzle,
|
|
[ex_extend] = spirv_extend,
|
|
[ex_cond] = spirv_cond,
|
|
[ex_field] = spirv_field_array,
|
|
[ex_array] = spirv_field_array,
|
|
[ex_loop] = spirv_loop,
|
|
[ex_select] = spirv_select,
|
|
[ex_intrinsic] = spirv_intrinsic,
|
|
};
|
|
|
|
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)
|
|
{
|
|
pr->module->entry_points = defspace_new (ds_backed);
|
|
pr->module->exec_modes = defspace_new (ds_backed);
|
|
pr->module->strings = defspace_new (ds_backed);
|
|
pr->module->names = defspace_new (ds_backed);
|
|
pr->module->module_processed = defspace_new (ds_backed);
|
|
pr->module->decorations = defspace_new (ds_backed);
|
|
pr->module->globals = defspace_new (ds_backed);
|
|
pr->module->func_declarations = defspace_new (ds_backed);
|
|
pr->module->func_definitions = defspace_new (ds_backed);
|
|
DARRAY_INIT (&pr->module->interface_syms, 16);
|
|
|
|
spirvctx_t ctx = {
|
|
.module = pr->module,
|
|
.code_space = defspace_new (ds_backed),
|
|
.decl_space = defspace_new (ds_backed),
|
|
.type_ids = DARRAY_STATIC_INIT (64),
|
|
.label_ids = DARRAY_STATIC_INIT (64),
|
|
.id = 0,
|
|
};
|
|
auto space = defspace_new (ds_backed);
|
|
auto header = spirv_new_insn (0, 5, space);
|
|
INSN (header, 0) = SpvMagicNumber;
|
|
INSN (header, 1) = SpvVersion;
|
|
INSN (header, 2) = 0; // FIXME get a magic number for QFCC
|
|
INSN (header, 3) = 0; // Filled in later
|
|
INSN (header, 4) = 0; // Reserved
|
|
|
|
for (auto func = pr->func_head; func; func = func->next)
|
|
{
|
|
auto func_id = spirv_function (func, &ctx);
|
|
if (strncmp ("main", func->o_name, 4) == 0
|
|
&& (!func->o_name[4] || func->o_name[4] == '|')) {
|
|
auto model = SpvExecutionModelVertex;//FIXME
|
|
spirv_EntryPoint (func_id, func->o_name, model, &ctx);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
}
|
|
|
|
INSN (header, 3) = ctx.id + 1;
|
|
ADD_DATA (space, mod->entry_points);
|
|
ADD_DATA (space, mod->exec_modes);
|
|
ADD_DATA (space, mod->strings);
|
|
ADD_DATA (space, mod->names);
|
|
ADD_DATA (space, mod->module_processed);
|
|
ADD_DATA (space, mod->decorations);
|
|
ADD_DATA (space, mod->globals);
|
|
ADD_DATA (space, mod->func_declarations);
|
|
ADD_DATA (space, mod->func_definitions);
|
|
|
|
QFile *file = Qopen (filename, "wb");
|
|
Qwrite (file, space->data, 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)
|
|
{
|
|
spirv_extinst_import (module, import, nullptr);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static bool
|
|
spirv_value_too_large (const type_t *val_type)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
spirv_create_param (symtab_t *parameters, symbol_t *param, param_qual_t qual)
|
|
{
|
|
if (!param->name) {
|
|
if (qual == pq_out) {
|
|
warning (0, "unnamed out parameter");
|
|
}
|
|
param->name = save_string ("");
|
|
|
|
symtab_appendsymbol (parameters, param);
|
|
} else {
|
|
symtab_addsymbol (parameters, param);
|
|
}
|
|
auto type = param->type;
|
|
if (qual != pq_const) {
|
|
param->lvalue = true;
|
|
type = tagged_reference_type (SpvStorageClassFunction, type);
|
|
}
|
|
param->type = type;
|
|
}
|
|
|
|
static void
|
|
spirv_build_scope (symbol_t *fsym)
|
|
{
|
|
function_t *func = fsym->metafunc->func;
|
|
|
|
for (param_t *p = fsym->params; p; p = p->next) {
|
|
symbol_t *param;
|
|
if (is_void (p->type)) {
|
|
if (p->name) {
|
|
error (0, "invalid parameter type for %s", p->name);
|
|
} else if (p != fsym->params || p->next) {
|
|
error (0, "void must be the only parameter");
|
|
continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
param = new_symbol_type (p->name, p->type);
|
|
spirv_create_param (func->parameters, param, p->qual);
|
|
}
|
|
}
|
|
|
|
static void
|
|
spirv_build_code (function_t *func, const expr_t *statements)
|
|
{
|
|
func->exprs = statements;
|
|
}
|
|
|
|
static void
|
|
spirv_declare_sym (specifier_t spec, const expr_t *init, symtab_t *symtab,
|
|
expr_t *block)
|
|
{
|
|
symbol_t *sym = spec.sym;
|
|
if (sym->name[0]) {
|
|
symbol_t *check = symtab_lookup (symtab, sym->name);
|
|
if (check && check->table == symtab) {
|
|
error (0, "%s redefined", sym->name);
|
|
}
|
|
}
|
|
auto storage = spirv_storage_class (spec.storage);
|
|
sym->type = tagged_reference_type (storage, sym->type);
|
|
sym->lvalue = !spec.is_const;
|
|
sym->sy_type = sy_var;
|
|
sym->var.storage = spec.storage;
|
|
if (sym->name[0]) {
|
|
symtab_addsymbol (symtab, sym);
|
|
}
|
|
if (symtab->type == stab_local) {
|
|
if (init) {
|
|
if (!block && is_constexpr (init)) {
|
|
} else if (block) {
|
|
auto r = new_symbol_expr (sym);
|
|
auto e = assign_expr (r, init);
|
|
append_expr (block, e);
|
|
} else {
|
|
error (init, "non-constant initializer");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static const expr_t *
|
|
spirv_build_element_chain (element_chain_t *element_chain, const type_t *type,
|
|
const expr_t *eles)
|
|
{
|
|
type = unalias_type (type);
|
|
|
|
initstate_t state = {};
|
|
|
|
if (is_struct (type) || (is_nonscalar (type) && type->symtab)) {
|
|
state.field = type->symtab->symbols;
|
|
// find first initializable field
|
|
while (state.field && skip_field (state.field)) {
|
|
state.field = state.field->next;
|
|
}
|
|
if (state.field) {
|
|
state.type = state.field->type;
|
|
state.offset = state.field->id;
|
|
}
|
|
} else if (is_matrix (type)) {
|
|
state.type = column_type (type);
|
|
} else if (is_nonscalar (type)) {
|
|
state.type = base_type (type);
|
|
} else if (is_array (type)) {
|
|
state.type = dereference_type (type);
|
|
} else {
|
|
return error (eles, "invalid initialization");
|
|
}
|
|
if (!state.type) {
|
|
return error (eles, "initialization of incomplete type");
|
|
}
|
|
|
|
for (auto ele = eles->compound.head; ele; ele = ele->next) {
|
|
//FIXME designated initializers
|
|
if (!state.type) {
|
|
return new_error_expr ();
|
|
}
|
|
// FIXME vectors are special (ie, check for overlaps)
|
|
if (state.offset >= type_count (type)) {
|
|
if (options.warnings.initializer) {
|
|
warning (eles, "excessive elements in initializer");
|
|
}
|
|
break;
|
|
}
|
|
if (ele->expr && ele->expr->type == ex_compound) {
|
|
const expr_t *err;
|
|
if ((err = spirv_build_element_chain (element_chain, state.type,
|
|
ele->expr))) {
|
|
return err;
|
|
}
|
|
} else {
|
|
auto element = new_element (nullptr, nullptr);
|
|
auto expr = ele->expr;
|
|
if (expr) {
|
|
expr = cast_expr (state.type, expr);
|
|
}
|
|
if (is_error (expr)) {
|
|
return expr;
|
|
}
|
|
*element = (element_t) {
|
|
.type = state.type,
|
|
.offset = state.offset,
|
|
.expr = expr,
|
|
};
|
|
append_init_element (element_chain, element);
|
|
}
|
|
state.offset += type_count (state.type);
|
|
if (state.field) {
|
|
state.field = state.field->next;
|
|
// find next initializable field
|
|
while (state.field && skip_field (state.field)) {
|
|
state.field = state.field->next;
|
|
}
|
|
if (state.field) {
|
|
state.type = state.field->type;
|
|
state.offset = state.field->id;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static const expr_t *
|
|
spirv_initialized_temp (const type_t *type, const expr_t *src)
|
|
{
|
|
if (src->compound.type) {
|
|
type = src->compound.type;
|
|
}
|
|
|
|
scoped_src_loc (src);
|
|
auto new = new_compound_init ();
|
|
const expr_t *err;
|
|
if ((err = spirv_build_element_chain (&new->compound, type, src))) {
|
|
return err;
|
|
}
|
|
new->compound.type = type;
|
|
return new;
|
|
}
|
|
|
|
static const expr_t *
|
|
spirv_assign_vector (const expr_t *dst, const expr_t *src)
|
|
{
|
|
auto new = vector_to_compound (src);
|
|
return new_assign_expr (dst, new);;
|
|
}
|
|
|
|
target_t spirv_target = {
|
|
.value_too_large = spirv_value_too_large,
|
|
.build_scope = spirv_build_scope,
|
|
.build_code = spirv_build_code,
|
|
.declare_sym = spirv_declare_sym,
|
|
.initialized_temp = spirv_initialized_temp,
|
|
.assign_vector = spirv_assign_vector,
|
|
};
|