SPIRV-Tools/test/operand_pattern_test.cpp
Lei Zhang 1ef6b19260 Migrate to use unified grammar tables
Previously we keep a separate static grammar table for opcodes/
operands per SPIR-V version. This commit changes that to use a
single unified static grammar table for opcodes/operands.

This essentially changes how grammar facts are queried against
a certain target environment. There are only limited filtering
according to the desired target environment; a symbol is
considered as available as long as:

1. The target environment satisfies the minimal requirement of
   the symbol; or
2. There is at least one extension enabling this symbol.

Note that the second rule assumes the extension enabling the
symbol is indeed requested in the SPIR-V code; checking that
should be the validator's work.

Also fixed a few grammar related issues:
* Rounding mode capability requirements are moved to client APIs.
* Reserved symbols not available in any extension is no longer
  recognized by assembler.
2018-03-17 15:25:26 -04:00

266 lines
11 KiB
C++

// Copyright (c) 2015-2016 The Khronos Group 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.
#include "unit_spirv.h"
#include "gmock/gmock.h"
#include "source/operand.h"
using ::testing::Eq;
namespace {
TEST(OperandPattern, InitiallyEmpty) {
spv_operand_pattern_t empty;
EXPECT_THAT(empty, Eq(spv_operand_pattern_t{}));
EXPECT_EQ(0u, empty.size());
EXPECT_TRUE(empty.empty());
}
TEST(OperandPattern, PushBacksAreOnTheRight) {
spv_operand_pattern_t pattern;
pattern.push_back(SPV_OPERAND_TYPE_ID);
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_ID}));
EXPECT_EQ(1u, pattern.size());
EXPECT_TRUE(!pattern.empty());
EXPECT_EQ(SPV_OPERAND_TYPE_ID, pattern.back());
pattern.push_back(SPV_OPERAND_TYPE_NONE);
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_NONE}));
EXPECT_EQ(2u, pattern.size());
EXPECT_TRUE(!pattern.empty());
EXPECT_EQ(SPV_OPERAND_TYPE_NONE, pattern.back());
}
TEST(OperandPattern, PopBacksAreOnTheRight) {
spv_operand_pattern_t pattern{SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_LITERAL_INTEGER};
pattern.pop_back();
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_ID}));
pattern.pop_back();
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{}));
}
// A test case for typed mask expansion
struct MaskExpansionCase {
spv_operand_type_t type;
uint32_t mask;
spv_operand_pattern_t initial;
spv_operand_pattern_t expected;
};
using MaskExpansionTest = ::testing::TestWithParam<MaskExpansionCase>;
TEST_P(MaskExpansionTest, Sample) {
spv_operand_table operandTable = nullptr;
auto env = SPV_ENV_UNIVERSAL_1_0;
ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&operandTable, env));
spv_operand_pattern_t pattern(GetParam().initial);
spvPushOperandTypesForMask(env, operandTable, GetParam().type,
GetParam().mask, &pattern);
EXPECT_THAT(pattern, Eq(GetParam().expected));
}
// These macros let us write non-trivial examples without too much text.
#define PREFIX0 SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE
#define PREFIX1 \
SPV_OPERAND_TYPE_STORAGE_CLASS, SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE, \
SPV_OPERAND_TYPE_ID
INSTANTIATE_TEST_CASE_P(
OperandPattern, MaskExpansionTest,
::testing::ValuesIn(std::vector<MaskExpansionCase>{
// No bits means no change.
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, 0, {PREFIX0}, {PREFIX0}},
// Unknown bits means no change.
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
0xfffffffc,
{PREFIX1},
{PREFIX1}},
// Volatile has no operands.
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
SpvMemoryAccessVolatileMask,
{PREFIX0},
{PREFIX0}},
// Aligned has one literal number operand.
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
SpvMemoryAccessAlignedMask,
{PREFIX1},
{PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}},
// Volatile with Aligned still has just one literal number operand.
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask,
{PREFIX1},
{PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}},
}), );
#undef PREFIX0
#undef PREFIX1
// Returns a vector of all operand types that can be used in a pattern.
std::vector<spv_operand_type_t> allOperandTypes() {
std::vector<spv_operand_type_t> result;
for (int i = 0; i < SPV_OPERAND_TYPE_NUM_OPERAND_TYPES; i++) {
result.push_back(spv_operand_type_t(i));
}
return result;
}
using MatchableOperandExpansionTest =
::testing::TestWithParam<spv_operand_type_t>;
TEST_P(MatchableOperandExpansionTest, MatchableOperandsDontExpand) {
const spv_operand_type_t type = GetParam();
if (!spvOperandIsVariable(type)) {
spv_operand_pattern_t pattern;
const bool did_expand = spvExpandOperandSequenceOnce(type, &pattern);
EXPECT_FALSE(did_expand);
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{}));
}
}
INSTANTIATE_TEST_CASE_P(MatchableOperandExpansion,
MatchableOperandExpansionTest,
::testing::ValuesIn(allOperandTypes()), );
using VariableOperandExpansionTest =
::testing::TestWithParam<spv_operand_type_t>;
TEST_P(VariableOperandExpansionTest, NonMatchableOperandsExpand) {
const spv_operand_type_t type = GetParam();
if (spvOperandIsVariable(type)) {
spv_operand_pattern_t pattern;
const bool did_expand = spvExpandOperandSequenceOnce(type, &pattern);
EXPECT_TRUE(did_expand);
EXPECT_FALSE(pattern.empty());
// For the existing rules, the first expansion of a zero-or-more operand
// type yields a matchable operand type. This isn't strictly necessary.
EXPECT_FALSE(spvOperandIsVariable(pattern.back()));
}
}
INSTANTIATE_TEST_CASE_P(NonMatchableOperandExpansion,
VariableOperandExpansionTest,
::testing::ValuesIn(allOperandTypes()), );
TEST(AlternatePatternFollowingImmediate, Empty) {
EXPECT_THAT(spvAlternatePatternFollowingImmediate({}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, SingleElement) {
// Spot-check a random selection of types.
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_CAPABILITY}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_LOOP_CONTROL}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, SingleResultId) {
EXPECT_THAT(
spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_RESULT_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID}));
}
TEST(AlternatePatternFollowingImmediate, MultipleNonResultIds) {
EXPECT_THAT(
spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER,
SPV_OPERAND_TYPE_CAPABILITY, SPV_OPERAND_TYPE_LOOP_CONTROL,
SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, ResultIdFront) {
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_FP_ROUNDING_MODE,
SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_DIMENSIONALITY,
SPV_OPERAND_TYPE_LINKAGE_TYPE,
SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE,
SPV_OPERAND_TYPE_FP_ROUNDING_MODE, SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_VARIABLE_ID}),
Eq(spv_operand_pattern_t{
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, ResultIdMiddle) {
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_FP_ROUNDING_MODE,
SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_DIMENSIONALITY, SPV_OPERAND_TYPE_LINKAGE_TYPE,
SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE,
SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_FP_ROUNDING_MODE,
SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID}),
Eq(spv_operand_pattern_t{
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, ResultIdBack) {
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID}));
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_FP_ROUNDING_MODE, SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_RESULT_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_DIMENSIONALITY, SPV_OPERAND_TYPE_LINKAGE_TYPE,
SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE,
SPV_OPERAND_TYPE_FP_ROUNDING_MODE, SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_RESULT_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID}));
}
} // anonymous namespace