Skip to content

Conversation

s-perron
Copy link
Contributor

@s-perron s-perron commented Oct 7, 2025

[SPIRV][HLSL] Add Sema and CodeGen for implicit typed buffer counters

This commit implements the Sema and CodeGen portions of the typed buffer
counter proposal described in the HLSL WG proposal 0023.

This change introduces the necessary Sema and CodeGen logic to handle
implicit counter variables for typed buffers. This includes:

  • Extending HLSLResourceBindingAttr to store the implicit counter
    binding order ID.
  • Introducing the __builtin_hlsl_resource_counterhandlefromimplicitbinding
    builtin.
  • Updating SemaHLSL to correctly initialize global resource declarations
    and resource arrays with implicit counter buffers.
  • Adding CodeGen support for the new builtin, which generates a
    llvm.spv.resource.counterhandlefromimplicitbinding intrinsic for the
    SPIR-V target and aliases the main resource handle for the DXIL target.
  • Adding and updating tests to verify the new functionality.

Fixes #137032

The attribute needs to be preserved for rewriter scenarios.
Two places are updated to use the ResourceBindingAttrs helper struct
to make sure the HLSLVkBindingAttr is ignored when the target is DirectX.
@s-perron s-perron requested a review from hekota October 7, 2025 14:14
@s-perron
Copy link
Contributor Author

s-perron commented Oct 7, 2025

@hekota This depends on #161752. When that is merged, I will rebase this PR, and merge it.

@s-perron
Copy link
Contributor Author

s-perron commented Oct 7, 2025

@luciechoi Can you take a look at this PR?

This commit implements the Sema and CodeGen portions of the typed buffer
counter proposal described in the HLSL WG proposal 0023.

This change introduces the necessary Sema and CodeGen logic to handle
implicit counter variables for typed buffers. This includes:

- Extending `HLSLResourceBindingAttr` to store the implicit counter
  binding order ID.
- Introducing the `__builtin_hlsl_resource_counterhandlefromimplicitbinding`
  builtin.
- Updating `SemaHLSL` to correctly initialize global resource declarations
  and resource arrays with implicit counter buffers.
- Adding CodeGen support for the new builtin, which generates a
  `llvm.spv.resource.counterhandlefromimplicitbinding` intrinsic for the
  SPIR-V target and aliases the main resource handle for the DXIL target.
- Adding and updating tests to verify the new functionality.

Fixes llvm#137032
@luciechoi
Copy link
Contributor

LGTM

Copy link
Member

@hekota hekota left a comment

Choose a reason for hiding this comment

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

Glad to see this taking shape! Please update the PR title :)

Comment on lines +356 to +358
llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
Value *MainHandle = EmitScalarExpr(E->getArg(0));
if (CGM.getTriple().isSPIRV()) {
Copy link
Member

Choose a reason for hiding this comment

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

HandleTy is used only for the SPIR-V case and the conversion can be moved inside the if statement. Also, consider early exit for the DXIL case:

if (!CGM.getTriple().isSPIRV())
  return MainHandle;

Comment on lines +148 to +153
if (Binding.hasCounterImplicitOrderID())
CreateMethod = lookupMethod(
ResourceDecl, "__createFromBindingWithImplicitCounter", SC_Static);
else
CreateMethod =
lookupMethod(ResourceDecl, "__createFromBinding", SC_Static);
Copy link
Member

Choose a reason for hiding this comment

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

How about this?

  const char *Name = Binding.hasCounterImplicitOrderID()
                     ? "__createFromBindingWithImplicitCounter"
                     : "__createFromBinding";
  CreateMethod = lookupMethod(ResourceDecl, Name, SC_Static);

Comment on lines +943 to +944
if (Record->isCompleteDefinition())
return *this;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (Record->isCompleteDefinition())
return *this;
assert(!Record->isCompleteDefinition() && "record is already complete");

I've recently change these to asserts because the record should never be complete here.

.finalize();
}

BuiltinTypeDeclBuilder &
Copy link
Member

Choose a reason for hiding this comment

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

Could you please add a comment here and to the other new static method describing what the method looks like, similar to what the existing static create methods have? See line 867.

Comment on lines +976 to +977
if (Record->isCompleteDefinition())
return *this;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (Record->isCompleteDefinition())
return *this;
assert(!Record->isCompleteDefinition() && "record is already complete");

Comment on lines +3826 to +3827
// Re-create the binding object to pick up the new attribute.
Binding = ResourceBindingAttrs(VD);
Copy link
Member

Choose a reason for hiding this comment

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

This is needed only when a new attribute was added, so it can be moved to the else branch right after addImplicitBindingAttrToDecl.

llvm::SmallVector<Expr *> Args;

bool HasCounter = hasCounterHandle(ResourceDecl);
std::string CreateMethodName;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
std::string CreateMethodName;
const char *CreateMethodName;

Using const char * will prevent allocation and copying of the string.

Comment on lines +3875 to +3878
if (!CreateMethod) {
llvm::dbgs() << "STEVEN: failed to get method " << CreateMethodName << "\n";
VD->dumpColor();
}
Copy link
Member

Choose a reason for hiding this comment

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

Don't forget to remove before merging :)

Args.push_back(NameCast);

if (HasCounter) {
// Will this be in the correct order?
Copy link
Member

Choose a reason for hiding this comment

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

Yes, it will be in the correct order. initGlobalResourceDecl is called from ActOnUninitializedVarDecl, which follows shortly after 'ActOnVariableDeclarator`. There should not be any other declarations processed in-between these two calls.

// CHECK-NEXT: call void @hlsl::AppendStructuredBuffer<float>::AppendStructuredBuffer()(ptr {{.*}} %Buf3)

// Buf3 initialization part 2 - body of AppendStructuredBuffer<float> default C1 constructor that calls
// the default C2 constructor
// CHECK: define linkonce_odr hidden void @hlsl::StructuredBuffer<float>::StructuredBuffer()(ptr {{.*}} %this)
Copy link
Member

Choose a reason for hiding this comment

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

// Buf3 initialization part 1

Has this been removed intentionally? It should probably stay in, although the // CHECK: following it seems to be incorrectly refering to @hlsl::StructuredBuffer<float>::StructuredBuffer().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[HLSL][SPIRV] Generate and use the counter handle for RWStructuredBuffer
3 participants