Skip to content

[SPIR-V] Add proposal for global & local variable address spaces #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 6, 2025

Conversation

Keenuts
Copy link
Collaborator

@Keenuts Keenuts commented Jan 23, 2025

This commit adds a proposal on how to implement local and global variables in the SPIR-V backend given HLSL put them in the same address space while SPIR-V requires them to be in 2 distinct ones.

@Keenuts Keenuts requested a review from s-perron January 23, 2025 14:39
@damyanp damyanp requested a review from llvm-beanz January 27, 2025 18:17
Copy link
Collaborator

@s-perron s-perron left a comment

Choose a reason for hiding this comment

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

I think this makes sense. Just a few minor comments.

@Keenuts
Copy link
Collaborator Author

Keenuts commented Feb 6, 2025

@llvm-beanz is that proposal OK for you?

@Keenuts
Copy link
Collaborator Author

Keenuts commented Feb 18, 2025

@llvm-beanz ping

Copy link
Collaborator

@llvm-beanz llvm-beanz left a comment

Choose a reason for hiding this comment

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

Ugh... I worry a lot about the approach to make everything private.

When we add references to HLSL we will absolutely require address space annotation on the references. That's going to become a foundational part of HLSL, and we'll need to provide a convenient way to normalize address spaces for the cases where users don't want to overload functions or variables for every possible address space. Something like:

template <typename ParamTy, typename ArgTy> struct CastingValue {
  CastingValue(ArgTy &Arg) : A(Arg), P(Arg) {}
  ~CastingValue() { A = P; }
  operator ParamTy &() { return P; }
  ArgTy &A;
  ParamTy P;
};

template <typename ParamTy, typename ArgTy>
CastingValue<ParamTy, ArgTy> make_inout(ArgTy &Arg) {
  return CastingValue<ParamTy, ArgTy>(Arg);
}

void fn(int &V) {}

float call() {
  float F = 1.5;
  fn(make_inout<int>(F));
  return F;
}

The more I'm seeing these arrays of troubling options, the more I think we just need to bite the bullet and add the address space annotations now differentiating between Private and Function.

If "Bad solution 2" isn't an option (and I'm not really sure that noinline or library shares should be blockers), I think we likely need to reconsider just biting the bullet on "Bad solution 1".

Because HLSL doesn't have references, the inout and out parameter semantics will normalize the address spaces of variables passed across function boundaries, you can't take an address of an object, so it may mostly be fine. The biggest area I worry about is potential type inference problems around template instantiations, but we can deal with that if it is a problem.

@llvm-beanz
Copy link
Collaborator

PR llvm/llvm-project#127675 triggered something in my brain. If we go forward with making global and local variables have different address spaces, we'll need to have some strategy (probably just alwaysinline and addrspacecast) for enabling calling member functions on global and local variable declarations seamlessly.

@Keenuts
Copy link
Collaborator Author

Keenuts commented Feb 19, 2025

PR llvm/llvm-project#127675 triggered something in my brain. If we go forward with making global and local variables have different address spaces, we'll need to have some strategy (probably just alwaysinline and addrspacecast) for enabling calling member functions on global and local variable declarations seamlessly.

Yes, that's one of the issues I had when implementing this. Because we don't have yet defined how overloads are working depending on this, we have a problem:

  • do we want to allow function overloading depending on this address space?
  • if yes, how to handle source back-compat? I think the easiest solution would be to allow overloading, default on selecting the default address space version of the method, unless another is defined.

But this yields a new issue: what about const correctness? Is the address-space overload going first, before the const version?

Ex:

struct S {
void foo() {  }
void foo() const { }
void foo() addrspace(private) const { }
};

static S s;

[...]
s.foo(); // Shall we call `private const foo`, which is possible, or `default foo` because `s` is not const?

@Keenuts
Copy link
Collaborator Author

Keenuts commented Feb 19, 2025

My initial approach was to do consider the following:

struct MyStruct {
void foo() { }
};

static MyStruct private_global;

void main() {
    MyStruct function_local;
    private_global.foo();
    function_local.foo();
    // typeof(private_global) != typeof(function_local);
}
  • The struct is initially emitted with its member as-is.
  • each variable having a different address space, their type are now different (even if both use MyStruct, but the additional AS makes them different)
  • this means I duplicate the MyStruct type & methods, and create a new type for AS private.
  • when emitting codegen for private foo, each pointer is marked as addrspace(private)
  • when emitting codegen for local foo, each pointer is kept in the default AS.

This require duplicating the function in the IR: one version for the private AS, one for the default AS.
It requires some IR legalization and come codegen work, but I feel it would be the most natural way to use this.

This however requires references to have a fixed address space, making this illegal in HLSL:

MyStruct& get_var(bool condition, MyStruct& function_local) {
    return condition ? private_global : function_local;
}

I would be fine with this, as this is not legal in SPIR-V, but I do agree there is something fishy here. We'd want an addressspace attribute on the return type, or something visible.

@Keenuts
Copy link
Collaborator Author

Keenuts commented Feb 25, 2025

Thanks for the reviews!
As discussed in yesterday's meeting, changed the proposal to have 2 address spaces exposed at the HLSL level: hlsl_private and default.

(I could create an hlsl_function, but doesn't seem necessary).

@Keenuts Keenuts requested review from llvm-beanz and s-perron March 3, 2025 12:12
Copy link
Collaborator

@s-perron s-perron left a comment

Choose a reason for hiding this comment

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

I'm in favour of this solution, if it is acceptable to @llvm-beanz and his team.

I have a suggestion to reorganize. Don't mention rejected solutions while describing the problem. Leave it for the alternative. I believe that one of the alternative solutions has more problems than you let on.

@Keenuts
Copy link
Collaborator Author

Keenuts commented Mar 5, 2025

Thanks, removed the solution bits from the problem statement to leave them to alternative solutions.

I believe that one of the alternative solutions has more problems than you let on.

If that's the force-inline solution, the word masking the complexity is the "propagating": it's not a simple problem, and can require a big code rewrite/duplication yes.

Copy link
Collaborator

@llvm-beanz llvm-beanz left a comment

Choose a reason for hiding this comment

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

I don't have a whole lot of time this week to really dig deeply into this. I think for now the path of least resistance is going to be to ignore the fact that references may someday exist in HLSL. References are going to require a whole host of addresses space annotations as well as casting and conversion helpers.

On the topic of overloading based on address space, I'm inclined to align with modern C++ rather than define a new syntax (https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html), but certainly in the short term we're going to need to do some crazy hacks nonsense to preserve enough source compatibility so as to not be too much of a burden on adoption.


## Alternative design considered

### Force optimizations, and force inlining
Copy link
Collaborator

Choose a reason for hiding this comment

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

You mention this a bit above, but just to highlight: even with giving global and local variables their correct SPIRV address spaces, we're going to need to do some degree of this.

The member function problem is a source compatibility issue that I don't think we can just break. DXC even has some awful hacks to allow member functions to resolve on objects in cbuffers, despite the lack of proper constness on the implicit object (although that might be a small enough break that we can get away with not doing those hacks in Clang).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the point is that even if everything was inlined, the problem would not be solved because of export functions and the need to duplicate code. There might even be some instruction whose result address space cannot be determined by the types of the input.

Keenuts and others added 7 commits March 6, 2025 13:12
This commit adds a proposal on how to implement local and global
variables in the SPIR-V backend given HLSL put them in the same address
space while SPIR-V requires them to be in 2 distinct ones.

Signed-off-by: Nathan Gauër <[email protected]>
Signed-off-by: Nathan Gauër <[email protected]>
@Keenuts Keenuts merged commit 4c9e11a into llvm:main Mar 6, 2025
@Keenuts Keenuts deleted the spirv-as branch March 6, 2025 12:21
@Keenuts
Copy link
Collaborator Author

Keenuts commented Mar 6, 2025

I don't have a whole lot of time this week to really dig deeply into this. I think for now the path of least resistance is going to be to ignore the fact that references may someday exist in HLSL. References are going to require a whole host of addresses space annotations as well as casting and conversion helpers.

On the topic of overloading based on address space, I'm inclined to align with modern C++ rather than define a new syntax (https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html), but certainly in the short term we're going to need to do some crazy hacks nonsense to preserve enough source compatibility so as to not be too much of a burden on adoption.

Agree, there are many behaviors linked to the addition of address spaces (resources, input & this) which will require some later iteration.
Thanks for the review! Reformulated one of the alternatives as suggested by Steven, rebased and merged.

@damyanp damyanp moved this to Closed in HLSL Support Apr 25, 2025
joaosaffran added a commit to joaosaffran/wg-hlsl that referenced this pull request Jun 18, 2025
* [0002] RootSignatures - Versioning proposal (llvm#115)

This PR is adding the versioning information to Root Signature specs. It
includes:

- adding a new attribute to clang driver to specify root signature
versions.
- updating metadata representation to hold root signature versions.

Closes: llvm#113

* [0002] RootSignatures - Add default values to proposal (llvm#173)

- adds default value information to the Root Signature specification

Resolves llvm#172

* Proposal for mapping resource attributes to DXIL and SPIR-V (llvm#76)

* Constant buffers design document (llvm#94)

Design document describing how HLSL constant buffers will be handled in
Clang. Contains the design for parsing, codegen a lowering of `cbuffer`
declarations, including the default constant buffer `$Globals`.

Design for the `ConstantBuffer<T>` resource class is still work in
progress.

* Update proposal number on constant buffers proposal (llvm#188)

* [NNNN] Target Extension Types for Inline SPIR-V and Decorated Types (llvm#105)

This adds target extension types to represent `SpirvType` from [HLSL
0011, Inline
SPIR-V](https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md).

* [0018] SPIRV resource representation (llvm#98)

Adds a proposal for how HLSL resources will be represented in llvm-ir
when targeting SPIR-V.

* [SPIR-V] Add proposal for I/O builtin in Clang (llvm#97)

Initial proposal to implement Input/Output built ins with both semantic
& inline SPIR-V

* [SPIR-V] Add proposal for global & local variable address spaces (llvm#111)

This commit adds a proposal on how to implement local and global
variables in the SPIR-V backend given HLSL put them in the same address
space while SPIR-V requires them to be in 2 distinct ones.

---------

Signed-off-by: Nathan Gauër <[email protected]>
Co-authored-by: Steven Perron <[email protected]>

* [0002] RootSignatures - Add missing Descriptor Ranges `offset` (llvm#191)

- it appears the `offset` was mistakenly dropped as discarding this
information no longer allows the user to provide a manual offset from
the start of the descriptors heap and allow for register aliasing

- additionally, we do a small fix-up to update the flag values to be
their correct default as we adjust the parameter values

Resolves llvm#190

---------

Co-authored-by: Finn Plummer <[email protected]>

* [0002] RootSignatures - Misc updates to spec from testing (llvm#195)

This issue addresses the discrepancies found from testing
[here](llvm/llvm-project#130826).

The pr has been broken up to commits that address the bullet points in
the original issue. Briefly:

- First 2 commits work to have a concrete EBNF description of the
grammar that denotes that all parameter args can be specified in any
order
- It is also addressed that `NUMBER` can be more restrictive with
respect to what the dxc/clang compiler will accept
- Missing documentation for `unbounded` is added
- Add additional validation of some parameters that surface as errors in
dxc
- Remove the specification of 'AllowLowTierReservedHwCbLimit'

Resolves llvm#192

---------

Co-authored-by: Finn Plummer <[email protected]>

* [0021] Proposal for handling member function address spaces. (llvm#187)

As we expose new address spaces in HLSL, we will have a problem with the
address space for the `this` pointer in member functions.

* Resource Instance Analysis (llvm#207)

Fixes llvm#179

Proposes a generic architecture for organizing, resolving, and
validating Resource Instance metadata

---------

Co-authored-by: Damyan Pepper <[email protected]>
Co-authored-by: Helena Kotas <[email protected]>

* [0023] Proposal for separate counter resource handle (llvm#208)

This is a proposal to create a separte handle for the
counter resource in RWStructureBuffer and other that have associated
counters.

* [0002] RootSignatures - NFC Formatting Grammar to EBNF (llvm#198)

Update the formatting of the root signature grammar to use EBNF in a
consistent manner:

- The first commit just goes through to use `=` instead of `:` and
denote statements with ';' to comply with EBNF
- The second commit comes from a suggestion
[here](llvm#195 (comment))
to just use the `|` notation to specify the parameters in any order but
not explicitly denote that they can only occur once

Clean up pr to resolve llvm#192

---------

Co-authored-by: Finn Plummer <[email protected]>

* Implicit resource binding - document current DXC behavior (llvm#193)

Initial commit of the Implicit Resource Binding proposal that includes:
- brief overview section on resource bindings
- number of examples documenting how are implicit resource bindings are
assigned in DXC

Closes llvm#176

* Implicit resource bindings design (llvm#196)

Proposed design for implicit resource bindings in Clang.

Closes llvm#177

* Resource constructors proposal (llvm#197)

Proposal for resource classes constructors.

Closes llvm#209

* Remove tools/hlslpm (llvm#228)

As the process evolves, it becomes more effort to keep tools/hlslpm up
to date.

This change removes tools/hlslpm rather than keep a stale version
around.

* Update issue_tracking.md for latest thinking (llvm#229)

Describe epics, scenarios, deliverables and tasks, and the custom fields
used in the HLSL Support project.

---------

Co-authored-by: Finn Plummer <[email protected]>

* Add "Review" value for the "blocked" field (llvm#257)

Some issues are blocked on design review. This value can be used to
identify them.

* Resource binding: add another example and notes about resources in structs (llvm#214)

Add one more example showing binding of resource arrays in user-defined
structs.
Add a note to re-evaluate whether to bind resource arrays in structs
individually or as a continuous descriptor range later on when we start
working on the resources in structs design (llvm/wg-hlsl#llvm#212).

* [0026] Add proposal for symbol visibility. (llvm#272)

Section 3.6 of the
[HLSL
specification](https://microsoft.github.io/hlsl-specs/specs/hlsl.pdf)
defined the possible linkages for names. This proposal updates how these
linkages are represented in LLVM IR. The current implementation presents
challenges for the SPIR-V backend due to inconsistencies with OpenCL. In
HLSL, a
name can have external linkage and program linkage, among others. If a
name has
external linkage, it is visible outside the translation unit, but not
outside a
linked program. A name with program linkage is visible outside a
partially linked program.
We propose that names with program linkage in HLSL should have external
linkage
and default visibility in LLVM IR, while names with external linkage in
HLSL should have
external linkage and hidden visibility in LLVM IR. They both have
external
linkage because they are visible outside the translation unit. Default
visibility means the name is visible outside a shared library (program).
Hidden
visibility means the name is not visible outside the shared library
(program).

* Update proposals for implicit binding and resource constructors to match implementation (llvm#282)

* Add missing titles to proposals (llvm#283)

Adding title to proposals that were missing one.

* Unify new line endings to LF (llvm#286)

Add .gitattributes and set new line endings to LF.
Normalize existing files by running `git add --renormalize`.

* [NNNN] Implementation for `vk::constant_id` (llvm#287)

Design for the Clang implmentation of`vk::constant_id`.

---------

Signed-off-by: Nathan Gauër <[email protected]>
Co-authored-by: Finn Plummer <[email protected]>
Co-authored-by: Justin Bogner <[email protected]>
Co-authored-by: Helena Kotas <[email protected]>
Co-authored-by: Cassandra Beckley <[email protected]>
Co-authored-by: Steven Perron <[email protected]>
Co-authored-by: Nathan Gauër <[email protected]>
Co-authored-by: Finn Plummer <[email protected]>
Co-authored-by: Ashley Coleman <[email protected]>
Co-authored-by: Damyan Pepper <[email protected]>
Co-authored-by: Finn Plummer <[email protected]>
@damyanp damyanp removed this from HLSL Support Jun 25, 2025
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.

3 participants