• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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