C++程序  |  189行  |  6.12 KB

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