validate & test of literal's upper bits

Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/660
This commit is contained in:
Jesus Carabano 2017-10-18 12:00:02 +03:00 committed by David Neto
parent f35963588b
commit 4dbcef62ee
7 changed files with 251 additions and 0 deletions

View File

@ -47,6 +47,7 @@ SPVTOOLS_SRC_FILES := \
source/validate_image.cpp \
source/validate_instruction.cpp \
source/validate_layout.cpp \
source/validate_literals.cpp \
source/validate_logicals.cpp \
source/validate_primitives.cpp \
source/validate_type_unique.cpp

View File

@ -270,6 +270,7 @@ set(SPIRV_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/validate_image.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_layout.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_literals.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_logicals.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_primitives.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_type_unique.cpp

View File

@ -53,6 +53,7 @@ using libspirv::DataRulesPass;
using libspirv::Extension;
using libspirv::IdPass;
using libspirv::InstructionPass;
using libspirv::LiteralsPass;
using libspirv::ModuleLayoutPass;
using libspirv::ValidationState_t;
@ -188,6 +189,7 @@ spv_result_t ProcessInstruction(void* user_data,
if (auto error = ImagePass(_, inst)) return error;
if (auto error = AtomicsPass(_, inst)) return error;
if (auto error = PrimitivesPass(_, inst)) return error;
if (auto error = LiteralsPass(_, inst)) return error;
return SPV_SUCCESS;
}

View File

@ -143,6 +143,10 @@ spv_result_t ImagePass(ValidationState_t& _,
spv_result_t AtomicsPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst);
/// Validates correctness of literal numbers.
spv_result_t LiteralsPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst);
// Validates that capability declarations use operands allowed in the current
// context.
spv_result_t CapabilityPass(ValidationState_t& _,

View File

@ -0,0 +1,99 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Validates literal numbers.
#include "validate.h"
#include <cassert>
#include "diagnostic.h"
#include "opcode.h"
#include "val/instruction.h"
#include "val/validation_state.h"
namespace libspirv {
namespace {
// Returns true if the operand holds a literal number
bool IsLiteralNumber(const spv_parsed_operand_t* operand) {
switch (operand->number_kind) {
case SPV_NUMBER_SIGNED_INT:
case SPV_NUMBER_UNSIGNED_INT:
case SPV_NUMBER_FLOATING:
return true;
default:
return false;
}
}
// Verifies that the upper bits of the given upper |word| with given
// lower |width| are zero- or sign-extended when |signed_int| is true
bool VerifyUpperBits(uint32_t word, uint32_t width, bool signed_int) {
assert(width < 32);
assert(0 < width);
const uint32_t upper_mask = 0xFFFFFFFFu << width;
const uint32_t upper_bits = word & upper_mask;
bool result = false;
if (signed_int) {
const uint32_t sign_bit = word & (1u << (width - 1));
if (sign_bit) {
result = upper_bits == upper_mask;
} else {
result = upper_bits == 0;
}
} else {
result = upper_bits == 0;
}
return result;
}
} // namespace
// Validates that literal numbers are represented according to the spec
spv_result_t LiteralsPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
// For every operand that is a literal number
for (uint16_t i = 0; i < inst->num_operands; i++) {
const spv_parsed_operand_t* operand = inst->operands + i;
if (!IsLiteralNumber(operand)) continue;
// The upper bits are always in the last word (little-endian)
int last_index = operand->offset + operand->num_words - 1;
const uint32_t upper_word = inst->words[last_index];
// TODO(jcaraban): is the |word size| defined in some header?
const uint32_t word_size = 32;
uint32_t bit_width = operand->number_bit_width;
// Bit widths that are a multiple of the word size have no upper bits
const auto remaining_value_bits = bit_width % word_size;
if (remaining_value_bits == 0) continue;
const bool signedness = operand->number_kind == SPV_NUMBER_SIGNED_INT;
if (!VerifyUpperBits(upper_word, remaining_value_bits, signedness)) {
return _.diag(SPV_ERROR_INVALID_VALUE)
<< "The high-order bits of a literal number in instruction <id> "
<< inst->result_id << " must be 0 for a floating-point type, "
<< "or 0 for an integer type with Signedness of 0, "
<< "or sign extended when Signedness is 1";
}
}
return SPV_SUCCESS;
}
} // namespace libspirv

View File

@ -152,6 +152,12 @@ add_spvtools_unittest(TARGET val_instructions
LIBS ${SPIRV_TOOLS}
)
add_spvtools_unittest(TARGET val_literals
SRCS val_literals_test.cpp
${VAL_TEST_COMMON_SRCS}
LIBS ${SPIRV_TOOLS}
)
add_spvtools_unittest(TARGET val_extensions
SRCS val_extensions_test.cpp
${VAL_TEST_COMMON_SRCS}

View File

@ -0,0 +1,138 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Validation tests for ilegal literals
#include <string>
#include <utility>
#include "gmock/gmock.h"
#include "val_fixtures.h"
using ::testing::HasSubstr;
using ValidateLiterals = spvtest::ValidateBase<std::string>;
using ValidateLiteralsShader = spvtest::ValidateBase<std::string>;
using ValidateLiteralsKernel = spvtest::ValidateBase<std::string>;
namespace {
std::string GenerateShaderCode() {
std::string str = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Int16
OpCapability Int64
OpCapability Float16
OpCapability Float64
OpMemoryModel Logical GLSL450
%int16 = OpTypeInt 16 1
%uint16 = OpTypeInt 16 0
%int32 = OpTypeInt 32 1
%uint32 = OpTypeInt 32 0
%int64 = OpTypeInt 64 1
%uint64 = OpTypeInt 64 0
%half = OpTypeFloat 16
%float = OpTypeFloat 32
%double = OpTypeFloat 64
%10 = OpTypeVoid
)";
return str;
}
std::string GenerateKernelCode() {
std::string str = R"(
OpCapability Kernel
OpCapability Addresses
OpCapability Linkage
OpCapability Int8
OpMemoryModel Physical64 OpenCL
%uint8 = OpTypeInt 8 0
)";
return str;
}
TEST_F(ValidateLiterals, LiteralsShaderGood) {
std::string str = GenerateShaderCode() + R"(
%11 = OpConstant %int16 !0x00007FFF
%12 = OpConstant %int16 !0xFFFF8000
%13 = OpConstant %int16 !0xFFFFABCD
%14 = OpConstant %uint16 !0x0000ABCD
%15 = OpConstant %int16 -32768
%16 = OpConstant %uint16 65535
%17 = OpConstant %int32 -2147483648
%18 = OpConstant %uint32 4294967295
%19 = OpConstant %int64 -9223372036854775808
%20 = OpConstant %uint64 18446744073709551615
%21 = OpConstant %half !0x0000FFFF
%22 = OpConstant %float !0xFFFFFFFF
%23 = OpConstant %double !0xFFFFFFFF !0xFFFFFFFF
)";
CompileSuccessfully(str);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateLiteralsShader, LiteralsShaderBad) {
std::string str = GenerateShaderCode() + GetParam();
std::string inst_id = "11";
CompileSuccessfully(str);
EXPECT_EQ(SPV_ERROR_INVALID_VALUE, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("The high-order bits of a literal number in instruction <id> " +
inst_id +
" must be 0 for a floating-point type, "
"or 0 for an integer type with Signedness of 0, "
"or sign extended when Signedness is 1"));
}
INSTANTIATE_TEST_CASE_P(
LiteralsShaderCases, ValidateLiteralsShader,
::testing::Values("%11 = OpConstant %int16 !0xFFFF0000", // Sign bit is 0
"%11 = OpConstant %int16 !0x00008000", // Sign bit is 1
"%11 = OpConstant %int16 !0xABCD8000", // Sign bit is 1
"%11 = OpConstant %int16 !0xABCD0000",
"%11 = OpConstant %uint16 !0xABCD0000",
"%11 = OpConstant %half !0xABCD0000",
"%11 = OpConstant %half !0x00010000"));
TEST_F(ValidateLiterals, LiteralsKernelGood) {
std::string str = GenerateKernelCode() + R"(
%4 = OpConstant %uint8 !0x000000AB
%6 = OpConstant %uint8 255
)";
CompileSuccessfully(str);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateLiteralsKernel, LiteralsKernelBad) {
std::string str = GenerateKernelCode() + GetParam();
std::string inst_id = "2";
CompileSuccessfully(str);
EXPECT_EQ(SPV_ERROR_INVALID_VALUE, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("The high-order bits of a literal number in instruction <id> " +
inst_id +
" must be 0 for a floating-point type, "
"or 0 for an integer type with Signedness of 0, "
"or sign extended when Signedness is 1"));
}
INSTANTIATE_TEST_CASE_P(
LiteralsKernelCases, ValidateLiteralsKernel,
::testing::Values("%2 = OpConstant %uint8 !0xABCDEF00",
"%2 = OpConstant %uint8 !0xABCDEFFF"));
} // namespace