[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
Wed Feb 2 13:51:10 PST 2022


iliya-diyachkov added a comment.

In D116465#3291049 <https://reviews.llvm.org/D116465#3291049>, @rengolin wrote:

> Do you have a maximum ID? If not, than in theory, you just need a static global auto-increment ID (like a get_next_id() on some target emission class) and off you go emitting globally unique IDs.

Actually we need to output Bound (not implemented in this series) so all IDs should satisfy "0 < id < Bound". I need to note that according to the SPIR-V specification "Bound should be small, smaller is better, with all IDs in a module being densely packed and near 0".

I would say that we use a numbering similar to described get_next_id() but inside this pass. We store its results in a map and substitute them during emission. I suppose numbering on emission with get_next_id() would also use Register - ID mapping to give same IDs to the same registers.

> Note two definitions of Int 64 but with two different IDs. Would that be a binary format error for (native?) types? Would that be an error for constants, too?

Yes for types. From the specification: "Two different type IDs form, by definition, two different types. It is invalid to declare multiple nonaggregate, non-pointer type IDs having the same opcode and operands..."

As I know constant duplication is not forbidden, but such duplication looks like a bad solution since the Bound of IDs is recommended to be small. Also our reference tool (SPIRV-LLVM-Translator) does not duplicate constants as possible, and we would like to be conformant with it so the translator users will get similar results with the SPIRV LLVM backend (on transition to this tool).

> If each type can only be defined once, you could have a list of all possible types and defined them in the beginning (in your "global" function), from %0 to %N. Then, while emitting each function, you just use those IDs and you don't need to deduplicate.

"a list of all possible types" would be very long because it includes not only simple types but also pointer types, composite types, builtin types... And please remember about densely packed IDs: types also have IDs and we don't want to have gaps in ID space due to type IDs unused in a module.

In addition to types and constants, we need to put global variables, external function declaration, annotations, debug info and some other instructions to that "global" function. All this is presented in the form of instructions with their register operands.  The instructions should be only in the global function with no duplication in regular functions (however, there are a few exceptions).

All these instructions are generated in regular functions at first, and we need a pass (running on module) to copy them to the "global" function. Also we avoid duplication of all of the instructions where possible (as SPIRV-LLVM-Translator does too).

In the last implementation we copy required instructions to the "global" function in SPIRVGlobalTypesAndRegNum pass. We keep in the regular functions the instructions (that were copied to the "global" function) till emission but do not emit them (while the instructions' copies in the "global" function are emitted). Also we number all registers globally avoiding duplication. These global register numbers are stored in a "register alias" map. On emission we output IDs from the map instead of the old local register numbers.

In this way MIR is still consistent after the pass (unlike we had in the earlier implementation) and verifier does not output errors. If it's desirable I can provide MIR/SPIR-V dumps demonstrating how it works on specific tests.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D116465/new/

https://reviews.llvm.org/D116465



More information about the llvm-commits mailing list