blob: 69bf0f9776ded6bd66fe73f8ae539596408e4c11 [file] [log] [blame]
alan-bakerc4579bb2020-04-29 14:15:50 -04001// Copyright 2020 The Clspv Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "clspv/Option.h"
16
17#include "Layout.h"
18
19using namespace llvm;
20
21namespace {
22bool isScalarType(Type *type) {
alan-baker7efcaaa2020-05-06 19:33:27 -040023 return type->isIntegerTy() || type->isFloatingPointTy();
alan-bakerc4579bb2020-04-29 14:15:50 -040024}
25
26uint64_t structAlignment(StructType *type,
27 std::function<uint64_t(Type *)> alignFn) {
28 uint64_t maxAlign = 1;
29 for (unsigned i = 0; i < type->getStructNumElements(); i++) {
30 uint64_t align = alignFn(type->getStructElementType(i));
31 maxAlign = std::max(align, maxAlign);
32 }
33 return maxAlign;
34}
35
36uint64_t scalarAlignment(Type *type) {
37 // A scalar of size N has a scalar alignment of N.
38 if (isScalarType(type)) {
39 return type->getScalarSizeInBits() / 8;
40 }
41
42 // A vector or matrix type has a scalar alignment equal to that of its
43 // component type.
44 if (auto vec_type = dyn_cast<VectorType>(type)) {
45 return scalarAlignment(vec_type->getElementType());
46 }
47
48 // An array type has a scalar alignment equal to that of its element type.
49 if (type->isArrayTy()) {
50 return scalarAlignment(type->getArrayElementType());
51 }
52
53 // A structure has a scalar alignment equal to the largest scalar alignment of
54 // any of its members.
55 if (type->isStructTy()) {
56 return structAlignment(cast<StructType>(type), scalarAlignment);
57 }
58
59 llvm_unreachable("Unsupported type");
60}
61
62uint64_t baseAlignment(Type *type) {
63 // A scalar has a base alignment equal to its scalar alignment.
64 if (isScalarType(type)) {
65 return scalarAlignment(type);
66 }
67
68 if (auto vec_type = dyn_cast<VectorType>(type)) {
alan-baker5a8c3be2020-09-09 13:44:26 -040069 unsigned numElems = vec_type->getElementCount().getKnownMinValue();
alan-bakerc4579bb2020-04-29 14:15:50 -040070
71 // A two-component vector has a base alignment equal to twice its scalar
72 // alignment.
73 if (numElems == 2) {
74 return 2 * scalarAlignment(type);
75 }
76 // A three- or four-component vector has a base alignment equal to four
77 // times its scalar alignment.
78 if ((numElems == 3) || (numElems == 4)) {
79 return 4 * scalarAlignment(type);
80 }
81 }
82
83 // An array has a base alignment equal to the base alignment of its element
84 // type.
85 if (type->isArrayTy()) {
86 return baseAlignment(type->getArrayElementType());
87 }
88
89 // A structure has a base alignment equal to the largest base alignment of any
90 // of its members.
91 if (type->isStructTy()) {
92 return structAlignment(cast<StructType>(type), baseAlignment);
93 }
94
95 // TODO A row-major matrix of C columns has a base alignment equal to the base
96 // alignment of a vector of C matrix components.
97 // TODO A column-major matrix has a base alignment equal to the base alignment
98 // of the matrix column type.
99
100 llvm_unreachable("Unsupported type");
101}
102
103uint64_t extendedAlignment(Type *type) {
104 // A scalar, vector or matrix type has an extended alignment equal to its base
105 // alignment.
106 // TODO matrix type
107 if (isScalarType(type) || type->isVectorTy()) {
108 return baseAlignment(type);
109 }
110
111 // An array or structure type has an extended alignment equal to the largest
112 // extended alignment of any of its members, rounded up to a multiple of 16
113 if (type->isStructTy()) {
114 auto salign = structAlignment(cast<StructType>(type), extendedAlignment);
115 return alignTo(salign, 16);
116 }
117
118 if (type->isArrayTy()) {
119 auto salign = extendedAlignment(type->getArrayElementType());
120 return alignTo(salign, 16);
121 }
122
123 llvm_unreachable("Unsupported type");
124}
125
126uint64_t standardAlignment(Type *type, spv::StorageClass sclass) {
127 // If the scalarBlockLayout feature is enabled on the device then every member
128 // must be aligned according to its scalar alignment
129 if (clspv::Option::ScalarBlockLayout()) {
130 return scalarAlignment(type);
131 }
132
133 // All vectors must be aligned according to their scalar alignment
134 if (type->isVectorTy()) {
135 return scalarAlignment(type);
136 }
137
138 // If the uniformBufferStandardLayout feature is not enabled on the device,
139 // then any member of an OpTypeStruct with a storage class of Uniform and a
140 // decoration of Block must be aligned according to its extended alignment.
141 if (!clspv::Option::Std430UniformBufferLayout() &&
142 sclass == spv::StorageClassUniform) {
143 return extendedAlignment(type);
144 }
145
146 // Every other member must be aligned according to its base alignment
147 return baseAlignment(type);
148}
149
150bool improperlyStraddles(const DataLayout &DL, Type *type, unsigned offset) {
151 assert(type->isVectorTy());
152
153 auto size = DL.getTypeStoreSize(type);
154
155 // It is a vector with total size less than or equal to 16 bytes, and has
156 // Offset decorations placing its first byte at F and its last byte at L,
157 // where floor(F / 16) != floor(L / 16).
158 if ((size <= 16) && (offset % 16 + size > 16)) {
159 return true;
160 }
161
162 // It is a vector with total size greater than 16 bytes and has its Offset
163 // decorations placing its first byte at a non-integer multiple of 16
164 if ((size > 16) && (offset % 16 != 0)) {
165 return true;
166 }
167
168 return false;
169}
170} // namespace
171
172namespace clspv {
173
174// See 14.5 Shader Resource Interface in Vulkan spec
175bool isValidExplicitLayout(Module &M, StructType *STy, unsigned Member,
176 spv::StorageClass SClass, unsigned Offset,
177 unsigned PreviousMemberOffset) {
178
179 auto MemberType = STy->getElementType(Member);
180 auto Align = standardAlignment(MemberType, SClass);
181 auto &DL = M.getDataLayout();
182
183 // The Offset decoration of any member must be a multiple of its alignment
184 if (Offset % Align != 0) {
185 return false;
186 }
187
188 // TODO Any ArrayStride or MatrixStride decoration must be a multiple of the
189 // alignment of the array or matrix as defined above
190
191 if (!clspv::Option::ScalarBlockLayout()) {
192 // Vectors must not improperly straddle, as defined above
193 if (MemberType->isVectorTy() &&
194 improperlyStraddles(DL, MemberType, Offset)) {
195 return true;
196 }
197
198 // The Offset decoration of a member must not place it between the end
199 // of a structure or an array and the next multiple of the alignment of that
200 // structure or array
201 if (Member > 0) {
202 auto PType = STy->getElementType(Member - 1);
203 if (PType->isStructTy() || PType->isArrayTy()) {
204 auto PAlign = standardAlignment(PType, SClass);
205 if (Offset - PreviousMemberOffset < PAlign) {
206 return false;
207 }
208 }
209 }
210 }
211
212 return true;
213}
214
215bool isValidExplicitLayout(llvm::Module &M, llvm::StructType *STy,
216 spv::StorageClass SClass) {
217 auto const &DL = M.getDataLayout();
218 const auto StructLayout = DL.getStructLayout(STy);
219 bool ok = true;
220 auto previous_offset = 0;
221 for (unsigned i = 0; ok && i < STy->getNumElements(); i++) {
222 auto offset = StructLayout->getElementOffset(i);
223 ok &= isValidExplicitLayout(M, STy, i, SClass, offset, previous_offset);
224 previous_offset = offset;
225 }
226
227 return ok;
228}
229} // namespace clspv