• 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 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