[SPIRV] Forward-reference debug scopes with a reserved result id. (#229490)
This PR brings support for cycles when processing DIScope(s). This is
done using `SPV_KHR_relaxed_extended_instruction` for
`OpExtInstWithForwardRefsKHR`, which permits forward references (a use
prior to its definition).
The new emission algorithm works in the following way. DIScopes are
recursively processed as it was implemented at the start of these
stacked PRs. `getOrCreateDebugScope` reserves an id for the current
scope, and recurses to first emit its dependencies. If in the recursion
we hit a back edge (cycle), the scope even though not emitted yet will
have a reserved id. That undefined but reserved id is then used as an
operand alongside `OpExtInstWithForwardRefsKHR`.
When an instruction defining a DIScope is going to be emitted,
`getOrCreateDebugScope` makes sure that the scope's instruction uses its
reserved id. If the current scope is not supported by the backend, it
might be the case that the scope was still used as a forward reference.
This forward reference, which has been emitted, must still be closed to
remain valid. `getOrCreateDebugScope` keeps track of whether the scope
has been used in a forward reference, and if unsupported, it defines it
with a fresh DebugInfoNone. This might in some instances trigger invalid
SPIRV due to conflicts with the spec or bugs in `spirv-val`.
It is worth noting that illegal SPIRV due to DebugInfoNone will only
occur if the BE hits an unsupported feature, and the actual "bug" is the
missing feature. A design where a scope chain is only emitted if all of
it is supported would be more complex. If this is not acceptable by a
consumer, this forward reference emission is only enabled via a flag.
The backend currently has no support for cycles so it won't be a
regression. This PR also provides foundations for a more complex design
if needed.
As reference, the SPIRV-LLVM Translator already reserves ids for these
cycles, but it does not use `SPV_KHR_relaxed_extended_instruction`, so
the SPIRV is always invalid. Its SPIRV to LLVM path will also need to
handle that extension for debug info in order to work with this
implementation.
---------
Co-authored-by: Cursor <cursoragent@cursor.com>