vkd3d-shader/dxil: Read numeric constants.

This commit is contained in:
Conor McCarthy 2023-05-15 13:52:00 +10:00 committed by Alexandre Julliard
parent c96143abdc
commit 5d33fb4633
Notes: Alexandre Julliard 2023-07-11 23:13:49 +02:00
Approved-by: Giovanni Mascellani (@giomasce)
Approved-by: Henri Verbeet (@hverbeet)
Approved-by: Alexandre Julliard (@julliard)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/259
2 changed files with 318 additions and 4 deletions

View File

@ -77,6 +77,19 @@ enum bitcode_module_code
MODULE_CODE_FUNCTION = 8,
};
enum bitcode_constant_code
{
CST_CODE_SETTYPE = 1,
CST_CODE_NULL = 2,
CST_CODE_UNDEF = 3,
CST_CODE_INTEGER = 4,
CST_CODE_FLOAT = 6,
CST_CODE_STRING = 8,
CST_CODE_CE_GEP = 12,
CST_CODE_CE_INBOUNDS_GEP = 20,
CST_CODE_DATA = 22,
};
enum bitcode_type_code
{
TYPE_CODE_NUMENTRY = 1,
@ -158,6 +171,7 @@ struct sm6_type
enum sm6_value_type
{
VALUE_TYPE_FUNCTION,
VALUE_TYPE_REG,
};
struct sm6_function_data
@ -174,6 +188,7 @@ struct sm6_value
union
{
struct sm6_function_data function;
struct vkd3d_shader_register reg;
} u;
};
@ -259,6 +274,12 @@ struct dxil_global_abbrev
struct dxil_abbrev abbrev;
};
static size_t size_add_with_overflow_check(size_t a, size_t b)
{
size_t i = a + b;
return (i < a) ? SIZE_MAX : i;
}
static struct sm6_parser *sm6_parser(struct vkd3d_shader_parser *parser)
{
return CONTAINING_RECORD(parser, struct sm6_parser, p);
@ -845,6 +866,15 @@ static size_t dxil_block_compute_module_decl_count(const struct dxil_block *bloc
return count;
}
static size_t dxil_block_compute_constants_count(const struct dxil_block *block)
{
size_t i, count;
for (i = 0, count = 0; i < block->record_count; ++i)
count += block->records[i]->code != CST_CODE_SETTYPE;
return count;
}
static void dxil_global_abbrevs_cleanup(struct dxil_global_abbrev **abbrevs, size_t count)
{
size_t i;
@ -1174,6 +1204,16 @@ static inline bool sm6_type_is_void(const struct sm6_type *type)
return type->class == TYPE_CLASS_VOID;
}
static inline bool sm6_type_is_integer(const struct sm6_type *type)
{
return type->class == TYPE_CLASS_INTEGER;
}
static inline bool sm6_type_is_floating_point(const struct sm6_type *type)
{
return type->class == TYPE_CLASS_FLOAT;
}
static inline bool sm6_type_is_numeric(const struct sm6_type *type)
{
return type->class == TYPE_CLASS_INTEGER || type->class == TYPE_CLASS_FLOAT;
@ -1221,6 +1261,11 @@ static inline bool sm6_type_is_handle(const struct sm6_type *type)
return sm6_type_is_struct(type) && !strcmp(type->u.struc->name, "dx.types.Handle");
}
static inline const struct sm6_type *sm6_type_get_element_type(const struct sm6_type *type)
{
return (type->class == TYPE_CLASS_ARRAY || type->class == TYPE_CLASS_VECTOR) ? type->u.array.elem_type : type;
}
static const struct sm6_type *sm6_type_get_pointer_to_type(const struct sm6_type *type,
enum bitcode_address_space addr_space, struct sm6_parser *sm6)
{
@ -1353,10 +1398,70 @@ static inline struct sm6_value *sm6_parser_get_current_value(const struct sm6_pa
return &sm6->values[sm6->value_count];
}
static void sm6_parser_compute_max_value_count(struct sm6_parser *sm6,
const struct dxil_block *block)
static enum vkd3d_data_type vkd3d_data_type_from_sm6_type(const struct sm6_type *type)
{
sm6->value_capacity = dxil_block_compute_module_decl_count(block);
if (type->class == TYPE_CLASS_INTEGER)
{
switch (type->u.width)
{
case 8:
return VKD3D_DATA_UINT8;
case 32:
return VKD3D_DATA_UINT;
default:
FIXME("Unhandled width %u.\n", type->u.width);
return VKD3D_DATA_UINT;
}
}
else if (type->class == TYPE_CLASS_FLOAT)
{
switch (type->u.width)
{
case 32:
return VKD3D_DATA_FLOAT;
case 64:
return VKD3D_DATA_DOUBLE;
default:
FIXME("Unhandled width %u.\n", type->u.width);
return VKD3D_DATA_FLOAT;
}
}
FIXME("Unhandled type %u.\n", type->class);
return VKD3D_DATA_UINT;
}
/* Recurse through the block tree while maintaining a current value count. The current
* count is the sum of the global count plus all declarations within the current function.
* Store into value_capacity the highest count seen. */
static size_t sm6_parser_compute_max_value_count(struct sm6_parser *sm6,
const struct dxil_block *block, size_t value_count)
{
size_t i, old_value_count = value_count;
if (block->id == MODULE_BLOCK)
value_count = size_add_with_overflow_check(value_count, dxil_block_compute_module_decl_count(block));
for (i = 0; i < block->child_block_count; ++i)
value_count = sm6_parser_compute_max_value_count(sm6, block->child_blocks[i], value_count);
switch (block->id)
{
case CONSTANTS_BLOCK:
/* Function local constants are contained in a child block of the function block. */
value_count = size_add_with_overflow_check(value_count, dxil_block_compute_constants_count(block));
break;
case FUNCTION_BLOCK:
sm6->value_capacity = max(sm6->value_capacity, value_count);
/* The value count returns to its previous value after handling a function. */
if (value_count < SIZE_MAX)
value_count = old_value_count;
break;
default:
break;
}
return value_count;
}
static bool sm6_parser_declare_function(struct sm6_parser *sm6, const struct dxil_record *record)
@ -1423,6 +1528,156 @@ static bool sm6_parser_declare_function(struct sm6_parser *sm6, const struct dxi
return true;
}
static inline uint64_t decode_rotated_signed_value(uint64_t value)
{
if (value != 1)
{
bool neg = value & 1;
value >>= 1;
return neg ? -value : value;
}
return value << 63;
}
static inline float bitcast_uint64_to_float(uint64_t value)
{
union
{
uint32_t uint32_value;
float float_value;
} u;
u.uint32_value = value;
return u.float_value;
}
static inline double bitcast_uint64_to_double(uint64_t value)
{
union
{
uint64_t uint64_value;
double double_value;
} u;
u.uint64_value = value;
return u.double_value;
}
static enum vkd3d_result sm6_parser_constants_init(struct sm6_parser *sm6, const struct dxil_block *block)
{
enum vkd3d_shader_register_type reg_type = VKD3DSPR_INVALID;
const struct sm6_type *type, *elem_type;
enum vkd3d_data_type reg_data_type;
const struct dxil_record *record;
struct sm6_value *dst;
size_t i, value_idx;
uint64_t value;
for (i = 0, type = NULL; i < block->record_count; ++i)
{
sm6->p.location.column = i;
record = block->records[i];
value_idx = sm6->value_count;
if (record->code == CST_CODE_SETTYPE)
{
if (!dxil_record_validate_operand_count(record, 1, 1, sm6))
return VKD3D_ERROR_INVALID_SHADER;
if (!(type = sm6_parser_get_type(sm6, record->operands[0])))
return VKD3D_ERROR_INVALID_SHADER;
elem_type = sm6_type_get_element_type(type);
if (sm6_type_is_numeric(elem_type))
{
reg_data_type = vkd3d_data_type_from_sm6_type(elem_type);
reg_type = elem_type->u.width > 32 ? VKD3DSPR_IMMCONST64 : VKD3DSPR_IMMCONST;
}
else
{
reg_data_type = VKD3D_DATA_UNUSED;
reg_type = VKD3DSPR_INVALID;
}
if (i == block->record_count - 1)
WARN("Unused SETTYPE record.\n");
continue;
}
if (!type)
{
WARN("Constant record %zu has no type.\n", value_idx);
return VKD3D_ERROR_INVALID_SHADER;
}
dst = sm6_parser_get_current_value(sm6);
dst->type = type;
dst->value_type = VALUE_TYPE_REG;
dst->u.reg.type = reg_type;
dst->u.reg.immconst_type = VKD3D_IMMCONST_SCALAR;
dst->u.reg.data_type = reg_data_type;
switch (record->code)
{
case CST_CODE_NULL:
/* Register constant data is already zero-filled. */
break;
case CST_CODE_INTEGER:
if (!dxil_record_validate_operand_count(record, 1, 1, sm6))
return VKD3D_ERROR_INVALID_SHADER;
if (!sm6_type_is_integer(type))
{
WARN("Invalid integer of non-integer type %u at constant idx %zu.\n", type->class, value_idx);
return VKD3D_ERROR_INVALID_SHADER;
}
value = decode_rotated_signed_value(record->operands[0]);
if (type->u.width <= 32)
dst->u.reg.u.immconst_uint[0] = value & ((1ull << type->u.width) - 1);
else
dst->u.reg.u.immconst_uint64[0] = value;
break;
case CST_CODE_FLOAT:
if (!dxil_record_validate_operand_count(record, 1, 1, sm6))
return VKD3D_ERROR_INVALID_SHADER;
if (!sm6_type_is_floating_point(type))
{
WARN("Invalid float of non-fp type %u at constant idx %zu.\n", type->class, value_idx);
return VKD3D_ERROR_INVALID_SHADER;
}
if (type->u.width == 16)
FIXME("Half float type is not supported yet.\n");
else if (type->u.width == 32)
dst->u.reg.u.immconst_float[0] = bitcast_uint64_to_float(record->operands[0]);
else if (type->u.width == 64)
dst->u.reg.u.immconst_double[0] = bitcast_uint64_to_double(record->operands[0]);
else
vkd3d_unreachable();
break;
case CST_CODE_DATA:
WARN("Unhandled constant array.\n");
break;
default:
FIXME("Unhandled constant code %u.\n", record->code);
break;
}
++sm6->value_count;
}
return VKD3D_OK;
}
static enum vkd3d_result sm6_parser_globals_init(struct sm6_parser *sm6)
{
const struct dxil_block *block = &sm6->root_block;
@ -1471,6 +1726,44 @@ static enum vkd3d_result sm6_parser_globals_init(struct sm6_parser *sm6)
return VKD3D_OK;
}
static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const struct dxil_block *block,
unsigned int level)
{
enum vkd3d_result ret;
size_t i;
for (i = 0; i < block->child_block_count; ++i)
{
if ((ret = sm6_parser_module_init(sm6, block->child_blocks[i], level + 1)) < 0)
return ret;
}
sm6->p.location.line = block->id;
sm6->p.location.column = 0;
switch (block->id)
{
case CONSTANTS_BLOCK:
return sm6_parser_constants_init(sm6, block);
case BLOCKINFO_BLOCK:
case MODULE_BLOCK:
case PARAMATTR_BLOCK:
case PARAMATTR_GROUP_BLOCK:
case VALUE_SYMTAB_BLOCK:
case METADATA_BLOCK:
case METADATA_ATTACHMENT_BLOCK:
case TYPE_BLOCK:
break;
default:
FIXME("Unhandled block id %u.\n", block->id);
break;
}
return VKD3D_OK;
}
static void sm6_type_table_cleanup(struct sm6_type *types, size_t count)
{
size_t i;
@ -1682,7 +1975,13 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t
return ret;
}
sm6_parser_compute_max_value_count(sm6, &sm6->root_block);
if (sm6_parser_compute_max_value_count(sm6, &sm6->root_block, 0) == SIZE_MAX)
{
WARN("Value array count overflowed.\n");
vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
"Overflow occurred in the DXIL module value count.");
return VKD3D_ERROR_INVALID_SHADER;
}
if (!(sm6->values = vkd3d_calloc(sm6->value_capacity, sizeof(*sm6->values))))
{
ERR("Failed to allocate value array.\n");
@ -1697,6 +1996,19 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t
return ret;
}
if ((ret = sm6_parser_module_init(sm6, &sm6->root_block, 0)) < 0)
{
if (ret == VKD3D_ERROR_OUT_OF_MEMORY)
vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
"Out of memory parsing DXIL module.");
else if (ret == VKD3D_ERROR_INVALID_SHADER)
vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
"DXIL module is invalid.");
else
vkd3d_unreachable();
return ret;
}
dxil_block_destroy(&sm6->root_block);
return VKD3D_OK;

View File

@ -161,6 +161,7 @@ enum vkd3d_shader_error
VKD3D_SHADER_ERROR_DXIL_UNSUPPORTED_BITCODE_FORMAT = 8008,
VKD3D_SHADER_ERROR_DXIL_INVALID_FUNCTION_DCL = 8009,
VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_ID = 8010,
VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE = 8011,
VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER = 8300,
VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE = 8301,
@ -536,6 +537,7 @@ enum vkd3d_data_type
VKD3D_DATA_DOUBLE,
VKD3D_DATA_CONTINUED,
VKD3D_DATA_UNUSED,
VKD3D_DATA_UINT8,
};
enum vkd3d_immconst_type