1 // Copyright 2019 The SwiftShader 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 "SpirvShader.hpp"
16
17 #include <spirv/unified1/spirv.hpp>
18
19 namespace sw {
20
EvalSpecConstantOp(InsnIterator insn)21 void Spirv::EvalSpecConstantOp(InsnIterator insn)
22 {
23 auto opcode = static_cast<spv::Op>(insn.word(3));
24
25 switch(opcode)
26 {
27 case spv::OpIAdd:
28 case spv::OpISub:
29 case spv::OpIMul:
30 case spv::OpUDiv:
31 case spv::OpSDiv:
32 case spv::OpUMod:
33 case spv::OpSMod:
34 case spv::OpSRem:
35 case spv::OpShiftRightLogical:
36 case spv::OpShiftRightArithmetic:
37 case spv::OpShiftLeftLogical:
38 case spv::OpBitwiseOr:
39 case spv::OpLogicalOr:
40 case spv::OpBitwiseAnd:
41 case spv::OpLogicalAnd:
42 case spv::OpBitwiseXor:
43 case spv::OpLogicalEqual:
44 case spv::OpIEqual:
45 case spv::OpLogicalNotEqual:
46 case spv::OpINotEqual:
47 case spv::OpULessThan:
48 case spv::OpSLessThan:
49 case spv::OpUGreaterThan:
50 case spv::OpSGreaterThan:
51 case spv::OpULessThanEqual:
52 case spv::OpSLessThanEqual:
53 case spv::OpUGreaterThanEqual:
54 case spv::OpSGreaterThanEqual:
55 EvalSpecConstantBinaryOp(insn);
56 break;
57
58 case spv::OpSConvert:
59 case spv::OpFConvert:
60 case spv::OpUConvert:
61 case spv::OpSNegate:
62 case spv::OpNot:
63 case spv::OpLogicalNot:
64 case spv::OpQuantizeToF16:
65 EvalSpecConstantUnaryOp(insn);
66 break;
67
68 case spv::OpSelect:
69 {
70 auto &result = CreateConstant(insn);
71 const auto &cond = getObject(insn.word(4));
72 auto condIsScalar = (getType(cond).componentCount == 1);
73 const auto &left = getObject(insn.word(5));
74 const auto &right = getObject(insn.word(6));
75
76 for(auto i = 0u; i < getType(result).componentCount; i++)
77 {
78 auto sel = cond.constantValue[condIsScalar ? 0 : i];
79 result.constantValue[i] = sel ? left.constantValue[i] : right.constantValue[i];
80 }
81 break;
82 }
83
84 case spv::OpCompositeExtract:
85 {
86 auto &result = CreateConstant(insn);
87 const auto &compositeObject = getObject(insn.word(4));
88 auto firstComponent = WalkLiteralAccessChain(compositeObject.typeId(), Span(insn, 5, insn.wordCount() - 5));
89
90 for(auto i = 0u; i < getType(result).componentCount; i++)
91 {
92 result.constantValue[i] = compositeObject.constantValue[firstComponent + i];
93 }
94 break;
95 }
96
97 case spv::OpCompositeInsert:
98 {
99 auto &result = CreateConstant(insn);
100 const auto &newPart = getObject(insn.word(4));
101 const auto &oldObject = getObject(insn.word(5));
102 auto firstNewComponent = WalkLiteralAccessChain(result.typeId(), Span(insn, 6, insn.wordCount() - 6));
103
104 // old components before
105 for(auto i = 0u; i < firstNewComponent; i++)
106 {
107 result.constantValue[i] = oldObject.constantValue[i];
108 }
109 // new part
110 for(auto i = 0u; i < getType(newPart).componentCount; i++)
111 {
112 result.constantValue[firstNewComponent + i] = newPart.constantValue[i];
113 }
114 // old components after
115 for(auto i = firstNewComponent + getType(newPart).componentCount; i < getType(result).componentCount; i++)
116 {
117 result.constantValue[i] = oldObject.constantValue[i];
118 }
119 break;
120 }
121
122 case spv::OpVectorShuffle:
123 {
124 auto &result = CreateConstant(insn);
125 const auto &firstHalf = getObject(insn.word(4));
126 const auto &secondHalf = getObject(insn.word(5));
127
128 for(auto i = 0u; i < getType(result).componentCount; i++)
129 {
130 auto selector = insn.word(6 + i);
131 if(selector == static_cast<uint32_t>(-1))
132 {
133 // Undefined value, we'll use zero
134 result.constantValue[i] = 0;
135 }
136 else if(selector < getType(firstHalf).componentCount)
137 {
138 result.constantValue[i] = firstHalf.constantValue[selector];
139 }
140 else
141 {
142 result.constantValue[i] = secondHalf.constantValue[selector - getType(firstHalf).componentCount];
143 }
144 }
145 break;
146 }
147
148 default:
149 // Other spec constant ops are possible, but require capabilities that are
150 // not exposed in our Vulkan implementation (eg Kernel), so we should never
151 // get here for correct shaders.
152 UNSUPPORTED("EvalSpecConstantOp op: %s", OpcodeName(opcode));
153 }
154 }
155
EvalSpecConstantUnaryOp(InsnIterator insn)156 void Spirv::EvalSpecConstantUnaryOp(InsnIterator insn)
157 {
158 auto &result = CreateConstant(insn);
159
160 auto opcode = static_cast<spv::Op>(insn.word(3));
161 const auto &lhs = getObject(insn.word(4));
162 auto size = getType(lhs).componentCount;
163
164 for(auto i = 0u; i < size; i++)
165 {
166 auto &v = result.constantValue[i];
167 auto l = lhs.constantValue[i];
168
169 switch(opcode)
170 {
171 case spv::OpSConvert:
172 case spv::OpFConvert:
173 case spv::OpUConvert:
174 UNREACHABLE("Not possible until we have multiple bit widths");
175 break;
176
177 case spv::OpSNegate:
178 v = -(int)l;
179 break;
180 case spv::OpNot:
181 case spv::OpLogicalNot:
182 v = ~l;
183 break;
184
185 case spv::OpQuantizeToF16:
186 {
187 // Can do this nicer with host code, but want to perfectly mirror the reactor code we emit.
188 auto abs = bit_cast<float>(l & 0x7FFFFFFF);
189 auto sign = l & 0x80000000;
190 auto isZero = abs < 0.000061035f ? ~0u : 0u;
191 auto isInf = abs > 65504.0f ? ~0u : 0u;
192 auto isNaN = (abs != abs) ? ~0u : 0u;
193 auto isInfOrNan = isInf | isNaN;
194 v = l & 0xFFFFE000;
195 v &= ~isZero | 0x80000000;
196 v = sign | (isInfOrNan & 0x7F800000) | (~isInfOrNan & v);
197 v |= isNaN & 0x400000;
198 }
199 break;
200 default:
201 UNREACHABLE("EvalSpecConstantUnaryOp op: %s", OpcodeName(opcode));
202 }
203 }
204 }
205
EvalSpecConstantBinaryOp(InsnIterator insn)206 void Spirv::EvalSpecConstantBinaryOp(InsnIterator insn)
207 {
208 auto &result = CreateConstant(insn);
209
210 auto opcode = static_cast<spv::Op>(insn.word(3));
211 const auto &lhs = getObject(insn.word(4));
212 const auto &rhs = getObject(insn.word(5));
213 auto size = getType(lhs).componentCount;
214
215 for(auto i = 0u; i < size; i++)
216 {
217 auto &v = result.constantValue[i];
218 auto l = lhs.constantValue[i];
219 auto r = rhs.constantValue[i];
220
221 switch(opcode)
222 {
223 case spv::OpIAdd:
224 v = l + r;
225 break;
226 case spv::OpISub:
227 v = l - r;
228 break;
229 case spv::OpIMul:
230 v = l * r;
231 break;
232 case spv::OpUDiv:
233 v = (r == 0) ? 0 : l / r;
234 break;
235 case spv::OpUMod:
236 v = (r == 0) ? 0 : l % r;
237 break;
238 case spv::OpSDiv:
239 if(r == 0) r = UINT32_MAX;
240 if(l == static_cast<uint32_t>(INT32_MIN)) l = UINT32_MAX;
241 v = static_cast<int32_t>(l) / static_cast<int32_t>(r);
242 break;
243 case spv::OpSRem:
244 if(r == 0) r = UINT32_MAX;
245 if(l == static_cast<uint32_t>(INT32_MIN)) l = UINT32_MAX;
246 v = static_cast<int32_t>(l) % static_cast<int32_t>(r);
247 break;
248 case spv::OpSMod:
249 if(r == 0) r = UINT32_MAX;
250 if(l == static_cast<uint32_t>(INT32_MIN)) l = UINT32_MAX;
251 // Test if a signed-multiply would be negative.
252 v = static_cast<int32_t>(l) % static_cast<int32_t>(r);
253 if((v & 0x80000000) != (r & 0x80000000))
254 v += r;
255 break;
256 case spv::OpShiftRightLogical:
257 v = l >> r;
258 break;
259 case spv::OpShiftRightArithmetic:
260 v = static_cast<int32_t>(l) >> r;
261 break;
262 case spv::OpShiftLeftLogical:
263 v = l << r;
264 break;
265 case spv::OpBitwiseOr:
266 case spv::OpLogicalOr:
267 v = l | r;
268 break;
269 case spv::OpBitwiseAnd:
270 case spv::OpLogicalAnd:
271 v = l & r;
272 break;
273 case spv::OpBitwiseXor:
274 v = l ^ r;
275 break;
276 case spv::OpLogicalEqual:
277 case spv::OpIEqual:
278 v = (l == r) ? ~0u : 0u;
279 break;
280 case spv::OpLogicalNotEqual:
281 case spv::OpINotEqual:
282 v = (l != r) ? ~0u : 0u;
283 break;
284 case spv::OpULessThan:
285 v = l < r ? ~0u : 0u;
286 break;
287 case spv::OpSLessThan:
288 v = static_cast<int32_t>(l) < static_cast<int32_t>(r) ? ~0u : 0u;
289 break;
290 case spv::OpUGreaterThan:
291 v = l > r ? ~0u : 0u;
292 break;
293 case spv::OpSGreaterThan:
294 v = static_cast<int32_t>(l) > static_cast<int32_t>(r) ? ~0u : 0u;
295 break;
296 case spv::OpULessThanEqual:
297 v = l <= r ? ~0u : 0u;
298 break;
299 case spv::OpSLessThanEqual:
300 v = static_cast<int32_t>(l) <= static_cast<int32_t>(r) ? ~0u : 0u;
301 break;
302 case spv::OpUGreaterThanEqual:
303 v = l >= r ? ~0u : 0u;
304 break;
305 case spv::OpSGreaterThanEqual:
306 v = static_cast<int32_t>(l) >= static_cast<int32_t>(r) ? ~0u : 0u;
307 break;
308 default:
309 UNREACHABLE("EvalSpecConstantBinaryOp op: %s", OpcodeName(opcode));
310 }
311 }
312 }
313
314 } // namespace sw