xref: /aosp_15_r20/external/swiftshader/src/Pipeline/SpirvShaderGroup.cpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1*03ce13f7SAndroid Build Coastguard Worker // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2*03ce13f7SAndroid Build Coastguard Worker //
3*03ce13f7SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*03ce13f7SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*03ce13f7SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*03ce13f7SAndroid Build Coastguard Worker //
7*03ce13f7SAndroid Build Coastguard Worker //    http://www.apache.org/licenses/LICENSE-2.0
8*03ce13f7SAndroid Build Coastguard Worker //
9*03ce13f7SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*03ce13f7SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*03ce13f7SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*03ce13f7SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*03ce13f7SAndroid Build Coastguard Worker // limitations under the License.
14*03ce13f7SAndroid Build Coastguard Worker 
15*03ce13f7SAndroid Build Coastguard Worker #include "SpirvShader.hpp"
16*03ce13f7SAndroid Build Coastguard Worker 
17*03ce13f7SAndroid Build Coastguard Worker #include <spirv/unified1/spirv.hpp>
18*03ce13f7SAndroid Build Coastguard Worker 
19*03ce13f7SAndroid Build Coastguard Worker namespace sw {
20*03ce13f7SAndroid Build Coastguard Worker 
21*03ce13f7SAndroid Build Coastguard Worker // Template function to perform a binary group operation.
22*03ce13f7SAndroid Build Coastguard Worker // |TYPE| should be the type of the binary operation (as a SIMD::<ScalarType>).
23*03ce13f7SAndroid Build Coastguard Worker // |I| should be a type suitable to initialize the identity value.
24*03ce13f7SAndroid Build Coastguard Worker // |APPLY| should be a callable object that takes two RValue<TYPE> parameters
25*03ce13f7SAndroid Build Coastguard Worker // and returns a new RValue<TYPE> corresponding to the operation's result.
26*03ce13f7SAndroid Build Coastguard Worker template<typename TYPE, typename I, typename APPLY>
BinaryOperation(spv::GroupOperation operation,RValue<SIMD::UInt> value,RValue<SIMD::UInt> mask,const I identityValue,APPLY && apply)27*03ce13f7SAndroid Build Coastguard Worker static RValue<TYPE> BinaryOperation(
28*03ce13f7SAndroid Build Coastguard Worker     spv::GroupOperation operation,
29*03ce13f7SAndroid Build Coastguard Worker     RValue<SIMD::UInt> value,
30*03ce13f7SAndroid Build Coastguard Worker     RValue<SIMD::UInt> mask,
31*03ce13f7SAndroid Build Coastguard Worker     const I identityValue,
32*03ce13f7SAndroid Build Coastguard Worker     APPLY &&apply)
33*03ce13f7SAndroid Build Coastguard Worker {
34*03ce13f7SAndroid Build Coastguard Worker 	auto identity = TYPE(identityValue);
35*03ce13f7SAndroid Build Coastguard Worker 	SIMD::UInt v_uint = (value & mask) | (As<SIMD::UInt>(identity) & ~mask);
36*03ce13f7SAndroid Build Coastguard Worker 	TYPE v = As<TYPE>(v_uint);
37*03ce13f7SAndroid Build Coastguard Worker 
38*03ce13f7SAndroid Build Coastguard Worker 	switch(operation)
39*03ce13f7SAndroid Build Coastguard Worker 	{
40*03ce13f7SAndroid Build Coastguard Worker 	case spv::GroupOperationReduce:
41*03ce13f7SAndroid Build Coastguard Worker 		{
42*03ce13f7SAndroid Build Coastguard Worker 			// NOTE: floating-point add and multiply are not really commutative so
43*03ce13f7SAndroid Build Coastguard Worker 			//       ensure that all values in the final lanes are identical
44*03ce13f7SAndroid Build Coastguard Worker 			TYPE v2 = apply(v.xxzz, v.yyww);  // [xy]   [xy]   [zw]   [zw]
45*03ce13f7SAndroid Build Coastguard Worker 			return apply(v2.xxxx, v2.zzzz);   // [xyzw] [xyzw] [xyzw] [xyzw]
46*03ce13f7SAndroid Build Coastguard Worker 		}
47*03ce13f7SAndroid Build Coastguard Worker 		break;
48*03ce13f7SAndroid Build Coastguard Worker 	case spv::GroupOperationInclusiveScan:
49*03ce13f7SAndroid Build Coastguard Worker 		{
50*03ce13f7SAndroid Build Coastguard Worker 			TYPE v2 = apply(v, Shuffle(v, identity, 0x4012) /* [id, v.y, v.z, v.w] */);   // [x] [xy] [yz]  [zw]
51*03ce13f7SAndroid Build Coastguard Worker 			return apply(v2, Shuffle(v2, identity, 0x4401) /* [id,  id, v2.x, v2.y] */);  // [x] [xy] [xyz] [xyzw]
52*03ce13f7SAndroid Build Coastguard Worker 		}
53*03ce13f7SAndroid Build Coastguard Worker 		break;
54*03ce13f7SAndroid Build Coastguard Worker 	case spv::GroupOperationExclusiveScan:
55*03ce13f7SAndroid Build Coastguard Worker 		{
56*03ce13f7SAndroid Build Coastguard Worker 			TYPE v2 = apply(v, Shuffle(v, identity, 0x4012) /* [id, v.y, v.z, v.w] */);      // [x] [xy] [yz]  [zw]
57*03ce13f7SAndroid Build Coastguard Worker 			TYPE v3 = apply(v2, Shuffle(v2, identity, 0x4401) /* [id,  id, v2.x, v2.y] */);  // [x] [xy] [xyz] [xyzw]
58*03ce13f7SAndroid Build Coastguard Worker 			return Shuffle(v3, identity, 0x4012 /* [id, v3.x, v3.y, v3.z] */);               // [i] [x]  [xy]  [xyz]
59*03ce13f7SAndroid Build Coastguard Worker 		}
60*03ce13f7SAndroid Build Coastguard Worker 		break;
61*03ce13f7SAndroid Build Coastguard Worker 	default:
62*03ce13f7SAndroid Build Coastguard Worker 		UNSUPPORTED("Group operation: %d", operation);
63*03ce13f7SAndroid Build Coastguard Worker 		return identity;
64*03ce13f7SAndroid Build Coastguard Worker 	}
65*03ce13f7SAndroid Build Coastguard Worker }
66*03ce13f7SAndroid Build Coastguard Worker 
EmitGroupNonUniform(InsnIterator insn)67*03ce13f7SAndroid Build Coastguard Worker void SpirvEmitter::EmitGroupNonUniform(InsnIterator insn)
68*03ce13f7SAndroid Build Coastguard Worker {
69*03ce13f7SAndroid Build Coastguard Worker 	ASSERT(SIMD::Width == 4);  // EmitGroupNonUniform makes many assumptions that the SIMD vector width is 4
70*03ce13f7SAndroid Build Coastguard Worker 
71*03ce13f7SAndroid Build Coastguard Worker 	auto &type = shader.getType(Type::ID(insn.word(1)));
72*03ce13f7SAndroid Build Coastguard Worker 	Object::ID resultId = insn.word(2);
73*03ce13f7SAndroid Build Coastguard Worker 	auto scope = spv::Scope(shader.GetConstScalarInt(insn.word(3)));
74*03ce13f7SAndroid Build Coastguard Worker 	ASSERT_MSG(scope == spv::ScopeSubgroup, "Scope for Non Uniform Group Operations must be Subgroup for Vulkan 1.1");
75*03ce13f7SAndroid Build Coastguard Worker 
76*03ce13f7SAndroid Build Coastguard Worker 	auto &dst = createIntermediate(resultId, type.componentCount);
77*03ce13f7SAndroid Build Coastguard Worker 
78*03ce13f7SAndroid Build Coastguard Worker 	switch(insn.opcode())
79*03ce13f7SAndroid Build Coastguard Worker 	{
80*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformElect:
81*03ce13f7SAndroid Build Coastguard Worker 		{
82*03ce13f7SAndroid Build Coastguard Worker 			// Result is true only in the active invocation with the lowest id
83*03ce13f7SAndroid Build Coastguard Worker 			// in the group, otherwise result is false.
84*03ce13f7SAndroid Build Coastguard Worker 			SIMD::Int active = activeLaneMask();  // Considers helper invocations active. See b/151137030
85*03ce13f7SAndroid Build Coastguard Worker 			// TODO: Would be nice if we could write this as:
86*03ce13f7SAndroid Build Coastguard Worker 			//   elect = active & ~(active.Oxyz | active.OOxy | active.OOOx)
87*03ce13f7SAndroid Build Coastguard Worker 			auto v0111 = SIMD::Int(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
88*03ce13f7SAndroid Build Coastguard Worker 			auto elect = active & ~(v0111 & (active.xxyz | active.xxxy | active.xxxx));
89*03ce13f7SAndroid Build Coastguard Worker 			dst.move(0, elect);
90*03ce13f7SAndroid Build Coastguard Worker 		}
91*03ce13f7SAndroid Build Coastguard Worker 		break;
92*03ce13f7SAndroid Build Coastguard Worker 
93*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformAll:
94*03ce13f7SAndroid Build Coastguard Worker 		{
95*03ce13f7SAndroid Build Coastguard Worker 			Operand predicate(shader, *this, insn.word(4));
96*03ce13f7SAndroid Build Coastguard Worker 			dst.move(0, AndAll(predicate.UInt(0) | ~As<SIMD::UInt>(activeLaneMask())));  // Considers helper invocations active. See b/151137030
97*03ce13f7SAndroid Build Coastguard Worker 		}
98*03ce13f7SAndroid Build Coastguard Worker 		break;
99*03ce13f7SAndroid Build Coastguard Worker 
100*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformAny:
101*03ce13f7SAndroid Build Coastguard Worker 		{
102*03ce13f7SAndroid Build Coastguard Worker 			Operand predicate(shader, *this, insn.word(4));
103*03ce13f7SAndroid Build Coastguard Worker 			dst.move(0, OrAll(predicate.UInt(0) & As<SIMD::UInt>(activeLaneMask())));  // Considers helper invocations active. See b/151137030
104*03ce13f7SAndroid Build Coastguard Worker 		}
105*03ce13f7SAndroid Build Coastguard Worker 		break;
106*03ce13f7SAndroid Build Coastguard Worker 
107*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformAllEqual:
108*03ce13f7SAndroid Build Coastguard Worker 		{
109*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, insn.word(4));
110*03ce13f7SAndroid Build Coastguard Worker 			auto res = SIMD::UInt(0xffffffff);
111*03ce13f7SAndroid Build Coastguard Worker 			SIMD::UInt active = As<SIMD::UInt>(activeLaneMask());  // Considers helper invocations active. See b/151137030
112*03ce13f7SAndroid Build Coastguard Worker 			SIMD::UInt inactive = ~active;
113*03ce13f7SAndroid Build Coastguard Worker 			for(auto i = 0u; i < type.componentCount; i++)
114*03ce13f7SAndroid Build Coastguard Worker 			{
115*03ce13f7SAndroid Build Coastguard Worker 				SIMD::UInt v = value.UInt(i) & active;
116*03ce13f7SAndroid Build Coastguard Worker 				SIMD::UInt filled = v;
117*03ce13f7SAndroid Build Coastguard Worker 				for(int j = 0; j < SIMD::Width - 1; j++)
118*03ce13f7SAndroid Build Coastguard Worker 				{
119*03ce13f7SAndroid Build Coastguard Worker 					filled |= filled.yzwx & inactive;  // Populate inactive 'holes' with a live value
120*03ce13f7SAndroid Build Coastguard Worker 				}
121*03ce13f7SAndroid Build Coastguard Worker 				res &= AndAll(CmpEQ(filled.xyzw, filled.yzwx));
122*03ce13f7SAndroid Build Coastguard Worker 			}
123*03ce13f7SAndroid Build Coastguard Worker 			dst.move(0, res);
124*03ce13f7SAndroid Build Coastguard Worker 		}
125*03ce13f7SAndroid Build Coastguard Worker 		break;
126*03ce13f7SAndroid Build Coastguard Worker 
127*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformBroadcast:
128*03ce13f7SAndroid Build Coastguard Worker 		{
129*03ce13f7SAndroid Build Coastguard Worker 			auto valueId = Object::ID(insn.word(4));
130*03ce13f7SAndroid Build Coastguard Worker 			auto idId = Object::ID(insn.word(5));
131*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, valueId);
132*03ce13f7SAndroid Build Coastguard Worker 
133*03ce13f7SAndroid Build Coastguard Worker 			// Decide between the fast path for constants and the slow path for
134*03ce13f7SAndroid Build Coastguard Worker 			// intermediates.
135*03ce13f7SAndroid Build Coastguard Worker 			if(shader.getObject(idId).kind == Object::Kind::Constant)
136*03ce13f7SAndroid Build Coastguard Worker 			{
137*03ce13f7SAndroid Build Coastguard Worker 				auto id = SIMD::Int(shader.GetConstScalarInt(insn.word(5)));
138*03ce13f7SAndroid Build Coastguard Worker 				auto mask = CmpEQ(id, SIMD::Int(0, 1, 2, 3));
139*03ce13f7SAndroid Build Coastguard Worker 				for(auto i = 0u; i < type.componentCount; i++)
140*03ce13f7SAndroid Build Coastguard Worker 				{
141*03ce13f7SAndroid Build Coastguard Worker 					dst.move(i, OrAll(value.Int(i) & mask));
142*03ce13f7SAndroid Build Coastguard Worker 				}
143*03ce13f7SAndroid Build Coastguard Worker 			}
144*03ce13f7SAndroid Build Coastguard Worker 			else
145*03ce13f7SAndroid Build Coastguard Worker 			{
146*03ce13f7SAndroid Build Coastguard Worker 				Operand id(shader, *this, idId);
147*03ce13f7SAndroid Build Coastguard Worker 
148*03ce13f7SAndroid Build Coastguard Worker 				SIMD::UInt active = As<SIMD::UInt>(activeLaneMask());  // Considers helper invocations active. See b/151137030
149*03ce13f7SAndroid Build Coastguard Worker 				SIMD::UInt inactive = ~active;
150*03ce13f7SAndroid Build Coastguard Worker 				SIMD::UInt filled = id.UInt(0) & active;
151*03ce13f7SAndroid Build Coastguard Worker 
152*03ce13f7SAndroid Build Coastguard Worker 				for(int j = 0; j < SIMD::Width - 1; j++)
153*03ce13f7SAndroid Build Coastguard Worker 				{
154*03ce13f7SAndroid Build Coastguard Worker 					filled |= filled.yzwx & inactive;  // Populate inactive 'holes' with a live value
155*03ce13f7SAndroid Build Coastguard Worker 				}
156*03ce13f7SAndroid Build Coastguard Worker 
157*03ce13f7SAndroid Build Coastguard Worker 				auto mask = CmpEQ(filled, SIMD::UInt(0, 1, 2, 3));
158*03ce13f7SAndroid Build Coastguard Worker 
159*03ce13f7SAndroid Build Coastguard Worker 				for(uint32_t i = 0u; i < type.componentCount; i++)
160*03ce13f7SAndroid Build Coastguard Worker 				{
161*03ce13f7SAndroid Build Coastguard Worker 					dst.move(i, OrAll(value.UInt(i) & mask));
162*03ce13f7SAndroid Build Coastguard Worker 				}
163*03ce13f7SAndroid Build Coastguard Worker 			}
164*03ce13f7SAndroid Build Coastguard Worker 		}
165*03ce13f7SAndroid Build Coastguard Worker 		break;
166*03ce13f7SAndroid Build Coastguard Worker 
167*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformBroadcastFirst:
168*03ce13f7SAndroid Build Coastguard Worker 		{
169*03ce13f7SAndroid Build Coastguard Worker 			auto valueId = Object::ID(insn.word(4));
170*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, valueId);
171*03ce13f7SAndroid Build Coastguard Worker 			// Result is true only in the active invocation with the lowest id
172*03ce13f7SAndroid Build Coastguard Worker 			// in the group, otherwise result is false.
173*03ce13f7SAndroid Build Coastguard Worker 			SIMD::Int active = activeLaneMask();  // Considers helper invocations active. See b/151137030
174*03ce13f7SAndroid Build Coastguard Worker 			// TODO: Would be nice if we could write this as:
175*03ce13f7SAndroid Build Coastguard Worker 			//   elect = active & ~(active.Oxyz | active.OOxy | active.OOOx)
176*03ce13f7SAndroid Build Coastguard Worker 			auto v0111 = SIMD::Int(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
177*03ce13f7SAndroid Build Coastguard Worker 			auto elect = active & ~(v0111 & (active.xxyz | active.xxxy | active.xxxx));
178*03ce13f7SAndroid Build Coastguard Worker 			for(auto i = 0u; i < type.componentCount; i++)
179*03ce13f7SAndroid Build Coastguard Worker 			{
180*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, OrAll(value.Int(i) & elect));
181*03ce13f7SAndroid Build Coastguard Worker 			}
182*03ce13f7SAndroid Build Coastguard Worker 		}
183*03ce13f7SAndroid Build Coastguard Worker 		break;
184*03ce13f7SAndroid Build Coastguard Worker 
185*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformQuadBroadcast:
186*03ce13f7SAndroid Build Coastguard Worker 		{
187*03ce13f7SAndroid Build Coastguard Worker 			auto valueId = Object::ID(insn.word(4));
188*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, valueId);
189*03ce13f7SAndroid Build Coastguard Worker 
190*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(shader.getType(shader.getObject(insn.word(5))).componentCount == 1);
191*03ce13f7SAndroid Build Coastguard Worker 			auto indexId = Object::ID(insn.word(5));
192*03ce13f7SAndroid Build Coastguard Worker 			SIMD::Int index = Operand(shader, *this, indexId).Int(0);
193*03ce13f7SAndroid Build Coastguard Worker 
194*03ce13f7SAndroid Build Coastguard Worker 			SIMD::Int active = activeLaneMask();
195*03ce13f7SAndroid Build Coastguard Worker 			// Populate all lanes in index with the same value. Index is required to be
196*03ce13f7SAndroid Build Coastguard Worker 			// uniform per the SPIR-V spec, so all active lanes should be identical.
197*03ce13f7SAndroid Build Coastguard Worker 			index = OrAll(active & index);
198*03ce13f7SAndroid Build Coastguard Worker 			SIMD::Int mask = CmpEQ(index, SIMD::Int(0, 1, 2, 3));
199*03ce13f7SAndroid Build Coastguard Worker 
200*03ce13f7SAndroid Build Coastguard Worker 			for(auto i = 0u; i < type.componentCount; i++)
201*03ce13f7SAndroid Build Coastguard Worker 			{
202*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, OrAll(value.Int(i) & mask));
203*03ce13f7SAndroid Build Coastguard Worker 			}
204*03ce13f7SAndroid Build Coastguard Worker 		}
205*03ce13f7SAndroid Build Coastguard Worker 		break;
206*03ce13f7SAndroid Build Coastguard Worker 
207*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformQuadSwap:
208*03ce13f7SAndroid Build Coastguard Worker 		{
209*03ce13f7SAndroid Build Coastguard Worker 			auto valueId = Object::ID(insn.word(4));
210*03ce13f7SAndroid Build Coastguard Worker 			// SPIR-V spec: Drection must be a scalar of integer type and come from a constant instruction
211*03ce13f7SAndroid Build Coastguard Worker 			int direction = shader.GetConstScalarInt(insn.word(5));
212*03ce13f7SAndroid Build Coastguard Worker 
213*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, valueId);
214*03ce13f7SAndroid Build Coastguard Worker 			for(auto i = 0u; i < type.componentCount; i++)
215*03ce13f7SAndroid Build Coastguard Worker 			{
216*03ce13f7SAndroid Build Coastguard Worker 				SIMD::Int v = value.Int(i);
217*03ce13f7SAndroid Build Coastguard Worker 				switch(direction)
218*03ce13f7SAndroid Build Coastguard Worker 				{
219*03ce13f7SAndroid Build Coastguard Worker 				case 0:  // Horizontal
220*03ce13f7SAndroid Build Coastguard Worker 					dst.move(i, v.yxwz);
221*03ce13f7SAndroid Build Coastguard Worker 					break;
222*03ce13f7SAndroid Build Coastguard Worker 				case 1:  // Vertical
223*03ce13f7SAndroid Build Coastguard Worker 					dst.move(i, v.zwxy);
224*03ce13f7SAndroid Build Coastguard Worker 					break;
225*03ce13f7SAndroid Build Coastguard Worker 				case 2:  // Diagonal
226*03ce13f7SAndroid Build Coastguard Worker 					dst.move(i, v.wzyx);
227*03ce13f7SAndroid Build Coastguard Worker 					break;
228*03ce13f7SAndroid Build Coastguard Worker 				default:
229*03ce13f7SAndroid Build Coastguard Worker 					// The SPIR-V spec doesn't define what happens in this case,
230*03ce13f7SAndroid Build Coastguard Worker 					// so the result in undefined.
231*03ce13f7SAndroid Build Coastguard Worker 					UNSUPPORTED("SPIR-V does not define a OpGroupNonUniformQuadSwap result for a direction of %d", direction);
232*03ce13f7SAndroid Build Coastguard Worker 					break;
233*03ce13f7SAndroid Build Coastguard Worker 				}
234*03ce13f7SAndroid Build Coastguard Worker 			}
235*03ce13f7SAndroid Build Coastguard Worker 		}
236*03ce13f7SAndroid Build Coastguard Worker 		break;
237*03ce13f7SAndroid Build Coastguard Worker 
238*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformBallot:
239*03ce13f7SAndroid Build Coastguard Worker 		{
240*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(type.componentCount == 4);
241*03ce13f7SAndroid Build Coastguard Worker 			Operand predicate(shader, *this, insn.word(4));
242*03ce13f7SAndroid Build Coastguard Worker 			dst.move(0, SIMD::Int(SignMask(activeLaneMask() & predicate.Int(0))));  // Considers helper invocations active. See b/151137030
243*03ce13f7SAndroid Build Coastguard Worker 			dst.move(1, SIMD::Int(0));
244*03ce13f7SAndroid Build Coastguard Worker 			dst.move(2, SIMD::Int(0));
245*03ce13f7SAndroid Build Coastguard Worker 			dst.move(3, SIMD::Int(0));
246*03ce13f7SAndroid Build Coastguard Worker 		}
247*03ce13f7SAndroid Build Coastguard Worker 		break;
248*03ce13f7SAndroid Build Coastguard Worker 
249*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformInverseBallot:
250*03ce13f7SAndroid Build Coastguard Worker 		{
251*03ce13f7SAndroid Build Coastguard Worker 			auto valueId = Object::ID(insn.word(4));
252*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(type.componentCount == 1);
253*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(shader.getObjectType(valueId).componentCount == 4);
254*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, valueId);
255*03ce13f7SAndroid Build Coastguard Worker 			auto bit = (value.Int(0) >> SIMD::Int(0, 1, 2, 3)) & SIMD::Int(1);
256*03ce13f7SAndroid Build Coastguard Worker 			dst.move(0, -bit);
257*03ce13f7SAndroid Build Coastguard Worker 		}
258*03ce13f7SAndroid Build Coastguard Worker 		break;
259*03ce13f7SAndroid Build Coastguard Worker 
260*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformBallotBitExtract:
261*03ce13f7SAndroid Build Coastguard Worker 		{
262*03ce13f7SAndroid Build Coastguard Worker 			auto valueId = Object::ID(insn.word(4));
263*03ce13f7SAndroid Build Coastguard Worker 			auto indexId = Object::ID(insn.word(5));
264*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(type.componentCount == 1);
265*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(shader.getObjectType(valueId).componentCount == 4);
266*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(shader.getObjectType(indexId).componentCount == 1);
267*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, valueId);
268*03ce13f7SAndroid Build Coastguard Worker 			Operand index(shader, *this, indexId);
269*03ce13f7SAndroid Build Coastguard Worker 			auto vecIdx = index.Int(0) / SIMD::Int(32);
270*03ce13f7SAndroid Build Coastguard Worker 			auto bitIdx = index.Int(0) & SIMD::Int(31);
271*03ce13f7SAndroid Build Coastguard Worker 			auto bits = (value.Int(0) & CmpEQ(vecIdx, SIMD::Int(0))) |
272*03ce13f7SAndroid Build Coastguard Worker 			            (value.Int(1) & CmpEQ(vecIdx, SIMD::Int(1))) |
273*03ce13f7SAndroid Build Coastguard Worker 			            (value.Int(2) & CmpEQ(vecIdx, SIMD::Int(2))) |
274*03ce13f7SAndroid Build Coastguard Worker 			            (value.Int(3) & CmpEQ(vecIdx, SIMD::Int(3)));
275*03ce13f7SAndroid Build Coastguard Worker 			dst.move(0, -((bits >> bitIdx) & SIMD::Int(1)));
276*03ce13f7SAndroid Build Coastguard Worker 		}
277*03ce13f7SAndroid Build Coastguard Worker 		break;
278*03ce13f7SAndroid Build Coastguard Worker 
279*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformBallotBitCount:
280*03ce13f7SAndroid Build Coastguard Worker 		{
281*03ce13f7SAndroid Build Coastguard Worker 			auto operation = spv::GroupOperation(insn.word(4));
282*03ce13f7SAndroid Build Coastguard Worker 			auto valueId = Object::ID(insn.word(5));
283*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(type.componentCount == 1);
284*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(shader.getObjectType(valueId).componentCount == 4);
285*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, valueId);
286*03ce13f7SAndroid Build Coastguard Worker 			switch(operation)
287*03ce13f7SAndroid Build Coastguard Worker 			{
288*03ce13f7SAndroid Build Coastguard Worker 			case spv::GroupOperationReduce:
289*03ce13f7SAndroid Build Coastguard Worker 				dst.move(0, CountBits(value.UInt(0) & SIMD::UInt(15)));
290*03ce13f7SAndroid Build Coastguard Worker 				break;
291*03ce13f7SAndroid Build Coastguard Worker 			case spv::GroupOperationInclusiveScan:
292*03ce13f7SAndroid Build Coastguard Worker 				dst.move(0, CountBits(value.UInt(0) & SIMD::UInt(1, 3, 7, 15)));
293*03ce13f7SAndroid Build Coastguard Worker 				break;
294*03ce13f7SAndroid Build Coastguard Worker 			case spv::GroupOperationExclusiveScan:
295*03ce13f7SAndroid Build Coastguard Worker 				dst.move(0, CountBits(value.UInt(0) & SIMD::UInt(0, 1, 3, 7)));
296*03ce13f7SAndroid Build Coastguard Worker 				break;
297*03ce13f7SAndroid Build Coastguard Worker 			default:
298*03ce13f7SAndroid Build Coastguard Worker 				UNSUPPORTED("GroupOperation %d", int(operation));
299*03ce13f7SAndroid Build Coastguard Worker 			}
300*03ce13f7SAndroid Build Coastguard Worker 		}
301*03ce13f7SAndroid Build Coastguard Worker 		break;
302*03ce13f7SAndroid Build Coastguard Worker 
303*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformBallotFindLSB:
304*03ce13f7SAndroid Build Coastguard Worker 		{
305*03ce13f7SAndroid Build Coastguard Worker 			auto valueId = Object::ID(insn.word(4));
306*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(type.componentCount == 1);
307*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(shader.getObjectType(valueId).componentCount == 4);
308*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, valueId);
309*03ce13f7SAndroid Build Coastguard Worker 			dst.move(0, Cttz(value.UInt(0) & SIMD::UInt(15), false));
310*03ce13f7SAndroid Build Coastguard Worker 		}
311*03ce13f7SAndroid Build Coastguard Worker 		break;
312*03ce13f7SAndroid Build Coastguard Worker 
313*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformBallotFindMSB:
314*03ce13f7SAndroid Build Coastguard Worker 		{
315*03ce13f7SAndroid Build Coastguard Worker 			auto valueId = Object::ID(insn.word(4));
316*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(type.componentCount == 1);
317*03ce13f7SAndroid Build Coastguard Worker 			ASSERT(shader.getObjectType(valueId).componentCount == 4);
318*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, valueId);
319*03ce13f7SAndroid Build Coastguard Worker 			dst.move(0, SIMD::UInt(31) - Ctlz(value.UInt(0) & SIMD::UInt(15), false));
320*03ce13f7SAndroid Build Coastguard Worker 		}
321*03ce13f7SAndroid Build Coastguard Worker 		break;
322*03ce13f7SAndroid Build Coastguard Worker 
323*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformShuffle:
324*03ce13f7SAndroid Build Coastguard Worker 		{
325*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, insn.word(4));
326*03ce13f7SAndroid Build Coastguard Worker 			Operand id(shader, *this, insn.word(5));
327*03ce13f7SAndroid Build Coastguard Worker 			auto x = CmpEQ(SIMD::Int(0), id.Int(0));
328*03ce13f7SAndroid Build Coastguard Worker 			auto y = CmpEQ(SIMD::Int(1), id.Int(0));
329*03ce13f7SAndroid Build Coastguard Worker 			auto z = CmpEQ(SIMD::Int(2), id.Int(0));
330*03ce13f7SAndroid Build Coastguard Worker 			auto w = CmpEQ(SIMD::Int(3), id.Int(0));
331*03ce13f7SAndroid Build Coastguard Worker 			for(auto i = 0u; i < type.componentCount; i++)
332*03ce13f7SAndroid Build Coastguard Worker 			{
333*03ce13f7SAndroid Build Coastguard Worker 				SIMD::Int v = value.Int(i);
334*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, (x & v.xxxx) | (y & v.yyyy) | (z & v.zzzz) | (w & v.wwww));
335*03ce13f7SAndroid Build Coastguard Worker 			}
336*03ce13f7SAndroid Build Coastguard Worker 		}
337*03ce13f7SAndroid Build Coastguard Worker 		break;
338*03ce13f7SAndroid Build Coastguard Worker 
339*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformShuffleXor:
340*03ce13f7SAndroid Build Coastguard Worker 		{
341*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, insn.word(4));
342*03ce13f7SAndroid Build Coastguard Worker 			Operand mask(shader, *this, insn.word(5));
343*03ce13f7SAndroid Build Coastguard Worker 			auto x = CmpEQ(SIMD::Int(0), SIMD::Int(0, 1, 2, 3) ^ mask.Int(0));
344*03ce13f7SAndroid Build Coastguard Worker 			auto y = CmpEQ(SIMD::Int(1), SIMD::Int(0, 1, 2, 3) ^ mask.Int(0));
345*03ce13f7SAndroid Build Coastguard Worker 			auto z = CmpEQ(SIMD::Int(2), SIMD::Int(0, 1, 2, 3) ^ mask.Int(0));
346*03ce13f7SAndroid Build Coastguard Worker 			auto w = CmpEQ(SIMD::Int(3), SIMD::Int(0, 1, 2, 3) ^ mask.Int(0));
347*03ce13f7SAndroid Build Coastguard Worker 			for(auto i = 0u; i < type.componentCount; i++)
348*03ce13f7SAndroid Build Coastguard Worker 			{
349*03ce13f7SAndroid Build Coastguard Worker 				SIMD::Int v = value.Int(i);
350*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, (x & v.xxxx) | (y & v.yyyy) | (z & v.zzzz) | (w & v.wwww));
351*03ce13f7SAndroid Build Coastguard Worker 			}
352*03ce13f7SAndroid Build Coastguard Worker 		}
353*03ce13f7SAndroid Build Coastguard Worker 		break;
354*03ce13f7SAndroid Build Coastguard Worker 
355*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformShuffleUp:
356*03ce13f7SAndroid Build Coastguard Worker 		{
357*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, insn.word(4));
358*03ce13f7SAndroid Build Coastguard Worker 			Operand delta(shader, *this, insn.word(5));
359*03ce13f7SAndroid Build Coastguard Worker 			auto d0 = CmpEQ(SIMD::Int(0), delta.Int(0));
360*03ce13f7SAndroid Build Coastguard Worker 			auto d1 = CmpEQ(SIMD::Int(1), delta.Int(0));
361*03ce13f7SAndroid Build Coastguard Worker 			auto d2 = CmpEQ(SIMD::Int(2), delta.Int(0));
362*03ce13f7SAndroid Build Coastguard Worker 			auto d3 = CmpEQ(SIMD::Int(3), delta.Int(0));
363*03ce13f7SAndroid Build Coastguard Worker 			for(auto i = 0u; i < type.componentCount; i++)
364*03ce13f7SAndroid Build Coastguard Worker 			{
365*03ce13f7SAndroid Build Coastguard Worker 				SIMD::Int v = value.Int(i);
366*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, (d0 & v.xyzw) | (d1 & v.xxyz) | (d2 & v.xxxy) | (d3 & v.xxxx));
367*03ce13f7SAndroid Build Coastguard Worker 			}
368*03ce13f7SAndroid Build Coastguard Worker 		}
369*03ce13f7SAndroid Build Coastguard Worker 		break;
370*03ce13f7SAndroid Build Coastguard Worker 
371*03ce13f7SAndroid Build Coastguard Worker 	case spv::OpGroupNonUniformShuffleDown:
372*03ce13f7SAndroid Build Coastguard Worker 		{
373*03ce13f7SAndroid Build Coastguard Worker 			Operand value(shader, *this, insn.word(4));
374*03ce13f7SAndroid Build Coastguard Worker 			Operand delta(shader, *this, insn.word(5));
375*03ce13f7SAndroid Build Coastguard Worker 			auto d0 = CmpEQ(SIMD::Int(0), delta.Int(0));
376*03ce13f7SAndroid Build Coastguard Worker 			auto d1 = CmpEQ(SIMD::Int(1), delta.Int(0));
377*03ce13f7SAndroid Build Coastguard Worker 			auto d2 = CmpEQ(SIMD::Int(2), delta.Int(0));
378*03ce13f7SAndroid Build Coastguard Worker 			auto d3 = CmpEQ(SIMD::Int(3), delta.Int(0));
379*03ce13f7SAndroid Build Coastguard Worker 			for(auto i = 0u; i < type.componentCount; i++)
380*03ce13f7SAndroid Build Coastguard Worker 			{
381*03ce13f7SAndroid Build Coastguard Worker 				SIMD::Int v = value.Int(i);
382*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, (d0 & v.xyzw) | (d1 & v.yzww) | (d2 & v.zwww) | (d3 & v.wwww));
383*03ce13f7SAndroid Build Coastguard Worker 			}
384*03ce13f7SAndroid Build Coastguard Worker 		}
385*03ce13f7SAndroid Build Coastguard Worker 		break;
386*03ce13f7SAndroid Build Coastguard Worker 
387*03ce13f7SAndroid Build Coastguard Worker 	// The remaining instructions are GroupNonUniformArithmetic operations
388*03ce13f7SAndroid Build Coastguard Worker 	default:
389*03ce13f7SAndroid Build Coastguard Worker 		auto &type = shader.getType(Type::ID(insn.word(1)));
390*03ce13f7SAndroid Build Coastguard Worker 		auto operation = static_cast<spv::GroupOperation>(insn.word(4));
391*03ce13f7SAndroid Build Coastguard Worker 		Operand value(shader, *this, insn.word(5));
392*03ce13f7SAndroid Build Coastguard Worker 		auto mask = As<SIMD::UInt>(activeLaneMask());  // Considers helper invocations active. See b/151137030
393*03ce13f7SAndroid Build Coastguard Worker 
394*03ce13f7SAndroid Build Coastguard Worker 		for(uint32_t i = 0; i < type.componentCount; i++)
395*03ce13f7SAndroid Build Coastguard Worker 		{
396*03ce13f7SAndroid Build Coastguard Worker 			switch(insn.opcode())
397*03ce13f7SAndroid Build Coastguard Worker 			{
398*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformIAdd:
399*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::Int>(
400*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, 0,
401*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return a + b; }));
402*03ce13f7SAndroid Build Coastguard Worker 				break;
403*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformFAdd:
404*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::Float>(
405*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, 0.0f,
406*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return a + b; }));
407*03ce13f7SAndroid Build Coastguard Worker 				break;
408*03ce13f7SAndroid Build Coastguard Worker 
409*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformIMul:
410*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::Int>(
411*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, 1,
412*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return a * b; }));
413*03ce13f7SAndroid Build Coastguard Worker 				break;
414*03ce13f7SAndroid Build Coastguard Worker 
415*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformFMul:
416*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::Float>(
417*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, 1.0f,
418*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return a * b; }));
419*03ce13f7SAndroid Build Coastguard Worker 				break;
420*03ce13f7SAndroid Build Coastguard Worker 
421*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformBitwiseAnd:
422*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::UInt>(
423*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, ~0u,
424*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return a & b; }));
425*03ce13f7SAndroid Build Coastguard Worker 				break;
426*03ce13f7SAndroid Build Coastguard Worker 
427*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformBitwiseOr:
428*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::UInt>(
429*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, 0,
430*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return a | b; }));
431*03ce13f7SAndroid Build Coastguard Worker 				break;
432*03ce13f7SAndroid Build Coastguard Worker 
433*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformBitwiseXor:
434*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::UInt>(
435*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, 0,
436*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return a ^ b; }));
437*03ce13f7SAndroid Build Coastguard Worker 				break;
438*03ce13f7SAndroid Build Coastguard Worker 
439*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformSMin:
440*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::Int>(
441*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, INT32_MAX,
442*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return Min(a, b); }));
443*03ce13f7SAndroid Build Coastguard Worker 				break;
444*03ce13f7SAndroid Build Coastguard Worker 
445*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformUMin:
446*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::UInt>(
447*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, ~0u,
448*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return Min(a, b); }));
449*03ce13f7SAndroid Build Coastguard Worker 				break;
450*03ce13f7SAndroid Build Coastguard Worker 
451*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformFMin:
452*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::Float>(
453*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, SIMD::Float::infinity(),
454*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return NMin(a, b); }));
455*03ce13f7SAndroid Build Coastguard Worker 				break;
456*03ce13f7SAndroid Build Coastguard Worker 
457*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformSMax:
458*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::Int>(
459*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, INT32_MIN,
460*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return Max(a, b); }));
461*03ce13f7SAndroid Build Coastguard Worker 				break;
462*03ce13f7SAndroid Build Coastguard Worker 
463*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformUMax:
464*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::UInt>(
465*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, 0,
466*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return Max(a, b); }));
467*03ce13f7SAndroid Build Coastguard Worker 				break;
468*03ce13f7SAndroid Build Coastguard Worker 
469*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformFMax:
470*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::Float>(
471*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, -SIMD::Float::infinity(),
472*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) { return NMax(a, b); }));
473*03ce13f7SAndroid Build Coastguard Worker 				break;
474*03ce13f7SAndroid Build Coastguard Worker 
475*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformLogicalAnd:
476*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::UInt>(
477*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, ~0u,
478*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) {
479*03ce13f7SAndroid Build Coastguard Worker 					                SIMD::UInt zero = SIMD::UInt(0);
480*03ce13f7SAndroid Build Coastguard Worker 					                return CmpNEQ(a, zero) & CmpNEQ(b, zero);
481*03ce13f7SAndroid Build Coastguard Worker 				                }));
482*03ce13f7SAndroid Build Coastguard Worker 				break;
483*03ce13f7SAndroid Build Coastguard Worker 
484*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformLogicalOr:
485*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::UInt>(
486*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, 0,
487*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) {
488*03ce13f7SAndroid Build Coastguard Worker 					                SIMD::UInt zero = SIMD::UInt(0);
489*03ce13f7SAndroid Build Coastguard Worker 					                return CmpNEQ(a, zero) | CmpNEQ(b, zero);
490*03ce13f7SAndroid Build Coastguard Worker 				                }));
491*03ce13f7SAndroid Build Coastguard Worker 				break;
492*03ce13f7SAndroid Build Coastguard Worker 
493*03ce13f7SAndroid Build Coastguard Worker 			case spv::OpGroupNonUniformLogicalXor:
494*03ce13f7SAndroid Build Coastguard Worker 				dst.move(i, BinaryOperation<SIMD::UInt>(
495*03ce13f7SAndroid Build Coastguard Worker 				                operation, value.UInt(i), mask, 0,
496*03ce13f7SAndroid Build Coastguard Worker 				                [](auto a, auto b) {
497*03ce13f7SAndroid Build Coastguard Worker 					                SIMD::UInt zero = SIMD::UInt(0);
498*03ce13f7SAndroid Build Coastguard Worker 					                return CmpNEQ(a, zero) ^ CmpNEQ(b, zero);
499*03ce13f7SAndroid Build Coastguard Worker 				                }));
500*03ce13f7SAndroid Build Coastguard Worker 				break;
501*03ce13f7SAndroid Build Coastguard Worker 
502*03ce13f7SAndroid Build Coastguard Worker 			default:
503*03ce13f7SAndroid Build Coastguard Worker 				UNSUPPORTED("EmitGroupNonUniform op: %s", shader.OpcodeName(type.opcode()));
504*03ce13f7SAndroid Build Coastguard Worker 			}
505*03ce13f7SAndroid Build Coastguard Worker 		}
506*03ce13f7SAndroid Build Coastguard Worker 		break;
507*03ce13f7SAndroid Build Coastguard Worker 	}
508*03ce13f7SAndroid Build Coastguard Worker }
509*03ce13f7SAndroid Build Coastguard Worker 
510*03ce13f7SAndroid Build Coastguard Worker }  // namespace sw
511