[PATCH] D116465: [SPIRV 6/6] Add 2 essential passes and the simplest tests
Ilia Diachkov via Phabricator via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 3 04:00:55 PST 2022
iliya-diyachkov added a comment.
> So, what about a singleton emitter for types and constants?
>
> Instead of just get_next_id() you could have a get_type(...) and get_constant(...) that returns a cached MIR node if it has created a similar one already, or creates a new one, caches and returns it.
>
> In this way, there's only ever one copy of each and they're only generated once, with no need to deduplicate.
I agree that this is a natural desire to use some caching in this situation. But I join Alexander in the question how the proposed caching would work at module (cross-function) level? As I understand we can define %type1 register in func1 and refer to the register inside func1, but it's illegal to refer %type1 in func2 without a local definition of %type1 (let me remind that in SPIR-V the type definition is a regular MIR instruction which produces %type register).
In fact we do map types, constants, global vars and external function decls but at local function level (however I did not include this implementation to this series, replacing this with simple type/const instruction copying in extractInstructionsWithGlobalRegsToMetablockForMBB for now, in the next patches it will be changed to the map using). In llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h you can find the interface getOrCreateSPIRVType, which returns either existing OpType*** instruction or create new one. Then in GlobalTypesAndRegNumPass we walk the map and create only required type/const/var/fdecl instructions in the "global" function.
Anyway in this approach we need to leave the instructions' copies in regular functions to pass the verifier (in LLVM build with -DLLVM_ENABLE_EXPENSIVE_CHECKS=on -DLLVM_ENABLE_ABI_BREAKING_CHECKS=on).
Taking into account the implemented mapping now we keep duplicates at function level to make MIR consistent. For example:
After generation:
func1
%0 = OpTypeInt 32
%1 = OpTypeInt 64
...
%4 = OpIAdd %1 %2 %3
...
func2
%0 = OpTypeInt 32
%1 = OpTypeInt 64
...
%4 = OpIMul %1 %2 %3
...
After GlobalTypesAndRegNumPass:
global_func:
%0 = OpTypeInt 32
%1 = OpTypeInt 64
...
func1
%0 = OpTypeInt 32
%1 = OpTypeInt 64
...
%4 = OpIAdd %1 %2 %3
...
func2
%0 = OpTypeInt 32
%1 = OpTypeInt 64
...
%4 = OpIMul %1 %2 %3
...
After emitting:
global_func:
%0 = OpTypeInt 32
%1 = OpTypeInt 64
...
func1
...
%4 = OpIAdd %1 %2 %3
...
func2
...
%7 = OpIMul %1 %5 %6
...
And as far as I understand your suggestion this de-duplicated variant will break the verifier (after generation):
func1
%0 = OpTypeInt 32
%1 = OpTypeInt 64
...
%4 = OpIAdd %1 %2 %3
...
func2
// (no definition of %1)
...
%4 = OpIMul %1 %2 %3
...
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D116465/new/
https://reviews.llvm.org/D116465
More information about the llvm-commits
mailing list