• 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 "ShaderCore.hpp"
18 
19 #include "Vulkan/VkPipelineLayout.hpp"
20 
21 #include <spirv/unified1/spirv.hpp>
22 
23 namespace sw {
24 
EmitLoad(InsnIterator insn,EmitState * state) const25 SpirvShader::EmitResult SpirvShader::EmitLoad(InsnIterator insn, EmitState *state) const
26 {
27 	bool atomic = (insn.opcode() == spv::OpAtomicLoad);
28 	Object::ID resultId = insn.word(2);
29 	Object::ID pointerId = insn.word(3);
30 	auto &result = getObject(resultId);
31 	auto &resultTy = getType(result.type);
32 	auto &pointer = getObject(pointerId);
33 	auto &pointerTy = getType(pointer.type);
34 	std::memory_order memoryOrder = std::memory_order_relaxed;
35 
36 	ASSERT(getType(pointer.type).element == result.type);
37 	ASSERT(Type::ID(insn.word(1)) == result.type);
38 	ASSERT(!atomic || getType(getType(pointer.type).element).opcode() == spv::OpTypeInt);  // Vulkan 1.1: "Atomic instructions must declare a scalar 32-bit integer type, for the value pointed to by Pointer."
39 
40 	if(pointerTy.storageClass == spv::StorageClassUniformConstant)
41 	{
42 		// Just propagate the pointer.
43 		auto &ptr = state->getPointer(pointerId);
44 		state->createPointer(resultId, ptr);
45 		return EmitResult::Continue;
46 	}
47 
48 	if(atomic)
49 	{
50 		Object::ID semanticsId = insn.word(5);
51 		auto memorySemantics = static_cast<spv::MemorySemanticsMask>(getObject(semanticsId).constantValue[0]);
52 		memoryOrder = MemoryOrder(memorySemantics);
53 	}
54 
55 	auto ptr = GetPointerToData(pointerId, 0, state);
56 	bool interleavedByLane = IsStorageInterleavedByLane(pointerTy.storageClass);
57 	auto &dst = state->createIntermediate(resultId, resultTy.sizeInComponents);
58 	auto robustness = state->getOutOfBoundsBehavior(pointerTy.storageClass);
59 
60 	VisitMemoryObject(pointerId, [&](const MemoryElement &el) {
61 		auto p = ptr + el.offset;
62 		if(interleavedByLane) { p = InterleaveByLane(p); }  // TODO: Interleave once, then add offset?
63 		dst.move(el.index, p.Load<SIMD::Float>(robustness, state->activeLaneMask(), atomic, memoryOrder));
64 	});
65 
66 	return EmitResult::Continue;
67 }
68 
EmitStore(InsnIterator insn,EmitState * state) const69 SpirvShader::EmitResult SpirvShader::EmitStore(InsnIterator insn, EmitState *state) const
70 {
71 	bool atomic = (insn.opcode() == spv::OpAtomicStore);
72 	Object::ID pointerId = insn.word(1);
73 	Object::ID objectId = insn.word(atomic ? 4 : 2);
74 	auto &object = getObject(objectId);
75 	auto &pointer = getObject(pointerId);
76 	auto &pointerTy = getType(pointer.type);
77 	auto &elementTy = getType(pointerTy.element);
78 	std::memory_order memoryOrder = std::memory_order_relaxed;
79 
80 	if(atomic)
81 	{
82 		Object::ID semanticsId = insn.word(3);
83 		auto memorySemantics = static_cast<spv::MemorySemanticsMask>(getObject(semanticsId).constantValue[0]);
84 		memoryOrder = MemoryOrder(memorySemantics);
85 	}
86 
87 	ASSERT(!atomic || elementTy.opcode() == spv::OpTypeInt);  // Vulkan 1.1: "Atomic instructions must declare a scalar 32-bit integer type, for the value pointed to by Pointer."
88 
89 	auto ptr = GetPointerToData(pointerId, 0, state);
90 	bool interleavedByLane = IsStorageInterleavedByLane(pointerTy.storageClass);
91 	auto robustness = state->getOutOfBoundsBehavior(pointerTy.storageClass);
92 
93 	SIMD::Int mask = state->activeLaneMask();
94 	if(!StoresInHelperInvocation(pointerTy.storageClass))
95 	{
96 		mask = mask & state->storesAndAtomicsMask();
97 	}
98 
99 	if(object.kind == Object::Kind::Constant)
100 	{
101 		// Constant source data.
102 		const uint32_t *src = object.constantValue.get();
103 		VisitMemoryObject(pointerId, [&](const MemoryElement &el) {
104 			auto p = ptr + el.offset;
105 			if(interleavedByLane) { p = InterleaveByLane(p); }
106 			p.Store(SIMD::Int(src[el.index]), robustness, mask, atomic, memoryOrder);
107 		});
108 	}
109 	else
110 	{
111 		// Intermediate source data.
112 		auto &src = state->getIntermediate(objectId);
113 		VisitMemoryObject(pointerId, [&](const MemoryElement &el) {
114 			auto p = ptr + el.offset;
115 			if(interleavedByLane) { p = InterleaveByLane(p); }
116 			p.Store(src.Float(el.index), robustness, mask, atomic, memoryOrder);
117 		});
118 	}
119 
120 	return EmitResult::Continue;
121 }
122 
EmitVariable(InsnIterator insn,EmitState * state) const123 SpirvShader::EmitResult SpirvShader::EmitVariable(InsnIterator insn, EmitState *state) const
124 {
125 	auto routine = state->routine;
126 	Object::ID resultId = insn.word(2);
127 	auto &object = getObject(resultId);
128 	auto &objectTy = getType(object.type);
129 
130 	switch(objectTy.storageClass)
131 	{
132 		case spv::StorageClassOutput:
133 		case spv::StorageClassPrivate:
134 		case spv::StorageClassFunction:
135 		{
136 			ASSERT(objectTy.opcode() == spv::OpTypePointer);
137 			auto base = &routine->getVariable(resultId)[0];
138 			auto elementTy = getType(objectTy.element);
139 			auto size = elementTy.sizeInComponents * static_cast<uint32_t>(sizeof(float)) * SIMD::Width;
140 			state->createPointer(resultId, SIMD::Pointer(base, size));
141 			break;
142 		}
143 		case spv::StorageClassWorkgroup:
144 		{
145 			ASSERT(objectTy.opcode() == spv::OpTypePointer);
146 			auto base = &routine->workgroupMemory[0];
147 			auto size = workgroupMemory.size();
148 			state->createPointer(resultId, SIMD::Pointer(base, size, workgroupMemory.offsetOf(resultId)));
149 			break;
150 		}
151 		case spv::StorageClassInput:
152 		{
153 			if(object.kind == Object::Kind::InterfaceVariable)
154 			{
155 				auto &dst = routine->getVariable(resultId);
156 				int offset = 0;
157 				VisitInterface(resultId,
158 				               [&](Decorations const &d, AttribType type) {
159 					               auto scalarSlot = d.Location << 2 | d.Component;
160 					               dst[offset++] = routine->inputs[scalarSlot];
161 				               });
162 			}
163 			ASSERT(objectTy.opcode() == spv::OpTypePointer);
164 			auto base = &routine->getVariable(resultId)[0];
165 			auto elementTy = getType(objectTy.element);
166 			auto size = elementTy.sizeInComponents * static_cast<uint32_t>(sizeof(float)) * SIMD::Width;
167 			state->createPointer(resultId, SIMD::Pointer(base, size));
168 			break;
169 		}
170 		case spv::StorageClassUniformConstant:
171 		{
172 			const auto &d = descriptorDecorations.at(resultId);
173 			ASSERT(d.DescriptorSet >= 0);
174 			ASSERT(d.Binding >= 0);
175 
176 			uint32_t arrayIndex = 0;  // TODO(b/129523279)
177 			auto setLayout = routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
178 			if(setLayout->hasBinding(d.Binding))
179 			{
180 				uint32_t bindingOffset = static_cast<uint32_t>(setLayout->getBindingOffset(d.Binding, arrayIndex));
181 				Pointer<Byte> set = routine->descriptorSets[d.DescriptorSet];  // DescriptorSet*
182 				Pointer<Byte> binding = Pointer<Byte>(set + bindingOffset);    // vk::SampledImageDescriptor*
183 				auto size = 0;                                                 // Not required as this pointer is not directly used by SIMD::Read or SIMD::Write.
184 				state->createPointer(resultId, SIMD::Pointer(binding, size));
185 			}
186 			else
187 			{
188 				// TODO: Error if the variable with the non-existant binding is
189 				// used? Or perhaps strip these unused variable declarations as
190 				// a preprocess on the SPIR-V?
191 			}
192 			break;
193 		}
194 		case spv::StorageClassUniform:
195 		case spv::StorageClassStorageBuffer:
196 		{
197 			const auto &d = descriptorDecorations.at(resultId);
198 			ASSERT(d.DescriptorSet >= 0);
199 			auto size = 0;  // Not required as this pointer is not directly used by SIMD::Read or SIMD::Write.
200 			// Note: the module may contain descriptor set references that are not suitable for this implementation -- using a set index higher than the number
201 			// of descriptor set binding points we support. As long as the selected entrypoint doesn't actually touch the out of range binding points, this
202 			// is valid. In this case make the value nullptr to make it easier to diagnose an attempt to dereference it.
203 			if(d.DescriptorSet < vk::MAX_BOUND_DESCRIPTOR_SETS)
204 			{
205 				state->createPointer(resultId, SIMD::Pointer(routine->descriptorSets[d.DescriptorSet], size));
206 			}
207 			else
208 			{
209 				state->createPointer(resultId, SIMD::Pointer(nullptr, 0));
210 			}
211 			break;
212 		}
213 		case spv::StorageClassPushConstant:
214 		{
215 			state->createPointer(resultId, SIMD::Pointer(routine->pushConstants, vk::MAX_PUSH_CONSTANT_SIZE));
216 			break;
217 		}
218 		default:
219 			UNREACHABLE("Storage class %d", objectTy.storageClass);
220 			break;
221 	}
222 
223 	if(insn.wordCount() > 4)
224 	{
225 		Object::ID initializerId = insn.word(4);
226 		if(getObject(initializerId).kind != Object::Kind::Constant)
227 		{
228 			UNIMPLEMENTED("b/148241854: Non-constant initializers not yet implemented");  // FIXME(b/148241854)
229 		}
230 
231 		switch(objectTy.storageClass)
232 		{
233 			case spv::StorageClassOutput:
234 			case spv::StorageClassPrivate:
235 			case spv::StorageClassFunction:
236 			{
237 				bool interleavedByLane = IsStorageInterleavedByLane(objectTy.storageClass);
238 				auto ptr = GetPointerToData(resultId, 0, state);
239 				GenericValue initialValue(this, state, initializerId);
240 				VisitMemoryObject(resultId, [&](const MemoryElement &el) {
241 					auto p = ptr + el.offset;
242 					if(interleavedByLane) { p = InterleaveByLane(p); }
243 					auto robustness = OutOfBoundsBehavior::UndefinedBehavior;  // Local variables are always within bounds.
244 					p.Store(initialValue.Float(el.index), robustness, state->activeLaneMask());
245 				});
246 				break;
247 			}
248 			default:
249 				ASSERT_MSG(initializerId == 0, "Vulkan does not permit variables of storage class %d to have initializers", int(objectTy.storageClass));
250 		}
251 	}
252 
253 	return EmitResult::Continue;
254 }
255 
EmitCopyMemory(InsnIterator insn,EmitState * state) const256 SpirvShader::EmitResult SpirvShader::EmitCopyMemory(InsnIterator insn, EmitState *state) const
257 {
258 	Object::ID dstPtrId = insn.word(1);
259 	Object::ID srcPtrId = insn.word(2);
260 	auto &dstPtrTy = getType(getObject(dstPtrId).type);
261 	auto &srcPtrTy = getType(getObject(srcPtrId).type);
262 	ASSERT(dstPtrTy.element == srcPtrTy.element);
263 
264 	bool dstInterleavedByLane = IsStorageInterleavedByLane(dstPtrTy.storageClass);
265 	bool srcInterleavedByLane = IsStorageInterleavedByLane(srcPtrTy.storageClass);
266 	auto dstPtr = GetPointerToData(dstPtrId, 0, state);
267 	auto srcPtr = GetPointerToData(srcPtrId, 0, state);
268 
269 	std::unordered_map<uint32_t, uint32_t> srcOffsets;
270 
271 	VisitMemoryObject(srcPtrId, [&](const MemoryElement &el) { srcOffsets[el.index] = el.offset; });
272 
273 	VisitMemoryObject(dstPtrId, [&](const MemoryElement &el) {
274 		auto it = srcOffsets.find(el.index);
275 		ASSERT(it != srcOffsets.end());
276 		auto srcOffset = it->second;
277 		auto dstOffset = el.offset;
278 
279 		auto dst = dstPtr + dstOffset;
280 		auto src = srcPtr + srcOffset;
281 		if(dstInterleavedByLane) { dst = InterleaveByLane(dst); }
282 		if(srcInterleavedByLane) { src = InterleaveByLane(src); }
283 
284 		// TODO(b/131224163): Optimize based on src/dst storage classes.
285 		auto robustness = OutOfBoundsBehavior::RobustBufferAccess;
286 
287 		auto value = src.Load<SIMD::Float>(robustness, state->activeLaneMask());
288 		dst.Store(value, robustness, state->activeLaneMask());
289 	});
290 	return EmitResult::Continue;
291 }
292 
EmitMemoryBarrier(InsnIterator insn,EmitState * state) const293 SpirvShader::EmitResult SpirvShader::EmitMemoryBarrier(InsnIterator insn, EmitState *state) const
294 {
295 	auto semantics = spv::MemorySemanticsMask(GetConstScalarInt(insn.word(2)));
296 	// TODO: We probably want to consider the memory scope here. For now,
297 	// just always emit the full fence.
298 	Fence(semantics);
299 	return EmitResult::Continue;
300 }
301 
VisitMemoryObjectInner(sw::SpirvShader::Type::ID id,sw::SpirvShader::Decorations d,uint32_t & index,uint32_t offset,const MemoryVisitor & f) const302 void SpirvShader::VisitMemoryObjectInner(sw::SpirvShader::Type::ID id, sw::SpirvShader::Decorations d, uint32_t &index, uint32_t offset, const MemoryVisitor &f) const
303 {
304 	ApplyDecorationsForId(&d, id);
305 	auto const &type = getType(id);
306 
307 	if(d.HasOffset)
308 	{
309 		offset += d.Offset;
310 		d.HasOffset = false;
311 	}
312 
313 	switch(type.opcode())
314 	{
315 		case spv::OpTypePointer:
316 			VisitMemoryObjectInner(type.definition.word(3), d, index, offset, f);
317 			break;
318 		case spv::OpTypeInt:
319 		case spv::OpTypeFloat:
320 		case spv::OpTypeRuntimeArray:
321 			f(MemoryElement{ index++, offset, type });
322 			break;
323 		case spv::OpTypeVector:
324 		{
325 			auto elemStride = (d.InsideMatrix && d.HasRowMajor && d.RowMajor) ? d.MatrixStride : static_cast<int32_t>(sizeof(float));
326 			for(auto i = 0u; i < type.definition.word(3); i++)
327 			{
328 				VisitMemoryObjectInner(type.definition.word(2), d, index, offset + elemStride * i, f);
329 			}
330 			break;
331 		}
332 		case spv::OpTypeMatrix:
333 		{
334 			auto columnStride = (d.HasRowMajor && d.RowMajor) ? static_cast<int32_t>(sizeof(float)) : d.MatrixStride;
335 			d.InsideMatrix = true;
336 			for(auto i = 0u; i < type.definition.word(3); i++)
337 			{
338 				ASSERT(d.HasMatrixStride);
339 				VisitMemoryObjectInner(type.definition.word(2), d, index, offset + columnStride * i, f);
340 			}
341 			break;
342 		}
343 		case spv::OpTypeStruct:
344 			for(auto i = 0u; i < type.definition.wordCount() - 2; i++)
345 			{
346 				ApplyDecorationsForIdMember(&d, id, i);
347 				VisitMemoryObjectInner(type.definition.word(i + 2), d, index, offset, f);
348 			}
349 			break;
350 		case spv::OpTypeArray:
351 		{
352 			auto arraySize = GetConstScalarInt(type.definition.word(3));
353 			for(auto i = 0u; i < arraySize; i++)
354 			{
355 				ASSERT(d.HasArrayStride);
356 				VisitMemoryObjectInner(type.definition.word(2), d, index, offset + i * d.ArrayStride, f);
357 			}
358 			break;
359 		}
360 		default:
361 			UNREACHABLE("%s", OpcodeName(type.opcode()).c_str());
362 	}
363 }
364 
VisitMemoryObject(sw::SpirvShader::Object::ID id,const MemoryVisitor & f) const365 void SpirvShader::VisitMemoryObject(sw::SpirvShader::Object::ID id, const MemoryVisitor &f) const
366 {
367 	auto typeId = getObject(id).type;
368 	auto const &type = getType(typeId);
369 	if(IsExplicitLayout(type.storageClass))
370 	{
371 		Decorations d{};
372 		ApplyDecorationsForId(&d, id);
373 		uint32_t index = 0;
374 		VisitMemoryObjectInner(typeId, d, index, 0, f);
375 	}
376 	else
377 	{
378 		// Objects without explicit layout are tightly packed.
379 		auto &elType = getType(type.element);
380 		for(auto index = 0u; index < elType.sizeInComponents; index++)
381 		{
382 			auto offset = static_cast<uint32_t>(index * sizeof(float));
383 			f({ index, offset, elType });
384 		}
385 	}
386 }
387 
GetPointerToData(Object::ID id,int arrayIndex,EmitState const * state) const388 SIMD::Pointer SpirvShader::GetPointerToData(Object::ID id, int arrayIndex, EmitState const *state) const
389 {
390 	auto routine = state->routine;
391 	auto &object = getObject(id);
392 	switch(object.kind)
393 	{
394 		case Object::Kind::Pointer:
395 		case Object::Kind::InterfaceVariable:
396 			return state->getPointer(id);
397 
398 		case Object::Kind::DescriptorSet:
399 		{
400 			const auto &d = descriptorDecorations.at(id);
401 			ASSERT(d.DescriptorSet >= 0 && d.DescriptorSet < vk::MAX_BOUND_DESCRIPTOR_SETS);
402 			ASSERT(d.Binding >= 0);
403 
404 			auto set = state->getPointer(id);
405 
406 			auto setLayout = routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
407 			ASSERT_MSG(setLayout->hasBinding(d.Binding), "Descriptor set %d does not contain binding %d", int(d.DescriptorSet), int(d.Binding));
408 			int bindingOffset = static_cast<int>(setLayout->getBindingOffset(d.Binding, arrayIndex));
409 
410 			Pointer<Byte> descriptor = set.base + bindingOffset;                                           // BufferDescriptor*
411 			Pointer<Byte> data = *Pointer<Pointer<Byte>>(descriptor + OFFSET(vk::BufferDescriptor, ptr));  // void*
412 			Int size = *Pointer<Int>(descriptor + OFFSET(vk::BufferDescriptor, sizeInBytes));
413 			if(setLayout->isBindingDynamic(d.Binding))
414 			{
415 				uint32_t dynamicBindingIndex =
416 				    routine->pipelineLayout->getDynamicOffsetBase(d.DescriptorSet) +
417 				    setLayout->getDynamicDescriptorOffset(d.Binding) +
418 				    arrayIndex;
419 				Int offset = routine->descriptorDynamicOffsets[dynamicBindingIndex];
420 				Int robustnessSize = *Pointer<Int>(descriptor + OFFSET(vk::BufferDescriptor, robustnessSize));
421 				return SIMD::Pointer(data + offset, Min(size, robustnessSize - offset));
422 			}
423 			else
424 			{
425 				return SIMD::Pointer(data, size);
426 			}
427 		}
428 
429 		default:
430 			UNREACHABLE("Invalid pointer kind %d", int(object.kind));
431 			return SIMD::Pointer(Pointer<Byte>(), 0);
432 	}
433 }
434 
MemoryOrder(spv::MemorySemanticsMask memorySemantics)435 std::memory_order SpirvShader::MemoryOrder(spv::MemorySemanticsMask memorySemantics)
436 {
437 	auto control = static_cast<uint32_t>(memorySemantics) & static_cast<uint32_t>(
438 	                                                            spv::MemorySemanticsAcquireMask |
439 	                                                            spv::MemorySemanticsReleaseMask |
440 	                                                            spv::MemorySemanticsAcquireReleaseMask |
441 	                                                            spv::MemorySemanticsSequentiallyConsistentMask);
442 	switch(control)
443 	{
444 		case spv::MemorySemanticsMaskNone: return std::memory_order_relaxed;
445 		case spv::MemorySemanticsAcquireMask: return std::memory_order_acquire;
446 		case spv::MemorySemanticsReleaseMask: return std::memory_order_release;
447 		case spv::MemorySemanticsAcquireReleaseMask: return std::memory_order_acq_rel;
448 		case spv::MemorySemanticsSequentiallyConsistentMask: return std::memory_order_acq_rel;  // Vulkan 1.1: "SequentiallyConsistent is treated as AcquireRelease"
449 		default:
450 			// "it is invalid for more than one of these four bits to be set:
451 			// Acquire, Release, AcquireRelease, or SequentiallyConsistent."
452 			UNREACHABLE("MemorySemanticsMask: %x", int(control));
453 			return std::memory_order_acq_rel;
454 	}
455 }
456 
StoresInHelperInvocation(spv::StorageClass storageClass)457 bool SpirvShader::StoresInHelperInvocation(spv::StorageClass storageClass)
458 {
459 	switch(storageClass)
460 	{
461 		case spv::StorageClassUniform:
462 		case spv::StorageClassStorageBuffer:
463 		case spv::StorageClassImage:
464 			return false;
465 		default:
466 			return true;
467 	}
468 }
469 
IsExplicitLayout(spv::StorageClass storageClass)470 bool SpirvShader::IsExplicitLayout(spv::StorageClass storageClass)
471 {
472 	switch(storageClass)
473 	{
474 		case spv::StorageClassUniform:
475 		case spv::StorageClassStorageBuffer:
476 		case spv::StorageClassPushConstant:
477 			return true;
478 		default:
479 			return false;
480 	}
481 }
482 
InterleaveByLane(sw::SIMD::Pointer p)483 sw::SIMD::Pointer SpirvShader::InterleaveByLane(sw::SIMD::Pointer p)
484 {
485 	p *= sw::SIMD::Width;
486 	p.staticOffsets[0] += 0 * sizeof(float);
487 	p.staticOffsets[1] += 1 * sizeof(float);
488 	p.staticOffsets[2] += 2 * sizeof(float);
489 	p.staticOffsets[3] += 3 * sizeof(float);
490 	return p;
491 }
492 
IsStorageInterleavedByLane(spv::StorageClass storageClass)493 bool SpirvShader::IsStorageInterleavedByLane(spv::StorageClass storageClass)
494 {
495 	switch(storageClass)
496 	{
497 		case spv::StorageClassUniform:
498 		case spv::StorageClassStorageBuffer:
499 		case spv::StorageClassPushConstant:
500 		case spv::StorageClassWorkgroup:
501 		case spv::StorageClassImage:
502 			return false;
503 		default:
504 			return true;
505 	}
506 }
507 
508 }  // namespace sw