Home | History | Annotate | Download | only in compiler
      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