• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 SpirvShader::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 			auto const &cond = getObject(insn.word(4));
72 			auto condIsScalar = (getType(cond).componentCount == 1);
73 			auto const &left = getObject(insn.word(5));
74 			auto const &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 			auto const &compositeObject = getObject(insn.word(4));
88 			auto firstComponent = WalkLiteralAccessChain(compositeObject.typeId(), insn.wordCount() - 5, insn.wordPointer(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 			auto const &newPart = getObject(insn.word(4));
101 			auto const &oldObject = getObject(insn.word(5));
102 			auto firstNewComponent = WalkLiteralAccessChain(result.typeId(), insn.wordCount() - 6, insn.wordPointer(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 			auto const &firstHalf = getObject(insn.word(4));
126 			auto const &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 SpirvShader::EvalSpecConstantUnaryOp(InsnIterator insn)
157 {
158 	auto &result = CreateConstant(insn);
159 
160 	auto opcode = static_cast<spv::Op>(insn.word(3));
161 	auto const &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 				break;
199 			}
200 			default:
201 				UNREACHABLE("EvalSpecConstantUnaryOp op: %s", OpcodeName(opcode));
202 		}
203 	}
204 }
205 
EvalSpecConstantBinaryOp(InsnIterator insn)206 void SpirvShader::EvalSpecConstantBinaryOp(InsnIterator insn)
207 {
208 	auto &result = CreateConstant(insn);
209 
210 	auto opcode = static_cast<spv::Op>(insn.word(3));
211 	auto const &lhs = getObject(insn.word(4));
212 	auto const &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