diff --git a/spirv_reflect.c b/spirv_reflect.c index 3a124af3..3320fa4a 100644 --- a/spirv_reflect.c +++ b/spirv_reflect.c @@ -153,13 +153,26 @@ typedef struct SpvReflectPrvString { const char* string; } SpvReflectPrvString; +// There are a limit set of instructions that can touch an OpVariable, +// these are represented here with how it was accessed +// Examples: +// OpImageRead -> OpLoad -> OpVariable +// OpImageWrite -> OpLoad -> OpVariable +// OpStore -> OpAccessChain -> OpAccessChain -> OpVariable +// OpAtomicIAdd -> OpAccessChain -> OpVariable +// OpAtomicLoad -> OpImageTexelPointer -> OpVariable +typedef struct SpvReflectPrvAccessedVariable { + uint32_t result_id; + uint32_t variable_ptr; +} SpvReflectPrvAccessedVariable; + typedef struct SpvReflectPrvFunction { uint32_t id; uint32_t callee_count; uint32_t* callees; struct SpvReflectPrvFunction** callee_ptrs; - uint32_t accessed_ptr_count; - uint32_t* accessed_ptrs; + uint32_t accessed_variable_count; + SpvReflectPrvAccessedVariable* accessed_variables; } SpvReflectPrvFunction; typedef struct SpvReflectPrvAccessChain { @@ -233,6 +246,13 @@ static int SortCompareUint32(const void* a, const void* b) { return (int)*p_a - (int)*p_b; } +static int SortCompareAccessedVariable(const void* a, const void* b) { + const SpvReflectPrvAccessedVariable* p_a = (const SpvReflectPrvAccessedVariable*)a; + const SpvReflectPrvAccessedVariable* p_b = (const SpvReflectPrvAccessedVariable*)b; + + return (int)p_a->variable_ptr - (int)p_b->variable_ptr; +} + // // De-duplicates a sorted array and returns the new size. // @@ -270,28 +290,29 @@ static bool SearchSortedUint32(const uint32_t* arr, size_t size, uint32_t target return false; } -static SpvReflectResult IntersectSortedUint32(const uint32_t* p_arr0, size_t arr0_size, const uint32_t* p_arr1, size_t arr1_size, - uint32_t** pp_res, size_t* res_size) { +static SpvReflectResult IntersectSortedAccessedVariable(const SpvReflectPrvAccessedVariable* p_arr0, size_t arr0_size, + const uint32_t* p_arr1, size_t arr1_size, uint32_t** pp_res, + size_t* res_size) { *pp_res = NULL; *res_size = 0; if (IsNull(p_arr0) || IsNull(p_arr1)) { return SPV_REFLECT_RESULT_SUCCESS; } - const uint32_t* arr0_end = p_arr0 + arr0_size; - const uint32_t* arr1_end = p_arr1 + arr1_size; + const SpvReflectPrvAccessedVariable* p_arr0_end = p_arr0 + arr0_size; + const uint32_t* p_arr1_end = p_arr1 + arr1_size; - const uint32_t* idx0 = p_arr0; - const uint32_t* idx1 = p_arr1; - while (idx0 != arr0_end && idx1 != arr1_end) { - if (*idx0 < *idx1) { - ++idx0; - } else if (*idx0 > *idx1) { - ++idx1; + const SpvReflectPrvAccessedVariable* p_idx0 = p_arr0; + const uint32_t* p_idx1 = p_arr1; + while (p_idx0 != p_arr0_end && p_idx1 != p_arr1_end) { + if (p_idx0->variable_ptr < *p_idx1) { + ++p_idx0; + } else if (p_idx0->variable_ptr > *p_idx1) { + ++p_idx1; } else { ++*res_size; - ++idx0; - ++idx1; + ++p_idx0; + ++p_idx1; } } @@ -300,18 +321,18 @@ static SpvReflectResult IntersectSortedUint32(const uint32_t* p_arr0, size_t arr if (IsNull(*pp_res)) { return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; } - uint32_t* idxr = *pp_res; - idx0 = p_arr0; - idx1 = p_arr1; - while (idx0 != arr0_end && idx1 != arr1_end) { - if (*idx0 < *idx1) { - ++idx0; - } else if (*idx0 > *idx1) { - ++idx1; + uint32_t* p_idxr = *pp_res; + p_idx0 = p_arr0; + p_idx1 = p_arr1; + while (p_idx0 != p_arr0_end && p_idx1 != p_arr1_end) { + if (p_idx0->variable_ptr < *p_idx1) { + ++p_idx0; + } else if (p_idx0->variable_ptr > *p_idx1) { + ++p_idx1; } else { - *(idxr++) = *idx0; - ++idx0; - ++idx1; + *(p_idxr++) = p_idx0->variable_ptr; + ++p_idx0; + ++p_idx1; } } } @@ -602,7 +623,7 @@ static void DestroyParser(SpvReflectPrvParser* p_parser) { for (size_t i = 0; i < p_parser->function_count; ++i) { SafeFree(p_parser->functions[i].callees); SafeFree(p_parser->functions[i].callee_ptrs); - SafeFree(p_parser->functions[i].accessed_ptrs); + SafeFree(p_parser->functions[i].accessed_variables); } // Free access chains @@ -1039,8 +1060,9 @@ static SpvReflectResult ParseFunction(SpvReflectPrvParser* p_parser, SpvReflectP p_func->id = p_func_node->result_id; p_func->callee_count = 0; - p_func->accessed_ptr_count = 0; + p_func->accessed_variable_count = 0; + // First get count to know how much to allocate for (size_t i = first_label_index; i < p_parser->node_count; ++i) { SpvReflectPrvNode* p_node = &(p_parser->nodes[i]); if (p_node->op == SpvOpFunctionEnd) { @@ -1059,11 +1081,11 @@ static SpvReflectResult ParseFunction(SpvReflectPrvParser* p_parser, SpvReflectP case SpvOpInBoundsPtrAccessChain: case SpvOpStore: case SpvOpImageTexelPointer: { - ++(p_func->accessed_ptr_count); + ++(p_func->accessed_variable_count); } break; case SpvOpCopyMemory: case SpvOpCopyMemorySized: { - p_func->accessed_ptr_count += 2; + p_func->accessed_variable_count += 2; } break; default: break; @@ -1077,15 +1099,17 @@ static SpvReflectResult ParseFunction(SpvReflectPrvParser* p_parser, SpvReflectP } } - if (p_func->accessed_ptr_count > 0) { - p_func->accessed_ptrs = (uint32_t*)calloc(p_func->accessed_ptr_count, sizeof(*(p_func->accessed_ptrs))); - if (IsNull(p_func->accessed_ptrs)) { + if (p_func->accessed_variable_count > 0) { + p_func->accessed_variables = + (SpvReflectPrvAccessedVariable*)calloc(p_func->accessed_variable_count, sizeof(*(p_func->accessed_variables))); + if (IsNull(p_func->accessed_variables)) { return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; } } p_func->callee_count = 0; - p_func->accessed_ptr_count = 0; + p_func->accessed_variable_count = 0; + // Now have allocation, fill in values for (size_t i = first_label_index; i < p_parser->node_count; ++i) { SpvReflectPrvNode* p_node = &(p_parser->nodes[i]); if (p_node->op == SpvOpFunctionEnd) { @@ -1104,19 +1128,29 @@ static SpvReflectResult ParseFunction(SpvReflectPrvParser* p_parser, SpvReflectP case SpvOpGenericPtrMemSemantics: case SpvOpInBoundsPtrAccessChain: case SpvOpImageTexelPointer: { - CHECKED_READU32(p_parser, p_node->word_offset + 3, p_func->accessed_ptrs[p_func->accessed_ptr_count]); - (++p_func->accessed_ptr_count); + const uint32_t result_index = p_node->word_offset + 2; + const uint32_t ptr_index = p_node->word_offset + 3; + SpvReflectPrvAccessedVariable* access_ptr = &p_func->accessed_variables[p_func->accessed_variable_count]; + + // Need to track Result ID as not sure there has been any memory access through here yet + CHECKED_READU32(p_parser, result_index, access_ptr->result_id); + CHECKED_READU32(p_parser, ptr_index, access_ptr->variable_ptr); + (++p_func->accessed_variable_count); } break; case SpvOpStore: { - CHECKED_READU32(p_parser, p_node->word_offset + 2, p_func->accessed_ptrs[p_func->accessed_ptr_count]); - (++p_func->accessed_ptr_count); + const uint32_t result_index = p_node->word_offset + 2; + CHECKED_READU32(p_parser, result_index, p_func->accessed_variables[p_func->accessed_variable_count].variable_ptr); + (++p_func->accessed_variable_count); } break; case SpvOpCopyMemory: case SpvOpCopyMemorySized: { - CHECKED_READU32(p_parser, p_node->word_offset + 2, p_func->accessed_ptrs[p_func->accessed_ptr_count]); - (++p_func->accessed_ptr_count); - CHECKED_READU32(p_parser, p_node->word_offset + 3, p_func->accessed_ptrs[p_func->accessed_ptr_count]); - (++p_func->accessed_ptr_count); + // There is no result_id is being zero is same as being invalid + CHECKED_READU32(p_parser, p_node->word_offset + 1, + p_func->accessed_variables[p_func->accessed_variable_count].variable_ptr); + (++p_func->accessed_variable_count); + CHECKED_READU32(p_parser, p_node->word_offset + 2, + p_func->accessed_variables[p_func->accessed_variable_count].variable_ptr); + (++p_func->accessed_variable_count); } break; default: break; @@ -1128,10 +1162,10 @@ static SpvReflectResult ParseFunction(SpvReflectPrvParser* p_parser, SpvReflectP } p_func->callee_count = (uint32_t)DedupSortedUint32(p_func->callees, p_func->callee_count); - if (p_func->accessed_ptr_count > 0) { - qsort(p_func->accessed_ptrs, p_func->accessed_ptr_count, sizeof(*(p_func->accessed_ptrs)), SortCompareUint32); + if (p_func->accessed_variable_count > 0) { + qsort(p_func->accessed_variables, p_func->accessed_variable_count, sizeof(*(p_func->accessed_variables)), + SortCompareAccessedVariable); } - p_func->accessed_ptr_count = (uint32_t)DedupSortedUint32(p_func->accessed_ptrs, p_func->accessed_ptr_count); return SPV_REFLECT_RESULT_SUCCESS; } @@ -3046,60 +3080,59 @@ static SpvReflectResult ParseStaticallyUsedResources(SpvReflectPrvParser* p_pars } called_function_count = DedupSortedUint32(p_called_functions, called_function_count); - uint32_t used_variable_count = 0; + uint32_t used_acessed_count = 0; for (size_t i = 0, j = 0; i < called_function_count; ++i) { // No need to bounds check j because a missing ID issue would have been // found during TraverseCallGraph while (p_parser->functions[j].id != p_called_functions[i]) { ++j; } - used_variable_count += p_parser->functions[j].accessed_ptr_count; + used_acessed_count += p_parser->functions[j].accessed_variable_count; } - uint32_t* used_variables = NULL; - if (used_variable_count > 0) { - used_variables = (uint32_t*)calloc(used_variable_count, sizeof(*used_variables)); - if (IsNull(used_variables)) { + SpvReflectPrvAccessedVariable* p_used_accesses = NULL; + if (used_acessed_count > 0) { + p_used_accesses = (SpvReflectPrvAccessedVariable*)calloc(used_acessed_count, sizeof(SpvReflectPrvAccessedVariable)); + if (IsNull(p_used_accesses)) { SafeFree(p_called_functions); return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED; } } - used_variable_count = 0; + used_acessed_count = 0; for (size_t i = 0, j = 0; i < called_function_count; ++i) { while (p_parser->functions[j].id != p_called_functions[i]) { ++j; } - memcpy(&used_variables[used_variable_count], p_parser->functions[j].accessed_ptrs, - p_parser->functions[j].accessed_ptr_count * sizeof(*used_variables)); - used_variable_count += p_parser->functions[j].accessed_ptr_count; + memcpy(&p_used_accesses[used_acessed_count], p_parser->functions[j].accessed_variables, + p_parser->functions[j].accessed_variable_count * sizeof(SpvReflectPrvAccessedVariable)); + used_acessed_count += p_parser->functions[j].accessed_variable_count; } SafeFree(p_called_functions); - if (used_variable_count > 0) { - qsort(used_variables, used_variable_count, sizeof(*used_variables), SortCompareUint32); + if (used_acessed_count > 0) { + qsort(p_used_accesses, used_acessed_count, sizeof(*p_used_accesses), SortCompareAccessedVariable); } - used_variable_count = (uint32_t)DedupSortedUint32(used_variables, used_variable_count); // Do set intersection to find the used uniform and push constants size_t used_uniform_count = 0; - // - SpvReflectResult result0 = IntersectSortedUint32(used_variables, used_variable_count, uniforms, uniform_count, - &p_entry->used_uniforms, &used_uniform_count); + SpvReflectResult result0 = IntersectSortedAccessedVariable(p_used_accesses, used_acessed_count, uniforms, uniform_count, + &p_entry->used_uniforms, &used_uniform_count); size_t used_push_constant_count = 0; - // - SpvReflectResult result1 = IntersectSortedUint32(used_variables, used_variable_count, push_constants, push_constant_count, - &p_entry->used_push_constants, &used_push_constant_count); + SpvReflectResult result1 = + IntersectSortedAccessedVariable(p_used_accesses, used_acessed_count, push_constants, push_constant_count, + &p_entry->used_push_constants, &used_push_constant_count); - for (uint32_t j = 0; j < p_module->descriptor_binding_count; ++j) { - SpvReflectDescriptorBinding* p_binding = &p_module->descriptor_bindings[j]; - bool found = SearchSortedUint32(used_variables, used_variable_count, p_binding->spirv_id); - if (found) { - p_binding->accessed = 1; + for (uint32_t i = 0; i < p_module->descriptor_binding_count; ++i) { + SpvReflectDescriptorBinding* p_binding = &p_module->descriptor_bindings[i]; + for (uint32_t j = 0; j < used_acessed_count; j++) { + if (p_used_accesses[j].variable_ptr == p_binding->spirv_id) { + p_binding->accessed = 1; + } } } - SafeFree(used_variables); + SafeFree(p_used_accesses); if (result0 != SPV_REFLECT_RESULT_SUCCESS) { return result0; } diff --git a/tests/variable_access/copy_memory.spv b/tests/variable_access/copy_memory.spv new file mode 100644 index 00000000..fbbde8ed Binary files /dev/null and b/tests/variable_access/copy_memory.spv differ diff --git a/tests/variable_access/copy_memory.spv.yaml b/tests/variable_access/copy_memory.spv.yaml new file mode 100644 index 00000000..89e7d93c --- /dev/null +++ b/tests/variable_access/copy_memory.spv.yaml @@ -0,0 +1,161 @@ +%YAML 1.0 +--- +all_type_descriptions: + - &td0 + id: 2 + op: 28 + type_name: + struct_member_name: + storage_class: 0 # UniformConstant + type_flags: 0x20000008 # ARRAY FLOAT + decoration_flags: 0x00000000 # NONE + traits: + numeric: + scalar: { width: 32, signedness: 0 } + vector: { component_count: 0 } + matrix: { column_count: 0, row_count: 0, stride: 0 } + image: { dim: 0, depth: 0, arrayed: 0, ms: 0, sampled: 0, image_format: 0 } # dim=1D image_format=Unknown + array: { dims_count: 1, dims: [32,], stride: 4 } + member_count: 0 + members: + - &td1 + id: 3 + op: 30 + type_name: + struct_member_name: + storage_class: -1 # NOT APPLICABLE + type_flags: 0x10080000 # STRUCT EXTERNAL_BLOCK + decoration_flags: 0x00000001 # BLOCK + traits: + numeric: + scalar: { width: 0, signedness: 0 } + vector: { component_count: 0 } + matrix: { column_count: 0, row_count: 0, stride: 0 } + image: { dim: 0, depth: 0, arrayed: 0, ms: 0, sampled: 0, image_format: 0 } # dim=1D image_format=Unknown + array: { dims_count: 0, dims: [], stride: 0 } + member_count: 1 + members: + - *td0 +all_block_variables: + - &bv0 + name: + offset: 0 + absolute_offset: 0 + size: 128 + padded_size: 128 + decorations: 0x00000000 # NONE + numeric: + scalar: { width: 32, signedness: 0 } + vector: { component_count: 0 } + matrix: { column_count: 0, row_count: 0, stride: 0 } + array: { dims_count: 1, dims: [32,], stride: 4 } + member_count: 0 + members: + type_description: *td0 + - &bv1 + name: + offset: 0 + absolute_offset: 0 + size: 0 + padded_size: 0 + decorations: 0x00000000 # NONE + numeric: + scalar: { width: 0, signedness: 0 } + vector: { component_count: 0 } + matrix: { column_count: 0, row_count: 0, stride: 0 } + array: { dims_count: 0, dims: [], stride: 0 } + member_count: 1 + members: + - *bv0 + type_description: *td1 + - &bv2 + name: + offset: 0 + absolute_offset: 0 + size: 128 + padded_size: 128 + decorations: 0x00000000 # NONE + numeric: + scalar: { width: 32, signedness: 0 } + vector: { component_count: 0 } + matrix: { column_count: 0, row_count: 0, stride: 0 } + array: { dims_count: 1, dims: [32,], stride: 4 } + member_count: 0 + members: + type_description: *td0 + - &bv3 + name: + offset: 0 + absolute_offset: 0 + size: 0 + padded_size: 0 + decorations: 0x00000000 # NONE + numeric: + scalar: { width: 0, signedness: 0 } + vector: { component_count: 0 } + matrix: { column_count: 0, row_count: 0, stride: 0 } + array: { dims_count: 0, dims: [], stride: 0 } + member_count: 1 + members: + - *bv2 + type_description: *td1 +all_descriptor_bindings: + - &db0 + spirv_id: 5 + name: + binding: 0 + input_attachment_index: 0 + set: 0 + descriptor_type: 7 # VK_DESCRIPTOR_TYPE_STORAGE_BUFFER + resource_type: 8 # UAV + image: { dim: 0, depth: 0, arrayed: 0, ms: 0, sampled: 0, image_format: 0 } # dim=1D image_format=Unknown + block: *bv1 # + array: { dims_count: 0, dims: [] } + accessed: 1 + uav_counter_id: 4294967295 + uav_counter_binding: + type_description: *td1 + word_offset: { binding: 48, set: 44 } + - &db1 + spirv_id: 4 + name: + binding: 1 + input_attachment_index: 0 + set: 0 + descriptor_type: 7 # VK_DESCRIPTOR_TYPE_STORAGE_BUFFER + resource_type: 8 # UAV + image: { dim: 0, depth: 0, arrayed: 0, ms: 0, sampled: 0, image_format: 0 } # dim=1D image_format=Unknown + block: *bv3 # + array: { dims_count: 0, dims: [] } + accessed: 1 + uav_counter_id: 4294967295 + uav_counter_binding: + type_description: *td1 + word_offset: { binding: 40, set: 36 } +all_interface_variables: +module: + generator: 7 # Khronos SPIR-V Tools Assembler + entry_point_name: "main" + entry_point_id: 1 + source_language: 0 # Unknown + source_language_version: 0 + spirv_execution_model: 5 # GLCompute + shader_stage: 0x00000020 # CS + descriptor_binding_count: 2 + descriptor_bindings: + - *db0 # + - *db1 # + descriptor_set_count: 1 + descriptor_sets: + - set: 0 + binding_count: 2 + bindings: + - *db0 # + - *db1 # + input_variable_count: 0, + input_variables: + output_variable_count: 0, + output_variables: + push_constant_count: 0, + push_constants: +...