Create a pass to work around a driver bug related to OpUnreachable.

We have come across a driver bug where and OpUnreachable inside a loop
is causing the shader to go into an infinite loop.  This commit will try
to avoid this bug by turning OpUnreachable instructions that are
contained in a loop into branches to the loop merge block.

This is not added to "-O" and "-Os" because it should only be used if
the driver being targeted has this problem.

Fixes #1209.
This commit is contained in:
Steven Perron 2018-01-17 14:57:37 -05:00
parent 18618061cb
commit 34d4294c2c
10 changed files with 558 additions and 1 deletions

View File

@ -108,7 +108,8 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/type_manager.cpp \
source/opt/types.cpp \
source/opt/unify_const_pass.cpp \
source/opt/value_number_table.cpp
source/opt/value_number_table.cpp \
source/opt/workaround1209.cpp
# Locations of grammar files.
SPV_CORE10_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.0/spirv.core.grammar.json

View File

@ -475,6 +475,13 @@ Optimizer::PassToken CreatePrivateToLocalPass();
// and computations with constant operands.
Optimizer::PassToken CreateCCPPass();
// Creates a workaround driver bugs pass. This pass attempts to work around
// a known driver bug (issue #1209) by identifying the bad code sequences and
// rewriting them.
//
// Current workaround: Avoid OpUnreachable instructions in loops.
Optimizer::PassToken CreateWorkaround1209Pass();
} // namespace spvtools
#endif // SPIRV_TOOLS_OPTIMIZER_HPP_

View File

@ -71,6 +71,7 @@ add_library(SPIRV-Tools-opt
types.h
unify_const_pass.h
value_number_table.h
workaround1209.h
aggressive_dead_code_elim_pass.cpp
basic_block.cpp
@ -127,6 +128,7 @@ add_library(SPIRV-Tools-opt
types.cpp
unify_const_pass.cpp
value_number_table.cpp
workaround1209.cpp
)
spvtools_default_compile_options(SPIRV-Tools-opt)

View File

@ -349,4 +349,9 @@ Optimizer::PassToken CreateCCPPass() {
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
}
Optimizer::PassToken CreateWorkaround1209Pass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::Workaround1209>());
}
} // namespace spvtools

View File

@ -48,5 +48,6 @@
#include "strength_reduction_pass.h"
#include "strip_debug_info_pass.h"
#include "unify_const_pass.h"
#include "workaround1209.h"
#endif // LIBSPIRV_OPT_PASSES_H_

View File

@ -0,0 +1,68 @@
// Copyright (c) 2018 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.
#include "workaround1209.h"
#include <list>
#include <stack>
namespace spvtools {
namespace opt {
Pass::Status Workaround1209::Process(ir::IRContext* c) {
InitializeProcessing(c);
bool modified = false;
modified = RemoveOpUnreachableInLoops();
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
}
bool Workaround1209::RemoveOpUnreachableInLoops() {
bool modified = false;
for (auto& func : *get_module()) {
std::list<ir::BasicBlock*> structured_order;
cfg()->ComputeStructuredOrder(&func, &*func.begin(), &structured_order);
// Keep track of the loop merges. The top of the stack will always be the
// loop merge for the loop that immediately contains the basic block being
// processed.
std::stack<uint32_t> loop_merges;
for (ir::BasicBlock* bb : structured_order) {
if (!loop_merges.empty() && bb->id() == loop_merges.top()) {
loop_merges.pop();
}
if (bb->tail()->opcode() == SpvOpUnreachable) {
if (!loop_merges.empty()) {
// We found an OpUnreachable inside a loop.
// Replace it with an unconditional branch to the loop merge.
context()->KillInst(&*bb->tail());
std::unique_ptr<ir::Instruction> new_branch(
new ir::Instruction(context(), SpvOpBranch, 0, 0,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{loop_merges.top()}}}));
context()->AnalyzeDefUse(&*new_branch);
bb->AddInstruction(std::move(new_branch));
modified = true;
}
} else {
if (bb->GetLoopMergeInst()) {
loop_merges.push(bb->MergeBlockIdIfAny());
}
}
}
}
return modified;
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,41 @@
// Copyright (c) 2018 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.
#ifndef LIBSPIRV_OPT_WORKAROUND1209_H_
#define LIBSPIRV_OPT_WORKAROUND1209_H_
#include "pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
class Workaround1209 : public Pass {
public:
const char* name() const override { return "workaround-1209"; }
Status Process(ir::IRContext*) override;
private:
// There is at least one driver where an OpUnreachable found in a loop is not
// handled correctly. Workaround that by changing the OpUnreachable into a
// branch to the loop merge.
//
// Returns true if the code changed.
bool RemoveOpUnreachableInLoops();
};
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_OPT_WORKAROUND1209_H_

View File

@ -261,3 +261,8 @@ add_spvtools_unittest(TARGET ccp
SRCS ccp_test.cpp
LIBS SPIRV-Tools-opt
)
add_spvtools_unittest(TARGET pass_workaround1209
SRCS workaround1209_test.cpp pass_utils.cpp
LIBS SPIRV-Tools-opt
)

View File

@ -0,0 +1,421 @@
// 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.
#include "assembly_builder.h"
#include "gmock/gmock.h"
#include "pass_fixture.h"
#include "pass_utils.h"
#include <algorithm>
#include <cstdarg>
#include <iostream>
#include <sstream>
#include <unordered_set>
namespace {
using namespace spvtools;
using Workaround1209Test = PassTest<::testing::Test>;
#ifdef SPIRV_EFFCEE
TEST_F(Workaround1209Test, RemoveOpUnreachableInLoop) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %texcoord %gl_VertexIndex %_
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpName %texcoord "texcoord"
OpName %buf "buf"
OpMemberName %buf 0 "MVP"
OpMemberName %buf 1 "position"
OpMemberName %buf 2 "attr"
OpName %ubuf "ubuf"
OpName %gl_VertexIndex "gl_VertexIndex"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpName %_ ""
OpDecorate %texcoord Location 0
OpDecorate %_arr_v4float_uint_72 ArrayStride 16
OpDecorate %_arr_v4float_uint_72_0 ArrayStride 16
OpMemberDecorate %buf 0 ColMajor
OpMemberDecorate %buf 0 Offset 0
OpMemberDecorate %buf 0 MatrixStride 16
OpMemberDecorate %buf 1 Offset 64
OpMemberDecorate %buf 2 Offset 1216
OpDecorate %buf Block
OpDecorate %ubuf DescriptorSet 0
OpDecorate %ubuf Binding 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpDecorate %gl_PerVertex Block
%void = OpTypeVoid
%12 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%texcoord = OpVariable %_ptr_Output_v4float Output
%mat4v4float = OpTypeMatrix %v4float 4
%uint = OpTypeInt 32 0
%uint_72 = OpConstant %uint 72
%_arr_v4float_uint_72 = OpTypeArray %v4float %uint_72
%_arr_v4float_uint_72_0 = OpTypeArray %v4float %uint_72
%buf = OpTypeStruct %mat4v4float %_arr_v4float_uint_72 %_arr_v4float_uint_72_0
%_ptr_Uniform_buf = OpTypePointer Uniform %buf
%ubuf = OpVariable %_ptr_Uniform_buf Uniform
%int = OpTypeInt 32 1
%int_2 = OpConstant %int 2
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%gl_PerVertex = OpTypeStruct %v4float
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%float_1 = OpConstant %float 1
%28 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
%main = OpFunction %void None %12
%29 = OpLabel
OpBranch %30
%30 = OpLabel
; CHECK: OpLoopMerge [[merge:%[a-zA-Z_\d]+]]
OpLoopMerge %31 %32 None
OpBranch %33
%33 = OpLabel
; CHECK: OpSelectionMerge [[sel_merge:%[a-zA-Z_\d]+]]
OpSelectionMerge %34 None
OpSwitch %int_1 %35
%35 = OpLabel
%36 = OpLoad %int %gl_VertexIndex
%37 = OpAccessChain %_ptr_Uniform_v4float %ubuf %int_2 %36
%38 = OpLoad %v4float %37
OpStore %texcoord %38
%39 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %39 %28
OpBranch %31
; CHECK: [[sel_merge]] = OpLabel
%34 = OpLabel
; CHECK-NEXT: OpBranch [[merge]]
OpUnreachable
%32 = OpLabel
OpBranch %30
%31 = OpLabel
OpReturn
OpFunctionEnd)";
SinglePassRunAndMatch<opt::Workaround1209>(text, false);
}
TEST_F(Workaround1209Test, RemoveOpUnreachableInNestedLoop) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %2 "main" %3 %4 %5
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %2 "main"
OpName %3 "texcoord"
OpName %6 "buf"
OpMemberName %6 0 "MVP"
OpMemberName %6 1 "position"
OpMemberName %6 2 "attr"
OpName %7 "ubuf"
OpName %4 "gl_VertexIndex"
OpName %8 "gl_PerVertex"
OpMemberName %8 0 "gl_Position"
OpName %5 ""
OpDecorate %3 Location 0
OpDecorate %9 ArrayStride 16
OpDecorate %10 ArrayStride 16
OpMemberDecorate %6 0 ColMajor
OpMemberDecorate %6 0 Offset 0
OpMemberDecorate %6 0 MatrixStride 16
OpMemberDecorate %6 1 Offset 64
OpMemberDecorate %6 2 Offset 1216
OpDecorate %6 Block
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
OpDecorate %4 BuiltIn VertexIndex
OpMemberDecorate %8 0 BuiltIn Position
OpDecorate %8 Block
%11 = OpTypeVoid
%12 = OpTypeFunction %11
%13 = OpTypeFloat 32
%14 = OpTypeVector %13 4
%15 = OpTypePointer Output %14
%3 = OpVariable %15 Output
%16 = OpTypeMatrix %14 4
%17 = OpTypeInt 32 0
%18 = OpConstant %17 72
%9 = OpTypeArray %14 %18
%10 = OpTypeArray %14 %18
%6 = OpTypeStruct %16 %9 %10
%19 = OpTypePointer Uniform %6
%7 = OpVariable %19 Uniform
%20 = OpTypeInt 32 1
%21 = OpConstant %20 2
%22 = OpTypePointer Input %20
%4 = OpVariable %22 Input
%23 = OpTypePointer Uniform %14
%8 = OpTypeStruct %14
%24 = OpTypePointer Output %8
%5 = OpVariable %24 Output
%25 = OpConstant %20 0
%26 = OpConstant %20 1
%27 = OpConstant %13 1
%28 = OpConstantComposite %14 %27 %27 %27 %27
%2 = OpFunction %11 None %12
%29 = OpLabel
OpBranch %31
%31 = OpLabel
; CHECK: OpLoopMerge
OpLoopMerge %32 %33 None
OpBranch %30
%30 = OpLabel
; CHECK: OpLoopMerge [[merge:%[a-zA-Z_\d]+]]
OpLoopMerge %34 %35 None
OpBranch %36
%36 = OpLabel
; CHECK: OpSelectionMerge [[sel_merge:%[a-zA-Z_\d]+]]
OpSelectionMerge %37 None
OpSwitch %26 %38
%38 = OpLabel
%39 = OpLoad %20 %4
%40 = OpAccessChain %23 %7 %21 %39
%41 = OpLoad %14 %40
OpStore %3 %41
%42 = OpAccessChain %15 %5 %25
OpStore %42 %28
OpBranch %34
; CHECK: [[sel_merge]] = OpLabel
%37 = OpLabel
; CHECK-NEXT: OpBranch [[merge]]
OpUnreachable
%35 = OpLabel
OpBranch %30
%34 = OpLabel
OpBranch %32
%33 = OpLabel
OpBranch %31
%32 = OpLabel
OpReturn
OpFunctionEnd)";
SinglePassRunAndMatch<opt::Workaround1209>(text, false);
}
TEST_F(Workaround1209Test, RemoveOpUnreachableInAdjacentLoops) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %2 "main" %3 %4 %5
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %2 "main"
OpName %3 "texcoord"
OpName %6 "buf"
OpMemberName %6 0 "MVP"
OpMemberName %6 1 "position"
OpMemberName %6 2 "attr"
OpName %7 "ubuf"
OpName %4 "gl_VertexIndex"
OpName %8 "gl_PerVertex"
OpMemberName %8 0 "gl_Position"
OpName %5 ""
OpDecorate %3 Location 0
OpDecorate %9 ArrayStride 16
OpDecorate %10 ArrayStride 16
OpMemberDecorate %6 0 ColMajor
OpMemberDecorate %6 0 Offset 0
OpMemberDecorate %6 0 MatrixStride 16
OpMemberDecorate %6 1 Offset 64
OpMemberDecorate %6 2 Offset 1216
OpDecorate %6 Block
OpDecorate %7 DescriptorSet 0
OpDecorate %7 Binding 0
OpDecorate %4 BuiltIn VertexIndex
OpMemberDecorate %8 0 BuiltIn Position
OpDecorate %8 Block
%11 = OpTypeVoid
%12 = OpTypeFunction %11
%13 = OpTypeFloat 32
%14 = OpTypeVector %13 4
%15 = OpTypePointer Output %14
%3 = OpVariable %15 Output
%16 = OpTypeMatrix %14 4
%17 = OpTypeInt 32 0
%18 = OpConstant %17 72
%9 = OpTypeArray %14 %18
%10 = OpTypeArray %14 %18
%6 = OpTypeStruct %16 %9 %10
%19 = OpTypePointer Uniform %6
%7 = OpVariable %19 Uniform
%20 = OpTypeInt 32 1
%21 = OpConstant %20 2
%22 = OpTypePointer Input %20
%4 = OpVariable %22 Input
%23 = OpTypePointer Uniform %14
%8 = OpTypeStruct %14
%24 = OpTypePointer Output %8
%5 = OpVariable %24 Output
%25 = OpConstant %20 0
%26 = OpConstant %20 1
%27 = OpConstant %13 1
%28 = OpConstantComposite %14 %27 %27 %27 %27
%2 = OpFunction %11 None %12
%29 = OpLabel
OpBranch %30
%30 = OpLabel
; CHECK: OpLoopMerge [[merge1:%[a-zA-Z_\d]+]]
OpLoopMerge %31 %32 None
OpBranch %33
%33 = OpLabel
; CHECK: OpSelectionMerge [[sel_merge1:%[a-zA-Z_\d]+]]
OpSelectionMerge %34 None
OpSwitch %26 %35
%35 = OpLabel
%36 = OpLoad %20 %4
%37 = OpAccessChain %23 %7 %21 %36
%38 = OpLoad %14 %37
OpStore %3 %38
%39 = OpAccessChain %15 %5 %25
OpStore %39 %28
OpBranch %31
; CHECK: [[sel_merge1]] = OpLabel
%34 = OpLabel
; CHECK-NEXT: OpBranch [[merge1]]
OpUnreachable
%32 = OpLabel
OpBranch %30
%31 = OpLabel
; CHECK: OpLoopMerge [[merge2:%[a-zA-Z_\d]+]]
OpLoopMerge %40 %41 None
OpBranch %42
%42 = OpLabel
; CHECK: OpSelectionMerge [[sel_merge2:%[a-zA-Z_\d]+]]
OpSelectionMerge %43 None
OpSwitch %26 %44
%44 = OpLabel
%45 = OpLoad %20 %4
%46 = OpAccessChain %23 %7 %21 %45
%47 = OpLoad %14 %46
OpStore %3 %47
%48 = OpAccessChain %15 %5 %25
OpStore %48 %28
OpBranch %40
; CHECK: [[sel_merge2]] = OpLabel
%43 = OpLabel
; CHECK-NEXT: OpBranch [[merge2]]
OpUnreachable
%41 = OpLabel
OpBranch %31
%40 = OpLabel
OpReturn
OpFunctionEnd)";
SinglePassRunAndMatch<opt::Workaround1209>(text, false);
}
TEST_F(Workaround1209Test, LeaveUnreachableNotInLoop) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %texcoord %gl_VertexIndex %_
OpSource GLSL 400
OpSourceExtension "GL_ARB_separate_shader_objects"
OpSourceExtension "GL_ARB_shading_language_420pack"
OpName %main "main"
OpName %texcoord "texcoord"
OpName %buf "buf"
OpMemberName %buf 0 "MVP"
OpMemberName %buf 1 "position"
OpMemberName %buf 2 "attr"
OpName %ubuf "ubuf"
OpName %gl_VertexIndex "gl_VertexIndex"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpName %_ ""
OpDecorate %texcoord Location 0
OpDecorate %_arr_v4float_uint_72 ArrayStride 16
OpDecorate %_arr_v4float_uint_72_0 ArrayStride 16
OpMemberDecorate %buf 0 ColMajor
OpMemberDecorate %buf 0 Offset 0
OpMemberDecorate %buf 0 MatrixStride 16
OpMemberDecorate %buf 1 Offset 64
OpMemberDecorate %buf 2 Offset 1216
OpDecorate %buf Block
OpDecorate %ubuf DescriptorSet 0
OpDecorate %ubuf Binding 0
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpDecorate %gl_PerVertex Block
%void = OpTypeVoid
%12 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%texcoord = OpVariable %_ptr_Output_v4float Output
%mat4v4float = OpTypeMatrix %v4float 4
%uint = OpTypeInt 32 0
%uint_72 = OpConstant %uint 72
%_arr_v4float_uint_72 = OpTypeArray %v4float %uint_72
%_arr_v4float_uint_72_0 = OpTypeArray %v4float %uint_72
%buf = OpTypeStruct %mat4v4float %_arr_v4float_uint_72 %_arr_v4float_uint_72_0
%_ptr_Uniform_buf = OpTypePointer Uniform %buf
%ubuf = OpVariable %_ptr_Uniform_buf Uniform
%int = OpTypeInt 32 1
%int_2 = OpConstant %int 2
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%gl_PerVertex = OpTypeStruct %v4float
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%float_1 = OpConstant %float 1
%28 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
%main = OpFunction %void None %12
%29 = OpLabel
OpBranch %30
%30 = OpLabel
OpSelectionMerge %34 None
OpSwitch %int_1 %35
%35 = OpLabel
%36 = OpLoad %int %gl_VertexIndex
%37 = OpAccessChain %_ptr_Uniform_v4float %ubuf %int_2 %36
%38 = OpLoad %v4float %37
OpStore %texcoord %38
%39 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %39 %28
OpReturn
%34 = OpLabel
; CHECK: OpUnreachable
OpUnreachable
OpFunctionEnd)";
SinglePassRunAndMatch<opt::Workaround1209>(text, false);
}
#endif
} // anonymous namespace

View File

@ -253,6 +253,10 @@ Options (in lexicographical order):
Replaces instructions with equivalent and less expensive ones.
--strip-debug
Remove all debug instructions.
--workaround-1209
Rewrites instructions for which there are known driver bugs to
avoid triggering those bugs.
Current workarounds: Avoid OpUnreachable in loops.
--unify-const
Remove the duplicated constants.
-h, --help
@ -441,6 +445,8 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer,
optimizer->RegisterPass(CreatePrivateToLocalPass());
} else if (0 == strcmp(cur_arg, "--remove-duplicates")) {
optimizer->RegisterPass(CreateRemoveDuplicatesPass());
} else if (0 == strcmp(cur_arg, "--workaround-1209")) {
optimizer->RegisterPass(CreateWorkaround1209Pass());
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
options->relax_struct_store = true;
} else if (0 == strcmp(cur_arg, "--skip-validation")) {