1 /*
2 * Copyright 2017, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "GlobalAllocSPIRITPass.h"
18
19 #include "Context.h"
20 #include "spirit.h"
21 #include "transformer.h"
22
23 #include <sstream>
24
25 namespace android {
26 namespace spirit {
27
28 namespace {
29
30 // Metadata buffer for global allocations
31 // struct metadata {
32 // uint32_t element_size;
33 // uint32_t x_size;
34 // uint32_t y_size;
35 // uint32_t unused
36 // };
37
AddGAMetadata(Builder & b,Module * m)38 VariableInst *AddGAMetadata(Builder &b, Module *m) {
39 TypeIntInst *UInt32Ty = m->getUnsignedIntType(32);
40 std::vector<Instruction *> metadata{UInt32Ty, UInt32Ty, UInt32Ty, UInt32Ty};
41 auto MetadataStructTy = m->getStructType(metadata.data(), metadata.size());
42 // FIXME: workaround on a weird OpAccessChain member offset problem. Somehow
43 // when given constant indices, OpAccessChain returns pointers that are 4
44 // bytes less than what are supposed to be (at runtime). For now workaround
45 // this with +4 the member offsets.
46 MetadataStructTy->memberDecorate(0, Decoration::Offset)->addExtraOperand(4);
47 MetadataStructTy->memberDecorate(1, Decoration::Offset)->addExtraOperand(8);
48 MetadataStructTy->memberDecorate(2, Decoration::Offset)->addExtraOperand(12);
49 MetadataStructTy->memberDecorate(3, Decoration::Offset)->addExtraOperand(16);
50 // TBD: Implement getArrayType. RuntimeArray requires buffers and hence we
51 // cannot use PushConstant underneath
52 auto MetadataBufSTy = m->getRuntimeArrayType(MetadataStructTy);
53 // Stride of metadata.
54 MetadataBufSTy->decorate(Decoration::ArrayStride)
55 ->addExtraOperand(metadata.size() * sizeof(uint32_t));
56 auto MetadataSSBO = m->getStructType(MetadataBufSTy);
57 MetadataSSBO->decorate(Decoration::BufferBlock);
58 auto MetadataPtrTy = m->getPointerType(StorageClass::Uniform, MetadataSSBO);
59
60 VariableInst *MetadataVar =
61 b.MakeVariable(MetadataPtrTy, StorageClass::Uniform);
62 MetadataVar->decorate(Decoration::DescriptorSet)->addExtraOperand(0);
63 MetadataVar->decorate(Decoration::Binding)->addExtraOperand(1);
64 m->addVariable(MetadataVar);
65
66 return MetadataVar;
67 }
68
CreateGAIDMetadata(const llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> & Allocs)69 std::string CreateGAIDMetadata(
70 const llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> &Allocs) {
71
72 std::stringstream mapping;
73 bool printed = false;
74
75 mapping << "{\"__RSoV_GA\": {";
76 for (auto &A : Allocs) {
77 // Skip unused GAs
78 if (!A.hasID()) {
79 continue;
80 }
81 if (printed)
82 mapping << ", ";
83 // "GA name" to the ID of the GA
84 mapping << "\"" << A.VarName.substr(1) << "\":" << A.ID;
85 printed = true;
86 }
87 mapping << "}}";
88
89 if (printed)
90 return mapping.str().c_str();
91 else
92 return "";
93 }
94 } // anonymous namespace
95
96 // Replacing calls to lowered accessors, e.g., __rsov_rsAllocationGetDimX
97 // which was created from rsAllocationGetDimX by replacing the allocation
98 // with an ID in an earlier LLVM pass (see GlobalAllocationPass.cpp),
99 // to access the global allocation metadata.
100 //
101 // For example, the source code may look like:
102 //
103 // rs_allocation g;
104 // ...
105 // uint32_t foo = rsAllocationGetDimX(g);
106 //
107 // After the GlobalAllocPass, it would look like the LLVM IR
108 // equivalent of:
109 //
110 // uint32_t foo = __rsov_rsAllocationGetDimX(0);
111 //
112 // After that pass, g is removed, and references in intrinsics
113 // to g would be replaced with an assigned unique id (0 here), and
114 // rsAllocationGetDimX() would be replaced by __rsov_rsAllocationGetDimX()
115 // where the only difference is the argument being replaced by the unique
116 // ID. __rsov_rsAllocationGetDimX() does not really exist - it is used
117 // as a marker for this pass to work on.
118 //
119 // After this GAAccessTransformer pass, it would look like (in SPIRIT):
120 //
121 // uint32_t foo = Metadata[0].size_x;
122 //
123 // where the OpFunctionCall to __rsov_rsAllocationGetDim() is replaced by
124 // an OpAccessChain and OpLoad from the metadata buffer.
125
126 class GAAccessorTransformer : public Transformer {
127 public:
GAAccessorTransformer()128 GAAccessorTransformer()
129 : mBuilder(), mMetadata(nullptr),
130 mAllocs(rs2spirv::Context::getInstance().getGlobalAllocs()) {}
131
runAndSerialize(Module * module,int * error)132 std::vector<uint32_t> runAndSerialize(Module *module, int *error) override {
133 std::string GAMD = CreateGAIDMetadata(mAllocs);
134 if (GAMD.size() > 0) {
135 module->addString(GAMD.c_str());
136 }
137 mMetadata = AddGAMetadata(mBuilder, module);
138 return Transformer::runAndSerialize(module, error);
139 }
140
transform(FunctionCallInst * call)141 Instruction *transform(FunctionCallInst *call) {
142 FunctionInst *func =
143 static_cast<FunctionInst *>(call->mOperand1.mInstruction);
144 const char *name = getModule()->lookupNameByInstruction(func);
145 if (!name) {
146 return call;
147 }
148
149 Instruction *inst = nullptr;
150 // Maps name into a SPIR-V instruction
151 // TODO: generalize it to support more accessors
152 if (!strcmp(name, "__rsov_rsAllocationGetDimX")) {
153 TypeIntInst *UInt32Ty = getModule()->getUnsignedIntType(32);
154 // TODO: hardcoded layout
155 auto ConstZero = getModule()->getConstant(UInt32Ty, 0U);
156 auto ConstOne = getModule()->getConstant(UInt32Ty, 1U);
157
158 // TODO: Use constant memory later
159 auto resultPtrType =
160 getModule()->getPointerType(StorageClass::Uniform, UInt32Ty);
161 AccessChainInst *LoadPtr = mBuilder.MakeAccessChain(
162 resultPtrType, mMetadata, {ConstZero, ConstZero, ConstOne});
163 insert(LoadPtr);
164
165 inst = mBuilder.MakeLoad(UInt32Ty, LoadPtr);
166 inst->setId(call->getId());
167 } else {
168 inst = call;
169 }
170 return inst;
171 }
172
173 private:
174 Builder mBuilder;
175 VariableInst *mMetadata;
176 llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> &mAllocs;
177 };
178
179 } // namespace spirit
180 } // namespace android
181
182 namespace rs2spirv {
183
CreateGAPass(void)184 android::spirit::Pass *CreateGAPass(void) {
185 return new android::spirit::GAAccessorTransformer();
186 }
187
188 } // namespace rs2spirv
189