| /* |
| * Copyright 2017, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "GlobalAllocSPIRITPass.h" |
| |
| #include "Context.h" |
| #include "spirit.h" |
| #include "transformer.h" |
| |
| #include <sstream> |
| |
| namespace android { |
| namespace spirit { |
| |
| namespace { |
| |
| // Metadata buffer for global allocations |
| // struct metadata { |
| // uint32_t element_size; |
| // uint32_t x_size; |
| // uint32_t y_size; |
| // uint32_t unused |
| // }; |
| |
| VariableInst *AddGAMetadata(Builder &b, Module *m) { |
| TypeIntInst *UInt32Ty = m->getUnsignedIntType(32); |
| std::vector<Instruction *> metadata{UInt32Ty, UInt32Ty, UInt32Ty, UInt32Ty}; |
| auto MetadataStructTy = m->getStructType(metadata.data(), metadata.size()); |
| // FIXME: workaround on a weird OpAccessChain member offset problem. Somehow |
| // when given constant indices, OpAccessChain returns pointers that are 4 |
| // bytes less than what are supposed to be (at runtime). For now workaround |
| // this with +4 the member offsets. |
| MetadataStructTy->memberDecorate(0, Decoration::Offset)->addExtraOperand(4); |
| MetadataStructTy->memberDecorate(1, Decoration::Offset)->addExtraOperand(8); |
| MetadataStructTy->memberDecorate(2, Decoration::Offset)->addExtraOperand(12); |
| MetadataStructTy->memberDecorate(3, Decoration::Offset)->addExtraOperand(16); |
| // TBD: Implement getArrayType. RuntimeArray requires buffers and hence we |
| // cannot use PushConstant underneath |
| auto MetadataBufSTy = m->getRuntimeArrayType(MetadataStructTy); |
| // Stride of metadata. |
| MetadataBufSTy->decorate(Decoration::ArrayStride) |
| ->addExtraOperand(metadata.size() * sizeof(uint32_t)); |
| auto MetadataSSBO = m->getStructType(MetadataBufSTy); |
| MetadataSSBO->decorate(Decoration::BufferBlock); |
| auto MetadataPtrTy = m->getPointerType(StorageClass::Uniform, MetadataSSBO); |
| |
| VariableInst *MetadataVar = |
| b.MakeVariable(MetadataPtrTy, StorageClass::Uniform); |
| MetadataVar->decorate(Decoration::DescriptorSet)->addExtraOperand(0); |
| MetadataVar->decorate(Decoration::Binding)->addExtraOperand(1); |
| m->addVariable(MetadataVar); |
| |
| return MetadataVar; |
| } |
| |
| std::string CreateGAIDMetadata( |
| const llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> &Allocs) { |
| |
| std::stringstream mapping; |
| bool printed = false; |
| |
| mapping << "{\"__RSoV_GA\": {"; |
| for (auto &A : Allocs) { |
| // Skip unused GAs |
| if (!A.hasID()) { |
| continue; |
| } |
| if (printed) |
| mapping << ", "; |
| // "GA name" to the ID of the GA |
| mapping << "\"" << A.VarName.substr(1) << "\":" << A.ID; |
| printed = true; |
| } |
| mapping << "}}"; |
| |
| if (printed) |
| return mapping.str().c_str(); |
| else |
| return ""; |
| } |
| } // anonymous namespace |
| |
| // Replacing calls to lowered accessors, e.g., __rsov_rsAllocationGetDimX |
| // which was created from rsAllocationGetDimX by replacing the allocation |
| // with an ID in an earlier LLVM pass (see GlobalAllocationPass.cpp), |
| // to access the global allocation metadata. |
| // |
| // For example, the source code may look like: |
| // |
| // rs_allocation g; |
| // ... |
| // uint32_t foo = rsAllocationGetDimX(g); |
| // |
| // After the GlobalAllocPass, it would look like the LLVM IR |
| // equivalent of: |
| // |
| // uint32_t foo = __rsov_rsAllocationGetDimX(0); |
| // |
| // After that pass, g is removed, and references in intrinsics |
| // to g would be replaced with an assigned unique id (0 here), and |
| // rsAllocationGetDimX() would be replaced by __rsov_rsAllocationGetDimX() |
| // where the only difference is the argument being replaced by the unique |
| // ID. __rsov_rsAllocationGetDimX() does not really exist - it is used |
| // as a marker for this pass to work on. |
| // |
| // After this GAAccessTransformer pass, it would look like (in SPIRIT): |
| // |
| // uint32_t foo = Metadata[0].size_x; |
| // |
| // where the OpFunctionCall to __rsov_rsAllocationGetDim() is replaced by |
| // an OpAccessChain and OpLoad from the metadata buffer. |
| |
| class GAAccessorTransformer : public Transformer { |
| public: |
| GAAccessorTransformer() |
| : mBuilder(), mMetadata(nullptr), |
| mAllocs(rs2spirv::Context::getInstance().getGlobalAllocs()) {} |
| |
| std::vector<uint32_t> runAndSerialize(Module *module, int *error) override { |
| std::string GAMD = CreateGAIDMetadata(mAllocs); |
| if (GAMD.size() > 0) { |
| module->addString(GAMD.c_str()); |
| } |
| mMetadata = AddGAMetadata(mBuilder, module); |
| return Transformer::runAndSerialize(module, error); |
| } |
| |
| Instruction *transform(FunctionCallInst *call) { |
| FunctionInst *func = |
| static_cast<FunctionInst *>(call->mOperand1.mInstruction); |
| const char *name = getModule()->lookupNameByInstruction(func); |
| if (!name) { |
| return call; |
| } |
| |
| Instruction *inst = nullptr; |
| // Maps name into a SPIR-V instruction |
| // TODO: generalize it to support more accessors |
| if (!strcmp(name, "__rsov_rsAllocationGetDimX")) { |
| TypeIntInst *UInt32Ty = getModule()->getUnsignedIntType(32); |
| // TODO: hardcoded layout |
| auto ConstZero = getModule()->getConstant(UInt32Ty, 0U); |
| auto ConstOne = getModule()->getConstant(UInt32Ty, 1U); |
| |
| // TODO: Use constant memory later |
| auto resultPtrType = |
| getModule()->getPointerType(StorageClass::Uniform, UInt32Ty); |
| AccessChainInst *LoadPtr = mBuilder.MakeAccessChain( |
| resultPtrType, mMetadata, {ConstZero, ConstZero, ConstOne}); |
| insert(LoadPtr); |
| |
| inst = mBuilder.MakeLoad(UInt32Ty, LoadPtr); |
| inst->setId(call->getId()); |
| } else { |
| inst = call; |
| } |
| return inst; |
| } |
| |
| private: |
| Builder mBuilder; |
| VariableInst *mMetadata; |
| llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> &mAllocs; |
| }; |
| |
| } // namespace spirit |
| } // namespace android |
| |
| namespace rs2spirv { |
| |
| android::spirit::Pass *CreateGAPass(void) { |
| return new android::spirit::GAAccessorTransformer(); |
| } |
| |
| } // namespace rs2spirv |