Updated spirv-tools.

This commit is contained in:
Бранимир Караџић
2019-01-16 21:12:54 -08:00
parent 3c8b3c81ef
commit 16d7db088d
42 changed files with 2725 additions and 166 deletions

View File

@@ -1,20 +1,39 @@
Revision history for SPIRV-Tools
v2018.7-dev 2018-12-10
v2019.2-dev 2019-01-07
- Start v2019.2-dev
v2019.1 2019-01-07
- General:
- Created a new tool called spirv-reduce.
- Add cmake option to turn off SPIRV_TIMER_ENABLED (#2103)
- New optimization pass to update the memory model from GLSL450 to VulkanKHR.
- Recognize OpTypeAccelerationStructureNV as a type instruction and ray tracing storage classes.
- Fix GCC8 build.
- Add --target-env flag to spirv-opt.
- Add --webgpu-mode flag to run optimizations for webgpu.
- The output disassembled line number stead of byte offset in validation errors. (#2091)
- Optimizer
- Added the instrumentation passes for bindless validation.
- Added passes to help preserve OpLine information (#2027)
- Add basic support for EXT_fragment_invocation_density (#2100)
- Fix invalid OpPhi generated by merge-return. (#2172)
- Constant and type manager have been turned into analysies. (#2251)
Fixes:
- #2018: Don't inline functions with a return in a structured CFG contstruct.
- #2047: Fix bug in folding when volatile stores are present.
- #2053: Fix check for when folding floating pointer values is allowed.
- #2130: Don't inline recursive functions.
- #2202: Handle multiple edges between two basic blocks in SSA-rewriter.
- #2205: Don't unswitch a latch condition during loop unswitch.
- #2245: Don't fold branch in loop unswitch. Run dead branch elimination to fold them.
- #2204: Fix eliminate common uniform to place OpPhi instructions correctly.
- #2247: Fix type mismatches caused by scalar replacement.
- #2248: Fix missing OpPhi after merge return.
- #2211: After merge return, fix invalid continue target.
- #2210: Fix loop invariant code motion to not place code between merge instruction and branch.
- #2258: Handle CompositeInsert with no indices in VDCE.
- #2261: Have replace load size handle extact with no index.
- Validator
- Changed the naming convention of outputing ids with names in diagnostic messages.
- Added validation rules for UniformConstant variables in Vulkan.
@@ -32,12 +51,14 @@ v2018.7-dev 2018-12-10
- Allow Float16/Int8 for Vulkan 1.0 (#2153)
- Check binding annotations in resource variables (#2151, #2167)
- Validate OpForwardPointer (#2156)
- Validate operation for OpSpecConstantOp (#2260)
Fixes:
- #2049: Allow InstanceId for NV ray tracing
- Reduce
- Initial commit wit a few passes to reduce test cases.
- Validation is run after each reduction step.
Fixes:
v2018.6 2018-11-07
- General:

View File

@@ -11,7 +11,7 @@ vars = {
'googletest_revision': '98a0d007d7092b72eea0e501bb9ad17908a1a036',
'testing_revision': '340252637e2e7c72c0901dcbeeacfff419e19b59',
're2_revision': '6cf8ccd82dbaab2668e9b13596c68183c9ecd13f',
'spirv_headers_revision': 'd5b2e1255f706ce1f88812217e9a554f299848af',
'spirv_headers_revision': '79b6681aadcb53c27d1052e5f8a0e82a981dbf2f',
}
deps = {

View File

@@ -1 +1 @@
"v2018.7-dev", "SPIRV-Tools v2018.7-dev bc65303576278bfb67295395327dc395a6adbf98"
"v2019.2-dev", "SPIRV-Tools v2019.2-dev 3c8b3c81ef7f378717ef88916c06cb221eea890b"

View File

@@ -75,8 +75,8 @@ static const int kInstCommonOutCnt = 4;
// error.
//
// Vertex Shader Output Record Offsets
static const int kInstVertOutVertexId = kInstCommonOutCnt;
static const int kInstVertOutInstanceId = kInstCommonOutCnt + 1;
static const int kInstVertOutVertexIndex = kInstCommonOutCnt;
static const int kInstVertOutInstanceIndex = kInstCommonOutCnt + 1;
// Frag Shader Output Record Offsets
static const int kInstFragOutFragCoordX = kInstCommonOutCnt;

View File

@@ -58,33 +58,38 @@ if "%KOKORO_GITHUB_COMMIT%." == "." (
set BUILD_SHA=%KOKORO_GITHUB_COMMIT%
)
set CMAKE_FLAGS=-GNinja -DSPIRV_BUILD_COMPRESSION=ON -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_INSTALL_PREFIX=install -DRE2_BUILD_TESTING=OFF -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe
:: Skip building tests for VS2013
if %VS_VERSION% == 2013 (
cmake -GNinja -DSPIRV_SKIP_TESTS=ON -DSPIRV_BUILD_COMPRESSION=ON -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_INSTALL_PREFIX=install -DRE2_BUILD_TESTING=OFF -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe ..
) else (
cmake -GNinja -DSPIRV_BUILD_COMPRESSION=ON -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_INSTALL_PREFIX=install -DRE2_BUILD_TESTING=OFF -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe ..
set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_SKIP_TESTS=ON
)
if %ERRORLEVEL% GEQ 1 exit /b %ERRORLEVEL%
cmake %CMAKE_FLAGS% ..
if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL%
echo "Build everything... %DATE% %TIME%"
ninja
if %ERRORLEVEL% GEQ 1 exit /b %ERRORLEVEL%
if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL%
echo "Build Completed %DATE% %TIME%"
:: This lets us use !ERRORLEVEL! inside an IF ... () and get the actual error at that point.
setlocal ENABLEDELAYEDEXPANSION
:: ################################################
:: Run the tests (We no longer run tests on VS2013)
:: ################################################
if NOT %VS_VERSION% == 2013 (
echo "Running Tests... %DATE% %TIME%"
echo "Running Tests... %DATE% %TIME%"
if %VS_VERSION% NEQ 2013 (
ctest -C %BUILD_TYPE% --output-on-failure --timeout 300
if %ERRORLEVEL% GEQ 1 exit /b %ERRORLEVEL%
echo "Tests Completed %DATE% %TIME%"
if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL!
)
echo "Tests Completed %DATE% %TIME%"
:: Clean up some directories.
rm -rf %SRC%\build
rm -rf %SRC%\external
exit /b %ERRORLEVEL%
exit /b 0

View File

@@ -481,6 +481,9 @@ std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
case SpvOpTypeForwardPointer:
out = [](unsigned index) { return index == 0; };
break;
case SpvOpTypeArray:
out = [](unsigned index) { return index == 1; };
break;
default:
out = [](unsigned) { return false; };
break;

View File

@@ -15,7 +15,7 @@
#ifndef SOURCE_OPT_CONSTANTS_H_
#define SOURCE_OPT_CONSTANTS_H_
#include <inttypes.h>
#include <cinttypes>
#include <map>
#include <memory>
#include <unordered_map>

View File

@@ -115,8 +115,10 @@ uint32_t InstructionFolder::BinaryOperate(SpvOp opcode, uint32_t a,
// Shifting
case SpvOp::SpvOpShiftRightLogical:
if (b > 32) {
// This is undefined behaviour. Choose 0 for consistency.
if (b >= 32) {
// This is undefined behaviour when |b| > 32. Choose 0 for consistency.
// When |b| == 32, doing the shift in C++ in undefined, but the result
// will be 0, so just return that value.
return 0;
}
return a >> b;
@@ -125,10 +127,21 @@ uint32_t InstructionFolder::BinaryOperate(SpvOp opcode, uint32_t a,
// This is undefined behaviour. Choose 0 for consistency.
return 0;
}
if (b == 32) {
// Doing the shift in C++ is undefined, but the result is defined in the
// spir-v spec. Find that value another way.
if (static_cast<int32_t>(a) >= 0) {
return 0;
} else {
return static_cast<uint32_t>(-1);
}
}
return (static_cast<int32_t>(a)) >> b;
case SpvOp::SpvOpShiftLeftLogical:
if (b > 32) {
// This is undefined behaviour. Choose 0 for consistency.
if (b >= 32) {
// This is undefined behaviour when |b| > 32. Choose 0 for consistency.
// When |b| == 32, doing the shift in C++ in undefined, but the result
// will be 0, so just return that value.
return 0;
}
return a << b;
@@ -307,7 +320,8 @@ bool InstructionFolder::FoldBinaryIntegerOpToConstant(
if (constants[1] != nullptr) {
// When shifting by a value larger than the size of the result, the
// result is undefined. We are setting the undefined behaviour to a
// result of 0.
// result of 0. If the shift amount is the same as the size of the
// result, then the result is defined, and it 0.
uint32_t shift_amount = constants[1]->GetU32BitValue();
if (shift_amount >= 32) {
*result = 0;

View File

@@ -168,10 +168,10 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
switch (stage_idx) {
case SpvExecutionModelVertex: {
// Load and store VertexId and InstanceId
GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInVertexId),
kInstVertOutVertexId, base_offset_id, builder);
GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInInstanceId),
kInstVertOutInstanceId, base_offset_id, builder);
GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInVertexIndex),
kInstVertOutVertexIndex, base_offset_id, builder);
GenBuiltinOutputCode(context()->GetBuiltinVarId(SpvBuiltInInstanceIndex),
kInstVertOutInstanceIndex, base_offset_id, builder);
} break;
case SpvExecutionModelGLCompute: {
// Load and store GlobalInvocationId. Second word is unused; store zero.
@@ -309,6 +309,8 @@ uint32_t InstrumentPass::GetOutputBufferId() {
analysis::RuntimeArray uint_rarr_ty(reg_uint_ty);
analysis::Type* reg_uint_rarr_ty =
type_mgr->GetRegisteredType(&uint_rarr_ty);
uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(reg_uint_rarr_ty);
deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u);
analysis::Struct obuf_ty({reg_uint_ty, reg_uint_rarr_ty});
analysis::Type* reg_obuf_ty = type_mgr->GetRegisteredType(&obuf_ty);
uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_obuf_ty);

View File

@@ -455,6 +455,16 @@ class InstructionBuilder {
return AddInstruction(std::move(new_inst));
}
Instruction* AddStore(uint32_t ptr_id, uint32_t obj_id) {
std::vector<Operand> operands;
operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}});
operands.push_back({SPV_OPERAND_TYPE_ID, {obj_id}});
std::unique_ptr<Instruction> new_inst(
new Instruction(GetContext(), SpvOpStore, 0, 0, operands));
return AddInstruction(std::move(new_inst));
}
// Inserts the new instruction before the insertion point.
Instruction* AddInstruction(std::unique_ptr<Instruction>&& insn) {
Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));

View File

@@ -669,8 +669,8 @@ uint32_t IRContext::GetBuiltinVarId(uint32_t builtin) {
reg_type = type_mgr->GetRegisteredType(&v4float_ty);
break;
}
case SpvBuiltInVertexId:
case SpvBuiltInInstanceId:
case SpvBuiltInVertexIndex:
case SpvBuiltInInstanceIndex:
case SpvBuiltInPrimitiveId:
case SpvBuiltInInvocationId:
case SpvBuiltInGlobalInvocationId: {

View File

@@ -16,6 +16,7 @@
#include <utility>
#include "source/opt/ir_builder.h"
#include "source/opt/ir_context.h"
#include "source/util/make_unique.h"
@@ -68,6 +69,22 @@ void UpgradeMemoryModel::UpgradeInstructions() {
// instructions. Additionally, Workgroup storage class variables and function
// parameters are implicitly coherent in GLSL450.
// Upgrade modf and frexp first since they generate new stores.
for (auto& func : *get_module()) {
func.ForEachInst([this](Instruction* inst) {
if (inst->opcode() == SpvOpExtInst) {
auto ext_inst = inst->GetSingleWordInOperand(1u);
if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) {
auto import =
get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u));
if (reinterpret_cast<char*>(import->GetInOperand(0u).words.data()) ==
std::string("GLSL.std.450")) {
UpgradeExtInst(inst);
}
}
}
});
}
for (auto& func : *get_module()) {
func.ForEachInst([this](Instruction* inst) {
bool is_coherent = false;
@@ -104,11 +121,11 @@ void UpgradeMemoryModel::UpgradeInstructions() {
switch (inst->opcode()) {
case SpvOpLoad:
UpgradeFlags(inst, 1u, is_coherent, is_volatile, kAvailability,
UpgradeFlags(inst, 1u, is_coherent, is_volatile, kVisibility,
kMemory);
break;
case SpvOpStore:
UpgradeFlags(inst, 2u, is_coherent, is_volatile, kVisibility,
UpgradeFlags(inst, 2u, is_coherent, is_volatile, kAvailability,
kMemory);
break;
case SpvOpCopyMemory:
@@ -125,11 +142,11 @@ void UpgradeMemoryModel::UpgradeInstructions() {
break;
case SpvOpImageRead:
case SpvOpImageSparseRead:
UpgradeFlags(inst, 2u, is_coherent, is_volatile, kAvailability,
kImage);
UpgradeFlags(inst, 2u, is_coherent, is_volatile, kVisibility, kImage);
break;
case SpvOpImageWrite:
UpgradeFlags(inst, 3u, is_coherent, is_volatile, kVisibility, kImage);
UpgradeFlags(inst, 3u, is_coherent, is_volatile, kAvailability,
kImage);
break;
default:
break;
@@ -143,15 +160,15 @@ void UpgradeMemoryModel::UpgradeInstructions() {
}
// According to SPV_KHR_vulkan_memory_model, if both available and
// visible flags are used the first scope operand is for availability
// (reads) and the second is for visibiity (writes).
if (src_coherent) {
inst->AddOperand(
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}});
}
// (writes) and the second is for visibility (reads).
if (dst_coherent) {
inst->AddOperand(
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}});
}
if (src_coherent) {
inst->AddOperand(
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}});
}
});
}
}
@@ -581,5 +598,43 @@ bool UpgradeMemoryModel::IsDeviceScope(uint32_t scope_id) {
return false;
}
void UpgradeMemoryModel::UpgradeExtInst(Instruction* ext_inst) {
const bool is_modf = ext_inst->GetSingleWordInOperand(1u) == GLSLstd450Modf;
auto ptr_id = ext_inst->GetSingleWordInOperand(3u);
auto ptr_type_id = get_def_use_mgr()->GetDef(ptr_id)->type_id();
auto pointee_type_id =
get_def_use_mgr()->GetDef(ptr_type_id)->GetSingleWordInOperand(1u);
auto element_type_id = ext_inst->type_id();
std::vector<const analysis::Type*> element_types(2);
element_types[0] = context()->get_type_mgr()->GetType(element_type_id);
element_types[1] = context()->get_type_mgr()->GetType(pointee_type_id);
analysis::Struct struct_type(element_types);
uint32_t struct_id =
context()->get_type_mgr()->GetTypeInstruction(&struct_type);
// Change the operation
GLSLstd450 new_op = is_modf ? GLSLstd450ModfStruct : GLSLstd450FrexpStruct;
ext_inst->SetOperand(3u, {static_cast<uint32_t>(new_op)});
// Remove the pointer argument
ext_inst->RemoveOperand(5u);
// Set the type id to the new struct.
ext_inst->SetResultType(struct_id);
// The result is now a struct of the original result. The zero'th element is
// old result and should replace the old result. The one'th element needs to
// be stored via a new instruction.
auto where = ext_inst->NextNode();
InstructionBuilder builder(
context(), where,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
auto extract_0 =
builder.AddCompositeExtract(element_type_id, ext_inst->result_id(), {0});
context()->ReplaceAllUsesWith(ext_inst->result_id(), extract_0->result_id());
// The extract's input was just changed to itself, so fix that.
extract_0->SetInOperand(0u, {ext_inst->result_id()});
auto extract_1 =
builder.AddCompositeExtract(pointee_type_id, ext_inst->result_id(), {1});
builder.AddStore(ptr_id, extract_1->result_id());
}
} // namespace opt
} // namespace spvtools

View File

@@ -118,6 +118,11 @@ class UpgradeMemoryModel : public Pass {
// Returns true if |scope_id| is SpvScopeDevice.
bool IsDeviceScope(uint32_t scope_id);
// Upgrades GLSL.std.450 modf and frexp. Both instructions are replaced with
// their struct versions. New extracts and a store are added in order to
// facilitate adding memory model flags.
void UpgradeExtInst(Instruction* modf);
// Caches the result of TraceInstruction. For a given result id and set of
// indices, stores whether that combination is coherent and/or volatile.
std::unordered_map<std::pair<uint32_t, std::vector<uint32_t>>,

View File

@@ -169,6 +169,57 @@ spv_result_t ValidateForwardDecls(ValidationState_t& _) {
<< id_str.substr(0, id_str.size() - 1);
}
std::vector<std::string> CalculateNamesForEntryPoint(ValidationState_t& _,
const uint32_t id) {
auto id_descriptions = _.entry_point_descriptions(id);
auto id_names = std::vector<std::string>();
id_names.reserve((id_descriptions.size()));
for (auto description : id_descriptions) id_names.push_back(description.name);
return id_names;
}
spv_result_t ValidateEntryPointNameUnique(ValidationState_t& _,
const uint32_t id) {
auto id_names = CalculateNamesForEntryPoint(_, id);
const auto names =
std::unordered_set<std::string>(id_names.begin(), id_names.end());
if (id_names.size() != names.size()) {
std::sort(id_names.begin(), id_names.end());
for (size_t i = 0; i < id_names.size() - 1; i++) {
if (id_names[i] == id_names[i + 1]) {
return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id))
<< "Entry point name \"" << id_names[i]
<< "\" is not unique, which is not allow in WebGPU env.";
}
}
}
for (const auto other_id : _.entry_points()) {
if (other_id == id) continue;
const auto other_id_names = CalculateNamesForEntryPoint(_, other_id);
for (const auto other_id_name : other_id_names) {
if (names.find(other_id_name) != names.end()) {
return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id))
<< "Entry point name \"" << other_id_name
<< "\" is not unique, which is not allow in WebGPU env.";
}
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateEntryPointNamesUnique(ValidationState_t& _) {
for (const auto id : _.entry_points()) {
auto result = ValidateEntryPointNameUnique(_, id);
if (result != SPV_SUCCESS) return result;
}
return SPV_SUCCESS;
}
// Entry point validation. Based on 2.16.1 (Universal Validation Rules) of the
// SPIRV spec:
// * There is at least one OpEntryPoint instruction, unless the Linkage
@@ -177,7 +228,7 @@ spv_result_t ValidateForwardDecls(ValidationState_t& _) {
// OpFunctionCall instruction.
//
// Additionally enforces that entry points for Vulkan and WebGPU should not have
// recursion.
// recursion. And that entry names should be unique for WebGPU.
spv_result_t ValidateEntryPoints(ValidationState_t& _) {
_.ComputeFunctionToEntryPointMapping();
_.ComputeRecursiveEntryPoints();
@@ -206,6 +257,12 @@ spv_result_t ValidateEntryPoints(ValidationState_t& _) {
<< "Entry points may not have a call graph with cycles.";
}
}
// For WebGPU all entry point names must be unique.
if (spvIsWebGPUEnv(_.context()->target_env)) {
const auto result = ValidateEntryPointNamesUnique(_);
if (result != SPV_SUCCESS) return result;
}
}
return SPV_SUCCESS;

View File

@@ -127,6 +127,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
case SpvStorageClassAtomicCounter:
case SpvStorageClassImage:
case SpvStorageClassStorageBuffer:
case SpvStorageClassPhysicalStorageBufferEXT:
break;
default:
if (spvIsOpenCLEnv(_.context()->target_env)) {

View File

@@ -329,11 +329,19 @@ spv_result_t ValidateSpecConstantOp(ValidationState_t& _,
}
break;
case SpvOpUConvert:
if (!_.features().uconvert_spec_constant_op &&
!_.HasCapability(SpvCapabilityKernel)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "UConvert requires Kernel capability or extension "
"SPV_AMD_gpu_shader_int16";
}
break;
case SpvOpConvertFToS:
case SpvOpConvertSToF:
case SpvOpConvertFToU:
case SpvOpConvertUToF:
case SpvOpUConvert:
case SpvOpConvertPtrToU:
case SpvOpConvertUToPtr:
case SpvOpGenericCastToPtr:

View File

@@ -217,6 +217,23 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
if (!_.IsPointerType(input_type))
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Expected input to be a pointer: " << spvOpcodeString(opcode);
if (_.addressing_model() == SpvAddressingModelLogical)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Logical addressing not supported: "
<< spvOpcodeString(opcode);
if (_.addressing_model() ==
SpvAddressingModelPhysicalStorageBuffer64EXT) {
uint32_t input_storage_class = 0;
uint32_t input_data_type = 0;
_.GetPointerTypeInfo(input_type, &input_data_type,
&input_storage_class);
if (input_storage_class != SpvStorageClassPhysicalStorageBufferEXT)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Pointer storage class must be PhysicalStorageBufferEXT: "
<< spvOpcodeString(opcode);
}
break;
}
@@ -251,6 +268,23 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
if (!input_type || !_.IsIntScalarType(input_type))
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Expected int scalar as input: " << spvOpcodeString(opcode);
if (_.addressing_model() == SpvAddressingModelLogical)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Logical addressing not supported: "
<< spvOpcodeString(opcode);
if (_.addressing_model() ==
SpvAddressingModelPhysicalStorageBuffer64EXT) {
uint32_t result_storage_class = 0;
uint32_t result_data_type = 0;
_.GetPointerTypeInfo(result_type, &result_data_type,
&result_storage_class);
if (result_storage_class != SpvStorageClassPhysicalStorageBufferEXT)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Pointer storage class must be PhysicalStorageBufferEXT: "
<< spvOpcodeString(opcode);
}
break;
}

View File

@@ -218,6 +218,9 @@ uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
if (roundUp) baseAlignment = align(baseAlignment, 16u);
break;
}
case SpvOpTypePointer:
baseAlignment = vstate.pointer_size_and_alignment();
break;
default:
assert(0);
break;
@@ -254,6 +257,8 @@ uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) {
}
return max_member_alignment;
} break;
case SpvOpTypePointer:
return vstate.pointer_size_and_alignment();
default:
assert(0);
break;
@@ -331,6 +336,8 @@ uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)];
return offset + getSize(lastMember, constraint, constraints, vstate);
}
case SpvOpTypePointer:
return vstate.pointer_size_and_alignment();
default:
assert(0);
return 0;
@@ -847,7 +854,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
}
}
if (uniform || push_constant || storage_buffer) {
const bool phys_storage_buffer =
storageClass == SpvStorageClassPhysicalStorageBufferEXT;
if (uniform || push_constant || storage_buffer || phys_storage_buffer) {
const auto ptrInst = vstate.FindDef(words[1]);
assert(SpvOpTypePointer == ptrInst->opcode());
const auto id = ptrInst->words()[3];
@@ -899,9 +908,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
const bool blockDeco = SpvDecorationBlock == dec.dec_type();
const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type();
const bool blockRules = uniform && blockDeco;
const bool bufferRules = (uniform && bufferDeco) ||
(push_constant && blockDeco) ||
(storage_buffer && blockDeco);
const bool bufferRules =
(uniform && bufferDeco) || (push_constant && blockDeco) ||
((storage_buffer || phys_storage_buffer) && blockDeco);
if (blockRules || bufferRules) {
const char* deco_str = blockDeco ? "Block" : "BufferBlock";
spv_result_t recursive_status = SPV_SUCCESS;
@@ -1127,12 +1136,13 @@ spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate,
if (storage != SpvStorageClassStorageBuffer &&
storage != SpvStorageClassUniform &&
storage != SpvStorageClassPushConstant &&
storage != SpvStorageClassInput && storage != SpvStorageClassOutput) {
storage != SpvStorageClassInput && storage != SpvStorageClassOutput &&
storage != SpvStorageClassPhysicalStorageBufferEXT) {
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
<< "FPRoundingMode decoration can be applied only to the "
"Object operand of an OpStore in the StorageBuffer, "
"Uniform, PushConstant, Input, or Output Storage "
"Classes.";
"PhysicalStorageBufferEXT, Uniform, PushConstant, Input, or "
"Output Storage Classes.";
}
}
return SPV_SUCCESS;
@@ -1170,6 +1180,36 @@ spv_result_t CheckUniformDecoration(ValidationState_t& vstate,
return SPV_SUCCESS;
}
// Returns SPV_SUCCESS if validation rules are satisfied for NoSignedWrap or
// NoUnsignedWrap decorations. Otherwise emits a diagnostic and returns
// something other than SPV_SUCCESS. Assumes each decoration on a group has been
// propagated down to the group members.
spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate,
const Instruction& inst,
const Decoration& decoration) {
switch (inst.opcode()) {
case SpvOpIAdd:
case SpvOpISub:
case SpvOpIMul:
case SpvOpShiftLeftLogical:
case SpvOpSNegate:
return SPV_SUCCESS;
case SpvOpExtInst:
// TODO(dneto): Only certain extended instructions allow these
// decorations. For now allow anything.
return SPV_SUCCESS;
default:
break;
}
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
<< (decoration.dec_type() == SpvDecorationNoSignedWrap
? "NoSignedWrap"
: "NoUnsignedWrap")
<< " decoration may not be applied to "
<< spvOpcodeString(inst.opcode());
}
#define PASS_OR_BAIL_AT_LINE(X, LINE) \
{ \
spv_result_t e##LINE = (X); \
@@ -1206,6 +1246,10 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
case SpvDecorationUniform:
PASS_OR_BAIL(CheckUniformDecoration(vstate, *inst, decoration));
break;
case SpvDecorationNoSignedWrap:
case SpvDecorationNoUnsignedWrap:
PASS_OR_BAIL(CheckIntegerWrapDecoration(vstate, *inst, decoration));
break;
default:
break;
}

View File

@@ -111,6 +111,79 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _,
<< "' does not match the OpTypeFunction parameter "
"type of the same index.";
}
// Validate that PhysicalStorageBufferEXT have one of Restrict, Aliased,
// RestrictPointerEXT, or AliasedPointerEXT.
auto param_nonarray_type_id = param_type->id();
while (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypeArray) {
param_nonarray_type_id =
_.FindDef(param_nonarray_type_id)->GetOperandAs<uint32_t>(1u);
}
if (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypePointer) {
auto param_nonarray_type = _.FindDef(param_nonarray_type_id);
if (param_nonarray_type->GetOperandAs<uint32_t>(1u) ==
SpvStorageClassPhysicalStorageBufferEXT) {
// check for Aliased or Restrict
const auto& decorations = _.id_decorations(inst->id());
bool foundAliased = std::any_of(
decorations.begin(), decorations.end(), [](const Decoration& d) {
return SpvDecorationAliased == d.dec_type();
});
bool foundRestrict = std::any_of(
decorations.begin(), decorations.end(), [](const Decoration& d) {
return SpvDecorationRestrict == d.dec_type();
});
if (!foundAliased && !foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpFunctionParameter " << inst->id()
<< ": expected Aliased or Restrict for PhysicalStorageBufferEXT "
"pointer.";
}
if (foundAliased && foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpFunctionParameter " << inst->id()
<< ": can't specify both Aliased and Restrict for "
"PhysicalStorageBufferEXT pointer.";
}
} else {
const auto pointee_type_id =
param_nonarray_type->GetOperandAs<uint32_t>(2);
const auto pointee_type = _.FindDef(pointee_type_id);
if (SpvOpTypePointer == pointee_type->opcode() &&
pointee_type->GetOperandAs<uint32_t>(1u) ==
SpvStorageClassPhysicalStorageBufferEXT) {
// check for AliasedPointerEXT/RestrictPointerEXT
const auto& decorations = _.id_decorations(inst->id());
bool foundAliased = std::any_of(
decorations.begin(), decorations.end(), [](const Decoration& d) {
return SpvDecorationAliasedPointerEXT == d.dec_type();
});
bool foundRestrict = std::any_of(
decorations.begin(), decorations.end(), [](const Decoration& d) {
return SpvDecorationRestrictPointerEXT == d.dec_type();
});
if (!foundAliased && !foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpFunctionParameter " << inst->id()
<< ": expected AliasedPointerEXT or RestrictPointerEXT for "
"PhysicalStorageBufferEXT pointer.";
}
if (foundAliased && foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpFunctionParameter " << inst->id()
<< ": can't specify both AliasedPointerEXT and "
"RestrictPointerEXT for PhysicalStorageBufferEXT pointer.";
}
}
}
}
return SPV_SUCCESS;
}

View File

@@ -154,7 +154,8 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
const SpvOp type_opcode = type_inst->opcode();
switch (type_opcode) {
case SpvOpTypePointer: {
if (!_.features().variable_pointers &&
if (_.addressing_model() == SpvAddressingModelLogical &&
!_.features().variable_pointers &&
!_.features().variable_pointers_storage_buffer)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Using pointers with OpSelect requires capability "

View File

@@ -277,7 +277,20 @@ uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask) {
}
spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
uint32_t mask) {
uint32_t index) {
SpvStorageClass dst_sc, src_sc;
std::tie(dst_sc, src_sc) = GetStorageClass(_, inst);
if (inst->operands().size() <= index) {
if (src_sc == SpvStorageClassPhysicalStorageBufferEXT ||
dst_sc == SpvStorageClassPhysicalStorageBufferEXT) {
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Memory accesses with "
"PhysicalStorageBufferEXT "
"must use Aligned.";
}
return SPV_SUCCESS;
}
uint32_t mask = inst->GetOperandAs<uint32_t>(index);
if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) {
if (inst->opcode() == SpvOpLoad) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -314,13 +327,12 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
}
if (mask & SpvMemoryAccessNonPrivatePointerKHRMask) {
SpvStorageClass dst_sc, src_sc;
std::tie(dst_sc, src_sc) = GetStorageClass(_, inst);
if (dst_sc != SpvStorageClassUniform &&
dst_sc != SpvStorageClassWorkgroup &&
dst_sc != SpvStorageClassCrossWorkgroup &&
dst_sc != SpvStorageClassGeneric && dst_sc != SpvStorageClassImage &&
dst_sc != SpvStorageClassStorageBuffer) {
dst_sc != SpvStorageClassStorageBuffer &&
dst_sc != SpvStorageClassPhysicalStorageBufferEXT) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "NonPrivatePointerKHR requires a pointer in Uniform, "
"Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
@@ -330,7 +342,8 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
src_sc != SpvStorageClassWorkgroup &&
src_sc != SpvStorageClassCrossWorkgroup &&
src_sc != SpvStorageClassGeneric && src_sc != SpvStorageClassImage &&
src_sc != SpvStorageClassStorageBuffer) {
src_sc != SpvStorageClassStorageBuffer &&
src_sc != SpvStorageClassPhysicalStorageBufferEXT) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "NonPrivatePointerKHR requires a pointer in Uniform, "
"Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
@@ -338,6 +351,15 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
}
}
if (!(mask & SpvMemoryAccessAlignedMask)) {
if (src_sc == SpvStorageClassPhysicalStorageBufferEXT ||
dst_sc == SpvStorageClassPhysicalStorageBufferEXT) {
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Memory accesses with "
"PhysicalStorageBufferEXT "
"must use Aligned.";
}
}
return SPV_SUCCESS;
}
@@ -414,7 +436,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
// Variable pointer related restrictions.
auto pointee = _.FindDef(result_type->word(3));
const auto pointee = _.FindDef(result_type->word(3));
if (_.addressing_model() == SpvAddressingModelLogical &&
!_.options()->relax_logical_pointer) {
// VariablePointersStorageBuffer is implied by VariablePointers.
@@ -507,6 +529,125 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
}
// WebGPU: All variables with storage class Output, Private, or Function MUST
// have an initializer.
if (spvIsWebGPUEnv(_.context()->target_env) && inst->operands().size() <= 3 &&
(storage_class == SpvStorageClassOutput ||
storage_class == SpvStorageClassPrivate ||
storage_class == SpvStorageClassFunction)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpVariable, <id> '" << _.getIdName(inst->id())
<< "', must have an initializer.\n"
<< "From WebGPU execution environment spec:\n"
<< "All variables in the following storage classes must have an "
<< "initializer: Output, Private, or Function";
}
if (storage_class == SpvStorageClassPhysicalStorageBufferEXT) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "PhysicalStorageBufferEXT must not be used with OpVariable.";
}
auto pointee_base = pointee;
while (pointee_base->opcode() == SpvOpTypeArray) {
pointee_base = _.FindDef(pointee_base->GetOperandAs<uint32_t>(1u));
}
if (pointee_base->opcode() == SpvOpTypePointer) {
if (pointee_base->GetOperandAs<uint32_t>(1u) ==
SpvStorageClassPhysicalStorageBufferEXT) {
// check for AliasedPointerEXT/RestrictPointerEXT
bool foundAliased =
_.HasDecoration(inst->id(), SpvDecorationAliasedPointerEXT);
bool foundRestrict =
_.HasDecoration(inst->id(), SpvDecorationRestrictPointerEXT);
if (!foundAliased && !foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpVariable " << inst->id()
<< ": expected AliasedPointerEXT or RestrictPointerEXT for "
"PhysicalStorageBufferEXT pointer.";
}
if (foundAliased && foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpVariable " << inst->id()
<< ": can't specify both AliasedPointerEXT and "
"RestrictPointerEXT for PhysicalStorageBufferEXT pointer.";
}
}
}
// Vulkan specific validation rules for OpTypeRuntimeArray
if (spvIsVulkanEnv(_.context()->target_env)) {
const auto type_index = 2;
const auto value_id = result_type->GetOperandAs<uint32_t>(type_index);
auto value_type = _.FindDef(value_id);
// OpTypeRuntimeArray should only ever be in a container like OpTypeStruct,
// so should never appear as a bare variable.
// Unless the module has the RuntimeDescriptorArrayEXT capability.
if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) {
if (!_.HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpVariable, <id> '" << _.getIdName(inst->id())
<< "', is attempting to create memory for an illegal type, "
"OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only "
"appear as the final member of an OpTypeStruct, thus cannot "
"be instantiated via OpVariable";
} else {
// A bare variable OpTypeRuntimeArray is allowed in this context, but
// still need to check the storage class.
if (storage_class != SpvStorageClassStorageBuffer &&
storage_class != SpvStorageClassUniform &&
storage_class != SpvStorageClassUniformConstant) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "For Vulkan with RuntimeDescriptorArrayEXT, a variable "
"containing OpTypeRuntimeArray must have storage class of "
"StorageBuffer, Uniform, or UniformConstant.";
}
}
}
// If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it
// must either have the storage class StorageBuffer and be decorated
// with Block, or it must be in the Uniform storage class and be decorated
// as BufferBlock.
if (value_type && value_type->opcode() == SpvOpTypeStruct) {
bool contains_RTA = false;
for (size_t member_type_index = 1;
member_type_index < value_type->operands().size();
++member_type_index) {
const auto member_type_id =
value_type->GetOperandAs<uint32_t>(member_type_index);
const auto member_type = _.FindDef(member_type_id);
if (member_type->opcode() == SpvOpTypeRuntimeArray) {
contains_RTA = true;
break;
}
}
if (contains_RTA) {
if (storage_class == SpvStorageClassStorageBuffer) {
if (!_.HasDecoration(value_id, SpvDecorationBlock)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "For Vulkan, an OpTypeStruct variable containing an "
"OpTypeRuntimeArray must be decorated with Block if it "
"has storage class StorageBuffer.";
}
} else if (storage_class == SpvStorageClassUniform) {
if (!_.HasDecoration(value_id, SpvDecorationBufferBlock)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "For Vulkan, an OpTypeStruct variable containing an "
"OpTypeRuntimeArray must be decorated with BufferBlock "
"if it has storage class Uniform.";
}
} else {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "For Vulkan, OpTypeStruct variables containing "
"OpTypeRuntimeArray must have storage class of "
"StorageBuffer or Uniform.";
}
}
}
}
return SPV_SUCCESS;
}
@@ -550,11 +691,7 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) {
<< "'s type.";
}
if (inst->operands().size() > 3) {
if (auto error =
CheckMemoryAccess(_, inst, inst->GetOperandAs<uint32_t>(3)))
return error;
}
if (auto error = CheckMemoryAccess(_, inst, 3)) return error;
return SPV_SUCCESS;
}
@@ -642,11 +779,7 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
}
}
if (inst->operands().size() > 2) {
if (auto error =
CheckMemoryAccess(_, inst, inst->GetOperandAs<uint32_t>(2)))
return error;
}
if (auto error = CheckMemoryAccess(_, inst, 2)) return error;
return SPV_SUCCESS;
}
@@ -710,11 +843,7 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
<< _.getIdName(source_type->id()) << "'s type.";
}
if (inst->operands().size() > 2) {
if (auto error =
CheckMemoryAccess(_, inst, inst->GetOperandAs<uint32_t>(2)))
return error;
}
if (auto error = CheckMemoryAccess(_, inst, 2)) return error;
} else {
const auto size_id = inst->GetOperandAs<uint32_t>(2);
const auto size = _.FindDef(size_id);
@@ -758,11 +887,7 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
break;
}
if (inst->operands().size() > 3) {
if (auto error =
CheckMemoryAccess(_, inst, inst->GetOperandAs<uint32_t>(3)))
return error;
}
if (auto error = CheckMemoryAccess(_, inst, 3)) return error;
}
return SPV_SUCCESS;
}

View File

@@ -17,6 +17,7 @@
#include "source/val/validate.h"
#include "source/opcode.h"
#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
#include "source/val/validation_state.h"
@@ -106,6 +107,13 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) {
<< "' is a void type.";
}
if (spvIsVulkanEnv(_.context()->target_env) &&
element_type->opcode() == SpvOpTypeRuntimeArray) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpTypeArray Element Type <id> '" << _.getIdName(element_type_id)
<< "' is not valid in Vulkan environment.";
}
const auto length_index = 2;
const auto length_id = inst->GetOperandAs<uint32_t>(length_index);
const auto length = _.FindDef(length_id);
@@ -161,6 +169,14 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _,
<< _.getIdName(element_id) << "' is a void type.";
}
if (spvIsVulkanEnv(_.context()->target_env) &&
element_type->opcode() == SpvOpTypeRuntimeArray) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpTypeRuntimeArray Element Type <id> '"
<< _.getIdName(element_id)
<< "' is not valid in Vulkan environment.";
}
return SPV_SUCCESS;
}
@@ -206,7 +222,19 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
<< ".";
}
}
if (spvIsVulkanEnv(_.context()->target_env) &&
member_type->opcode() == SpvOpTypeRuntimeArray) {
const bool is_last_member =
member_type_index == inst->operands().size() - 1;
if (!is_last_member) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "In Vulkan, OpTypeRuntimeArray must only be used for the "
"last member of an OpTypeStruct";
}
}
}
std::unordered_set<uint32_t> built_in_members;
for (auto decoration : _.id_decorations(struct_id)) {
if (decoration.dec_type() == SpvDecorationBuiltIn &&

View File

@@ -171,6 +171,7 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx,
grammar_(ctx),
addressing_model_(SpvAddressingModelMax),
memory_model_(SpvMemoryModelMax),
pointer_size_and_alignment_(0),
in_function_(false),
num_of_warnings_(0),
max_num_of_warnings_(max_warnings) {
@@ -411,6 +412,11 @@ void ValidationState_t::RegisterExtension(Extension ext) {
// https://github.com/KhronosGroup/SPIRV-Tools/issues/1375
features_.declare_float16_type = true;
break;
case kSPV_AMD_gpu_shader_int16:
// This is not yet in the extension, but it's recommended for it.
// See https://github.com/KhronosGroup/glslang/issues/848
features_.uconvert_spec_constant_op = true;
break;
case kSPV_AMD_shader_ballot:
// The grammar doesn't encode the fact that SPV_AMD_shader_ballot
// enables the use of group operations Reduce, InclusiveScan,
@@ -435,6 +441,17 @@ bool ValidationState_t::HasAnyOfExtensions(
void ValidationState_t::set_addressing_model(SpvAddressingModel am) {
addressing_model_ = am;
switch (am) {
case SpvAddressingModelPhysical32:
pointer_size_and_alignment_ = 4;
break;
default:
// fall through
case SpvAddressingModelPhysical64:
case SpvAddressingModelPhysicalStorageBuffer64EXT:
pointer_size_and_alignment_ = 8;
break;
}
}
SpvAddressingModel ValidationState_t::addressing_model() const {

View File

@@ -15,6 +15,7 @@
#ifndef SOURCE_VAL_VALIDATION_STATE_H_
#define SOURCE_VAL_VALIDATION_STATE_H_
#include <algorithm>
#include <map>
#include <set>
#include <string>
@@ -104,6 +105,10 @@ class ValidationState_t {
// - ArrayStride and MatrixStride are multiples of scalar alignment
// Members need not be listed in offset order
bool scalar_block_layout = false;
// Permit UConvert as an OpSpecConstantOp operation.
// The Kernel capability already enables it, separately from this flag.
bool uconvert_spec_constant_op = false;
};
ValidationState_t(const spv_const_context context,
@@ -339,6 +344,11 @@ class ValidationState_t {
/// Returns the addressing model of this module, or Logical if uninitialized.
SpvAddressingModel addressing_model() const;
/// Returns the addressing model of this module, or Logical if uninitialized.
uint32_t pointer_size_and_alignment() const {
return pointer_size_and_alignment_;
}
/// Sets the memory model of this module.
void set_memory_model(SpvMemoryModel mm);
@@ -387,17 +397,23 @@ class ValidationState_t {
std::vector<Decoration>& id_decorations(uint32_t id) {
return id_decorations_[id];
}
const std::vector<Decoration>& id_decorations(uint32_t id) const {
// TODO: This would throw or generate SIGABRT if id has no
// decorations. Remove/refactor this function.
return id_decorations_.at(id);
}
// Returns const pointer to the internal decoration container.
const std::map<uint32_t, std::vector<Decoration>>& id_decorations() const {
return id_decorations_;
}
/// Returns true if the given id <id> has the given decoration <dec>,
/// otherwise returns false.
bool HasDecoration(uint32_t id, SpvDecoration dec) {
const auto& decorations = id_decorations_.find(id);
if (decorations == id_decorations_.end()) return false;
return std::any_of(
decorations->second.begin(), decorations->second.end(),
[dec](const Decoration& d) { return dec == d.dec_type(); });
}
/// Finds id's def, if it exists. If found, returns the definition otherwise
/// nullptr
const Instruction* FindDef(uint32_t id) const;
@@ -656,6 +672,9 @@ class ValidationState_t {
SpvAddressingModel addressing_model_;
SpvMemoryModel memory_model_;
// pointer size derived from addressing model. Assumes all storage classes
// have the same pointer size (for physical pointer types).
uint32_t pointer_size_and_alignment_;
/// NOTE: See correspoding getter functions
bool in_function_;

View File

@@ -197,6 +197,8 @@ ParsedInstruction MakeParsedInt32TypeInstruction(uint32_t result_id) {
class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> {
protected:
~BinaryParseTest() { spvDiagnosticDestroy(diagnostic_); }
void Parse(const SpirvVector& words, spv_result_t expected_result,
bool flip_words = false) {
SpirvVector flipped_words(words);

View File

@@ -34,8 +34,12 @@ using ::testing::HasSubstr;
class BinaryToText : public ::testing::Test {
public:
BinaryToText() : context(spvContextCreate(SPV_ENV_UNIVERSAL_1_0)) {}
~BinaryToText() { spvContextDestroy(context); }
BinaryToText()
: context(spvContextCreate(SPV_ENV_UNIVERSAL_1_0)), binary(nullptr) {}
~BinaryToText() {
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
virtual void SetUp() {
const char* textStr = R"(
@@ -63,17 +67,20 @@ class BinaryToText : public ::testing::Test {
spv_diagnostic diagnostic = nullptr;
spv_result_t error =
spvTextToBinary(context, text.str, text.length, &binary, &diagnostic);
if (error) {
spvDiagnosticPrint(diagnostic);
spvDiagnosticDestroy(diagnostic);
ASSERT_EQ(SPV_SUCCESS, error);
}
spvDiagnosticPrint(diagnostic);
spvDiagnosticDestroy(diagnostic);
ASSERT_EQ(SPV_SUCCESS, error);
}
virtual void TearDown() { spvBinaryDestroy(binary); }
virtual void TearDown() {
spvBinaryDestroy(binary);
binary = nullptr;
}
// Compiles the given assembly text, and saves it into 'binary'.
void CompileSuccessfully(std::string text) {
spvBinaryDestroy(binary);
binary = nullptr;
spv_diagnostic diagnostic = nullptr;
EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, text.c_str(), text.size(),
&binary, &diagnostic));

View File

@@ -61,7 +61,7 @@ OpFunctionEnd
; Generator: Khronos SPIR-V Tools Assembler; 0
; Bound: 9
; Schema: 0)";
spv_binary binary;
spv_binary binary = nullptr;
spv_diagnostic diagnostic;
spv_result_t error = spvTextToBinary(context, spirv.c_str(), spirv.size(),
&binary, &diagnostic);
@@ -102,6 +102,7 @@ OpFunctionEnd
}
EXPECT_EQ(spirv_header + spirv, output_text->str);
spvTextDestroy(output_text);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}

View File

@@ -81,6 +81,7 @@ add_spvtools_unittest(TARGET opt
type_manager_test.cpp
types_test.cpp
unify_const_test.cpp
upgrade_memory_model_test.cpp
utils_test.cpp pass_utils.cpp
value_table_test.cpp
vector_dce_test.cpp
@@ -88,9 +89,3 @@ add_spvtools_unittest(TARGET opt
LIBS SPIRV-Tools-opt
PCH_FILE pch_test_opt
)
add_spvtools_unittest(TARGET upgrade_memory_model
SRCS upgrade_memory_model_test.cpp pass_utils.cpp
LIBS SPIRV-Tools-opt
PCH_FILE pch_test_opt
)

View File

@@ -168,6 +168,7 @@ OpName %main "main"
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%int_n24 = OpConstant %int -24
%int_min = OpConstant %int -2147483648
%int_max = OpConstant %int 2147483647
%long_0 = OpConstant %long 0
@@ -486,7 +487,7 @@ INSTANTIATE_TEST_CASE_P(TestCase, IntegerInstructionFoldingTest,
"OpReturn\n" +
"OpFunctionEnd",
2, 0),
// Test case 21: fold signed n >> 42 (undefined, so set to zero).
// Test case 23: fold signed n >> 42 (undefined, so set to zero).
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
@@ -496,7 +497,7 @@ INSTANTIATE_TEST_CASE_P(TestCase, IntegerInstructionFoldingTest,
"OpReturn\n" +
"OpFunctionEnd",
2, 0),
// Test case 22: fold n << 42 (undefined, so set to zero).
// Test case 24: fold n << 42 (undefined, so set to zero).
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
@@ -505,6 +506,38 @@ INSTANTIATE_TEST_CASE_P(TestCase, IntegerInstructionFoldingTest,
"%2 = OpShiftLeftLogical %int %load %uint_42\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 0),
// Test case 25: fold -24 >> 32 (defined as -1)
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%2 = OpShiftRightArithmetic %int %int_n24 %uint_32\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, -1),
// Test case 26: fold 2 >> 32 (signed)
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%2 = OpShiftRightArithmetic %int %int_2 %uint_32\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 0),
// Test case 27: fold 2 >> 32 (unsigned)
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%2 = OpShiftRightLogical %int %int_2 %uint_32\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 0),
// Test case 28: fold 2 << 32
InstructionFoldingCase<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%2 = OpShiftLeftLogical %int %int_2 %uint_32\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 0)
));
// clang-format on

View File

@@ -92,7 +92,8 @@ OpDecorate %_entryPointOutput_vColor Location 0
)";
const std::string new_annots =
R"(OpDecorate %_struct_55 Block
R"(OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_55 Block
OpMemberDecorate %_struct_55 0 Offset 0
OpMemberDecorate %_struct_55 1 Offset 4
OpDecorate %57 DescriptorSet 7
@@ -441,6 +442,7 @@ OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_63 Block
OpMemberDecorate %_struct_63 0 Offset 0
OpMemberDecorate %_struct_63 1 Offset 4
@@ -735,6 +737,7 @@ OpDecorate %_entryPointOutput_vColor Location 0
OpDecorate %10 DescriptorSet 7
OpDecorate %10 Binding 0
OpDecorate %gl_FragCoord BuiltIn FragCoord
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_34 Block
OpMemberDecorate %_struct_34 0 Offset 0
OpMemberDecorate %_struct_34 1 Offset 4
@@ -968,6 +971,7 @@ OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_51 Block
OpMemberDecorate %_struct_51 0 Offset 0
OpMemberDecorate %_struct_51 1 Offset 4
@@ -1193,6 +1197,7 @@ OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_49 Block
OpMemberDecorate %_struct_49 0 Offset 0
OpMemberDecorate %_struct_49 1 Offset 4
@@ -1418,6 +1423,7 @@ OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_48 Block
OpMemberDecorate %_struct_48 0 Offset 0
OpMemberDecorate %_struct_48 1 Offset 4
@@ -1644,7 +1650,7 @@ OpCapability Sampled1D
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %_ %coords2D %gl_VertexID %gl_InstanceID
OpEntryPoint Vertex %main "main" %_ %coords2D %gl_VertexIndex %gl_InstanceIndex
OpSource GLSL 450
OpName %main "main"
OpName %lod "lod"
@@ -1672,13 +1678,14 @@ OpDecorate %foo Block
OpDecorate %__0 DescriptorSet 0
OpDecorate %__0 Binding 5
OpDecorate %coords2D Location 0
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_61 Block
OpMemberDecorate %_struct_61 0 Offset 0
OpMemberDecorate %_struct_61 1 Offset 4
OpDecorate %63 DescriptorSet 7
OpDecorate %63 Binding 0
OpDecorate %gl_VertexID BuiltIn VertexId
OpDecorate %gl_InstanceID BuiltIn InstanceId
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
%void = OpTypeVoid
%12 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -1723,8 +1730,8 @@ OpDecorate %gl_InstanceID BuiltIn InstanceId
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%_ptr_Input_uint = OpTypePointer Input %uint
%gl_VertexID = OpVariable %_ptr_Input_uint Input
%gl_InstanceID = OpVariable %_ptr_Input_uint Input
%gl_VertexIndex = OpVariable %_ptr_Input_uint Input
%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
%uint_5 = OpConstant %uint 5
%uint_6 = OpConstant %uint 6
%uint_7 = OpConstant %uint 7
@@ -1812,11 +1819,11 @@ OpStore %81 %55
%83 = OpIAdd %uint %68 %uint_3
%84 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %83
OpStore %84 %uint_0
%87 = OpLoad %uint %gl_VertexID
%87 = OpLoad %uint %gl_VertexIndex
%88 = OpIAdd %uint %68 %uint_4
%89 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %88
OpStore %89 %87
%91 = OpLoad %uint %gl_InstanceID
%91 = OpLoad %uint %gl_InstanceIndex
%93 = OpIAdd %uint %68 %uint_5
%94 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %93
OpStore %94 %91

View File

@@ -49,7 +49,7 @@ class PassTest : public TestT {
[](spv_message_level_t, const char*, const spv_position_t&,
const char* message) { std::cerr << message << std::endl; }),
context_(nullptr),
tools_(SPV_ENV_UNIVERSAL_1_1),
tools_(SPV_ENV_UNIVERSAL_1_3),
manager_(new PassManager()),
assemble_options_(SpirvTools::kDefaultAssembleOption),
disassemble_options_(SpirvTools::kDefaultDisassembleOption) {}
@@ -59,7 +59,7 @@ class PassTest : public TestT {
// from pass Process() function.
std::tuple<std::vector<uint32_t>, Pass::Status> OptimizeToBinary(
Pass* pass, const std::string& original, bool skip_nop) {
context_ = std::move(BuildModule(SPV_ENV_UNIVERSAL_1_1, consumer_, original,
context_ = std::move(BuildModule(SPV_ENV_UNIVERSAL_1_3, consumer_, original,
assemble_options_));
EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
<< original << std::endl;
@@ -97,7 +97,7 @@ class PassTest : public TestT {
std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>(
assembly, skip_nop, std::forward<Args>(args)...);
if (do_validation) {
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_1;
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_3;
spv_context spvContext = spvContextCreate(target_env);
spv_diagnostic diagnostic = nullptr;
spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()};
@@ -134,7 +134,7 @@ class PassTest : public TestT {
EXPECT_EQ(original == expected,
status == Pass::Status::SuccessWithoutChange);
if (do_validation) {
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_1;
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_3;
spv_context spvContext = spvContextCreate(target_env);
spv_diagnostic diagnostic = nullptr;
spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()};
@@ -202,7 +202,7 @@ class PassTest : public TestT {
void RunAndCheck(const std::string& original, const std::string& expected) {
assert(manager_->NumPasses());
context_ = std::move(BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, original,
context_ = std::move(BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, original,
assemble_options_));
ASSERT_NE(nullptr, context());

View File

@@ -23,7 +23,6 @@ using namespace spvtools;
using UpgradeMemoryModelTest = opt::PassTest<::testing::Test>;
#ifdef SPIRV_EFFCEE
TEST_F(UpgradeMemoryModelTest, InvalidMemoryModelOpenCL) {
const std::string text = R"(
; CHECK: OpMemoryModel Logical OpenCL
@@ -80,8 +79,8 @@ OpDecorate %var Coherent
TEST_F(UpgradeMemoryModelTest, WorkgroupVariable) {
const std::string text = R"(
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 2
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
@@ -104,8 +103,8 @@ OpFunctionEnd
TEST_F(UpgradeMemoryModelTest, WorkgroupFunctionParameter) {
const std::string text = R"(
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 2
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
@@ -129,8 +128,8 @@ TEST_F(UpgradeMemoryModelTest, SimpleUniformVariable) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
@@ -156,8 +155,8 @@ TEST_F(UpgradeMemoryModelTest, SimpleUniformFunctionParameter) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
@@ -209,8 +208,8 @@ TEST_F(UpgradeMemoryModelTest, SimpleUniformVariableCopied) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
@@ -237,8 +236,8 @@ TEST_F(UpgradeMemoryModelTest, SimpleUniformFunctionParameterCopied) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
@@ -266,8 +265,8 @@ TEST_F(UpgradeMemoryModelTest, SimpleUniformVariableAccessChain) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
@@ -298,8 +297,8 @@ TEST_F(UpgradeMemoryModelTest, SimpleUniformFunctionParameterAccessChain) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
@@ -331,8 +330,8 @@ TEST_F(UpgradeMemoryModelTest, VariablePointerSelect) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpCapability VariablePointers
@@ -364,8 +363,8 @@ TEST_F(UpgradeMemoryModelTest, VariablePointerSelectConservative) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpCapability VariablePointers
@@ -397,8 +396,8 @@ TEST_F(UpgradeMemoryModelTest, VariablePointerIncrement) {
const std::string text = R"(
; CHECK-NOT: OpDecorate {{%\w+}} Coherent
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpCapability VariablePointers
@@ -440,8 +439,8 @@ TEST_F(UpgradeMemoryModelTest, CoherentStructElement) {
const std::string text = R"(
; CHECK-NOT: OpMemberDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -471,8 +470,8 @@ TEST_F(UpgradeMemoryModelTest, CoherentElementFullStructAccess) {
const std::string text = R"(
; CHECK-NOT: OpMemberDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -530,8 +529,8 @@ TEST_F(UpgradeMemoryModelTest, MultiIndexAccessCoherent) {
const std::string text = R"(
; CHECK-NOT: OpMemberDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -600,8 +599,8 @@ TEST_F(UpgradeMemoryModelTest, ConsecutiveAccessChainCoherent) {
const std::string text = R"(
; CHECK-NOT: OpMemberDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -682,8 +681,8 @@ TEST_F(UpgradeMemoryModelTest, CoherentStructElementAccess) {
const std::string text = R"(
; CHECK-NOT: OpMemberDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -723,8 +722,8 @@ TEST_F(UpgradeMemoryModelTest, NonCoherentLoadCoherentStore) {
const std::string text = R"(
; CHECK-NOT: OpMemberDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK-NOT: MakePointerAvailableKHR
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK-NOT: MakePointerVisibleKHR
; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -823,7 +822,7 @@ TEST_F(UpgradeMemoryModelTest, CopyMemoryTwoScopes) {
; CHECK-NOT: OpDecorate
; CHECK-DAG: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5
; CHECK-DAG: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|MakePointerVisibleKHR|NonPrivatePointerKHR [[queuefamily]] [[workgroup]]
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|MakePointerVisibleKHR|NonPrivatePointerKHR [[workgroup]] [[queuefamily]]
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -883,8 +882,8 @@ TEST_F(UpgradeMemoryModelTest, CoherentImageRead) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailableKHR|NonPrivateTexelKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpCapability StorageImageReadWithoutFormat
@@ -917,9 +916,9 @@ TEST_F(UpgradeMemoryModelTest, CoherentImageReadExtractedFromSampledImage) {
; CHECK-NOT: OpDecorate
; CHECK: [[image:%\w+]] = OpTypeImage
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad [[image]] {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad [[image]] {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK-NOT: NonPrivatePointerKHR
; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailableKHR|NonPrivateTexelKHR [[scope]]
; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpCapability StorageImageReadWithoutFormat
@@ -990,8 +989,8 @@ TEST_F(UpgradeMemoryModelTest, CoherentImageWrite) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR
; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR
; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailableKHR|NonPrivateTexelKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpCapability StorageImageWriteWithoutFormat
@@ -1023,9 +1022,9 @@ TEST_F(UpgradeMemoryModelTest, CoherentImageWriteExtractFromSampledImage) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR
; CHECK-NOT: NonPrivatePointerKHR
; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]]
; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailableKHR|NonPrivateTexelKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpCapability StorageImageWriteWithoutFormat
@@ -1098,8 +1097,8 @@ TEST_F(UpgradeMemoryModelTest, CoherentImageSparseRead) {
const std::string text = R"(
; CHECK-NOT: OpDecorate
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailableKHR|NonPrivateTexelKHR [[scope]]
; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpCapability StorageImageReadWithoutFormat
@@ -1135,9 +1134,9 @@ TEST_F(UpgradeMemoryModelTest,
; CHECK-NOT: OpDecorate
; CHECK: [[image:%\w+]] = OpTypeImage
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: OpLoad [[image]] {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]]
; CHECK: OpLoad [[image]] {{%\w+}} MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
; CHECK-NOT: NonPrivatePointerKHR
; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailableKHR|NonPrivateTexelKHR [[scope]]
; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisibleKHR|NonPrivateTexelKHR [[scope]]
OpCapability Shader
OpCapability Linkage
OpCapability StorageImageReadWithoutFormat
@@ -1430,6 +1429,288 @@ OpFunctionEnd
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
#endif
TEST_F(UpgradeMemoryModelTest, UpgradeModfNoFlags) {
const std::string text = R"(
; CHECK: [[float:%\w+]] = OpTypeFloat 32
; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0
; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[float]]
; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer
; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[float]]
; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]]
; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1
; CHECK: OpStore [[var]] [[ex1]]
; CHECK-NOT: NonPrivatePointerKHR
; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
OpCapability Shader
OpMemoryModel Logical GLSL450
%import = OpExtInstImport "GLSL.std.450"
OpEntryPoint GLCompute %func "func"
%void = OpTypeVoid
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%ptr_ssbo_float = OpTypePointer StorageBuffer %float
%ssbo_var = OpVariable %ptr_ssbo_float StorageBuffer
%func_ty = OpTypeFunction %void
%func = OpFunction %void None %func_ty
%1 = OpLabel
%2 = OpExtInst %float %import Modf %float_0 %ssbo_var
%3 = OpFAdd %float %float_0 %2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, UpgradeModfWorkgroupCoherent) {
const std::string text = R"(
; CHECK: [[float:%\w+]] = OpTypeFloat 32
; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0
; CHECK: [[ptr:%\w+]] = OpTypePointer Workgroup [[float]]
; CHECK: [[var:%\w+]] = OpVariable [[ptr]] Workgroup
; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[float]]
; CHECK: [[wg_scope:%\w+]] = OpConstant {{%\w+}} 2
; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]]
; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1
; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailableKHR|NonPrivatePointerKHR [[wg_scope]]
; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
OpCapability Shader
OpMemoryModel Logical GLSL450
%import = OpExtInstImport "GLSL.std.450"
OpEntryPoint GLCompute %func "func"
OpDecorate %wg_var Coherent
%void = OpTypeVoid
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%ptr_wg_float = OpTypePointer Workgroup %float
%wg_var = OpVariable %ptr_wg_float Workgroup
%func_ty = OpTypeFunction %void
%func = OpFunction %void None %func_ty
%1 = OpLabel
%2 = OpExtInst %float %import Modf %float_0 %wg_var
%3 = OpFAdd %float %float_0 %2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, UpgradeModfSSBOCoherent) {
const std::string text = R"(
; CHECK: [[float:%\w+]] = OpTypeFloat 32
; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0
; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[float]]
; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer
; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[float]]
; CHECK: [[qf_scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]]
; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1
; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailableKHR|NonPrivatePointerKHR [[qf_scope]]
; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
OpCapability Shader
OpMemoryModel Logical GLSL450
%import = OpExtInstImport "GLSL.std.450"
OpEntryPoint GLCompute %func "func"
OpDecorate %ssbo_var Coherent
%void = OpTypeVoid
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%ptr_ssbo_float = OpTypePointer StorageBuffer %float
%ssbo_var = OpVariable %ptr_ssbo_float StorageBuffer
%func_ty = OpTypeFunction %void
%func = OpFunction %void None %func_ty
%1 = OpLabel
%2 = OpExtInst %float %import Modf %float_0 %ssbo_var
%3 = OpFAdd %float %float_0 %2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, UpgradeModfSSBOVolatile) {
const std::string text = R"(
; CHECK: [[float:%\w+]] = OpTypeFloat 32
; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0
; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[float]]
; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer
; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[float]]
; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]]
; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1
; CHECK: OpStore [[var]] [[ex1]] Volatile
; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
OpCapability Shader
OpMemoryModel Logical GLSL450
%import = OpExtInstImport "GLSL.std.450"
OpEntryPoint GLCompute %func "func"
OpDecorate %wg_var Volatile
%void = OpTypeVoid
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%ptr_ssbo_float = OpTypePointer StorageBuffer %float
%wg_var = OpVariable %ptr_ssbo_float StorageBuffer
%func_ty = OpTypeFunction %void
%func = OpFunction %void None %func_ty
%1 = OpLabel
%2 = OpExtInst %float %import Modf %float_0 %wg_var
%3 = OpFAdd %float %float_0 %2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, UpgradeFrexpNoFlags) {
const std::string text = R"(
; CHECK: [[float:%\w+]] = OpTypeFloat 32
; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[int]]
; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer
; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[int]]
; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]]
; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1
; CHECK: OpStore [[var]] [[ex1]]
; CHECK-NOT: NonPrivatePointerKHR
; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
OpCapability Shader
OpMemoryModel Logical GLSL450
%import = OpExtInstImport "GLSL.std.450"
OpEntryPoint GLCompute %func "func"
%void = OpTypeVoid
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%int = OpTypeInt 32 0
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
%func_ty = OpTypeFunction %void
%func = OpFunction %void None %func_ty
%1 = OpLabel
%2 = OpExtInst %float %import Frexp %float_0 %ssbo_var
%3 = OpFAdd %float %float_0 %2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, UpgradeFrexpWorkgroupCoherent) {
const std::string text = R"(
; CHECK: [[float:%\w+]] = OpTypeFloat 32
; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[ptr:%\w+]] = OpTypePointer Workgroup [[int]]
; CHECK: [[var:%\w+]] = OpVariable [[ptr]] Workgroup
; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[int]]
; CHECK: [[wg_scope:%\w+]] = OpConstant {{%\w+}} 2
; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]]
; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1
; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailableKHR|NonPrivatePointerKHR [[wg_scope]]
; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
OpCapability Shader
OpMemoryModel Logical GLSL450
%import = OpExtInstImport "GLSL.std.450"
OpEntryPoint GLCompute %func "func"
OpDecorate %wg_var Coherent
%void = OpTypeVoid
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%int = OpTypeInt 32 0
%ptr_wg_int = OpTypePointer Workgroup %int
%wg_var = OpVariable %ptr_wg_int Workgroup
%func_ty = OpTypeFunction %void
%func = OpFunction %void None %func_ty
%1 = OpLabel
%2 = OpExtInst %float %import Frexp %float_0 %wg_var
%3 = OpFAdd %float %float_0 %2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, UpgradeFrexpSSBOCoherent) {
const std::string text = R"(
; CHECK: [[float:%\w+]] = OpTypeFloat 32
; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[int]]
; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer
; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[int]]
; CHECK: [[qf_scope:%\w+]] = OpConstant {{%\w+}} 5
; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]]
; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1
; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailableKHR|NonPrivatePointerKHR [[qf_scope]]
; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
OpCapability Shader
OpMemoryModel Logical GLSL450
%import = OpExtInstImport "GLSL.std.450"
OpEntryPoint GLCompute %func "func"
OpDecorate %ssbo_var Coherent
%void = OpTypeVoid
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%int = OpTypeInt 32 0
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
%func_ty = OpTypeFunction %void
%func = OpFunction %void None %func_ty
%1 = OpLabel
%2 = OpExtInst %float %import Frexp %float_0 %ssbo_var
%3 = OpFAdd %float %float_0 %2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, UpgradeFrexpSSBOVolatile) {
const std::string text = R"(
; CHECK: [[float:%\w+]] = OpTypeFloat 32
; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0
; CHECK: [[int:%\w+]] = OpTypeInt 32 0
; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[int]]
; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer
; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[int]]
; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]]
; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0
; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1
; CHECK: OpStore [[var]] [[ex1]] Volatile
; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]]
OpCapability Shader
OpMemoryModel Logical GLSL450
%import = OpExtInstImport "GLSL.std.450"
OpEntryPoint GLCompute %func "func"
OpDecorate %wg_var Volatile
%void = OpTypeVoid
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%int = OpTypeInt 32 0
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%wg_var = OpVariable %ptr_ssbo_int StorageBuffer
%func_ty = OpTypeFunction %void
%func = OpFunction %void None %func_ty
%1 = OpLabel
%2 = OpExtInst %float %import Frexp %float_0 %wg_var
%3 = OpFAdd %float %float_0 %2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
} // namespace

View File

@@ -61,6 +61,8 @@ class TextToBinaryTestBase : public T {
// compilation success. Returns the compiled code.
SpirvVector CompileSuccessfully(const std::string& txt,
spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
DestroyBinary();
DestroyDiagnostic();
spv_result_t status =
spvTextToBinary(ScopedContext(env).context, txt.c_str(), txt.size(),
&binary, &diagnostic);
@@ -79,6 +81,8 @@ class TextToBinaryTestBase : public T {
// Returns the error message(s).
std::string CompileFailure(const std::string& txt,
spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
DestroyBinary();
DestroyDiagnostic();
EXPECT_NE(SPV_SUCCESS,
spvTextToBinary(ScopedContext(env).context, txt.c_str(),
txt.size(), &binary, &diagnostic))
@@ -94,6 +98,7 @@ class TextToBinaryTestBase : public T {
uint32_t disassemble_options = SPV_BINARY_TO_TEXT_OPTION_NONE,
spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
DestroyBinary();
DestroyDiagnostic();
ScopedContext context(env);
disassemble_options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER;
spv_result_t error = spvTextToBinary(context.context, txt.c_str(),
@@ -126,6 +131,8 @@ class TextToBinaryTestBase : public T {
// Returns the error message.
std::string EncodeSuccessfullyDecodeFailed(
const std::string& txt, const SpirvVector& words_to_append) {
DestroyBinary();
DestroyDiagnostic();
SpirvVector code =
spvtest::Concatenate({CompileSuccessfully(txt), words_to_append});
@@ -169,6 +176,12 @@ class TextToBinaryTestBase : public T {
binary = nullptr;
}
// Destroys the diagnostic, if it exists.
void DestroyDiagnostic() {
spvDiagnosticDestroy(diagnostic);
diagnostic = nullptr;
}
spv_diagnostic diagnostic;
std::string textString;

View File

@@ -280,6 +280,20 @@ INSTANTIATE_TEST_CASE_P(
"InBoundsPtrAccessChain"),
}));
INSTANTIATE_TEST_CASE_P(
UConvertInAMD_gpu_shader_int16, ValidateConstantOp,
ValuesIn(std::vector<ConstantOpCase>{
// SPV_AMD_gpu_shader_int16 should enable UConvert for OpSpecConstantOp
// https://github.com/KhronosGroup/glslang/issues/848
{SPV_ENV_UNIVERSAL_1_0,
"OpCapability Shader "
"OpCapability Linkage ; So we don't need to define a function\n"
"OpExtension \"SPV_AMD_gpu_shader_int16\" "
"OpMemoryModel Logical Simple " kBasicTypes
"%v = OpSpecConstantOp %uint UConvert %uint_0",
true, ""},
}));
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -1302,6 +1302,118 @@ OpFunctionEnd
"type"));
}
TEST_F(ValidateConversion, ConvertUToPtrPSBSuccess) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%uint64 = OpTypeInt 64 0
%u64_1 = OpConstant %uint64 1
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpConvertUToPtr %ptr %u64_1
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateConversion, ConvertUToPtrPSBStorageClass) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%uint64 = OpTypeInt 64 0
%u64_1 = OpConstant %uint64 1
%ptr = OpTypePointer Function %uint64
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpConvertUToPtr %ptr %u64_1
%val2 = OpConvertPtrToU %uint64 %val1
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Pointer storage class must be "
"PhysicalStorageBufferEXT: ConvertUToPtr"));
}
TEST_F(ValidateConversion, ConvertPtrToUPSBSuccess) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %val1 RestrictPointerEXT
%uint64 = OpTypeInt 64 0
%u64_1 = OpConstant %uint64 1
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %pptr_f Function
%val2 = OpLoad %ptr %val1
%val3 = OpConvertPtrToU %uint64 %val2
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateConversion, ConvertPtrToUPSBStorageClass) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%uint64 = OpTypeInt 64 0
%u64_1 = OpConstant %uint64 1
%ptr = OpTypePointer Function %uint64
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %ptr Function
%val2 = OpConvertPtrToU %uint64 %val1
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Pointer storage class must be "
"PhysicalStorageBufferEXT: ConvertPtrToU"));
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -36,6 +36,24 @@ std::string HeaderWith(std::string cap) {
cap + " OpMemoryModel Logical GLSL450 ";
}
std::string WebGPUHeaderWith(std::string cap) {
return R"(
OpCapability Shader
OpCapability )" +
cap + R"(
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
)";
}
std::string webgpu_header = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
)";
std::string header = R"(
OpCapability Shader
OpCapability Linkage
@@ -249,6 +267,18 @@ TEST_F(ValidateData, int8_with_storage_push_constant_8_good) {
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
}
TEST_F(ValidateData, webgpu_int8_bad) {
std::string str = WebGPUHeaderWith("Int8") + "%2 = OpTypeInt 8 0";
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Capability Int8 is not allowed by WebGPU specification (or "
"requires extension)\n"
" OpCapability Int8\n"));
}
TEST_F(ValidateData, int16_good) {
std::string str = header_with_int16 + "%2 = OpTypeInt 16 1";
CompileSuccessfully(str.c_str());
@@ -308,6 +338,34 @@ TEST_F(ValidateData, int16_bad) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int16_cap_error));
}
TEST_F(ValidateData, webgpu_int16_bad) {
std::string str = WebGPUHeaderWith("Int16") + "%2 = OpTypeInt 16 1";
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Capability Int16 is not allowed by WebGPU specification (or "
"requires extension)\n"
" OpCapability Int16\n"));
}
TEST_F(ValidateData, webgpu_int32_good) {
std::string str = webgpu_header + R"(
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%uint_t = OpTypeInt 32 0
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
TEST_F(ValidateData, int64_good) {
std::string str = header_with_int64 + "%2 = OpTypeInt 64 1";
CompileSuccessfully(str.c_str());
@@ -321,6 +379,18 @@ TEST_F(ValidateData, int64_bad) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int64_cap_error));
}
TEST_F(ValidateData, webgpu_int64_bad) {
std::string str = WebGPUHeaderWith("Int64") + "%2 = OpTypeInt 64 1";
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Capability Int64 is not allowed by WebGPU specification (or "
"requires extension)\n"
" OpCapability Int64\n"));
}
// Number of bits in an integer may be only one of: {8,16,32,64}
TEST_F(ValidateData, int_invalid_num_bits) {
std::string str = header + "%2 = OpTypeInt 48 1";
@@ -348,6 +418,34 @@ TEST_F(ValidateData, float16_bad) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float16_cap_error));
}
TEST_F(ValidateData, webgpu_float16_bad) {
std::string str = WebGPUHeaderWith("Float16") + "%2 = OpTypeFloat 16";
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Capability Float16 is not allowed by WebGPU specification (or "
"requires extension)\n"
" OpCapability Float16\n"));
}
TEST_F(ValidateData, webgpu_float32_good) {
std::string str = webgpu_header + R"(
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%float_t = OpTypeFloat 32
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
TEST_F(ValidateData, float64_good) {
std::string str = header_with_float64 + "%2 = OpTypeFloat 64";
CompileSuccessfully(str.c_str());
@@ -361,6 +459,18 @@ TEST_F(ValidateData, float64_bad) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float64_cap_error));
}
TEST_F(ValidateData, webgpu_float64_bad) {
std::string str = WebGPUHeaderWith("Float64") + "%2 = OpTypeFloat 64";
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Capability Float64 is not allowed by WebGPU specification (or "
"requires extension)\n"
" OpCapability Float64\n"));
}
// Number of bits in a float may be only one of: {16,32,64}
TEST_F(ValidateData, float_invalid_num_bits) {
std::string str = header + "%2 = OpTypeFloat 48";
@@ -703,6 +813,65 @@ TEST_F(ValidateData, void_runtime_array) {
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '1[%void]' is a void type."));
}
TEST_F(ValidateData, vulkan_RTA_array_at_end_of_struct) {
std::string str = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpMemberDecorate %struct_t 1 Offset 4
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%array_t = OpTypeRuntimeArray %uint_t
%struct_t = OpTypeStruct %uint_t %array_t
%struct_ptr = OpTypePointer StorageBuffer %struct_t
%2 = OpVariable %struct_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateData, vulkan_RTA_not_at_end_of_struct) {
std::string str = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpMemberDecorate %struct_t 1 Offset 4
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%array_t = OpTypeRuntimeArray %uint_t
%struct_t = OpTypeStruct %array_t %uint_t
%struct_ptr = OpTypePointer StorageBuffer %struct_t
%2 = OpVariable %struct_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("In Vulkan, OpTypeRuntimeArray must only be used for "
"the last member of an OpTypeStruct\n %_struct_3 = "
"OpTypeStruct %_runtimearr_uint %uint\n"));
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -4345,11 +4345,11 @@ OpFunctionEnd
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("FPRoundingMode decoration can be applied only to the "
"Object operand of an OpStore in the StorageBuffer, Uniform, "
"PushConstant, Input, or Output Storage Classes."));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("FPRoundingMode decoration can be applied only to the "
"Object operand of an OpStore in the StorageBuffer, "
"PhysicalStorageBufferEXT, Uniform, "
"PushConstant, Input, or Output Storage Classes."));
}
TEST_F(ValidateDecorations, FPRoundingModeMultipleOpStoreGood) {
@@ -4810,6 +4810,498 @@ TEST_F(ValidateDecorations, BlockAndBufferBlockDecorationsOnSameID) {
"ID '2' decorated with both BufferBlock and Block is not allowed."));
}
std::string MakeIntegerShader(
const std::string& decoration, const std::string& inst,
const std::string& extension =
"OpExtension \"SPV_KHR_no_integer_wrap_decoration\"") {
return R"(
OpCapability Shader
OpCapability Linkage
)" + extension +
R"(
%glsl = OpExtInstImport "GLSL.std.450"
%opencl = OpExtInstImport "OpenCL.std"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpName %entry "entry"
)" + decoration +
R"(
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%int = OpTypeInt 32 1
%zero = OpConstantNull %int
%float = OpTypeFloat 32
%float0 = OpConstantNull %float
%main = OpFunction %void None %voidfn
%entry = OpLabel
)" + inst +
R"(
OpReturn
OpFunctionEnd)";
}
// NoSignedWrap
TEST_F(ValidateDecorations, NoSignedWrapOnTypeBad) {
std::string spirv = MakeIntegerShader("OpDecorate %void NoSignedWrap", "");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("NoSignedWrap decoration may not be applied to TypeVoid"));
}
TEST_F(ValidateDecorations, NoSignedWrapOnLabelBad) {
std::string spirv = MakeIntegerShader("OpDecorate %entry NoSignedWrap", "");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("NoSignedWrap decoration may not be applied to Label"));
}
TEST_F(ValidateDecorations, NoSignedWrapRequiresExtensionBad) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
"%val = OpIAdd %int %zero %zero", "");
CompileSuccessfully(spirv);
EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("requires one of these extensions: "
"SPV_KHR_no_integer_wrap_decoration"));
}
TEST_F(ValidateDecorations, NoSignedWrapIAddGood) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
"%val = OpIAdd %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoSignedWrapISubGood) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
"%val = OpISub %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoSignedWrapIMulGood) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
"%val = OpIMul %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoSignedWrapShiftLeftLogicalGood) {
std::string spirv =
MakeIntegerShader("OpDecorate %val NoSignedWrap",
"%val = OpShiftLeftLogical %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoSignedWrapSNegateGood) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
"%val = OpSNegate %int %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoSignedWrapSRemBad) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
"%val = OpSRem %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("NoSignedWrap decoration may not be applied to SRem"));
}
TEST_F(ValidateDecorations, NoSignedWrapFAddBad) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
"%val = OpFAdd %float %float0 %float0");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("NoSignedWrap decoration may not be applied to FAdd"));
}
TEST_F(ValidateDecorations, NoSignedWrapExtInstOpenCLGood) {
std::string spirv =
MakeIntegerShader("OpDecorate %val NoSignedWrap",
"%val = OpExtInst %int %opencl s_abs %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoSignedWrapExtInstGLSLGood) {
std::string spirv = MakeIntegerShader(
"OpDecorate %val NoSignedWrap", "%val = OpExtInst %int %glsl SAbs %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
// TODO(dneto): For NoSignedWrap and NoUnsignedWrap, permit
// "OpExtInst for instruction numbers specified in the extended
// instruction-set specifications as accepting this decoration."
// NoUnignedWrap
TEST_F(ValidateDecorations, NoUnsignedWrapOnTypeBad) {
std::string spirv = MakeIntegerShader("OpDecorate %void NoUnsignedWrap", "");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("NoUnsignedWrap decoration may not be applied to TypeVoid"));
}
TEST_F(ValidateDecorations, NoUnsignedWrapOnLabelBad) {
std::string spirv = MakeIntegerShader("OpDecorate %entry NoUnsignedWrap", "");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("NoUnsignedWrap decoration may not be applied to Label"));
}
TEST_F(ValidateDecorations, NoUnsignedWrapRequiresExtensionBad) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpIAdd %int %zero %zero", "");
CompileSuccessfully(spirv);
EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("requires one of these extensions: "
"SPV_KHR_no_integer_wrap_decoration"));
}
TEST_F(ValidateDecorations, NoUnsignedWrapIAddGood) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpIAdd %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoUnsignedWrapISubGood) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpISub %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoUnsignedWrapIMulGood) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpIMul %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoUnsignedWrapShiftLeftLogicalGood) {
std::string spirv =
MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpShiftLeftLogical %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoUnsignedWrapSNegateGood) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpSNegate %int %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoUnsignedWrapSRemBad) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpSRem %int %zero %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("NoUnsignedWrap decoration may not be applied to SRem"));
}
TEST_F(ValidateDecorations, NoUnsignedWrapFAddBad) {
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpFAdd %float %float0 %float0");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("NoUnsignedWrap decoration may not be applied to FAdd"));
}
TEST_F(ValidateDecorations, NoUnsignedWrapExtInstOpenCLGood) {
std::string spirv =
MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpExtInst %int %opencl s_abs %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, NoUnsignedWrapExtInstGLSLGood) {
std::string spirv =
MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
"%val = OpExtInst %int %glsl SAbs %zero");
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
// TODO(dneto): For NoUnsignedWrap and NoUnsignedWrap, permit
// "OpExtInst for instruction numbers specified in the extended
// instruction-set specifications as accepting this decoration."
TEST_F(ValidateDecorations, PSBAliasedRestrictPointerSuccess) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %val1 RestrictPointerEXT
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %pptr_f Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateDecorations, PSBAliasedRestrictPointerMissing) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %pptr_f Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("expected AliasedPointerEXT or RestrictPointerEXT for "
"PhysicalStorageBufferEXT pointer"));
}
TEST_F(ValidateDecorations, PSBAliasedRestrictPointerBoth) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %val1 RestrictPointerEXT
OpDecorate %val1 AliasedPointerEXT
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %pptr_f Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("can't specify both AliasedPointerEXT and RestrictPointerEXT "
"for PhysicalStorageBufferEXT pointer"));
}
TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamSuccess) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %fparam Restrict
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%fnptr = OpTypeFunction %void %ptr
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
%fn = OpFunction %void None %fnptr
%fparam = OpFunctionParameter %ptr
%lab = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamMissing) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%fnptr = OpTypeFunction %void %ptr
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
%fn = OpFunction %void None %fnptr
%fparam = OpFunctionParameter %ptr
%lab = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("expected Aliased or Restrict for "
"PhysicalStorageBufferEXT pointer"));
}
TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamBoth) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %fparam Restrict
OpDecorate %fparam Aliased
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%fnptr = OpTypeFunction %void %ptr
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
%fn = OpFunction %void None %fnptr
%fparam = OpFunctionParameter %ptr
%lab = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("can't specify both Aliased and Restrict for "
"PhysicalStorageBufferEXT pointer"));
}
TEST_F(ValidateDecorations, PSBFPRoundingModeSuccess) {
std::string spirv = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Shader
OpCapability Linkage
OpCapability StorageBuffer16BitAccess
OpExtension "SPV_EXT_physical_storage_buffer"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpExtension "SPV_KHR_variable_pointers"
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint GLCompute %main "main"
OpDecorate %_ FPRoundingMode RTE
OpDecorate %half_ptr_var AliasedPointerEXT
%half = OpTypeFloat 16
%float = OpTypeFloat 32
%float_1_25 = OpConstant %float 1.25
%half_ptr = OpTypePointer PhysicalStorageBufferEXT %half
%half_pptr_f = OpTypePointer Function %half_ptr
%void = OpTypeVoid
%func = OpTypeFunction %void
%main = OpFunction %void None %func
%main_entry = OpLabel
%half_ptr_var = OpVariable %half_pptr_f Function
%val1 = OpLoad %half_ptr %half_ptr_var
%_ = OpFConvert %half %float_1_25
OpStore %val1 %_ Aligned 2
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -56,6 +56,18 @@ class ValidateBase : public ::testing::Test,
spv_result_t ValidateAndRetrieveValidationState(
spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
// Destroys the stored binary.
void DestroyBinary() {
spvBinaryDestroy(binary_);
binary_ = nullptr;
}
// Destroys the stored diagnostic.
void DestroyDiagnostic() {
spvDiagnosticDestroy(diagnostic_);
diagnostic_ = nullptr;
}
std::string getDiagnosticString();
spv_position_t getErrorPosition();
spv_validator_options getValidatorOptions();
@@ -67,7 +79,7 @@ class ValidateBase : public ::testing::Test,
};
template <typename T>
ValidateBase<T>::ValidateBase() : binary_(), diagnostic_() {
ValidateBase<T>::ValidateBase() : binary_(nullptr), diagnostic_(nullptr) {
// Initialize to default command line options. Different tests can then
// specialize specific options as necessary.
options_ = spvValidatorOptionsCreate();
@@ -83,14 +95,15 @@ void ValidateBase<T>::TearDown() {
if (diagnostic_) {
spvDiagnosticPrint(diagnostic_);
}
spvDiagnosticDestroy(diagnostic_);
spvBinaryDestroy(binary_);
DestroyBinary();
DestroyDiagnostic();
spvValidatorOptionsDestroy(options_);
}
template <typename T>
void ValidateBase<T>::CompileSuccessfully(std::string code,
spv_target_env env) {
DestroyBinary();
spv_diagnostic diagnostic = nullptr;
ASSERT_EQ(SPV_SUCCESS,
spvTextToBinary(ScopedContext(env).context, code.c_str(),
@@ -98,6 +111,7 @@ void ValidateBase<T>::CompileSuccessfully(std::string code,
<< "ERROR: " << diagnostic->error
<< "\nSPIR-V could not be compiled into binary:\n"
<< code;
spvDiagnosticDestroy(diagnostic);
}
template <typename T>
@@ -110,6 +124,7 @@ void ValidateBase<T>::OverwriteAssembledBinary(uint32_t index, uint32_t word) {
template <typename T>
spv_result_t ValidateBase<T>::ValidateInstructions(spv_target_env env) {
DestroyDiagnostic();
return spvValidateWithOptions(ScopedContext(env).context, options_,
get_const_binary(), &diagnostic_);
}
@@ -117,6 +132,7 @@ spv_result_t ValidateBase<T>::ValidateInstructions(spv_target_env env) {
template <typename T>
spv_result_t ValidateBase<T>::ValidateAndRetrieveValidationState(
spv_target_env env) {
DestroyDiagnostic();
return spvtools::val::ValidateBinaryAndKeepValidationState(
ScopedContext(env).context, options_, get_const_binary()->code,
get_const_binary()->wordCount, &diagnostic_, &vstate_);

View File

@@ -780,6 +780,8 @@ class OpTypeArrayLengthTest
// Runs spvValidate() on v, printing any errors via spvDiagnosticPrint().
spv_result_t Val(const SpirvVector& v, const std::string& expected_err = "") {
spv_const_binary_t cbinary{v.data(), v.size()};
spvDiagnosticDestroy(diagnostic_);
diagnostic_ = nullptr;
const auto status =
spvValidate(ScopedContext().context, &cbinary, &diagnostic_);
if (status != SPV_SUCCESS) {

View File

@@ -919,6 +919,36 @@ TEST_F(ValidateLogicals, OpSGreaterThanDifferentBitWidth) {
"width: SGreaterThan"));
}
TEST_F(ValidateLogicals, PSBSelectSuccess) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %val1 AliasedPointerEXT
%uint64 = OpTypeInt 64 0
%bool = OpTypeBool
%true = OpConstantTrue %bool
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %pptr_f Function
%val2 = OpLoad %ptr %val1
%val3 = OpSelect %ptr %true %val2 %val2
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -138,7 +138,9 @@ OpFunctionEnd
TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceRuntimeArrayGood) {
std::string spirv = R"(
OpCapability RuntimeDescriptorArrayEXT
OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
@@ -455,6 +457,93 @@ OpFunctionEnd
" %5 = OpVariable %_ptr_Uniform_float Uniform %float_1\n"));
}
TEST_F(ValidateMemory, WebGPUOutputStorageClassWithoutInitializerBad) {
std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%float = OpTypeFloat 32
%float_ptr = OpTypePointer Output %float
%1 = OpVariable %float_ptr Output
%void = OpTypeVoid
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%2 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpVariable, <id> '4[%4]', must have an initializer.\n"
"From WebGPU execution environment spec:\n"
"All variables in the following storage classes must have an "
"initializer: Output, Private, or Function\n"
" %4 = OpVariable %_ptr_Output_float Output\n"));
}
TEST_F(ValidateMemory, WebGPUFunctionStorageClassWithoutInitializerBad) {
std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%float = OpTypeFloat 32
%float_ptr = OpTypePointer Function %float
%void = OpTypeVoid
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
%2 = OpVariable %float_ptr Function
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpVariable, <id> '7[%7]', must have an initializer.\n"
"From WebGPU execution environment spec:\n"
"All variables in the following storage classes must have an "
"initializer: Output, Private, or Function\n"
" %7 = OpVariable %_ptr_Function_float Function\n"));
}
TEST_F(ValidateMemory, WebGPUPrivateStorageClassWithoutInitializerBad) {
std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%float = OpTypeFloat 32
%float_ptr = OpTypePointer Private %float
%1 = OpVariable %float_ptr Private
%void = OpTypeVoid
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%2 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpVariable, <id> '4[%4]', must have an initializer.\n"
"From WebGPU execution environment spec:\n"
"All variables in the following storage classes must have an "
"initializer: Output, Private, or Function\n"
" %4 = OpVariable %_ptr_Private_float Private\n"));
}
TEST_F(ValidateMemory, VulkanInitializerWithOutputStorageClassesGood) {
std::string spirv = R"(
OpCapability Shader
@@ -1537,6 +1626,732 @@ OpFunctionEnd
HasSubstr("Operand 1[%incorrect] requires a type"));
}
TEST_F(ValidateMemory, PSBLoadAlignedSuccess) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %val1 AliasedPointerEXT
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %pptr_f Function
%val2 = OpLoad %ptr %val1
%val3 = OpLoad %uint64 %val2 Aligned 8
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateMemory, PSBLoadAlignedMissing) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %val1 AliasedPointerEXT
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %pptr_f Function
%val2 = OpLoad %ptr %val1
%val3 = OpLoad %uint64 %val2
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"Memory accesses with PhysicalStorageBufferEXT must use Aligned"));
}
TEST_F(ValidateMemory, PSBStoreAlignedSuccess) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %val1 AliasedPointerEXT
%uint64 = OpTypeInt 64 0
%u64_1 = OpConstant %uint64 1
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %pptr_f Function
%val2 = OpLoad %ptr %val1
OpStore %val2 %u64_1 Aligned 8
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateMemory, PSBStoreAlignedMissing) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %val1 AliasedPointerEXT
%uint64 = OpTypeInt 64 0
%u64_1 = OpConstant %uint64 1
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
%val1 = OpVariable %pptr_f Function
%val2 = OpLoad %ptr %val1
OpStore %val2 %u64_1 None
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"Memory accesses with PhysicalStorageBufferEXT must use Aligned"));
}
TEST_F(ValidateMemory, PSBVariable) {
const std::string body = R"(
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %val1 AliasedPointerEXT
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
%val1 = OpVariable %ptr PhysicalStorageBufferEXT
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("PhysicalStorageBufferEXT must not be used with OpVariable"));
}
TEST_F(ValidateMemory, VulkanRTAOutsideOfStructBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%sampler_t = OpTypeSampler
%array_t = OpTypeRuntimeArray %sampler_t
%array_ptr = OpTypePointer UniformConstant %array_t
%2 = OpVariable %array_ptr UniformConstant
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpVariable, <id> '5[%5]', is attempting to create memory for an "
"illegal type, OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray "
"can only appear as the final member of an OpTypeStruct, thus cannot "
"be instantiated via OpVariable\n %5 = OpVariable "
"%_ptr_UniformConstant__runtimearr_2 UniformConstant\n"));
}
TEST_F(ValidateMemory, VulkanRTAOutsideOfStructWithRuntimeDescriptorArrayGood) {
std::string spirv = R"(
OpCapability Shader
OpCapability RuntimeDescriptorArrayEXT
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%sampler_t = OpTypeSampler
%array_t = OpTypeRuntimeArray %sampler_t
%array_sb_ptr = OpTypePointer StorageBuffer %array_t
%2 = OpVariable %array_sb_ptr StorageBuffer
%array_uc_ptr = OpTypePointer UniformConstant %array_t
%3 = OpVariable %array_uc_ptr UniformConstant
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(
ValidateMemory,
VulkanRTAOutsideOfStructWithRuntimeDescriptorArrayAndWrongStorageClassBad) {
std::string spirv = R"(
OpCapability Shader
OpCapability RuntimeDescriptorArrayEXT
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%uint_t = OpTypeInt 32 0
%array_t = OpTypeRuntimeArray %uint_t
%array_ptr = OpTypePointer Workgroup %array_t
%2 = OpVariable %array_ptr Workgroup
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("For Vulkan with RuntimeDescriptorArrayEXT, a variable "
"containing OpTypeRuntimeArray must have storage class of "
"StorageBuffer, Uniform, or UniformConstant.\n %5 = "
"OpVariable %_ptr_Workgroup__runtimearr_uint Workgroup\n"));
}
TEST_F(ValidateMemory, VulkanRTAInsideStorageBufferStructGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%array_t = OpTypeRuntimeArray %uint_t
%struct_t = OpTypeStruct %array_t
%struct_ptr = OpTypePointer StorageBuffer %struct_t
%2 = OpVariable %struct_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateMemory, VulkanRTAInsideWrongStorageClassStructBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%uint_t = OpTypeInt 32 0
%array_t = OpTypeRuntimeArray %uint_t
%struct_t = OpTypeStruct %array_t
%struct_ptr = OpTypePointer Workgroup %struct_t
%2 = OpVariable %struct_ptr Workgroup
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"For Vulkan, OpTypeStruct variables containing OpTypeRuntimeArray "
"must have storage class of StorageBuffer or Uniform.\n %6 = "
"OpVariable %_ptr_Workgroup__struct_4 Workgroup\n"));
}
TEST_F(ValidateMemory, VulkanRTAInsideStorageBufferStructWithoutBlockBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%uint_t = OpTypeInt 32 0
%array_t = OpTypeRuntimeArray %uint_t
%struct_t = OpTypeStruct %array_t
%struct_ptr = OpTypePointer StorageBuffer %struct_t
%2 = OpVariable %struct_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("For Vulkan, an OpTypeStruct variable containing an "
"OpTypeRuntimeArray must be decorated with Block if it "
"has storage class StorageBuffer.\n %6 = OpVariable "
"%_ptr_StorageBuffer__struct_4 StorageBuffer\n"));
}
TEST_F(ValidateMemory, VulkanRTAInsideUniformStructGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpDecorate %struct_t BufferBlock
%uint_t = OpTypeInt 32 0
%array_t = OpTypeRuntimeArray %uint_t
%struct_t = OpTypeStruct %array_t
%struct_ptr = OpTypePointer Uniform %struct_t
%2 = OpVariable %struct_ptr Uniform
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateMemory, VulkanRTAInsideUniformStructWithoutBufferBlockBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%uint_t = OpTypeInt 32 0
%array_t = OpTypeRuntimeArray %uint_t
%struct_t = OpTypeStruct %array_t
%struct_ptr = OpTypePointer Uniform %struct_t
%2 = OpVariable %struct_ptr Uniform
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("For Vulkan, an OpTypeStruct variable containing an "
"OpTypeRuntimeArray must be decorated with BufferBlock "
"if it has storage class Uniform.\n %6 = OpVariable "
"%_ptr_Uniform__struct_4 Uniform\n"));
}
TEST_F(ValidateMemory, VulkanRTAInsideRTABad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%sampler_t = OpTypeSampler
%inner_array_t = OpTypeRuntimeArray %sampler_t
%array_t = OpTypeRuntimeArray %inner_array_t
%array_ptr = OpTypePointer UniformConstant %array_t
%2 = OpVariable %array_ptr UniformConstant
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '3[%_runtimearr_2]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_2 = "
"OpTypeRuntimeArray %_runtimearr_2\n"));
}
TEST_F(ValidateMemory, VulkanRTAInsideRTAWithRuntimeDescriptorArrayBad) {
std::string spirv = R"(
OpCapability RuntimeDescriptorArrayEXT
OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t Block
%uint_t = OpTypeInt 32 0
%inner_array_t = OpTypeRuntimeArray %uint_t
%array_t = OpTypeRuntimeArray %inner_array_t
%array_ptr = OpTypePointer StorageBuffer %array_t
%2 = OpVariable %array_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '4[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_uint = "
"OpTypeRuntimeArray %_runtimearr_uint\n"));
}
TEST_F(ValidateMemory,
VulkanUniformStructInsideRTAWithRuntimeDescriptorArrayGood) {
std::string spirv = R"(
OpCapability RuntimeDescriptorArrayEXT
OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%struct_t = OpTypeStruct %uint_t
%array_t = OpTypeRuntimeArray %struct_t
%array_ptr = OpTypePointer Uniform %array_t
%2 = OpVariable %array_ptr Uniform
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateMemory, VulkanRTAInsideRTAInsideStructBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%inner_array_t = OpTypeRuntimeArray %uint_t
%array_t = OpTypeRuntimeArray %inner_array_t
%struct_t = OpTypeStruct %array_t
%struct_ptr = OpTypePointer StorageBuffer %struct_t
%2 = OpVariable %struct_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '5[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_uint = "
"OpTypeRuntimeArray %_runtimearr_uint\n"));
}
TEST_F(ValidateMemory,
VulkanRTAInsideRTAInsideStructWithRuntimeDescriptorArrayBad) {
std::string spirv = R"(
OpCapability RuntimeDescriptorArrayEXT
OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%inner_array_t = OpTypeRuntimeArray %uint_t
%array_t = OpTypeRuntimeArray %inner_array_t
%struct_t = OpTypeStruct %array_t
%struct_ptr = OpTypePointer StorageBuffer %struct_t
%2 = OpVariable %struct_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '5[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_uint = "
"OpTypeRuntimeArray %_runtimearr_uint\n"));
}
TEST_F(ValidateMemory, VulkanRTAInsideArrayBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%uint_t = OpTypeInt 32 0
%dim = OpConstant %uint_t 1
%sampler_t = OpTypeSampler
%inner_array_t = OpTypeRuntimeArray %sampler_t
%array_t = OpTypeArray %inner_array_t %dim
%array_ptr = OpTypePointer UniformConstant %array_t
%2 = OpVariable %array_ptr UniformConstant
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeArray Element Type <id> '5[%_runtimearr_4]' is not "
"valid in Vulkan environment.\n %_arr__runtimearr_4_uint_1 = "
"OpTypeArray %_runtimearr_4 %uint_1\n"));
}
TEST_F(ValidateMemory, VulkanRTAInsideArrayWithRuntimeDescriptorArrayBad) {
std::string spirv = R"(
OpCapability RuntimeDescriptorArrayEXT
OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t Block
%uint_t = OpTypeInt 32 0
%dim = OpConstant %uint_t 1
%sampler_t = OpTypeSampler
%inner_array_t = OpTypeRuntimeArray %uint_t
%array_t = OpTypeRuntimeArray %inner_array_t
%array_ptr = OpTypePointer StorageBuffer %array_t
%2 = OpVariable %array_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '6[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_uint = "
"OpTypeRuntimeArray %_runtimearr_uint\n"));
}
TEST_F(ValidateMemory, VulkanRTAInsideArrayInsideStructBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%dim = OpConstant %uint_t 1
%inner_array_t = OpTypeRuntimeArray %uint_t
%array_t = OpTypeArray %inner_array_t %dim
%struct_t = OpTypeStruct %array_t
%struct_ptr = OpTypePointer StorageBuffer %struct_t
%2 = OpVariable %struct_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeArray Element Type <id> '6[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_arr__runtimearr_uint_uint_1 "
"= OpTypeArray %_runtimearr_uint %uint_1\n"));
}
TEST_F(ValidateMemory,
VulkanRTAInsideArrayInsideStructWithRuntimeDescriptorArrayBad) {
std::string spirv = R"(
OpCapability RuntimeDescriptorArrayEXT
OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%dim = OpConstant %uint_t 1
%inner_array_t = OpTypeRuntimeArray %uint_t
%array_t = OpTypeArray %inner_array_t %dim
%struct_t = OpTypeStruct %array_t
%struct_ptr = OpTypePointer StorageBuffer %struct_t
%2 = OpVariable %struct_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeArray Element Type <id> '6[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_arr__runtimearr_uint_uint_1 "
"= OpTypeArray %_runtimearr_uint %uint_1\n"));
}
TEST_F(ValidateMemory, VulkanRTAStructInsideRTAWithRuntimeDescriptorArrayGood) {
std::string spirv = R"(
OpCapability RuntimeDescriptorArrayEXT
OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%inner_array_t = OpTypeRuntimeArray %uint_t
%struct_t = OpTypeStruct %inner_array_t
%array_t = OpTypeRuntimeArray %struct_t
%array_ptr = OpTypePointer StorageBuffer %array_t
%2 = OpVariable %array_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateMemory, VulkanRTAStructInsideArrayGood) {
std::string spirv = R"(
OpCapability RuntimeDescriptorArrayEXT
OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %array_t ArrayStride 4
OpMemberDecorate %struct_t 0 Offset 0
OpDecorate %struct_t Block
%uint_t = OpTypeInt 32 0
%inner_array_t = OpTypeRuntimeArray %uint_t
%struct_t = OpTypeStruct %inner_array_t
%array_size = OpConstant %uint_t 5
%array_t = OpTypeArray %struct_t %array_size
%array_ptr = OpTypePointer StorageBuffer %array_t
%2 = OpVariable %array_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
%func = OpFunction %void None %func_t
%1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -306,6 +306,54 @@ TEST_F(ValidationStateTest, CheckWebGPUIndirectlyRecursiveBodyBad) {
"called.\n %9 = OpFunctionCall %_struct_5 %10\n"));
}
TEST_F(ValidationStateTest,
CheckWebGPUDuplicateEntryNamesDifferentFunctionsBad) {
std::string spirv = std::string(kVulkanMemoryHeader) + R"(
OpEntryPoint Fragment %func_1 "main"
OpEntryPoint Vertex %func_2 "main"
OpExecutionMode %func_1 OriginUpperLeft
%void = OpTypeVoid
%void_f = OpTypeFunction %void
%func_1 = OpFunction %void None %void_f
%label_1 = OpLabel
OpReturn
OpFunctionEnd
%func_2 = OpFunction %void None %void_f
%label_2 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Entry point name \"main\" is not unique, which is not allow "
"in WebGPU env.\n %1 = OpFunction %void None %4\n"));
}
TEST_F(ValidationStateTest, CheckWebGPUDuplicateEntryNamesSameFunctionBad) {
std::string spirv = std::string(kVulkanMemoryHeader) + R"(
OpEntryPoint GLCompute %func_1 "main"
OpEntryPoint Vertex %func_1 "main"
%void = OpTypeVoid
%void_f = OpTypeFunction %void
%func_1 = OpFunction %void None %void_f
%label_1 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Entry point name \"main\" is not unique, which is not allow "
"in WebGPU env.\n %1 = OpFunction %void None %3\n"));
}
} // namespace
} // namespace val
} // namespace spvtools