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 25namespace android { 26namespace spirit { 27 28namespace { 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 38VariableInst *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 69std::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 126class GAAccessorTransformer : public Transformer { 127public: 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 173private: 174 Builder mBuilder; 175 VariableInst *mMetadata; 176 llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> &mAllocs; 177}; 178 179} // namespace spirit 180} // namespace android 181 182namespace rs2spirv { 183 184android::spirit::Pass *CreateGAPass(void) { 185 return new android::spirit::GAAccessorTransformer(); 186} 187 188} // namespace rs2spirv 189