/* * 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