Skip to content

Conversation

@V-FEXrt
Copy link
Collaborator

@V-FEXrt V-FEXrt commented Oct 24, 2025

Proposal to discuss various options to enable experimental features for DXIL ops and HLSL intrinsics

* Doesn't consume any of the current opcode space
* Obvious from reading the DXIL that experimental/extension is being used
Cons:
* Transistion from experimental to stable seems quite complicated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 * Transistion from experimental to stable seems quite complicated

May be worth digging into this a bit more before we discuss it. I think the assumption here is that this transition would actually be to move to a constant op-code value. But why would we need to do that? What if the actual mechanism for making something stable is to just mark a feature ID as stable?

Copy link
Collaborator Author

@V-FEXrt V-FEXrt Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the assumption here is that this transition would actually be to move to a constant op-code value.

Yep, a core assumption in the document is that "stable" opcodes should stay the same as they currently are in DXIL.

But why would we need to do that? What if the actual mechanism for making something stable is to just mark a feature ID as stable?

That's an interesting point and I have some messy thoughts below but I'll try to keep them organized :) I'll also say, this proposal is derived from something @tex3d wrote and I'm not certain I'm doing it justice.

So If I'm reading your message correctly, it sounds like we have a list of "experimental" extensions somewhere then the following

%feature_id = i32 123
%cool_operation = i32 456
%opcode = i32 dx.create.extensionop(%feature_id, %cool_operaton)
%result = i32 dx.op.binary(%opcode, %a, %b)

and once the feature is no longer experimental then we remove feature ID 123 from the experimental list?

My main criticism is that we'd be "splitting" up stable opcodes. All the pre SM6.10 opcodes would use the immediate constant while all SM6.10+ stable opcodes would essentially just be extensions. Oh also, idk how much it would actually matter but this would also increase binary size over flattening them back down into an imm constant

aside: actually, writing it out this proposal is very similar to the "Single Specific Experimental Opcode" proposal below with the main difference being that you can reused %opcode in this one

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, this is in line with what I was suggesting.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorporated into the document! Feel free to resolve this comment if you are happy with it :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we were willing to leave the experimental ops in a separate table, we could always do the same with the partitioned approach. Both approaches would leave behind reserved opcode slots for any opcodes deprecated during feature development.

Copy link
Member

@damyanp damyanp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great write up, thanks!

I've left some editorial comments and one thing that might be good to expand on, but other than that this looks like it's in pretty good shape to merge.

@V-FEXrt V-FEXrt marked this pull request as ready for review October 24, 2025 20:58
Comment on lines +25 to +26
solves real world use cases. Traditionally this has been done by marking all in
development opcodes as expirmental opcodes of the next dxil version release.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There has never been a way of "marking" opcodes as experimental. They are simply added at the end of the most recent released DXIL opcodes. We have an enum that defines the number of released opcodes for an indicator of which set is frozen for which DXIL version.

Suggested change
solves real world use cases. Traditionally this has been done by marking all in
development opcodes as expirmental opcodes of the next dxil version release.
solves real world use cases. Traditionally this has been done by adding new
opcodes right after the last released opcode in the prior DXIL version.

Comment on lines +27 to +32
In most cases this is sufficient as the opcodes are accepted into the next
release but challenges arise when one or more opcodes are rejected or delayed
from the release while the opcodes following them are not. In the rejection
case the opcodes must be burned and in the delayed case the opcodes must be
manually moved to the next release with special casing code. This proposal
seaks to implemented a systematic method to handle these issues.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has actually been problematic for many releases. Each of these issues has been a PITA for many releases:

  • opcodes can't be changed without either a breaking change across in-development driver, compiler, and test scenarios, or burning opcodes, turning them into "reserved".
  • The intention is that unused opcodes are removed, and the final opcodes for the next shader model are compacted into the block of final opcodes. This creates a breaking change line again, across compiler version, drivers, and any compiled code.
  • Independent feature development requires difficult synchronization since all ops must be serialized into one compact list of enumerated ops.
  • There's no way to bridge the transition between preview and final versions.

We never had the opportunity to clean up our ops for SM 6.9 before we found out we couldn't change them. This left multiple ranges of "reserved" DXIL ops behind that are now part of DXIL 1.9, 28 ops in total!

Suggested change
In most cases this is sufficient as the opcodes are accepted into the next
release but challenges arise when one or more opcodes are rejected or delayed
from the release while the opcodes following them are not. In the rejection
case the opcodes must be burned and in the delayed case the opcodes must be
manually moved to the next release with special casing code. This proposal
seaks to implemented a systematic method to handle these issues.
In some cases this is sufficient, when feature development is unified, opcodes
don't change after being added, and all opcodes in a contiguous block starting
from the prior release are accepted into the next release.
But challenges arise during parallel feature development, from experimental
feature evolution requiring opcode changes, or when a feature and its opcodes
are excluded from the release while the opcodes following them are not.
Excluded opcodes must either be turned into reserved opcodes or a breaking DXIL
change must be synchronized between the compiler, tests, and drivers.
This proposal seeks to implement a systematic method to handle these issues.

## Goals
This proposal seeks to address the following points:
* Needless churn when experimental op are delayed or rejected
* Expermental op boundary point is rigid and moves with every SM update
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Expermental op boundary point is rigid and moves with every SM update
* Experimental feature boundaries are rigid and unaffected by SM updates

This proposal seeks to address the following points:
* Needless churn when experimental op are delayed or rejected
* Expermental op boundary point is rigid and moves with every SM update
* Long term experiments (and potentially extensions) aren't currently feasible
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Long term experiments (and potentially extensions) aren't currently feasible
* Enable long term experiments and potentially extensions

Comment on lines +43 to +44
* IHV drivers can support both experimental and stable versions of an op simultaneously
* simplifies migrations
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* IHV drivers can support both experimental and stable versions of an op simultaneously
* simplifies migrations
* Soft transitions between versions of experimental ops and final ops simplify migrations
* IHV drivers can support multiple experimental versions and the final version of a set of ops in the same driver

* Doesn't consume large portions of the current opcode space
* obvious from reading the DXIL that experimental/extension is being used
Cons:
* transistion from experimental to stable isn't just dropping the `x`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one actually makes the transition significantly harder than the other options. For the other options, you could use a regex search/replace to update opcodes (with some risk of missing some cases). In theory, you could even add a symbolic opcode replacer to avoid having to ever update the tests.

But with this approach, the instruction stream is changed in a way that can't easily be updated with text search/replace, or maped through a symbolic opcode replacement utility.

* Unclear how to allocate extension vs experimental ops in the opx space

### Extension/Experimental Feature Opcode
Relaxing the restriction that DXIL opcodes are immediate constants would allow
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know exactly how much we would break with this proposed, but I'm pretty sure it would be a lot. This would be a hard adjustment to make. It complicates DXIL op construction, as you'll need to lookup or create a special op in the entry block just to provide the OpCode.

DXIL op property lookup by OpCode happens a lot for DXIL operations, and this would complicate and increase overhead for that operation.

* Doesn't consume any of the current opcode space
* Obvious from reading the DXIL that experimental/extension is being used
Cons:
* Transistion from experimental to stable seems quite complicated
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we were willing to leave the experimental ops in a separate table, we could always do the same with the partitioned approach. Both approaches would leave behind reserved opcode slots for any opcodes deprecated during feature development.


Intrinsic functions should be handled in a reasonable way. Ideally this means
that an intrinsic is only available if the experimental/extension op is also
available. Likely this means updating gen_intrin_main to mark an intrinsic as
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the way to do this is with a shader model attribute on the intrinsic definition. This could continue to work, but would just require an update to the shader model requirement for experimental feature intrinsics when not being accepted into the next shader model. That's probably not a very big deal to manage actually.

Another approach would be to add a new attribute you can define on the intrinsic, like feature=FeatureName, which would tie the intrinsic to a feature, which could be marked experimental until it ships, at which point it would impact the minimum shader model requirement, just like the shader model attribute.


### Single Specific Experimental Opcode with varargs
A new opcode class `dx.op.extension` is introduced as a core stable opcode in
which named opcode subsets can be called directly.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a non-starter, given the inability to use any of the DXIL op infrastructure for experimental ops, then requiring re-implementation for final ops.

Additionally, we need separate function defs in llvm for different attributes, so we'd at least have to have overload-like permutations keyed off attributes, which the DXIL op system also doesn't do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants