Skip to content
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

Hoist resource variables using a global initializer function correctly #4894

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

ArielG-NV
Copy link
Contributor

@ArielG-NV ArielG-NV commented Aug 21, 2024

Fixes: #4874
Goal:
Hoist resource variables using a global initializer function in such a way that any global with initializer function has any possible resource variables hoisted into their use location directly (otherwise many targets won't work).
Changes:

  1. If legalize a global kIROp and it creates a global, make a GlobalVar

[only done for resource variables for simplicity of legalization pass]

  1. Add a legalization pass that moves any global GlobalVar stores (initializers) into the GlobalVar init block
  2. If we have a GlobalVar which is computed to be an alias (direct assignment to another variable), hoist the alias's value into IRGlobalVar use-sites.

Fixes: shader-slang#4874
Hoist resource variables using a global initializer function in such a way that the initializer function is passed into every entry-point opposed to emitting a local-variable asignment in global scope.
@ArielG-NV ArielG-NV added the pr: non-breaking PRs without breaking changes label Aug 21, 2024

// If our call is in global scope (initializer) we should
// hoist the call into the start of every entry-point
if (!m_context->builder->getInsertLoc().getInst()->getParent()
Copy link
Collaborator

Choose a reason for hiding this comment

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

I understand what this is trying to achieve here, but I think this is likely not the right way.

Usually if you have a global variable and you want to initialize it with some code (e.g. a call), you create a IRGlobalVar, which is a IRGlobalValueWithCode that can have a body just like a IRFunc. Inside the body you create a basic block with instructions + a return instruct that returns the value of the init expr.

Here instead of inserting a call into global scope, we should be creating that IRGlobalVar, and rely on follow up passes to insert the logic in the body into entry points.

@ArielG-NV ArielG-NV marked this pull request as draft August 21, 2024 13:36
@ArielG-NV ArielG-NV marked this pull request as ready for review August 21, 2024 20:03
//TEST_INPUT:ubuffer(data=[1 2 3 4 5 6 7 8], stride=4):name=inputBuffer
RWStructuredBuffer<uint> inputBuffer;

static const RWStructuredBuffer<uint> gBuffer = inputBuffer;
Copy link
Collaborator

Choose a reason for hiding this comment

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

In general I think we will have issues when there are global resource variables that is being written to dynamically, so I am not sure if this is something we want to allow...

static const currently is overloaded to also mean compile time constants, maybe we should disallow static consts of resource type as well. Is this test coming from some existing user code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In general I think we will have issues when there are global resource variables that is being written to dynamically, so I am not sure if this is something we want to allow...

We will have an issue with dynamic resource variable accesses.
Slang is lacking a compiler pass to manage non-compile-time known resource accesses such as:

RWStructuredBuffer<uint> outputBuffer;
RWStructuredBuffer<uint> inputBuffer1;
RWStructuredBuffer<uint> inputBuffer2;
RWStructuredBuffer<uint> inputBuffer3;

[numthreads(4,4,4)]
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
{
    RWStructuredBuffer<uint> resource;
    if(dispatchThreadID.x == 1)
    {
        resource = inputBuffer1;
    }
    else if(dispatchThreadID.x == 2)
    {
        resource = inputBuffer2;
    }
    else if(dispatchThreadID.x == 3)
    {
        resource = inputBuffer3;
    }

    // This is illegal since resource is not known at compile time.
    outputBuffer[0] = resource[1];
}

From what I understand to add this would just piggy-back on our dynamic dispatch system logic:

  1. Diagnose all non-constexpr resources, assign all resources of equal type a different ID (like with dynamic dispatch).
  2. Any assignments to a resource assigns an ID (not the actual resource)
  3. All accesses to a non-compile-time resource specializes all texture intrinsic uses to pick the correct texture using our ID (if/else binary tree)

Copy link
Contributor Author

@ArielG-NV ArielG-NV Aug 22, 2024

Choose a reason for hiding this comment

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

static const currently is overloaded to also mean compile time constants, maybe we should disallow static consts of resource type as well. Is this test coming from some existing user code?

No test currently uses static const Stuff gStuff = Stuff( inputBuffer, inputBuffer );.
The problem is that some tests use static const Stuff gStuff = { inputBuffer, inputBuffer }, which once #4854 is merged will be an issue since we start emitting what is effectively static const Stuff gStuff = Stuff( inputBuffer, inputBuffer );.

@csyonghe
Copy link
Collaborator

Reading at the code here, it seems clear to me that we need to legalize out the resource typed global variable out of existence before we reach legalizeType, instead of letting legalizeType doing weird things such as inserting a store in global scope and then try to clean up that mess in a separate pass.

In this case, the global static const should have been lowered into a IRGlobalConstant whose value is an IRCall. We should then recognize these IRGlobalConstants and IRCalls and inline them before hitting legalization.

@ArielG-NV
Copy link
Contributor Author

ArielG-NV commented Aug 23, 2024

In this case, the global static const should have been lowered into a IRGlobalConstant whose value is an IRCall. We should then recognize these IRGlobalConstants and IRCalls and inline them before hitting legalization.

I will work on this solution 👍

Edit: As per conversation outside github-comments, work on this PR will halt for now.

@ArielG-NV ArielG-NV marked this pull request as draft August 26, 2024 18:41
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr: non-breaking PRs without breaking changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Resource variables used in a global initializer function do not hoist correctly
3 participants