# Copyright (c) 2018 Google LLC
#
# 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.
import placeholder
import expect
import re
from spirv_test_framework import inside_spirv_testsuite
def empty_main_assembly():
return """
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %4 "main"
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd"""
@inside_spirv_testsuite('SpirvOptBase')
class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_3):
"""Tests that spirv-opt accepts a SPIR-V object file."""
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
output = placeholder.TempFileName('output.spv')
spirv_args = [shader, '-o', output]
expected_object_filenames = (output)
@inside_spirv_testsuite('SpirvOptFlags')
class TestHelpFlag(expect.ReturnCodeIsZero, expect.StdoutMatch):
"""Test the --help flag."""
spirv_args = ['--help']
expected_stdout = re.compile(r'.*The SPIR-V binary is read from <input>')
@inside_spirv_testsuite('SpirvOptFlags')
class TestValidPassFlags(expect.ValidObjectFile1_3,
expect.ExecutedListOfPasses):
"""Tests that spirv-opt accepts all valid optimization flags."""
flags = [
'--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids',
'--convert-local-access-chains', '--copy-propagate-arrays',
'--eliminate-common-uniform', '--eliminate-dead-branches',
'--eliminate-dead-code-aggressive', '--eliminate-dead-const',
'--eliminate-dead-functions', '--eliminate-dead-inserts',
'--eliminate-dead-variables', '--eliminate-insert-extract',
'--eliminate-local-multi-store', '--eliminate-local-single-block',
'--eliminate-local-single-store', '--flatten-decorations',
'--fold-spec-const-op-composite', '--freeze-spec-const',
'--if-conversion', '--inline-entry-points-exhaustive', '--loop-fission',
'20', '--loop-fusion', '5', '--loop-unroll', '--loop-unroll-partial', '3',
'--loop-peeling', '--merge-blocks', '--merge-return', '--loop-unswitch',
'--private-to-local', '--reduce-load-size', '--redundancy-elimination',
'--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite',
'--scalar-replacement', '--scalar-replacement=42', '--strength-reduction',
'--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209',
'--unify-const'
]
expected_passes = [
'ccp',
'cfg-cleanup',
'combine-access-chains',
'compact-ids',
'convert-local-access-chains',
'copy-propagate-arrays',
'eliminate-common-uniform',
'eliminate-dead-branches',
'eliminate-dead-code-aggressive',
'eliminate-dead-const',
'eliminate-dead-functions',
'eliminate-dead-inserts',
'eliminate-dead-variables',
# --eliminate-insert-extract runs the simplify-instructions pass.
'simplify-instructions',
'eliminate-local-multi-store',
'eliminate-local-single-block',
'eliminate-local-single-store',
'flatten-decorations',
'fold-spec-const-op-composite',
'freeze-spec-const',
'if-conversion',
'inline-entry-points-exhaustive',
'loop-fission',
'loop-fusion',
'loop-unroll',
'loop-unroll',
'loop-peeling',
'merge-blocks',
'merge-return',
'loop-unswitch',
'private-to-local',
'reduce-load-size',
'redundancy-elimination',
'remove-duplicates',
'replace-invalid-opcode',
'ssa-rewrite',
'scalar-replacement=100',
'scalar-replacement=42',
'strength-reduction',
'strip-debug',
'strip-reflect',
'vector-dce',
'workaround-1209',
'unify-const'
]
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
output = placeholder.TempFileName('output.spv')
spirv_args = [shader, '-o', output, '--print-all'] + flags
expected_object_filenames = (output)
@inside_spirv_testsuite('SpirvOptFlags')
class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_3,
expect.ExecutedListOfPasses):
"""Tests that spirv-opt schedules all the passes triggered by -O."""
flags = ['-O']
expected_passes = [
'eliminate-dead-branches',
'merge-return',
'inline-entry-points-exhaustive',
'eliminate-dead-code-aggressive',
'private-to-local',
'eliminate-local-single-block',
'eliminate-local-single-store',
'eliminate-dead-code-aggressive',
'scalar-replacement=100',
'convert-local-access-chains',
'eliminate-local-single-block',
'eliminate-local-single-store',
'eliminate-dead-code-aggressive',
'eliminate-local-multi-store',
'eliminate-dead-code-aggressive',
'ccp',
'eliminate-dead-code-aggressive',
'redundancy-elimination',
'combine-access-chains',
'simplify-instructions',
'vector-dce',
'eliminate-dead-inserts',
'eliminate-dead-branches',
'simplify-instructions',
'if-conversion',
'copy-propagate-arrays',
'reduce-load-size',
'eliminate-dead-code-aggressive',
'merge-blocks',
'redundancy-elimination',
'eliminate-dead-branches',
'merge-blocks',
'simplify-instructions',
]
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
output = placeholder.TempFileName('output.spv')
spirv_args = [shader, '-o', output, '--print-all'] + flags
expected_object_filenames = (output)
@inside_spirv_testsuite('SpirvOptFlags')
class TestSizeOptimizationPasses(expect.ValidObjectFile1_3,
expect.ExecutedListOfPasses):
"""Tests that spirv-opt schedules all the passes triggered by -Os."""
flags = ['-Os']
expected_passes = [
'eliminate-dead-branches',
'merge-return',
'inline-entry-points-exhaustive',
'eliminate-dead-code-aggressive',
'private-to-local',
'scalar-replacement=100',
'convert-local-access-chains',
'eliminate-local-single-block',
'eliminate-local-single-store',
'eliminate-dead-code-aggressive',
'simplify-instructions',
'eliminate-dead-inserts',
'eliminate-local-multi-store',
'eliminate-dead-code-aggressive',
'ccp',
'eliminate-dead-code-aggressive',
'eliminate-dead-branches',
'if-conversion',
'eliminate-dead-code-aggressive',
'merge-blocks',
'simplify-instructions',
'eliminate-dead-inserts',
'redundancy-elimination',
'cfg-cleanup',
'eliminate-dead-code-aggressive',
]
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
output = placeholder.TempFileName('output.spv')
spirv_args = [shader, '-o', output, '--print-all'] + flags
expected_object_filenames = (output)
@inside_spirv_testsuite('SpirvOptFlags')
class TestLegalizationPasses(expect.ValidObjectFile1_3,
expect.ExecutedListOfPasses):
"""Tests that spirv-opt schedules all the passes triggered by --legalize-hlsl.
"""
flags = ['--legalize-hlsl']
expected_passes = [
'eliminate-dead-branches',
'merge-return',
'inline-entry-points-exhaustive',
'eliminate-dead-functions',
'private-to-local',
'eliminate-local-single-block',
'eliminate-local-single-store',
'eliminate-dead-code-aggressive',
'scalar-replacement=0',
'eliminate-local-single-block',
'eliminate-local-single-store',
'eliminate-dead-code-aggressive',
'eliminate-local-multi-store',
'eliminate-dead-code-aggressive',
'ccp',
'loop-unroll',
'eliminate-dead-branches',
'simplify-instructions',
'eliminate-dead-code-aggressive',
'copy-propagate-arrays',
'vector-dce',
'eliminate-dead-inserts',
'reduce-load-size',
'eliminate-dead-code-aggressive',
]
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
output = placeholder.TempFileName('output.spv')
spirv_args = [shader, '-o', output, '--print-all'] + flags
expected_object_filenames = (output)
@inside_spirv_testsuite('SpirvOptFlags')
class TestScalarReplacementArgsNegative(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --scalar-replacement."""
spirv_args = ['--scalar-replacement=-10']
expected_error_substr = 'must have no arguments or a non-negative integer argument'
@inside_spirv_testsuite('SpirvOptFlags')
class TestScalarReplacementArgsInvalidNumber(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --scalar-replacement."""
spirv_args = ['--scalar-replacement=a10f']
expected_error_substr = 'must have no arguments or a non-negative integer argument'
@inside_spirv_testsuite('SpirvOptFlags')
class TestLoopFissionArgsNegative(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --loop-fission."""
spirv_args = ['--loop-fission=-10']
expected_error_substr = 'must have a positive integer argument'
@inside_spirv_testsuite('SpirvOptFlags')
class TestLoopFissionArgsInvalidNumber(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --loop-fission."""
spirv_args = ['--loop-fission=a10f']
expected_error_substr = 'must have a positive integer argument'
@inside_spirv_testsuite('SpirvOptFlags')
class TestLoopFusionArgsNegative(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --loop-fusion."""
spirv_args = ['--loop-fusion=-10']
expected_error_substr = 'must have a positive integer argument'
@inside_spirv_testsuite('SpirvOptFlags')
class TestLoopFusionArgsInvalidNumber(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --loop-fusion."""
spirv_args = ['--loop-fusion=a10f']
expected_error_substr = 'must have a positive integer argument'
@inside_spirv_testsuite('SpirvOptFlags')
class TestLoopUnrollPartialArgsNegative(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --loop-unroll-partial."""
spirv_args = ['--loop-unroll-partial=-10']
expected_error_substr = 'must have a positive integer argument'
@inside_spirv_testsuite('SpirvOptFlags')
class TestLoopUnrollPartialArgsInvalidNumber(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --loop-unroll-partial."""
spirv_args = ['--loop-unroll-partial=a10f']
expected_error_substr = 'must have a positive integer argument'
@inside_spirv_testsuite('SpirvOptFlags')
class TestLoopPeelingThresholdArgsNegative(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --loop-peeling-threshold."""
spirv_args = ['--loop-peeling-threshold=-10']
expected_error_substr = 'must have a positive integer argument'
@inside_spirv_testsuite('SpirvOptFlags')
class TestLoopPeelingThresholdArgsInvalidNumber(expect.ErrorMessageSubstr):
"""Tests invalid arguments to --loop-peeling-threshold."""
spirv_args = ['--loop-peeling-threshold=a10f']
expected_error_substr = 'must have a positive integer argument'