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 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(0); 64 m->addVariable(MetadataVar); 65 66 return MetadataVar; 67 } 68 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: 128 GAAccessorTransformer() 129 : mBuilder(), mMetadata(nullptr), 130 mAllocs(rs2spirv::Context::getInstance().getGlobalAllocs()) {} 131 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 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 184 android::spirit::Pass *CreateGAPass(void) { 185 return new android::spirit::GAAccessorTransformer(); 186 } 187 188 } // namespace rs2spirv 189