Use opaque context object to hold SPIR-V info tables.
authorLei Zhang <antiagainst@google.com>
Thu, 12 Nov 2015 18:48:30 +0000 (13:48 -0500)
committerDavid Neto <dneto@google.com>
Thu, 12 Nov 2015 21:27:51 +0000 (16:27 -0500)
Previously the opcode table is declared as an global array and we
have spvOpcodeTableInitialize() modifying it. That can result in
race condition. Now spvOpcodeTabelGet() copies the whole underlying
array.

25 files changed:
CMakeLists.txt
include/libspirv/libspirv.h
source/assembly_grammar.cpp
source/assembly_grammar.h
source/binary.cpp
source/disassemble.cpp
source/opcode.cpp
source/table.cpp [new file with mode: 0644]
source/table.h
source/text.cpp
source/validate.cpp
test/AssemblyFormat.cpp
test/BinaryDestroy.cpp
test/BinaryToText.cpp
test/ExtInstGLSLstd450.cpp
test/ImmediateInt.cpp
test/NamedId.cpp
test/TestFixture.h
test/TextDestroy.cpp
test/TextToBinary.cpp
test/Validate.cpp
test/ValidateID.cpp
tools/as/as.cpp
tools/dis/dis.cpp
tools/val/val.cpp

index d4cba38..5cc0df4 100644 (file)
@@ -126,6 +126,7 @@ set(SPIRV_SOURCES
   ${CMAKE_CURRENT_SOURCE_DIR}/source/opcode.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/source/operand.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/source/print.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/source/table.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/source/text.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/source/text_handler.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/source/validate.cpp
index 12b35b1..74ea9b0 100644 (file)
@@ -321,6 +321,10 @@ typedef struct spv_diagnostic_t {
   bool isTextSource;
 } spv_diagnostic_t;
 
+// Opaque struct containing the context used to operate on a SPIR-V module.
+// Its object is used by various tranformation API functions.
+struct spv_context_t;
+
 // Type Definitions
 
 typedef spv_const_binary_t* spv_const_binary;
@@ -328,14 +332,23 @@ typedef spv_binary_t* spv_binary;
 typedef spv_text_t* spv_text;
 typedef spv_position_t* spv_position;
 typedef spv_diagnostic_t* spv_diagnostic;
+typedef const spv_context_t* spv_const_context;
+typedef spv_context_t* spv_context;
 
 // Platform API
 
+// Creates a context object.
+spv_context spvContextCreate();
+
+// Destroys the given context object.
+void spvContextDestroy(spv_context context);
+
 // Encodes the given SPIR-V assembly text to its binary representation. The
 // length parameter specifies the number of bytes for text. Encoded binary will
 // be stored into *binary. Any error will be written into *diagnostic.
-spv_result_t spvTextToBinary(const char* text, const size_t length,
-                             spv_binary* binary, spv_diagnostic* diagnostic);
+spv_result_t spvTextToBinary(const spv_const_context context, const char* text,
+                             const size_t length, spv_binary* binary,
+                             spv_diagnostic* diagnostic);
 
 // @brief Frees an allocated text stream. This is a no-op if the text parameter
 // is a null pointer.
@@ -345,7 +358,8 @@ void spvTextDestroy(spv_text text);
 // word_count parameter specifies the number of words for binary. The options
 // parameter is a bit field of spv_binary_to_text_options_t. Decoded text will
 // be stored into *text. Any error will be written into *diagnostic.
-spv_result_t spvBinaryToText(const uint32_t* binary, const size_t word_count,
+spv_result_t spvBinaryToText(const spv_const_context context,
+                             const uint32_t* binary, const size_t word_count,
                              const uint32_t options, spv_text* text,
                              spv_diagnostic* diagnostic);
 
@@ -355,7 +369,8 @@ void spvBinaryDestroy(spv_binary binary);
 
 // Validates a SPIR-V binary for correctness. The options parameter is a bit
 // field of spv_validation_options_t.
-spv_result_t spvValidate(const spv_const_binary binary, const uint32_t options,
+spv_result_t spvValidate(const spv_const_context context,
+                         const spv_const_binary binary, const uint32_t options,
                          spv_diagnostic* pDiagnostic);
 
 // Creates a diagnostic object. The position parameter specifies the location in
@@ -396,8 +411,8 @@ typedef spv_result_t (*spv_parsed_instruction_fn_t)(
 // returns SPV_ERROR_INVALID_BINARY and emits a diagnostic.  If a callback
 // returns anything other than SPV_SUCCESS, then that error code is returned
 // and parsing terminates early.
-spv_result_t spvBinaryParse(void* user_data, const uint32_t* words,
-                            const size_t num_words,
+spv_result_t spvBinaryParse(const spv_const_context context, void* user_data,
+                            const uint32_t* words, const size_t num_words,
                             spv_parsed_header_fn_t parse_header,
                             spv_parsed_instruction_fn_t parse_instruction,
                             spv_diagnostic* diagnostic);
index 448d9dc..2419bd5 100644 (file)
@@ -86,30 +86,6 @@ spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable,
   return SPV_SUCCESS;
 }
 
-// Returns the operand table.
-spv_operand_table GetDefaultOperandTable() {
-  spv_operand_table result = nullptr;
-  spvOperandTableGet(&result);
-  assert(result);
-  return result;
-}
-
-// Returns the opcode table.
-spv_opcode_table GetDefaultOpcodeTable() {
-  spv_opcode_table result = nullptr;
-  spvOpcodeTableGet(&result);
-  assert(result);
-  return result;
-}
-
-// Returns the extended instruction table.
-spv_ext_inst_table GetDefaultExtInstTable() {
-  spv_ext_inst_table result = nullptr;
-  spvExtInstTableGet(&result);
-  assert(result);
-  return result;
-}
-
 // Associates an opcode with its name.
 struct SpecConstantOpcodeEntry {
   SpvOp opcode;
@@ -197,12 +173,6 @@ const size_t kNumOpSpecConstantOpcodes =
 
 namespace libspirv {
 
-AssemblyGrammar::AssemblyGrammar()
-    : AssemblyGrammar(GetDefaultOperandTable(), GetDefaultOpcodeTable(),
-                      GetDefaultExtInstTable()) {
-  assert(isValid());
-}
-
 bool AssemblyGrammar::isValid() const {
   return operandTable_ && opcodeTable_ && extInstTable_;
 }
index 4966ed6..4aabfe0 100644 (file)
@@ -37,15 +37,10 @@ namespace libspirv {
 // Contains methods to query for valid instructions and operands.
 class AssemblyGrammar {
  public:
-  AssemblyGrammar(const spv_operand_table operand_table,
-                  const spv_opcode_table opcode_table,
-                  const spv_ext_inst_table ext_inst_table)
-      : operandTable_(operand_table),
-        opcodeTable_(opcode_table),
-        extInstTable_(ext_inst_table) {}
-
-  // Constructor that gets the required data itself.
-  AssemblyGrammar();
+  AssemblyGrammar(const spv_const_context context)
+      : operandTable_(context->operand_table),
+        opcodeTable_(context->opcode_table),
+        extInstTable_(context->ext_inst_table) {}
 
   // Returns true if the internal tables have been initialized with valid data.
   bool isValid() const;
index 062eadd..fe052cf 100644 (file)
@@ -108,9 +108,11 @@ namespace {
 class Parser {
  public:
   // The user_data value is provided to the callbacks as context.
-  Parser(void* user_data, spv_parsed_header_fn_t parsed_header_fn,
+  Parser(const spv_const_context context, void* user_data,
+         spv_parsed_header_fn_t parsed_header_fn,
          spv_parsed_instruction_fn_t parsed_instruction_fn)
-      : user_data_(user_data),
+      : grammar_(context),
+        user_data_(user_data),
         parsed_header_fn_(parsed_header_fn),
         parsed_instruction_fn_(parsed_instruction_fn) {}
 
@@ -672,12 +674,12 @@ void Parser::recordNumberType(const spv_parsed_instruction_t* inst) {
 
 }  // anonymous namespace
 
-spv_result_t spvBinaryParse(void* user_data, const uint32_t* code,
-                            const size_t num_words,
+spv_result_t spvBinaryParse(const spv_const_context context, void* user_data,
+                            const uint32_t* code, const size_t num_words,
                             spv_parsed_header_fn_t parsed_header,
                             spv_parsed_instruction_fn_t parsed_instruction,
                             spv_diagnostic* diagnostic) {
-  Parser parser(user_data, parsed_header, parsed_instruction);
+  Parser parser(context, user_data, parsed_header, parsed_instruction);
   return parser.parse(code, num_words, diagnostic);
 }
 
index 181f433..5fccf43 100644 (file)
@@ -370,19 +370,20 @@ spv_result_t DisassembleInstruction(
 
 }  // anonymous namespace
 
-spv_result_t spvBinaryToText(const uint32_t* code, const size_t wordCount,
+spv_result_t spvBinaryToText(const spv_const_context context,
+                             const uint32_t* code, const size_t wordCount,
                              const uint32_t options, spv_text* pText,
                              spv_diagnostic* pDiagnostic) {
   // Invalid arguments return error codes, but don't necessarily generate
   // diagnostics.  These are programmer errors, not user errors.
   if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
-  const libspirv::AssemblyGrammar grammar;
+  const libspirv::AssemblyGrammar grammar(context);
   if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE;
 
   Disassembler disassembler(grammar, code, wordCount, options);
-  if (auto error =
-          spvBinaryParse(&disassembler, code, wordCount, DisassembleHeader,
-                         DisassembleInstruction, pDiagnostic)) {
+  if (auto error = spvBinaryParse(context, &disassembler, code, wordCount,
+                                  DisassembleHeader, DisassembleInstruction,
+                                  pDiagnostic)) {
     return error;
   }
 
index c78997c..c2cab6d 100644 (file)
@@ -29,6 +29,8 @@
 #include <assert.h>
 #include <string.h>
 
+#include <cstdlib>
+
 #include "endian.h"
 #include "instruction.h"
 #include "libspirv/libspirv.h"
@@ -46,7 +48,7 @@ namespace {
 //
 // TODO(dneto): Some of the macros are quite unreadable.  We could make
 // good use of constexpr functions, but some compilers don't support that yet.
-spv_opcode_desc_t opcodeTableEntries[] = {
+const spv_opcode_desc_t opcodeTableEntries[] = {
 #define EmptyList \
   {}
 #define List(...) \
@@ -71,10 +73,6 @@ spv_opcode_desc_t opcodeTableEntries[] = {
 #undef Instruction
 };
 
-// Has the opcodeTableEntries table been fully elaborated?
-// That is, are the operandTypes fields initialized?
-bool opcodeTableInitialized = false;
-
 // Opcode API
 
 // Converts the given operand class enum (from the SPIR-V document generation
@@ -244,9 +242,11 @@ spv_operand_type_t convertOperandClassToType(SpvOp opcode,
 }  // anonymous namespace
 
 // Finish populating the opcodeTableEntries array.
-void spvOpcodeTableInitialize() {
+void spvOpcodeTableInitialize(spv_opcode_desc_t* entries,
+                              uint32_t num_entries) {
   // Compute the operandTypes field for each entry.
-  for (auto& opcode : opcodeTableEntries) {
+  for (uint32_t i = 0; i < num_entries; ++i) {
+    spv_opcode_desc_t& opcode = entries[i];
     opcode.numTypes = 0;
     // Type ID always comes first, if present.
     if (opcode.hasType)
@@ -282,7 +282,6 @@ void spvOpcodeTableInitialize() {
            "Operand class list is too long.  Expand "
            "spv_opcode_desc_t.operandClass");
   }
-  opcodeTableInitialized = true;
 }
 
 const char* spvGeneratorStr(uint32_t generator) {
@@ -320,15 +319,18 @@ void spvOpcodeSplit(const uint32_t word, uint16_t* pWordCount, SpvOp* pOpcode) {
 spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable) {
   if (!pInstTable) return SPV_ERROR_INVALID_POINTER;
 
-  static spv_opcode_table_t table = {
-      sizeof(opcodeTableEntries) / sizeof(spv_opcode_desc_t),
-      opcodeTableEntries};
+  const uint32_t size = sizeof(opcodeTableEntries);
+  spv_opcode_desc_t* copied_entries =
+      static_cast<spv_opcode_desc_t*>(::malloc(size));
+  if (!copied_entries) return SPV_ERROR_OUT_OF_MEMORY;
+  ::memcpy(copied_entries, opcodeTableEntries, size);
+
+  const uint32_t count = sizeof(opcodeTableEntries) / sizeof(spv_opcode_desc_t);
+  spv_opcode_table_t* table = new spv_opcode_table_t{count, copied_entries};
 
-  // TODO(dneto): Consider thread safety of initialization.
-  // That is, ordering effects of the flag vs. the table updates.
-  if (!opcodeTableInitialized) spvOpcodeTableInitialize();
+  spvOpcodeTableInitialize(copied_entries, count);
 
-  *pInstTable = &table;
+  *pInstTable = table;
 
   return SPV_SUCCESS;
 }
diff --git a/source/table.cpp b/source/table.cpp
new file mode 100644 (file)
index 0000000..b056176
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (c) 2015 The Khronos Group Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include "table.h"
+
+#include <cstdlib>
+
+spv_context spvContextCreate() {
+  spv_opcode_table opcode_table;
+  spv_operand_table operand_table;
+  spv_ext_inst_table ext_inst_table;
+
+  spvOpcodeTableGet(&opcode_table);
+  spvOperandTableGet(&operand_table);
+  spvExtInstTableGet(&ext_inst_table);
+
+  return new spv_context_t{opcode_table, operand_table, ext_inst_table};
+}
+
+void spvContextDestroy(spv_context context) {
+  ::free(context->opcode_table->entries);
+  delete context->opcode_table;
+  delete context;
+}
index d87f17c..e6559af 100644 (file)
@@ -76,7 +76,7 @@ typedef struct spv_ext_inst_group_t {
 
 typedef struct spv_opcode_table_t {
   const uint32_t count;
-  const spv_opcode_desc_t* entries;
+  spv_opcode_desc_t* entries;
 } spv_opcode_table_t;
 
 typedef struct spv_operand_table_t {
@@ -97,6 +97,12 @@ typedef const spv_opcode_table_t* spv_opcode_table;
 typedef const spv_operand_table_t* spv_operand_table;
 typedef const spv_ext_inst_table_t* spv_ext_inst_table;
 
+struct spv_context_t {
+  const spv_opcode_table opcode_table;
+  const spv_operand_table operand_table;
+  const spv_ext_inst_table ext_inst_table;
+};
+
 /// @brief Populate the Opcode table
 ///
 /// @param[out] pOpcodeTable table to be populated
index aa870c7..bc8fd96 100644 (file)
@@ -735,11 +735,12 @@ spv_result_t spvTextToBinaryInternal(const libspirv::AssemblyGrammar& grammar,
 
 }  // anonymous namespace
 
-spv_result_t spvTextToBinary(const char* input_text,
+spv_result_t spvTextToBinary(const spv_const_context context,
+                             const char* input_text,
                              const size_t input_text_size, spv_binary* pBinary,
                              spv_diagnostic* pDiagnostic) {
   spv_text_t text = {input_text, input_text_size};
-  libspirv::AssemblyGrammar grammar;
+  libspirv::AssemblyGrammar grammar(context);
 
   spv_result_t result =
       spvTextToBinaryInternal(grammar, &text, pBinary, pDiagnostic);
index 65ee86f..e7b52d5 100644 (file)
@@ -261,20 +261,10 @@ spv_result_t spvValidateIDs(const spv_instruction_t* pInsts,
   return SPV_SUCCESS;
 }
 
-spv_result_t spvValidate(const spv_const_binary binary, const uint32_t options,
+spv_result_t spvValidate(const spv_const_context context,
+                         const spv_const_binary binary, const uint32_t options,
                          spv_diagnostic* pDiagnostic) {
   if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
-  spv_opcode_table opcode_table = nullptr;
-  spvOpcodeTableGet(&opcode_table);
-  assert(opcode_table);
-
-  spv_operand_table operand_table = nullptr;
-  spvOperandTableGet(&operand_table);
-  assert(operand_table);
-
-  spv_ext_inst_table ext_inst_table = nullptr;
-  spvExtInstTableGet(&ext_inst_table);
-  assert(ext_inst_table);
 
   spv_endianness_t endian;
   spv_position_t position = {};
@@ -306,9 +296,9 @@ spv_result_t spvValidate(const spv_const_binary binary, const uint32_t options,
   if (spvIsInBitfield(SPV_VALIDATE_BASIC_BIT, options)) {
     position.index = SPV_INDEX_INSTRUCTION;
     // TODO: Imcomplete implementation
-    spvCheckReturn(spvValidateBasic(instructions.data(), instructions.size(),
-                                    opcode_table, operand_table, &position,
-                                    pDiagnostic));
+    spvCheckReturn(spvValidateBasic(
+        instructions.data(), instructions.size(), context->opcode_table,
+        context->operand_table, &position, pDiagnostic));
   }
 
   if (spvIsInBitfield(SPV_VALIDATE_LAYOUT_BIT, options)) {
@@ -318,9 +308,10 @@ spv_result_t spvValidate(const spv_const_binary binary, const uint32_t options,
 
   if (spvIsInBitfield(SPV_VALIDATE_ID_BIT, options)) {
     position.index = SPV_INDEX_INSTRUCTION;
-    spvCheckReturn(spvValidateIDs(instructions.data(), instructions.size(),
-                                  header.bound, opcode_table, operand_table,
-                                  ext_inst_table, &position, pDiagnostic));
+    spvCheckReturn(
+        spvValidateIDs(instructions.data(), instructions.size(), header.bound,
+                       context->opcode_table, context->operand_table,
+                       context->ext_inst_table, &position, pDiagnostic));
   }
 
   if (spvIsInBitfield(SPV_VALIDATE_RULES_BIT, options)) {
index 3d9957c..aa5bda8 100644 (file)
@@ -32,8 +32,9 @@ using spvtest::TextToBinaryTest;
 
 TEST_F(TextToBinaryTest, NotPlacingResultIDAtTheBeginning) {
   SetText("OpTypeMatrix %1 %2 1000");
-  EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
-            spvTextToBinary(text.str, text.length, &binary, &diagnostic));
+  EXPECT_EQ(
+      SPV_ERROR_INVALID_TEXT,
+      spvTextToBinary(context, text.str, text.length, &binary, &diagnostic));
   ASSERT_NE(nullptr, diagnostic);
   EXPECT_STREQ(
       "Expected <result-id> at the beginning of an instruction, found "
index 29f6e31..5ff2aff 100644 (file)
@@ -40,13 +40,15 @@ using BinaryDestroySomething = spvtest::TextToBinaryTest;
 
 // Checks safety of destroying a validly constructed binary.
 TEST_F(BinaryDestroySomething, Default) {
+  spv_context context = spvContextCreate();
   // Use a binary object constructed by the API instead of rolling our own.
   SetText("OpSource OpenCL_C 120");
   spv_binary my_binary = nullptr;
-  ASSERT_EQ(SPV_SUCCESS,
-            spvTextToBinary(text.str, text.length, &my_binary, &diagnostic));
+  ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, text.str, text.length,
+                                         &my_binary, &diagnostic));
   ASSERT_NE(nullptr, my_binary);
   spvBinaryDestroy(my_binary);
+  spvContextDestroy(context);
 }
 
 }  // anonymous namespace
index c4b9d3a..5ca8737 100644 (file)
@@ -40,6 +40,9 @@ using spvtest::TextToBinaryTest;
 namespace {
 class BinaryToText : public ::testing::Test {
  public:
+  BinaryToText() : context(spvContextCreate()) {}
+  ~BinaryToText() { spvContextDestroy(context); }
+
   virtual void SetUp() {
     const char* textStr = R"(
       OpSource OpenCL_C 12
@@ -65,7 +68,7 @@ class BinaryToText : public ::testing::Test {
     spv_text_t text = {textStr, strlen(textStr)};
     spv_diagnostic diagnostic = nullptr;
     spv_result_t error =
-        spvTextToBinary(text.str, text.length, &binary, &diagnostic);
+        spvTextToBinary(context, text.str, text.length, &binary, &diagnostic);
     if (error) {
       spvDiagnosticPrint(diagnostic);
       spvDiagnosticDestroy(diagnostic);
@@ -78,19 +81,21 @@ class BinaryToText : public ::testing::Test {
   // Compiles the given assembly text, and saves it into 'binary'.
   void CompileSuccessfully(std::string text) {
     spv_diagnostic diagnostic = nullptr;
-    EXPECT_EQ(SPV_SUCCESS,
-              spvTextToBinary(text.c_str(), text.size(), &binary, &diagnostic));
+    EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, text.c_str(), text.size(),
+                                           &binary, &diagnostic));
   }
 
+  spv_context context;
   spv_binary binary;
 };
 
 TEST_F(BinaryToText, Default) {
   spv_text text = nullptr;
   spv_diagnostic diagnostic = nullptr;
-  ASSERT_EQ(SPV_SUCCESS, spvBinaryToText(binary->code, binary->wordCount,
-                                         SPV_BINARY_TO_TEXT_OPTION_NONE, &text,
-                                         &diagnostic));
+  ASSERT_EQ(
+      SPV_SUCCESS,
+      spvBinaryToText(context, binary->code, binary->wordCount,
+                      SPV_BINARY_TO_TEXT_OPTION_NONE, &text, &diagnostic));
   printf("%s", text->str);
   spvTextDestroy(text);
 }
@@ -98,9 +103,10 @@ TEST_F(BinaryToText, Default) {
 TEST_F(BinaryToText, MissingModule) {
   spv_text text;
   spv_diagnostic diagnostic = nullptr;
-  EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
-            spvBinaryToText(nullptr, 42, SPV_BINARY_TO_TEXT_OPTION_NONE, &text,
-                            &diagnostic));
+  EXPECT_EQ(
+      SPV_ERROR_INVALID_BINARY,
+      spvBinaryToText(context, nullptr, 42, SPV_BINARY_TO_TEXT_OPTION_NONE,
+                      &text, &diagnostic));
   EXPECT_THAT(diagnostic->error, Eq(std::string("Missing module.")));
   if (diagnostic) {
     spvDiagnosticPrint(diagnostic);
@@ -118,8 +124,8 @@ TEST_F(BinaryToText, TruncatedModule) {
     spv_diagnostic diagnostic = nullptr;
     EXPECT_EQ(
         SPV_ERROR_INVALID_BINARY,
-        spvBinaryToText(binary->code, length, SPV_BINARY_TO_TEXT_OPTION_NONE,
-                        &text, &diagnostic));
+        spvBinaryToText(context, binary->code, length,
+                        SPV_BINARY_TO_TEXT_OPTION_NONE, &text, &diagnostic));
     ASSERT_NE(nullptr, diagnostic);
     std::stringstream expected;
     expected << "Module has incomplete header: only " << length
@@ -139,7 +145,7 @@ TEST_F(BinaryToText, InvalidMagicNumber) {
   spv_text text;
   EXPECT_EQ(
       SPV_ERROR_INVALID_BINARY,
-      spvBinaryToText(damaged_binary.data(), damaged_binary.size(),
+      spvBinaryToText(context, damaged_binary.data(), damaged_binary.size(),
                       SPV_BINARY_TO_TEXT_OPTION_NONE, &text, &diagnostic));
   ASSERT_NE(nullptr, diagnostic);
   std::stringstream expected;
@@ -152,7 +158,7 @@ TEST_F(BinaryToText, InvalidMagicNumber) {
 TEST_F(BinaryToText, InvalidDiagnostic) {
   spv_text text;
   ASSERT_EQ(SPV_ERROR_INVALID_DIAGNOSTIC,
-            spvBinaryToText(binary->code, binary->wordCount,
+            spvBinaryToText(context, binary->code, binary->wordCount,
                             SPV_BINARY_TO_TEXT_OPTION_NONE, &text, nullptr));
 }
 
index 649f56d..4a7cb52 100644 (file)
@@ -49,6 +49,7 @@ struct ExtInstContext {
 using ExtInstGLSLstd450RoundTripTest = ::testing::TestWithParam<ExtInstContext>;
 
 TEST_P(ExtInstGLSLstd450RoundTripTest, ParameterizedExtInst) {
+  spv_context context = spvContextCreate();
   const std::string spirv = R"(
 OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
@@ -71,8 +72,8 @@ OpFunctionEnd
 ; Schema: 0)";
   spv_binary binary;
   spv_diagnostic diagnostic;
-  spv_result_t error =
-      spvTextToBinary(spirv.c_str(), spirv.size(), &binary, &diagnostic);
+  spv_result_t error = spvTextToBinary(context, spirv.c_str(), spirv.size(),
+                                       &binary, &diagnostic);
   if (error) {
     spvDiagnosticPrint(diagnostic);
     spvDiagnosticDestroy(diagnostic);
@@ -98,7 +99,7 @@ OpFunctionEnd
 
   // Check round trip gives the same text.
   spv_text output_text = nullptr;
-  error = spvBinaryToText(binary->code, binary->wordCount,
+  error = spvBinaryToText(context, binary->code, binary->wordCount,
                           SPV_BINARY_TO_TEXT_OPTION_NONE, &output_text,
                           &diagnostic);
 
@@ -109,6 +110,7 @@ OpFunctionEnd
   }
   EXPECT_EQ(spirv_header + spirv, output_text->str);
   spvTextDestroy(output_text);
+  spvContextDestroy(context);
 }
 
 static const char* kF32Type = R"(%4 = OpTypeFloat 32)";
index 56c0b7d..e78ddfe 100644 (file)
@@ -46,8 +46,8 @@ using ::testing::StrEq;
 
 TEST_F(TextToBinaryTest, ImmediateIntOpCode) {
   SetText("!0x00FF00FF");
-  ASSERT_EQ(SPV_SUCCESS,
-            spvTextToBinary(text.str, text.length, &binary, &diagnostic));
+  ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, text.str, text.length,
+                                         &binary, &diagnostic));
   EXPECT_EQ(0x00FF00FF, binary->code[5]);
   if (diagnostic) {
     spvDiagnosticPrint(diagnostic);
@@ -56,8 +56,8 @@ TEST_F(TextToBinaryTest, ImmediateIntOpCode) {
 
 TEST_F(TextToBinaryTest, ImmediateIntOperand) {
   SetText("OpCapability !0x00FF00FF");
-  EXPECT_EQ(SPV_SUCCESS,
-            spvTextToBinary(text.str, text.length, &binary, &diagnostic));
+  EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, text.str, text.length,
+                                         &binary, &diagnostic));
   EXPECT_EQ(0x00FF00FF, binary->code[6]);
   if (diagnostic) {
     spvDiagnosticPrint(diagnostic);
index 7b4a318..4fe9c8d 100644 (file)
@@ -32,6 +32,7 @@
 namespace {
 
 TEST(NamedId, Default) {
+  spv_context context = spvContextCreate();
   const char* spirv = R"(
           OpCapability Shader
           OpMemoryModel Logical Simple
@@ -48,7 +49,7 @@ TEST(NamedId, Default) {
   spv_binary binary = nullptr;
   spv_diagnostic diagnostic;
   spv_result_t error =
-      spvTextToBinary(text.str, text.length, &binary, &diagnostic);
+      spvTextToBinary(context, text.str, text.length, &binary, &diagnostic);
   if (error) {
     spvDiagnosticPrint(diagnostic);
     spvDiagnosticDestroy(diagnostic);
@@ -56,7 +57,7 @@ TEST(NamedId, Default) {
     ASSERT_EQ(SPV_SUCCESS, error);
   }
   error = spvBinaryToText(
-      binary->code, binary->wordCount,
+      context, binary->code, binary->wordCount,
       SPV_BINARY_TO_TEXT_OPTION_PRINT | SPV_BINARY_TO_TEXT_OPTION_COLOR,
       nullptr, &diagnostic);
   if (error) {
@@ -66,6 +67,7 @@ TEST(NamedId, Default) {
     ASSERT_EQ(SPV_SUCCESS, error);
   }
   spvBinaryDestroy(binary);
+  spvContextDestroy(context);
 }
 
 struct IdCheckCase {
index 6fabd43..3aef86d 100644 (file)
@@ -42,7 +42,11 @@ class TextToBinaryTestBase : public T {
   // Offset into a SpirvVector at which the first instruction starts.
   const SpirvVector::size_type kFirstInstruction = 5;
 
-  TextToBinaryTestBase() : diagnostic(nullptr), text(), binary(nullptr) {
+  TextToBinaryTestBase()
+      : context(spvContextCreate()),
+        diagnostic(nullptr),
+        text(),
+        binary(nullptr) {
     char textStr[] = "substitute the text member variable with your test";
     text = {textStr, strlen(textStr)};
   }
@@ -50,6 +54,7 @@ class TextToBinaryTestBase : public T {
   virtual ~TextToBinaryTestBase() {
     DestroyBinary();
     if (diagnostic) spvDiagnosticDestroy(diagnostic);
+    spvContextDestroy(context);
   }
 
   // Returns subvector v[from:end).
@@ -61,8 +66,8 @@ class TextToBinaryTestBase : public T {
   // Compiles SPIR-V text in the given assembly syntax format, asserting
   // compilation success. Returns the compiled code.
   SpirvVector CompileSuccessfully(const std::string& text) {
-    spv_result_t status =
-        spvTextToBinary(text.c_str(), text.size(), &binary, &diagnostic);
+    spv_result_t status = spvTextToBinary(context, text.c_str(), text.size(),
+                                          &binary, &diagnostic);
     EXPECT_EQ(SPV_SUCCESS, status) << text;
     SpirvVector code_copy;
     if (status == SPV_SUCCESS) {
@@ -77,8 +82,8 @@ class TextToBinaryTestBase : public T {
   // Compiles SPIR-V text with the given format, asserting compilation failure.
   // Returns the error message(s).
   std::string CompileFailure(const std::string& text) {
-    EXPECT_NE(SPV_SUCCESS,
-              spvTextToBinary(text.c_str(), text.size(), &binary, &diagnostic))
+    EXPECT_NE(SPV_SUCCESS, spvTextToBinary(context, text.c_str(), text.size(),
+                                           &binary, &diagnostic))
         << text;
     DestroyBinary();
     return diagnostic->error;
@@ -95,8 +100,8 @@ class TextToBinaryTestBase : public T {
   std::string EncodeAndDecodeSuccessfully(const std::string& text,
                                           uint32_t disassemble_options) {
     DestroyBinary();
-    spv_result_t error =
-        spvTextToBinary(text.c_str(), text.size(), &binary, &diagnostic);
+    spv_result_t error = spvTextToBinary(context, text.c_str(), text.size(),
+                                         &binary, &diagnostic);
     if (error) {
       spvDiagnosticPrint(diagnostic);
       spvDiagnosticDestroy(diagnostic);
@@ -105,7 +110,7 @@ class TextToBinaryTestBase : public T {
     if (!binary) return "";
 
     spv_text decoded_text;
-    error = spvBinaryToText(binary->code, binary->wordCount,
+    error = spvBinaryToText(context, binary->code, binary->wordCount,
                             disassemble_options, &decoded_text, &diagnostic);
     if (error) {
       spvDiagnosticPrint(diagnostic);
@@ -132,7 +137,7 @@ class TextToBinaryTestBase : public T {
         spvtest::Concatenate({CompileSuccessfully(text), words_to_append});
 
     spv_text decoded_text;
-    EXPECT_NE(SPV_SUCCESS, spvBinaryToText(code.data(), code.size(),
+    EXPECT_NE(SPV_SUCCESS, spvBinaryToText(context, code.data(), code.size(),
                                            SPV_BINARY_TO_TEXT_OPTION_NONE,
                                            &decoded_text, &diagnostic));
     if (diagnostic) {
@@ -169,6 +174,7 @@ class TextToBinaryTestBase : public T {
     binary = nullptr;
   }
 
+  spv_context context;
   spv_diagnostic diagnostic;
 
   std::string textString;
index 86e56e6..a55d572 100644 (file)
@@ -31,6 +31,7 @@ namespace {
 TEST(TextDestroy, DestroyNull) { spvBinaryDestroy(nullptr); }
 
 TEST(TextDestroy, Default) {
+  spv_context context = spvContextCreate();
   char textStr[] = R"(
       OpSource OpenCL_C 12
       OpMemoryModel Physical64 OpenCL
@@ -55,8 +56,8 @@ TEST(TextDestroy, Default) {
 
   spv_binary binary = nullptr;
   spv_diagnostic diagnostic = nullptr;
-  EXPECT_EQ(SPV_SUCCESS,
-            spvTextToBinary(textStr, strlen(textStr), &binary, &diagnostic));
+  EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, textStr, strlen(textStr),
+                                         &binary, &diagnostic));
   EXPECT_NE(nullptr, binary);
   EXPECT_NE(nullptr, binary->code);
   EXPECT_NE(0, binary->wordCount);
@@ -66,8 +67,9 @@ TEST(TextDestroy, Default) {
   }
 
   spv_text resultText = nullptr;
-  EXPECT_EQ(SPV_SUCCESS, spvBinaryToText(binary->code, binary->wordCount, 0,
-                                         &resultText, &diagnostic));
+  EXPECT_EQ(SPV_SUCCESS,
+            spvBinaryToText(context, binary->code, binary->wordCount, 0,
+                            &resultText, &diagnostic));
   spvBinaryDestroy(binary);
   if (diagnostic) {
     spvDiagnosticPrint(diagnostic);
@@ -77,6 +79,7 @@ TEST(TextDestroy, Default) {
   EXPECT_NE(nullptr, resultText->str);
   EXPECT_NE(0, resultText->length);
   spvTextDestroy(resultText);
+  spvContextDestroy(context);
 }
 
 }  // anonymous namespace
index e854bb5..a5f5493 100644 (file)
@@ -71,14 +71,15 @@ struct MaskCase {
 using GoodMaskParseTest = ::testing::TestWithParam<MaskCase>;
 
 TEST_P(GoodMaskParseTest, GoodMaskExpressions) {
-  spv_operand_table operandTable;
-  ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&operandTable));
+  spv_context context = spvContextCreate();
 
   uint32_t value;
-  EXPECT_EQ(SPV_SUCCESS, AssemblyGrammar(operandTable, nullptr, nullptr)
-                             .parseMaskOperand(GetParam().which_enum,
-                                               GetParam().expression, &value));
+  EXPECT_EQ(SPV_SUCCESS,
+            AssemblyGrammar(context).parseMaskOperand(
+                GetParam().which_enum, GetParam().expression, &value));
   EXPECT_EQ(GetParam().expected_value, value);
+
+  spvContextDestroy(context);
 }
 
 INSTANTIATE_TEST_CASE_P(
@@ -115,14 +116,14 @@ INSTANTIATE_TEST_CASE_P(
 using BadFPFastMathMaskParseTest = ::testing::TestWithParam<const char*>;
 
 TEST_P(BadFPFastMathMaskParseTest, BadMaskExpressions) {
-  spv_operand_table operandTable;
-  ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&operandTable));
+  spv_context context = spvContextCreate();
 
   uint32_t value;
   EXPECT_NE(SPV_SUCCESS,
-            AssemblyGrammar(operandTable, nullptr, nullptr)
-                .parseMaskOperand(SPV_OPERAND_TYPE_FP_FAST_MATH_MODE,
-                                  GetParam(), &value));
+            AssemblyGrammar(context).parseMaskOperand(
+                SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, GetParam(), &value));
+
+  spvContextDestroy(context);
 }
 
 INSTANTIATE_TEST_CASE_P(ParseMask, BadFPFastMathMaskParseTest,
@@ -141,7 +142,7 @@ union char_word_t {
 TEST_F(TextToBinaryTest, InvalidText) {
   spv_binary binary;
   ASSERT_EQ(SPV_ERROR_INVALID_TEXT,
-            spvTextToBinary(nullptr, 0, &binary, &diagnostic));
+            spvTextToBinary(context, nullptr, 0, &binary, &diagnostic));
   EXPECT_NE(nullptr, diagnostic);
   EXPECT_THAT(diagnostic->error, Eq(std::string("Missing assembly text.")));
 }
@@ -149,8 +150,9 @@ TEST_F(TextToBinaryTest, InvalidText) {
 TEST_F(TextToBinaryTest, InvalidPointer) {
   SetText(
       "OpEntryPoint Kernel 0 \"\"\nOpExecutionMode 0 LocalSizeHint 1 1 1\n");
-  ASSERT_EQ(SPV_ERROR_INVALID_POINTER,
-            spvTextToBinary(text.str, text.length, nullptr, &diagnostic));
+  ASSERT_EQ(
+      SPV_ERROR_INVALID_POINTER,
+      spvTextToBinary(context, text.str, text.length, nullptr, &diagnostic));
 }
 
 TEST_F(TextToBinaryTest, InvalidDiagnostic) {
@@ -158,7 +160,7 @@ TEST_F(TextToBinaryTest, InvalidDiagnostic) {
       "OpEntryPoint Kernel 0 \"\"\nOpExecutionMode 0 LocalSizeHint 1 1 1\n");
   spv_binary binary;
   ASSERT_EQ(SPV_ERROR_INVALID_DIAGNOSTIC,
-            spvTextToBinary(text.str, text.length, &binary, nullptr));
+            spvTextToBinary(context, text.str, text.length, &binary, nullptr));
 }
 
 TEST_F(TextToBinaryTest, InvalidPrefix) {
index 8222a20..2733ace 100644 (file)
@@ -30,11 +30,13 @@ namespace {
 
 class Validate : public ::testing::Test {
  public:
-  Validate() : binary() {}
+  Validate() : context(spvContextCreate()), binary() {}
+  ~Validate() { spvContextDestroy(context); }
 
   virtual void TearDown() { spvBinaryDestroy(binary); }
   spv_const_binary get_const_binary() { return spv_const_binary(binary); }
 
+  spv_context context;
   spv_binary binary;
 };
 
@@ -52,9 +54,9 @@ OpFunctionEnd
 )";
   spv_diagnostic diagnostic = nullptr;
   ASSERT_EQ(SPV_SUCCESS,
-            spvTextToBinary(str, strlen(str), &binary, &diagnostic));
-  ASSERT_EQ(SPV_SUCCESS,
-            spvValidate(get_const_binary(), SPV_VALIDATE_ALL, &diagnostic));
+            spvTextToBinary(context, str, strlen(str), &binary, &diagnostic));
+  ASSERT_EQ(SPV_SUCCESS, spvValidate(context, get_const_binary(),
+                                     SPV_VALIDATE_ALL, &diagnostic));
   if (diagnostic) {
     spvDiagnosticPrint(diagnostic);
     spvDiagnosticDestroy(diagnostic);
@@ -75,9 +77,9 @@ OpFunctionEnd
 )";
   spv_diagnostic diagnostic = nullptr;
   ASSERT_EQ(SPV_SUCCESS,
-            spvTextToBinary(str, strlen(str), &binary, &diagnostic));
-  ASSERT_EQ(SPV_ERROR_INVALID_ID,
-            spvValidate(get_const_binary(), SPV_VALIDATE_ALL, &diagnostic));
+            spvTextToBinary(context, str, strlen(str), &binary, &diagnostic));
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, spvValidate(context, get_const_binary(),
+                                              SPV_VALIDATE_ALL, &diagnostic));
   ASSERT_NE(nullptr, diagnostic);
   spvDiagnosticPrint(diagnostic);
   spvDiagnosticDestroy(diagnostic);
@@ -97,10 +99,10 @@ OpFunctionEnd
 )";
   spv_diagnostic diagnostic = nullptr;
   ASSERT_EQ(SPV_SUCCESS,
-            spvTextToBinary(str, strlen(str), &binary, &diagnostic));
+            spvTextToBinary(context, str, strlen(str), &binary, &diagnostic));
   // TODO: Fix setting of bound in spvTextTo, then remove this!
-  ASSERT_EQ(SPV_ERROR_INVALID_ID,
-            spvValidate(get_const_binary(), SPV_VALIDATE_ALL, &diagnostic));
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, spvValidate(context, get_const_binary(),
+                                              SPV_VALIDATE_ALL, &diagnostic));
   ASSERT_NE(nullptr, diagnostic);
   spvDiagnosticPrint(diagnostic);
   spvDiagnosticDestroy(diagnostic);
index ca5c53a..956eb04 100644 (file)
@@ -41,22 +41,24 @@ class ValidateID : public ::testing::Test {
   spv_binary binary;
 };
 
-#define CHECK(str, expected)                                             \
-  spv_diagnostic diagnostic;                                             \
-  spv_result_t error =                                                   \
-      spvTextToBinary(str, strlen(str), &binary, &diagnostic);           \
-  if (error) {                                                           \
-    spvDiagnosticPrint(diagnostic);                                      \
-    spvDiagnosticDestroy(diagnostic);                                    \
-    ASSERT_EQ(SPV_SUCCESS, error);                                       \
-  }                                                                      \
-  spv_result_t result =                                                  \
-      spvValidate(get_const_binary(), SPV_VALIDATE_ID_BIT, &diagnostic); \
-  if (SPV_SUCCESS != result) {                                           \
-    spvDiagnosticPrint(diagnostic);                                      \
-    spvDiagnosticDestroy(diagnostic);                                    \
-  }                                                                      \
-  ASSERT_EQ(expected, result);
+#define CHECK(str, expected)                                            \
+  spv_diagnostic diagnostic;                                            \
+  spv_context context = spvContextCreate();                             \
+  spv_result_t error =                                                  \
+      spvTextToBinary(context, str, strlen(str), &binary, &diagnostic); \
+  if (error) {                                                          \
+    spvDiagnosticPrint(diagnostic);                                     \
+    spvDiagnosticDestroy(diagnostic);                                   \
+    ASSERT_EQ(SPV_SUCCESS, error);                                      \
+  }                                                                     \
+  spv_result_t result = spvValidate(context, get_const_binary(),        \
+                                    SPV_VALIDATE_ID_BIT, &diagnostic);  \
+  if (SPV_SUCCESS != result) {                                          \
+    spvDiagnosticPrint(diagnostic);                                     \
+    spvDiagnosticDestroy(diagnostic);                                   \
+  }                                                                     \
+  ASSERT_EQ(expected, result);                                          \
+  spvContextDestroy(context);
 
 // TODO: OpUndef
 
index f78a008..82b91fe 100644 (file)
@@ -94,8 +94,10 @@ int main(int argc, char** argv) {
 
   spv_binary binary;
   spv_diagnostic diagnostic = nullptr;
-  spv_result_t error =
-      spvTextToBinary(contents.data(), contents.size(), &binary, &diagnostic);
+  spv_context context = spvContextCreate();
+  spv_result_t error = spvTextToBinary(context, contents.data(),
+                                       contents.size(), &binary, &diagnostic);
+  spvContextDestroy(context);
   if (error) {
     spvDiagnosticPrint(diagnostic);
     spvDiagnosticDestroy(diagnostic);
index a1a58d8..1bf04a2 100644 (file)
@@ -146,8 +146,11 @@ int main(int argc, char** argv) {
   spv_text text;
   spv_text* textOrNull = print_to_stdout ? nullptr : &text;
   spv_diagnostic diagnostic = nullptr;
-  spv_result_t error = spvBinaryToText(contents.data(), contents.size(),
-                                       options, textOrNull, &diagnostic);
+  spv_context context = spvContextCreate();
+  spv_result_t error =
+      spvBinaryToText(context, contents.data(), contents.size(), options,
+                      textOrNull, &diagnostic);
+  spvContextDestroy(context);
   if (error) {
     spvDiagnosticPrint(diagnostic);
     spvDiagnosticDestroy(diagnostic);
index 35b4432..29e7035 100644 (file)
@@ -99,7 +99,9 @@ int main(int argc, char** argv) {
   spv_const_binary_t binary = {contents.data(), contents.size()};
 
   spv_diagnostic diagnostic = nullptr;
-  spv_result_t error = spvValidate(&binary, options, &diagnostic);
+  spv_context context = spvContextCreate();
+  spv_result_t error = spvValidate(context, &binary, options, &diagnostic);
+  spvContextDestroy(context);
   if (error) {
     spvDiagnosticPrint(diagnostic);
     spvDiagnosticDestroy(diagnostic);