From 007f894b9493eb80091899dab971e369f1673a4f Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Fri, 20 Jan 2023 11:13:01 +1000 Subject: [PATCH] vkd3d-shader/sm1: Store parsed instructions in an array. --- libs/vkd3d-shader/d3dbc.c | 112 +++++++++++++++-------- libs/vkd3d-shader/vkd3d_shader_main.c | 102 +++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 63 +++++++++++++ 3 files changed, 241 insertions(+), 36 deletions(-) diff --git a/libs/vkd3d-shader/d3dbc.c b/libs/vkd3d-shader/d3dbc.c index c5518752..89905869 100644 --- a/libs/vkd3d-shader/d3dbc.c +++ b/libs/vkd3d-shader/d3dbc.c @@ -210,13 +210,6 @@ struct vkd3d_shader_sm1_parser const uint32_t *start, *end; bool abort; - struct vkd3d_shader_src_param src_rel_addr[4]; - struct vkd3d_shader_src_param pred_rel_addr; - struct vkd3d_shader_src_param dst_rel_addr; - struct vkd3d_shader_src_param src_param[4]; - struct vkd3d_shader_src_param pred_param; - struct vkd3d_shader_dst_param dst_param; - struct vkd3d_shader_parser p; }; @@ -556,33 +549,50 @@ static void shader_sm1_destroy(struct vkd3d_shader_parser *parser) { struct vkd3d_shader_sm1_parser *sm1 = vkd3d_shader_sm1_parser(parser); + shader_instruction_array_destroy(&parser->instructions); free_shader_desc(&sm1->p.shader_desc); vkd3d_free(sm1); } static void shader_sm1_read_src_param(struct vkd3d_shader_sm1_parser *sm1, const uint32_t **ptr, - struct vkd3d_shader_src_param *src_param, struct vkd3d_shader_src_param *src_rel_addr) + struct vkd3d_shader_src_param *src_param) { + struct vkd3d_shader_src_param *src_rel_addr = NULL; uint32_t token, addr_token; shader_sm1_read_param(sm1, ptr, &token, &addr_token); if (has_relative_address(token)) + { + if (!(src_rel_addr = shader_parser_get_src_params(&sm1->p, 1))) + { + vkd3d_shader_parser_error(&sm1->p, VKD3D_SHADER_ERROR_D3DBC_OUT_OF_MEMORY, + "Out of memory."); + sm1->abort = true; + return; + } shader_sm1_parse_src_param(addr_token, NULL, src_rel_addr); - else - src_rel_addr = NULL; + } shader_sm1_parse_src_param(token, src_rel_addr, src_param); } static void shader_sm1_read_dst_param(struct vkd3d_shader_sm1_parser *sm1, const uint32_t **ptr, - struct vkd3d_shader_dst_param *dst_param, struct vkd3d_shader_src_param *dst_rel_addr) + struct vkd3d_shader_dst_param *dst_param) { + struct vkd3d_shader_src_param *dst_rel_addr = NULL; uint32_t token, addr_token; shader_sm1_read_param(sm1, ptr, &token, &addr_token); if (has_relative_address(token)) + { + if (!(dst_rel_addr = shader_parser_get_src_params(&sm1->p, 1))) + { + vkd3d_shader_parser_error(&sm1->p, VKD3D_SHADER_ERROR_D3DBC_OUT_OF_MEMORY, + "Out of memory."); + sm1->abort = true; + return; + } shader_sm1_parse_src_param(addr_token, NULL, dst_rel_addr); - else - dst_rel_addr = NULL; + } shader_sm1_parse_dst_param(token, dst_rel_addr, dst_param); } @@ -731,10 +741,13 @@ static void shader_sm1_validate_instruction(struct vkd3d_shader_sm1_parser *sm1, static void shader_sm1_read_instruction(struct vkd3d_shader_parser *parser, struct vkd3d_shader_instruction *ins) { struct vkd3d_shader_sm1_parser *sm1 = vkd3d_shader_sm1_parser(parser); + struct vkd3d_shader_src_param *src_params, *predicate; const struct vkd3d_sm1_opcode_info *opcode_info; + struct vkd3d_shader_dst_param *dst_param; const uint32_t **ptr = &parser->ptr; uint32_t opcode_token; const uint32_t *p; + bool predicated; unsigned int i; shader_sm1_read_comment(sm1); @@ -761,11 +774,18 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_parser *parser, stru ins->coissue = opcode_token & VKD3D_SM1_COISSUE; ins->raw = false; ins->structured = false; - ins->predicate = opcode_token & VKD3D_SM1_INSTRUCTION_PREDICATED ? &sm1->pred_param : NULL; + predicated = !!(opcode_token & VKD3D_SM1_INSTRUCTION_PREDICATED); + ins->predicate = predicate = predicated ? shader_parser_get_src_params(parser, 1) : NULL; ins->dst_count = opcode_info->dst_count; - ins->dst = &sm1->dst_param; + ins->dst = dst_param = shader_parser_get_dst_params(parser, ins->dst_count); ins->src_count = opcode_info->src_count; - ins->src = sm1->src_param; + ins->src = src_params = shader_parser_get_src_params(parser, ins->src_count); + if ((!predicate && predicated) || (!src_params && ins->src_count) || (!dst_param && ins->dst_count)) + { + vkd3d_shader_parser_error(parser, VKD3D_SHADER_ERROR_D3DBC_OUT_OF_MEMORY, "Out of memory."); + goto fail; + } + ins->resource_type = VKD3D_SHADER_RESOURCE_NONE; ins->resource_stride = 0; ins->resource_data_type[0] = VKD3D_DATA_FLOAT; @@ -790,32 +810,32 @@ static void shader_sm1_read_instruction(struct vkd3d_shader_parser *parser, stru } else if (ins->handler_idx == VKD3DSIH_DEF) { - shader_sm1_read_dst_param(sm1, &p, &sm1->dst_param, &sm1->dst_rel_addr); - shader_sm1_read_immconst(sm1, &p, &sm1->src_param[0], VKD3D_IMMCONST_VEC4, VKD3D_DATA_FLOAT); + shader_sm1_read_dst_param(sm1, &p, dst_param); + shader_sm1_read_immconst(sm1, &p, &src_params[0], VKD3D_IMMCONST_VEC4, VKD3D_DATA_FLOAT); } else if (ins->handler_idx == VKD3DSIH_DEFB) { - shader_sm1_read_dst_param(sm1, &p, &sm1->dst_param, &sm1->dst_rel_addr); - shader_sm1_read_immconst(sm1, &p, &sm1->src_param[0], VKD3D_IMMCONST_SCALAR, VKD3D_DATA_UINT); + shader_sm1_read_dst_param(sm1, &p, dst_param); + shader_sm1_read_immconst(sm1, &p, &src_params[0], VKD3D_IMMCONST_SCALAR, VKD3D_DATA_UINT); } else if (ins->handler_idx == VKD3DSIH_DEFI) { - shader_sm1_read_dst_param(sm1, &p, &sm1->dst_param, &sm1->dst_rel_addr); - shader_sm1_read_immconst(sm1, &p, &sm1->src_param[0], VKD3D_IMMCONST_VEC4, VKD3D_DATA_INT); + shader_sm1_read_dst_param(sm1, &p, dst_param); + shader_sm1_read_immconst(sm1, &p, &src_params[0], VKD3D_IMMCONST_VEC4, VKD3D_DATA_INT); } else { /* Destination token */ if (ins->dst_count) - shader_sm1_read_dst_param(sm1, &p, &sm1->dst_param, &sm1->dst_rel_addr); + shader_sm1_read_dst_param(sm1, &p, dst_param); /* Predication token */ if (ins->predicate) - shader_sm1_read_src_param(sm1, &p, &sm1->pred_param, &sm1->pred_rel_addr); + shader_sm1_read_src_param(sm1, &p, predicate); /* Other source tokens */ for (i = 0; i < ins->src_count; ++i) - shader_sm1_read_src_param(sm1, &p, &sm1->src_param[i], &sm1->src_rel_addr[i]); + shader_sm1_read_src_param(sm1, &p, &src_params[i]); } if (sm1->abort) @@ -851,20 +871,12 @@ static bool shader_sm1_is_end(struct vkd3d_shader_parser *parser) return false; } -static void shader_sm1_reset(struct vkd3d_shader_parser *parser) -{ - struct vkd3d_shader_sm1_parser *sm1 = vkd3d_shader_sm1_parser(parser); - - parser->ptr = sm1->start; - parser->failed = false; -} - const struct vkd3d_shader_parser_ops shader_sm1_parser_ops = { - .parser_reset = shader_sm1_reset, + .parser_reset = shader_parser_reset, .parser_destroy = shader_sm1_destroy, - .parser_read_instruction = shader_sm1_read_instruction, - .parser_is_end = shader_sm1_is_end, + .parser_read_instruction = shader_parser_read_instruction, + .parser_is_end = shader_parser_is_end, }; static enum vkd3d_result shader_sm1_init(struct vkd3d_shader_sm1_parser *sm1, @@ -922,6 +934,10 @@ static enum vkd3d_result shader_sm1_init(struct vkd3d_shader_sm1_parser *sm1, sm1->start = &code[1]; sm1->end = &code[token_count]; + /* Estimate instruction count to avoid reallocation in most shaders. */ + if (!shader_instruction_array_init(&sm1->p.instructions, code_size != ~(size_t)0 ? token_count / 4u + 4: 16)) + return VKD3D_ERROR_OUT_OF_MEMORY; + vkd3d_shader_parser_init(&sm1->p, message_context, compile_info->source_name, &version, &shader_sm1_parser_ops); shader_desc = &sm1->p.shader_desc; shader_desc->byte_code = code; @@ -934,6 +950,8 @@ static enum vkd3d_result shader_sm1_init(struct vkd3d_shader_sm1_parser *sm1, int vkd3d_shader_sm1_parser_create(const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_message_context *message_context, struct vkd3d_shader_parser **parser) { + struct vkd3d_shader_instruction_array *instructions; + struct vkd3d_shader_instruction *ins; struct vkd3d_shader_sm1_parser *sm1; int ret; @@ -950,6 +968,28 @@ int vkd3d_shader_sm1_parser_create(const struct vkd3d_shader_compile_info *compi return ret; } + instructions = &sm1->p.instructions; + while (!shader_sm1_is_end(&sm1->p)) + { + if (!shader_instruction_array_reserve(instructions, instructions->count + 1)) + { + ERR("Failed to allocate instructions.\n"); + vkd3d_shader_parser_error(&sm1->p, VKD3D_SHADER_ERROR_D3DBC_OUT_OF_MEMORY, "Out of memory."); + shader_sm1_destroy(&sm1->p); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + ins = &instructions->elements[instructions->count]; + shader_sm1_read_instruction(&sm1->p, ins); + + if (ins->handler_idx == VKD3DSIH_INVALID) + { + WARN("Encountered unrecognized or invalid instruction.\n"); + shader_sm1_destroy(&sm1->p); + return VKD3D_ERROR_INVALID_SHADER; + } + ++instructions->count; + } + *parser = &sm1->p; return VKD3D_OK; diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index 72a6d2d8..d5e7e737 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -461,6 +461,22 @@ void VKD3D_PRINTF_FUNC(3, 4) vkd3d_shader_parser_warning(struct vkd3d_shader_par va_end(args); } +void shader_parser_reset(struct vkd3d_shader_parser *parser) +{ + parser->instruction_idx = 0; + parser->failed = false; +} + +void shader_parser_read_instruction(struct vkd3d_shader_parser *parser, struct vkd3d_shader_instruction *ins) +{ + *ins = parser->instructions.elements[parser->instruction_idx++]; +} + +bool shader_parser_is_end(struct vkd3d_shader_parser *parser) +{ + return parser->instruction_idx >= parser->instructions.count; +} + static int vkd3d_shader_validate_compile_info(const struct vkd3d_shader_compile_info *compile_info, bool validate_target_type) { @@ -1583,3 +1599,89 @@ void vkd3d_shader_set_log_callback(PFN_vkd3d_log callback) { vkd3d_dbg_set_log_callback(callback); } + +static struct vkd3d_shader_param_node *shader_param_allocator_node_create( + struct vkd3d_shader_param_allocator *allocator) +{ + struct vkd3d_shader_param_node *node; + + if (!(node = vkd3d_malloc(offsetof(struct vkd3d_shader_param_node, param[allocator->count * allocator->stride])))) + return NULL; + node->next = NULL; + return node; +} + +static void shader_param_allocator_init(struct vkd3d_shader_param_allocator *allocator, + unsigned int count, unsigned int stride) +{ + allocator->count = max(count, 4); + allocator->stride = stride; + allocator->head = NULL; + allocator->current = NULL; + allocator->index = allocator->count; +} + +static void shader_param_allocator_destroy(struct vkd3d_shader_param_allocator *allocator) +{ + struct vkd3d_shader_param_node *current = allocator->head; + + while (current) + { + struct vkd3d_shader_param_node *next = current->next; + vkd3d_free(current); + current = next; + } +} + +void *shader_param_allocator_get(struct vkd3d_shader_param_allocator *allocator, unsigned int count) +{ + void *params; + + if (!count) + return NULL; + + if (count > allocator->count - allocator->index) + { + struct vkd3d_shader_param_node *next = shader_param_allocator_node_create(allocator); + + if (!next) + return NULL; + if (allocator->current) + allocator->current->next = next; + allocator->current = next; + allocator->index = 0; + } + + params = &allocator->current->param[allocator->index * allocator->stride]; + allocator->index += count; + return params; +} + +bool shader_instruction_array_init(struct vkd3d_shader_instruction_array *instructions, unsigned int reserve) +{ + memset(instructions, 0, sizeof(*instructions)); + /* Size the parameter initial allocations so they are large enough for most shaders. The + * code path for chained allocations will be tested if a few shaders need to use it. */ + shader_param_allocator_init(&instructions->dst_params, reserve - reserve / 8u, + sizeof(*instructions->elements->dst)); + shader_param_allocator_init(&instructions->src_params, reserve * 2u, sizeof(*instructions->elements->src)); + return shader_instruction_array_reserve(instructions, reserve); +} + +bool shader_instruction_array_reserve(struct vkd3d_shader_instruction_array *instructions, unsigned int reserve) +{ + if (!vkd3d_array_reserve((void **)&instructions->elements, &instructions->capacity, reserve, + sizeof(*instructions->elements))) + { + ERR("Failed to allocate instructions.\n"); + return false; + } + return true; +} + +void shader_instruction_array_destroy(struct vkd3d_shader_instruction_array *instructions) +{ + vkd3d_free(instructions->elements); + shader_param_allocator_destroy(&instructions->dst_params); + shader_param_allocator_destroy(&instructions->src_params); +} diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 5002a13c..848ac45d 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -133,6 +133,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_D3DBC_INVALID_VERSION_TOKEN = 7001, VKD3D_SHADER_ERROR_D3DBC_INVALID_OPCODE = 7002, VKD3D_SHADER_ERROR_D3DBC_INVALID_RESOURCE_TYPE = 7003, + VKD3D_SHADER_ERROR_D3DBC_OUT_OF_MEMORY = 7004, VKD3D_SHADER_WARNING_D3DBC_IGNORED_INSTRUCTION_FLAGS= 7300, }; @@ -929,6 +930,51 @@ struct vkd3d_shader_location unsigned int line, column; }; +struct vkd3d_shader_param_node +{ + struct vkd3d_shader_param_node *next; + uint8_t param[]; +}; + +struct vkd3d_shader_param_allocator +{ + struct vkd3d_shader_param_node *head; + struct vkd3d_shader_param_node *current; + unsigned int count; + unsigned int stride; + unsigned int index; +}; + +void *shader_param_allocator_get(struct vkd3d_shader_param_allocator *allocator, unsigned int count); + +static inline struct vkd3d_shader_src_param *shader_src_param_allocator_get( + struct vkd3d_shader_param_allocator *allocator, unsigned int count) +{ + assert(allocator->stride == sizeof(struct vkd3d_shader_src_param)); + return shader_param_allocator_get(allocator, count); +} + +static inline struct vkd3d_shader_dst_param *shader_dst_param_allocator_get( + struct vkd3d_shader_param_allocator *allocator, unsigned int count) +{ + assert(allocator->stride == sizeof(struct vkd3d_shader_dst_param)); + return shader_param_allocator_get(allocator, count); +} + +struct vkd3d_shader_instruction_array +{ + struct vkd3d_shader_instruction *elements; + size_t capacity; + size_t count; + + struct vkd3d_shader_param_allocator src_params; + struct vkd3d_shader_param_allocator dst_params; +}; + +bool shader_instruction_array_init(struct vkd3d_shader_instruction_array *instructions, unsigned int reserve); +bool shader_instruction_array_reserve(struct vkd3d_shader_instruction_array *instructions, unsigned int reserve); +void shader_instruction_array_destroy(struct vkd3d_shader_instruction_array *instructions); + struct vkd3d_shader_parser { struct vkd3d_shader_message_context *message_context; @@ -939,6 +985,8 @@ struct vkd3d_shader_parser struct vkd3d_shader_version shader_version; const uint32_t *ptr; const struct vkd3d_shader_parser_ops *ops; + struct vkd3d_shader_instruction_array instructions; + size_t instruction_idx; }; struct vkd3d_shader_parser_ops @@ -956,6 +1004,21 @@ void vkd3d_shader_parser_init(struct vkd3d_shader_parser *parser, const struct vkd3d_shader_version *version, const struct vkd3d_shader_parser_ops *ops); void vkd3d_shader_parser_warning(struct vkd3d_shader_parser *parser, enum vkd3d_shader_error error, const char *format, ...) VKD3D_PRINTF_FUNC(3, 4); +void shader_parser_reset(struct vkd3d_shader_parser *parser); +void shader_parser_read_instruction(struct vkd3d_shader_parser *parser, struct vkd3d_shader_instruction *ins); +bool shader_parser_is_end(struct vkd3d_shader_parser *parser); + +static inline struct vkd3d_shader_dst_param *shader_parser_get_dst_params( + struct vkd3d_shader_parser *parser, unsigned int count) +{ + return shader_dst_param_allocator_get(&parser->instructions.dst_params, count); +} + +static inline struct vkd3d_shader_src_param *shader_parser_get_src_params( + struct vkd3d_shader_parser *parser, unsigned int count) +{ + return shader_src_param_allocator_get(&parser->instructions.src_params, count); +} static inline void vkd3d_shader_parser_destroy(struct vkd3d_shader_parser *parser) {