summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-ir-specialize-buffer-load-arg.cpp
blob: 353c6a104f31881e620a575af39e8c6cd7dc9ce9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// slang-ir-specialize-buffer-load-arg.cpp
#include "slang-ir-specialize-buffer-load-arg.h"

#include "slang-ir.h"
#include "slang-ir-insts.h"
#include "slang-ir-specialize-function-call.h"

namespace Slang
{

// This file implements a pass that translates function call sites where
// the result of a buffer load from a global shader parameter (e.g., a
// global constant buffer) is being passed through to the callee. It
// replaces those with calls to specialized callee functions that directly
// reference the chosen global.
//
// As swith most of our IR passes, we encapsulate the logic here in a context
// type so that the data that needs to be shared throughout the pass can
// be conveniently scoped.

struct FuncBufferLoadSpecializationCondition : FunctionCallSpecializeCondition
{
    typedef FunctionCallSpecializeCondition Super;

    virtual bool doesParamWantSpecialization(IRParam* param, IRInst* arg)
    {
        // We only want to specialize for `struct` types and not base types.
        //
        // TODO: We might want to consider some criteria here for the "large-ness"
        // of a structure (in terms of bytes and/or fields), so that we don't
        // eliminate loads of sufficiently small types (which are cheap to pass
        // by value).
        //
        auto paramType = param->getDataType();
        if(!as<IRStructType>(paramType))
            return false;

        // We also only want to specialize for arguments that are a load
        // from some kind of global shader parameter.
        //
        IRInst* a = arg;
        if (auto argLoad = as<IRLoad>(arg))
        {
            a = argLoad->getPtr();
        }
        else
        {
            return false;
        }

        // We want to handle loads from a shader parameter that is an array
        // of buffers, and not just a single global buffer.
        //
        while (auto argGetElement = as<IRGetElement>(a))
        {
            a = argGetElement->getBase();
        }

        // The "root" of the parameter must be a reference to a global-scope
        // shader parameter, so that we know we can substitute it into the callee.
        //
        if (auto argGlobalParam = as<IRGlobalParam>(a))
        {
            return true;
        }
        else
        {
            return false;
        }

        // TODO: There are other patterns that we could attempt to optimize here.
        // For example, this logic only handles loads of the *entire* contents of
        // a buffer, so it would miss:
        //
        // * A load of a large structure from field in a constant buffer, so that
        //   the value loaded is not the entire buffer contents.
        //
        // * A load of a large structure from a structured buffer, or any other kind
        //   of buffer that requires an index.
        //
        // * Any resource load that is not expressed at the IR level with a `load`
        //   instruction (e.g., those that might use an intrinsic function).
        //
    }
};

void specializeFuncsForBufferLoadArgs(
    BackEndCompileRequest*  compileRequest,
    TargetRequest*          targetRequest,
    IRModule*               module)
{
    FuncBufferLoadSpecializationCondition condition;
    specializeFunctionCalls(compileRequest, targetRequest, module, &condition);
}

}