Disassembler support for mask expressions.
authorDavid Neto <dneto@google.com>
Fri, 25 Sep 2015 16:43:37 +0000 (12:43 -0400)
committerDavid Neto <dneto@google.com>
Mon, 26 Oct 2015 16:55:33 +0000 (12:55 -0400)
readme.md
source/binary.cpp
test/BinaryToText.cpp

index a79d78a..f2d6ecb 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -12,9 +12,9 @@ into other code bases directly.
 ### Assembler and disassembler
 
 * Based on SPIR-V 0.99 Revision 32
-  * Assembler supports core instructions and enumerants from Rev 32.
+  * Supports core instructions and enumerants from Rev 32.
   * Capability dependencies may be incomplete or incorrect.
-* Assembler supports all core instructions, for 32-bit code.
+* Supports all core instructions, for 32-bit code.
   * Handling of non-32-bit literal numbers has been recently clarified.
     Those changes have not been applied.
 * All GLSL std450 extended instructions are supported.
@@ -42,6 +42,8 @@ The validator is incomplete.  See the Future Work section for more information.
     same numeric ID as named ID `%foo`.
   * In particular, an ID with a number for a name doesn't necessarily
     map to that number. E.g. `%2` doesn't necessarily map to ID 2.
+* Disassembler emits mask expressions for mask combinations instead of
+  erroring out.
 
 2015-09-18
 * MILESTONE: This version of the assembler supports all of SPIR-V Rev31,
index 4fa862b..5c21e7f 100644 (file)
@@ -305,18 +305,13 @@ spv_result_t spvBinaryDecodeOperand(
     case SPV_OPERAND_TYPE_DIMENSIONALITY:
     case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
     case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
-    case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
     case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
     case SPV_OPERAND_TYPE_LINKAGE_TYPE:
     case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
     case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
     case SPV_OPERAND_TYPE_DECORATION:
     case SPV_OPERAND_TYPE_BUILT_IN:
-    case SPV_OPERAND_TYPE_SELECTION_CONTROL:
-    case SPV_OPERAND_TYPE_LOOP_CONTROL:
-    case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
     case SPV_OPERAND_TYPE_MEMORY_SEMANTICS:
-    case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
     case SPV_OPERAND_TYPE_GROUP_OPERATION:
     case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
@@ -326,13 +321,66 @@ spv_result_t spvBinaryDecodeOperand(
                                      spvFixWord(words[0], endian), &entry)) {
         DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " operand '"
                    << words[0] << "'.";
-        return SPV_ERROR_INVALID_TEXT;
+        return SPV_ERROR_INVALID_TEXT; // TODO(dneto): Surely this is invalid binary.
       }
       stream.get() << entry->name;
       // Prepare to accept operands to this operand, if needed.
       spvPrependOperandTypes(entry->operandTypes, pExpectedOperands);
       position->index++;
     } break;
+    case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
+    case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
+    case SPV_OPERAND_TYPE_LOOP_CONTROL:
+    case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
+    case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
+    case SPV_OPERAND_TYPE_SELECTION_CONTROL: {
+      // This operand is a mask.
+      // Scan it from least significant bit to most significant bit.  For each
+      // set bit, emit the name of that bit and prepare to parse its operands,
+      // if any.
+      uint32_t remaining_word = spvFixWord(words[0], endian);
+      uint32_t mask;
+      int num_emitted = 0;
+      for (mask = 1; remaining_word; mask <<= 1) {
+        if (remaining_word & mask) {
+          remaining_word ^= mask;
+          spv_operand_desc entry;
+          if (spvOperandTableValueLookup(operandTable, type, mask, &entry)) {
+            DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " operand '"
+                       << words[0] << "'.";
+            return SPV_ERROR_INVALID_BINARY;
+          }
+          if (num_emitted) stream.get() << "|";
+          stream.get() << entry->name;
+          num_emitted++;
+        }
+      }
+      if (!num_emitted) {
+        // An operand value of 0 was provided, so represent it by the name
+        // of the 0 value. In many cases, that's "None".
+        spv_operand_desc entry;
+        if (SPV_SUCCESS ==
+            spvOperandTableValueLookup(operandTable, type, 0, &entry)) {
+          stream.get() << entry->name;
+          // Prepare for its operands, if any.
+          spvPrependOperandTypes(entry->operandTypes, pExpectedOperands);
+        }
+      }
+      // Prepare for subsequent operands, if any.
+      // Scan from MSB to LSB since we can only prepend operands to a pattern.
+      remaining_word = spvFixWord(words[0], endian);
+      for (mask = (1u << 31); remaining_word; mask >>= 1) {
+        if (remaining_word & mask) {
+          remaining_word ^= mask;
+          spv_operand_desc entry;
+          if (SPV_SUCCESS ==
+              spvOperandTableValueLookup(operandTable, type, mask, &entry)) {
+            spvPrependOperandTypes(entry->operandTypes, pExpectedOperands);
+          }
+        }
+      }
+      position->index++;
+    } break;
     default: {
       DIAGNOSTIC << "Invalid binary operand '" << type << "'";
       return SPV_ERROR_INVALID_BINARY;
index a3bdc33..d0a9760 100644 (file)
 
 #include "UnitSPIRV.h"
 
+#include "gmock/gmock.h"
+#include "TestFixture.h"
+
+using ::testing::Eq;
+
 namespace {
 
 class BinaryToText : public ::testing::Test {
@@ -264,4 +269,96 @@ TEST(BinaryToTextSmall, LiteralDouble) {
   spvTextDestroy(text);
 }
 
+using RoundTripInstructionsTest =
+    spvtest::TextToBinaryTestBase<::testing::TestWithParam<std::string>>;
+
+TEST_P(RoundTripInstructionsTest, Sample) {
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(GetParam()),
+              Eq(GetParam()));
+};
+
+// clang-format off
+INSTANTIATE_TEST_CASE_P(
+    MemoryAccessMasks, RoundTripInstructionsTest,
+    ::testing::ValuesIn(std::vector<std::string>{
+        "OpStore %1 %2\n",       // 3 words long.
+        "OpStore %1 %2 None\n",  // 4 words long, explicit final 0.
+        "OpStore %1 %2 Volatile\n",
+        "OpStore %1 %2 Aligned 8\n",
+        "OpStore %1 %2 Nontemporal\n",
+        // Combinations show the names from LSB to MSB
+        "OpStore %1 %2 Volatile|Aligned 16\n",
+        "OpStore %1 %2 Volatile|Nontemporal\n",
+        "OpStore %1 %2 Volatile|Aligned|Nontemporal 32\n",
+    }));
+// clang-format on
+
+INSTANTIATE_TEST_CASE_P(
+    FPFastMathModeMasks, RoundTripInstructionsTest,
+    ::testing::ValuesIn(std::vector<std::string>{
+        "OpDecorate %1 FPFastMathMode None\n",
+        "OpDecorate %1 FPFastMathMode NotNaN\n",
+        "OpDecorate %1 FPFastMathMode NotInf\n",
+        "OpDecorate %1 FPFastMathMode NSZ\n",
+        "OpDecorate %1 FPFastMathMode AllowRecip\n",
+        "OpDecorate %1 FPFastMathMode Fast\n",
+        // Combinations show the names from LSB to MSB
+        "OpDecorate %1 FPFastMathMode NotNaN|NotInf\n",
+        "OpDecorate %1 FPFastMathMode NSZ|AllowRecip\n",
+        "OpDecorate %1 FPFastMathMode NotNaN|NotInf|NSZ|AllowRecip|Fast\n",
+    }));
+
+INSTANTIATE_TEST_CASE_P(LoopControlMasks, RoundTripInstructionsTest,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "OpLoopMerge %1 %2 None\n",
+                            "OpLoopMerge %1 %2 Unroll\n",
+                            "OpLoopMerge %1 %2 DontUnroll\n",
+                            "OpLoopMerge %1 %2 Unroll|DontUnroll\n",
+                        }));
+
+INSTANTIATE_TEST_CASE_P(SelectionControlMasks, RoundTripInstructionsTest,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "OpSelectionMerge %1 None\n",
+                            "OpSelectionMerge %1 Flatten\n",
+                            "OpSelectionMerge %1 DontFlatten\n",
+                            "OpSelectionMerge %1 Flatten|DontFlatten\n",
+                        }));
+
+// clang-format off
+INSTANTIATE_TEST_CASE_P(
+    FunctionControlMasks, RoundTripInstructionsTest,
+    ::testing::ValuesIn(std::vector<std::string>{
+        "%2 = OpFunction %1 None %3\n",
+        "%2 = OpFunction %1 Inline %3\n",
+        "%2 = OpFunction %1 DontInline %3\n",
+        "%2 = OpFunction %1 Pure %3\n",
+        "%2 = OpFunction %1 Const %3\n",
+        "%2 = OpFunction %1 Inline|Pure|Const %3\n",
+        "%2 = OpFunction %1 DontInline|Const %3\n",
+    }));
+// clang-format on
+
+// clang-format off
+INSTANTIATE_TEST_CASE_P(
+    ImageMasks, RoundTripInstructionsTest,
+    ::testing::ValuesIn(std::vector<std::string>{
+        "%2 = OpImageFetch %1 %3 %4\n",
+        "%2 = OpImageFetch %1 %3 %4 None\n",
+        "%2 = OpImageFetch %1 %3 %4 Bias %5\n",
+        "%2 = OpImageFetch %1 %3 %4 Lod %5\n",
+        "%2 = OpImageFetch %1 %3 %4 Grad %5 %6\n",
+        "%2 = OpImageFetch %1 %3 %4 ConstOffset %5\n",
+        "%2 = OpImageFetch %1 %3 %4 Offset %5\n",
+        "%2 = OpImageFetch %1 %3 %4 ConstOffsets %5\n",
+        "%2 = OpImageFetch %1 %3 %4 Sample %5\n",
+        "%2 = OpImageFetch %1 %3 %4 MinLod %5\n",
+        "%2 = OpImageFetch %1 %3 %4 Bias|Lod|Grad %5 %6 %7 %8\n",
+        "%2 = OpImageFetch %1 %3 %4 ConstOffset|Offset|ConstOffsets"
+              " %5 %6 %7\n",
+        "%2 = OpImageFetch %1 %3 %4 Sample|MinLod %5 %6\n",
+        "%2 = OpImageFetch %1 %3 %4"
+              " Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod"
+              " %5 %6 %7 %8 %9 %10 %11 %12 %13\n"}));
+// clang-format on
+
 }  // anonymous namespace