diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 91bd777c0..c299253b4 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -217,6 +217,7 @@ typedef struct ex_value_s { struct daglabel_s *daglabel;///< dag label for this value const type_t *type; etype_t lltype; + unsigned id; union { uint8_t raw_value; ///< for memcpy const char *string_val; ///< string constant @@ -328,6 +329,7 @@ typedef struct expr_s { struct operand_s *op; expr_type type; ///< the type of the result of this expression int printid; ///< avoid duplicate output when printing + unsigned id; unsigned paren:1; ///< the expression is enclosed in () unsigned implicit:1; ///< don't warn for implicit casts unsigned nodag:1; ///< prevent use of dags for this expression diff --git a/tools/qfcc/include/symtab.h b/tools/qfcc/include/symtab.h index f55abaeb1..5a3108fa9 100644 --- a/tools/qfcc/include/symtab.h +++ b/tools/qfcc/include/symtab.h @@ -68,7 +68,8 @@ typedef struct symbol_s { const struct type_s *type; ///< type of object to which symbol refers struct param_s *params; ///< the parameters if a function bool no_auto_init:1; ///< skip for non-designated initializers - bool lvalue; + bool lvalue:1; + struct attribute_s *attributes; union { int offset; ///< sy_offset struct def_s *def; ///< sy_def diff --git a/tools/qfcc/source/glsl-layout.c b/tools/qfcc/source/glsl-layout.c index c48d4a91f..2f94492e6 100644 --- a/tools/qfcc/source/glsl-layout.c +++ b/tools/qfcc/source/glsl-layout.c @@ -52,20 +52,45 @@ glsl_layout_location_invalid (specifier_t spec, const expr_t *qual_name, error (qual_name, "not allowed for vulkan"); } +static void +set_attribute (attribute_t **attributes, const char *name, const expr_t *val) +{ + for (auto a = *attributes; a; a = a->next) { + if (strcmp (a->name, name) == 0) { + a->params = val; + return; + } + } + auto attr = new_attribute (name, val); + attr->next = *attributes; + *attributes = attr; +} + static void glsl_layout_location (specifier_t spec, const expr_t *qual_name, const expr_t *val) { - notice (qual_name, "%s %s", expr_string (qual_name), - get_value_string (val->value)); + const char *name = expr_string (qual_name); + set_attribute (&spec.sym->attributes, name, val); } static void glsl_layout_constant_id (specifier_t spec, const expr_t *qual_name, const expr_t *val) { - notice (qual_name, "%s %s", expr_string (qual_name), - get_value_string (val->value)); + if (spec.sym->sy_type == sy_const) { + auto expr = new_value_expr (spec.sym->value, false); + spec.sym->sy_type = sy_expr; + spec.sym->expr = expr; + } + if (spec.sym->sy_type == sy_expr && spec.sym->expr->type == ex_value) { + auto expr = new_unary_expr ('+', spec.sym->expr); + expr->expr.constant = true; + expr->expr.type = spec.sym->type; + spec.sym->expr = expr; + } + const char *name = expr_string (qual_name); + set_attribute (&spec.sym->attributes, name, val); } static void diff --git a/tools/qfcc/source/spirv.c b/tools/qfcc/source/spirv.c index adb0612ec..284079a04 100644 --- a/tools/qfcc/source/spirv.c +++ b/tools/qfcc/source/spirv.c @@ -30,6 +30,8 @@ #include +#include + #include "QF/quakeio.h" #include "tools/qfcc/include/def.h" @@ -412,6 +414,265 @@ spirv_EntryPoint (unsigned func_id, const char *func_name, 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) { @@ -446,6 +707,13 @@ spirv_write (struct pr_info_s *pr, const char *filename) 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)