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