Mesa (master): aco: add framework for testing isel and integration tests

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Thu Jul 30 16:28:49 UTC 2020


Module: Mesa
Branch: master
Commit: d488d0fd7baff77204dbd3fd0d625b5974728534
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=d488d0fd7baff77204dbd3fd0d625b5974728534

Author: Rhys Perry <pendingchaos02 at gmail.com>
Date:   Fri Feb 14 11:32:18 2020 +0000

aco: add framework for testing isel and integration tests

And add some simple tests to demonstrate/test the pipeline builder and
glsl_scraper.py.

Signed-off-by: Rhys Perry <pendingchaos02 at gmail.com>
Acked-by: Samuel Pitoiset <samuel.pitoiset at gmail.com>
Acked-by: Daniel Schürmann <daniel at schuermann.dev>
Acked-by: Timur Kristóf <timur.kristof at gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3521>

---

 meson.build                            |   4 +
 meson_options.txt                      |   2 +-
 src/amd/compiler/tests/glsl_scraper.py | 339 ++++++++++++++++++
 src/amd/compiler/tests/helpers.cpp     | 605 +++++++++++++++++++++++++++++++++
 src/amd/compiler/tests/helpers.h       | 103 ++++++
 src/amd/compiler/tests/meson.build     |  15 +-
 src/amd/compiler/tests/test_isel.cpp   |  82 +++++
 src/vulkan/overlay-layer/meson.build   |   4 +-
 8 files changed, 1148 insertions(+), 6 deletions(-)

diff --git a/meson.build b/meson.build
index fc6eecaeaa9..011b1eca832 100644
--- a/meson.build
+++ b/meson.build
@@ -543,6 +543,10 @@ if with_gallium_zink
   dep_vulkan = dependency('vulkan')
 endif
 
+if with_vulkan_overlay_layer or with_aco_tests
+  prog_glslang = find_program('glslangValidator')
+endif
+
 _xvmc = get_option('gallium-xvmc')
 if _xvmc == 'true'
   _xvmc = 'enabled'
diff --git a/meson_options.txt b/meson_options.txt
index 63290eba415..ae0e7cc1d71 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -292,7 +292,7 @@ option(
   'build-aco-tests',
   type : 'boolean',
   value : false,
-  description : 'Build ACO tests. These do not require an AMD GPU.'
+  description : 'Build ACO tests. These require RADV and glslang but not an AMD GPU.'
 )
 option(
   'install-intel-gpu-tests',
diff --git a/src/amd/compiler/tests/glsl_scraper.py b/src/amd/compiler/tests/glsl_scraper.py
new file mode 100644
index 00000000000..c2ca6ebb93e
--- /dev/null
+++ b/src/amd/compiler/tests/glsl_scraper.py
@@ -0,0 +1,339 @@
+#! /usr/bin/env python3
+# Taken from Crucible and modified to parse declarations
+
+import argparse
+import io
+import os
+import re
+import shutil
+import struct
+import subprocess
+import sys
+import tempfile
+from textwrap import dedent
+
+class ShaderCompileError(RuntimeError):
+    def __init__(self, *args):
+        super(ShaderCompileError, self).__init__(*args)
+
+target_env_re = re.compile(r'QO_TARGET_ENV\s+(\S+)')
+
+stage_to_glslang_stage = {
+    'VERTEX': 'vert',
+    'TESS_CONTROL': 'tesc',
+    'TESS_EVALUATION': 'tese',
+    'GEOMETRY': 'geom',
+    'FRAGMENT': 'frag',
+    'COMPUTE': 'comp',
+}
+
+base_layout_qualifier_id_re = r'({0}\s*=\s*(?P<{0}>\d+))'
+id_re = '(?P<name_%d>[^(gl_)]\S+)'
+type_re = '(?P<dtype_%d>\S+)'
+location_re = base_layout_qualifier_id_re.format('location')
+component_re = base_layout_qualifier_id_re.format('component')
+binding_re = base_layout_qualifier_id_re.format('binding')
+set_re = base_layout_qualifier_id_re.format('set')
+unk_re = r'\S+(=\d+)?'
+layout_qualifier_re = r'layout\W*\((%s)+\)' % '|'.join([location_re, binding_re, set_re, unk_re, '[, ]+'])
+ubo_decl_re = 'uniform\W+%s(\W*{)?(?P<type_ubo>)' % (id_re%0)
+ssbo_decl_re = 'buffer\W+%s(\W*{)?(?P<type_ssbo>)' % (id_re%1)
+image_buffer_decl_re = r'uniform\W+imageBuffer\w+%s;(?P<type_img_buf>)' % (id_re%2)
+image_decl_re = r'uniform\W+image\S+\W+%s;(?P<type_img>)' % (id_re%3)
+texture_buffer_decl_re = r'uniform\W+textureBuffer\w+%s;(?P<type_tex_buf>)' % (id_re%4)
+combined_texture_sampler_decl_re = r'uniform\W+sampler\S+\W+%s;(?P<type_combined>)' % (id_re%5)
+texture_decl_re = r'uniform\W+texture\S+\W+%s;(?P<type_tex>)' % (id_re%6)
+sampler_decl_re = r'uniform\W+sampler\w+%s;(?P<type_samp>)' % (id_re%7)
+input_re = r'in\W+%s\W+%s;(?P<type_in>)' % (type_re%0, id_re%8)
+output_re = r'out\W+%s\W+%s;(?P<type_out>)' % (type_re%1, id_re%9)
+match_decl_re = re.compile(layout_qualifier_re + r'\W*((' + r')|('.join([ubo_decl_re, ssbo_decl_re, image_buffer_decl_re, image_decl_re, texture_buffer_decl_re, combined_texture_sampler_decl_re, texture_decl_re, sampler_decl_re, input_re, output_re]) + r'))$')
+
+class Shader:
+    def __init__(self, stage):
+        self.glsl = None
+        self.stream = io.StringIO()
+        self.stage = stage
+        self.dwords = None
+        self.target_env = ""
+        self.declarations = []
+
+    def add_text(self, s):
+        self.stream.write(s)
+
+    def finish_text(self, start_line, end_line):
+        self.glsl = self.stream.getvalue()
+        self.stream = None
+
+        # Handle the QO_EXTENSION macro
+        self.glsl = self.glsl.replace('QO_EXTENSION', '#extension')
+
+        # Handle the QO_DEFINE macro
+        self.glsl = self.glsl.replace('QO_DEFINE', '#define')
+
+        m = target_env_re.search(self.glsl)
+        if m:
+            self.target_env = m.group(1)
+        self.glsl = self.glsl.replace('QO_TARGET_ENV', '// --target-env')
+
+        self.start_line = start_line
+        self.end_line = end_line
+
+    def __run_glslang(self, extra_args=[]):
+        stage = stage_to_glslang_stage[self.stage]
+        stage_flags = ['-S', stage]
+
+        in_file = tempfile.NamedTemporaryFile(suffix='.'+stage)
+        src = ('#version 450\n' + self.glsl).encode('utf-8')
+        in_file.write(src)
+        in_file.flush()
+        out_file = tempfile.NamedTemporaryFile(suffix='.spirv')
+        args = [glslang, '-H'] + extra_args + stage_flags
+        if self.target_env:
+            args += ['--target-env', self.target_env]
+        args += ['-o', out_file.name, in_file.name]
+        with subprocess.Popen(args,
+                              stdout = subprocess.PIPE,
+                              stderr = subprocess.PIPE,
+                              stdin = subprocess.PIPE) as proc:
+
+            out, err = proc.communicate(timeout=30)
+            in_file.close()
+
+            if proc.returncode != 0:
+                # Unfortunately, glslang dumps errors to standard out.
+                # However, since we don't really want to count on that,
+                # we'll grab the output of both
+                message = out.decode('utf-8') + '\n' + err.decode('utf-8')
+                raise ShaderCompileError(message.strip())
+
+            out_file.seek(0)
+            spirv = out_file.read()
+            out_file.close()
+            return (spirv, out)
+
+    def _parse_declarations(self):
+        for line in self.glsl.splitlines():
+            res = re.match(match_decl_re, line.lstrip().rstrip())
+            if res == None:
+                continue
+            res = {k:v for k, v in res.groupdict().items() if v != None}
+            name = [v for k, v in res.items() if k.startswith('name_')][0]
+            data_type = ([v for k, v in res.items() if k.startswith('dtype_')] + [''])[0]
+            decl_type = [k for k, v in res.items() if k.startswith('type_')][0][5:]
+            location = int(res.get('location', 0))
+            component = int(res.get('component', 0))
+            binding = int(res.get('binding', 0))
+            desc_set = int(res.get('set', 0))
+            self.declarations.append('{"%s", "%s", QoShaderDeclType_%s, %d, %d, %d, %d}' %
+                                     (name, data_type, decl_type, location, component, binding, desc_set))
+
+    def compile(self):
+        def dwords(f):
+            while True:
+                dword_str = f.read(4)
+                if not dword_str:
+                    return
+                assert len(dword_str) == 4
+                yield struct.unpack('I', dword_str)[0]
+
+        (spirv, assembly) = self.__run_glslang()
+        self.dwords = list(dwords(io.BytesIO(spirv)))
+        self.assembly = str(assembly, 'utf-8')
+
+        self._parse_declarations()
+
+    def _dump_glsl_code(self, f):
+        # Dump GLSL code for reference.  Use // instead of /* */
+        # comments so we don't need to escape the GLSL code.
+        f.write('// GLSL code:\n')
+        f.write('//')
+        for line in self.glsl.splitlines():
+            f.write('\n// {0}'.format(line))
+        f.write('\n\n')
+
+    def _dump_spirv_code(self, f, var_name):
+        f.write('/* SPIR-V Assembly:\n')
+        f.write(' *\n')
+        for line in self.assembly.splitlines():
+            f.write(' * ' + line + '\n')
+        f.write(' */\n')
+
+        f.write('static const uint32_t {0}[] = {{'.format(var_name))
+        line_start = 0
+        while line_start < len(self.dwords):
+            f.write('\n    ')
+            for i in range(line_start, min(line_start + 6, len(self.dwords))):
+                f.write(' 0x{:08x},'.format(self.dwords[i]))
+            line_start += 6
+        f.write('\n};\n')
+
+    def dump_c_code(self, f):
+        f.write('\n\n')
+        var_prefix = '__qonos_shader{0}'.format(self.end_line)
+
+        self._dump_glsl_code(f)
+        self._dump_spirv_code(f, var_prefix + '_spir_v_src')
+        f.write('static const QoShaderDecl {0}_decls[] = {{{1}}};\n'.format(var_prefix, ', '.join(self.declarations)))
+
+        f.write(dedent("""\
+            static const QoShaderModuleCreateInfo {0}_info = {{
+                .spirvSize = sizeof({0}_spir_v_src),
+                .pSpirv = {0}_spir_v_src,
+                .declarationCount = sizeof({0}_decls) / sizeof({0}_decls[0]),
+                .pDeclarations = {0}_decls,
+            """.format(var_prefix)))
+
+        f.write("    .stage = VK_SHADER_STAGE_{0}_BIT,\n".format(self.stage))
+
+        f.write('};\n')
+
+        f.write('#define __qonos_shader{0}_info __qonos_shader{1}_info\n'\
+                .format(self.start_line, self.end_line))
+
+token_exp = re.compile(r'(qoShaderModuleCreateInfoGLSL|qoCreateShaderModuleGLSL|\(|\)|,)')
+
+class Parser:
+    def __init__(self, f):
+        self.infile = f
+        self.paren_depth = 0
+        self.shader = None
+        self.line_number = 1
+        self.shaders = []
+
+        def tokenize(f):
+            leftover = ''
+            for line in f:
+                pos = 0
+                while True:
+                    m = token_exp.search(line, pos)
+                    if m:
+                        if m.start() > pos:
+                            leftover += line[pos:m.start()]
+                        pos = m.end()
+
+                        if leftover:
+                            yield leftover
+                            leftover = ''
+
+                        yield m.group(0)
+
+                    else:
+                        leftover += line[pos:]
+                        break
+
+                self.line_number += 1
+
+            if leftover:
+                yield leftover
+
+        self.token_iter = tokenize(self.infile)
+
+    def handle_shader_src(self):
+        paren_depth = 1
+        for t in self.token_iter:
+            if t == '(':
+                paren_depth += 1
+            elif t == ')':
+                paren_depth -= 1
+                if paren_depth == 0:
+                    return
+
+            self.current_shader.add_text(t)
+
+    def handle_macro(self, macro):
+        t = next(self.token_iter)
+        assert t == '('
+
+        start_line = self.line_number
+
+        if macro == 'qoCreateShaderModuleGLSL':
+            # Throw away the device parameter
+            t = next(self.token_iter)
+            t = next(self.token_iter)
+            assert t == ','
+
+        stage = next(self.token_iter).strip()
+
+        t = next(self.token_iter)
+        assert t == ','
+
+        self.current_shader = Shader(stage)
+        self.handle_shader_src()
+        self.current_shader.finish_text(start_line, self.line_number)
+
+        self.shaders.append(self.current_shader)
+        self.current_shader = None
+
+    def run(self):
+        for t in self.token_iter:
+            if t in ('qoShaderModuleCreateInfoGLSL', 'qoCreateShaderModuleGLSL'):
+                self.handle_macro(t)
+
+def open_file(name, mode):
+    if name == '-':
+        if mode == 'w':
+            return sys.stdout
+        elif mode == 'r':
+            return sys.stdin
+        else:
+            assert False
+    else:
+        return open(name, mode)
+
+def parse_args():
+    description = dedent("""\
+        This program scrapes a C file for any instance of the
+        qoShaderModuleCreateInfoGLSL and qoCreateShaderModuleGLSL macaros,
+        grabs the GLSL source code, compiles it to SPIR-V.  The resulting
+        SPIR-V code is written to another C file as an array of 32-bit
+        words.
+
+        If '-' is passed as the input file or output file, stdin or stdout
+        will be used instead of a file on disc.""")
+
+    p = argparse.ArgumentParser(
+            description=description,
+            formatter_class=argparse.RawDescriptionHelpFormatter)
+    p.add_argument('-o', '--outfile', default='-',
+                        help='Output to the given file (default: stdout).')
+    p.add_argument('--with-glslang', metavar='PATH',
+                        default='glslangValidator',
+                        dest='glslang',
+                        help='Full path to the glslangValidator shader compiler.')
+    p.add_argument('infile', metavar='INFILE')
+
+    return p.parse_args()
+
+
+args = parse_args()
+infname = args.infile
+outfname = args.outfile
+glslang = args.glslang
+
+with open_file(infname, 'r') as infile:
+    parser = Parser(infile)
+    parser.run()
+
+for shader in parser.shaders:
+    shader.compile()
+
+with open_file(outfname, 'w') as outfile:
+    outfile.write(dedent("""\
+        /* ==========================  DO NOT EDIT!  ==========================
+         *             This file is autogenerated by glsl_scraper.py.
+         */
+
+        #include <stdint.h>
+
+        #define __QO_SHADER_INFO_VAR2(_line) __qonos_shader ## _line ## _info
+        #define __QO_SHADER_INFO_VAR(_line) __QO_SHADER_INFO_VAR2(_line)
+
+        #define qoShaderModuleCreateInfoGLSL(stage, ...)  \\
+            __QO_SHADER_INFO_VAR(__LINE__)
+
+        #define qoCreateShaderModuleGLSL(dev, stage, ...) \\
+            __qoCreateShaderModule((dev), &__QO_SHADER_INFO_VAR(__LINE__))
+        """))
+
+    for shader in parser.shaders:
+        shader.dump_c_code(outfile)
diff --git a/src/amd/compiler/tests/helpers.cpp b/src/amd/compiler/tests/helpers.cpp
index f2b7ec786ab..58539db074b 100644
--- a/src/amd/compiler/tests/helpers.cpp
+++ b/src/amd/compiler/tests/helpers.cpp
@@ -22,12 +22,21 @@
  *
  */
 #include "helpers.h"
+#include "vulkan/vk_format.h"
+#include "llvm/ac_llvm_util.h"
 #include <stdio.h>
 #include <sstream>
 #include <llvm-c/Target.h>
+#include <mutex>
 
 using namespace aco;
 
+extern "C" {
+PFN_vkVoidFunction VKAPI_CALL vk_icdGetInstanceProcAddr(
+	VkInstance                                  instance,
+	const char*                                 pName);
+}
+
 ac_shader_config config;
 radv_shader_info info;
 std::unique_ptr<Program> program;
@@ -36,6 +45,35 @@ Temp inputs[16];
 Temp exec_input;
 const char *subvariant = "";
 
+static VkInstance instance_cache[CHIP_LAST] = {VK_NULL_HANDLE};
+static VkDevice device_cache[CHIP_LAST] = {VK_NULL_HANDLE};
+static std::mutex create_device_mutex;
+
+#define FUNCTION_LIST\
+   ITEM(CreateInstance)\
+   ITEM(DestroyInstance)\
+   ITEM(EnumeratePhysicalDevices)\
+   ITEM(GetPhysicalDeviceProperties2)\
+   ITEM(CreateDevice)\
+   ITEM(DestroyDevice)\
+   ITEM(CreateShaderModule)\
+   ITEM(DestroyShaderModule)\
+   ITEM(CreateGraphicsPipelines)\
+   ITEM(CreateComputePipelines)\
+   ITEM(DestroyPipeline)\
+   ITEM(CreateDescriptorSetLayout)\
+   ITEM(DestroyDescriptorSetLayout)\
+   ITEM(CreatePipelineLayout)\
+   ITEM(DestroyPipelineLayout)\
+   ITEM(CreateRenderPass)\
+   ITEM(DestroyRenderPass)\
+   ITEM(GetPipelineExecutablePropertiesKHR)\
+   ITEM(GetPipelineExecutableInternalRepresentationsKHR)
+
+#define ITEM(n) PFN_vk##n n;
+FUNCTION_LIST
+#undef ITEM
+
 void create_program(enum chip_class chip_class, Stage stage, unsigned wave_size, enum radeon_family family)
 {
    memset(&config, 0, sizeof(config));
@@ -168,3 +206,570 @@ void writeout(unsigned i, Temp tmp)
       bld.pseudo(aco_opcode::p_unit_test, Operand(i));
 }
 
+VkDevice get_vk_device(enum chip_class chip_class)
+{
+   enum radeon_family family;
+   switch (chip_class) {
+   case GFX6:
+      family = CHIP_TAHITI;
+      break;
+   case GFX7:
+      family = CHIP_BONAIRE;
+      break;
+   case GFX8:
+      family = CHIP_POLARIS10;
+      break;
+   case GFX9:
+      family = CHIP_VEGA10;
+      break;
+   case GFX10:
+      family = CHIP_NAVI10;
+      break;
+   default:
+      family = CHIP_UNKNOWN;
+      break;
+   }
+   return get_vk_device(family);
+}
+
+VkDevice get_vk_device(enum radeon_family family)
+{
+   assert(family != CHIP_UNKNOWN);
+
+   std::lock_guard<std::mutex> guard(create_device_mutex);
+
+   if (device_cache[family])
+      return device_cache[family];
+
+   setenv("RADV_FORCE_FAMILY", ac_get_llvm_processor_name(family), 1);
+
+   VkApplicationInfo app_info = {};
+   app_info.pApplicationName = "aco_tests";
+   app_info.apiVersion = VK_API_VERSION_1_2;
+   VkInstanceCreateInfo instance_create_info = {};
+   instance_create_info.pApplicationInfo = &app_info;
+   instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+   VkResult result = ((PFN_vkCreateInstance)vk_icdGetInstanceProcAddr(NULL, "vkCreateInstance"))(&instance_create_info, NULL, &instance_cache[family]);
+   assert(result == VK_SUCCESS);
+
+   #define ITEM(n) n = (PFN_vk##n)vk_icdGetInstanceProcAddr(instance_cache[family], "vk" #n);
+   FUNCTION_LIST
+   #undef ITEM
+
+   uint32_t device_count = 1;
+   VkPhysicalDevice device = VK_NULL_HANDLE;
+   result = EnumeratePhysicalDevices(instance_cache[family], &device_count, &device);
+   assert(result == VK_SUCCESS);
+   assert(device != VK_NULL_HANDLE);
+
+   VkDeviceCreateInfo device_create_info = {};
+   device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+   static const char *extensions[] = {"VK_KHR_pipeline_executable_properties"};
+   device_create_info.enabledExtensionCount = sizeof(extensions) / sizeof(extensions[0]);
+   device_create_info.ppEnabledExtensionNames = extensions;
+   result = CreateDevice(device, &device_create_info, NULL, &device_cache[family]);
+
+   return device_cache[family];
+}
+
+static struct DestroyDevices {
+   ~DestroyDevices() {
+      for (unsigned i = 0; i < CHIP_LAST; i++) {
+         if (!device_cache[i])
+            continue;
+         DestroyDevice(device_cache[i], NULL);
+         DestroyInstance(instance_cache[i], NULL);
+      }
+   }
+} destroy_devices;
+
+void print_pipeline_ir(VkDevice device, VkPipeline pipeline, VkShaderStageFlagBits stages,
+                       const char *name, bool remove_encoding)
+{
+   uint32_t executable_count = 16;
+   VkPipelineExecutablePropertiesKHR executables[16];
+   VkPipelineInfoKHR pipeline_info;
+   pipeline_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR;
+   pipeline_info.pNext = NULL;
+   pipeline_info.pipeline = pipeline;
+   VkResult result = GetPipelineExecutablePropertiesKHR(device, &pipeline_info, &executable_count, executables);
+   assert(result == VK_SUCCESS);
+
+   uint32_t executable = 0;
+   for (; executable < executable_count; executable++) {
+      if (executables[executable].stages == stages)
+         break;
+   }
+   assert(executable != executable_count);
+
+   VkPipelineExecutableInfoKHR exec_info;
+   exec_info.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR;
+   exec_info.pNext = NULL;
+   exec_info.pipeline = pipeline;
+   exec_info.executableIndex = executable;
+
+   uint32_t ir_count = 16;
+   VkPipelineExecutableInternalRepresentationKHR ir[16];
+   memset(ir, 0, sizeof(ir));
+   result = GetPipelineExecutableInternalRepresentationsKHR(device, &exec_info, &ir_count, ir);
+   assert(result == VK_SUCCESS);
+
+   for (unsigned i = 0; i < ir_count; i++) {
+      if (strcmp(ir[i].name, name))
+         continue;
+
+      char *data = (char*)malloc(ir[i].dataSize);
+      ir[i].pData = data;
+      result = GetPipelineExecutableInternalRepresentationsKHR(device, &exec_info, &ir_count, ir);
+      assert(result == VK_SUCCESS);
+
+      if (remove_encoding) {
+         for (char *c = data; *c; c++) {
+            if (*c == ';') {
+               for (; *c && *c != '\n'; c++)
+                  *c = ' ';
+            }
+         }
+      }
+
+      fprintf(output, "%s", data);
+      free(data);
+      return;
+   }
+}
+
+VkShaderModule __qoCreateShaderModule(VkDevice dev, const QoShaderModuleCreateInfo *info)
+{
+    VkShaderModuleCreateInfo module_info;
+    module_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+    module_info.pNext = NULL;
+    module_info.flags = 0;
+    module_info.codeSize = info->spirvSize;
+    module_info.pCode = (const uint32_t*)info->pSpirv;
+
+    VkShaderModule module;
+    VkResult result = CreateShaderModule(dev, &module_info, NULL, &module);
+    assert(result == VK_SUCCESS);
+
+    return module;
+}
+
+PipelineBuilder::PipelineBuilder(VkDevice dev) {
+   memset(this, 0, sizeof(*this));
+   topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+   device = dev;
+}
+
+PipelineBuilder::~PipelineBuilder()
+{
+   DestroyPipeline(device, pipeline, NULL);
+
+   for (unsigned i = 0; i < (is_compute() ? 1 : gfx_pipeline_info.stageCount); i++) {
+      VkPipelineShaderStageCreateInfo *stage_info = &stages[i];
+      if (owned_stages & stage_info->stage)
+         DestroyShaderModule(device, stage_info->module, NULL);
+   }
+
+   DestroyPipelineLayout(device, pipeline_layout, NULL);
+
+   for (unsigned i = 0; i < util_bitcount64(desc_layouts_used); i++)
+      DestroyDescriptorSetLayout(device, desc_layouts[i], NULL);
+
+   DestroyRenderPass(device, render_pass, NULL);
+}
+
+void PipelineBuilder::add_desc_binding(VkShaderStageFlags stage_flags, uint32_t layout,
+                                       uint32_t binding, VkDescriptorType type, uint32_t count)
+{
+   desc_layouts_used |= 1ull << layout;
+   desc_bindings[layout][num_desc_bindings[layout]++] = {binding, type, count, stage_flags, NULL};
+}
+
+void PipelineBuilder::add_vertex_binding(uint32_t binding, uint32_t stride, VkVertexInputRate rate)
+{
+   vs_bindings[vs_input.vertexBindingDescriptionCount++] = {binding, stride, rate};
+}
+
+void PipelineBuilder::add_vertex_attribute(uint32_t location, uint32_t binding, VkFormat format, uint32_t offset)
+{
+   vs_attributes[vs_input.vertexAttributeDescriptionCount++] = {location, binding, format, offset};
+}
+
+void PipelineBuilder::add_resource_decls(QoShaderModuleCreateInfo *module)
+{
+   for (unsigned i = 0; i < module->declarationCount; i++) {
+      const QoShaderDecl *decl = &module->pDeclarations[i];
+      switch (decl->decl_type) {
+      case QoShaderDeclType_ubo:
+         add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
+         break;
+      case QoShaderDeclType_ssbo:
+         add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
+         break;
+      case QoShaderDeclType_img_buf:
+         add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
+         break;
+      case QoShaderDeclType_img:
+         add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
+         break;
+      case QoShaderDeclType_tex_buf:
+         add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
+         break;
+      case QoShaderDeclType_combined:
+         add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
+         break;
+      case QoShaderDeclType_tex:
+         add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
+         break;
+      case QoShaderDeclType_samp:
+         add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_SAMPLER);
+         break;
+      default:
+         break;
+      }
+   }
+}
+
+void PipelineBuilder::add_io_decls(QoShaderModuleCreateInfo *module)
+{
+   unsigned next_vtx_offset = 0;
+   for (unsigned i = 0; i < module->declarationCount; i++) {
+      const QoShaderDecl *decl = &module->pDeclarations[i];
+      switch (decl->decl_type) {
+      case QoShaderDeclType_in:
+         if (module->stage == VK_SHADER_STAGE_VERTEX_BIT) {
+            if (!strcmp(decl->type, "float") || decl->type[0] == 'v')
+               add_vertex_attribute(decl->location, 0, VK_FORMAT_R32G32B32A32_SFLOAT, next_vtx_offset);
+            else if (decl->type[0] == 'u')
+               add_vertex_attribute(decl->location, 0, VK_FORMAT_R32G32B32A32_UINT, next_vtx_offset);
+            else if (decl->type[0] == 'i')
+               add_vertex_attribute(decl->location, 0, VK_FORMAT_R32G32B32A32_SINT, next_vtx_offset);
+            next_vtx_offset += 16;
+         }
+         break;
+      case QoShaderDeclType_out:
+         if (module->stage == VK_SHADER_STAGE_FRAGMENT_BIT) {
+            if (!strcmp(decl->type, "float") || decl->type[0] == 'v')
+               color_outputs[decl->location] = VK_FORMAT_R32G32B32A32_SFLOAT;
+            else if (decl->type[0] == 'u')
+               color_outputs[decl->location] = VK_FORMAT_R32G32B32A32_UINT;
+            else if (decl->type[0] == 'i')
+               color_outputs[decl->location] = VK_FORMAT_R32G32B32A32_SINT;
+         }
+         break;
+      default:
+         break;
+      }
+   }
+   if (next_vtx_offset)
+      add_vertex_binding(0, next_vtx_offset);
+}
+
+void PipelineBuilder::add_stage(VkShaderStageFlagBits stage, VkShaderModule module, const char *name)
+{
+   VkPipelineShaderStageCreateInfo *stage_info;
+   if (stage == VK_SHADER_STAGE_COMPUTE_BIT)
+      stage_info = &stages[0];
+   else
+      stage_info = &stages[gfx_pipeline_info.stageCount++];
+   stage_info->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+   stage_info->pNext = NULL;
+   stage_info->flags = 0;
+   stage_info->stage = stage;
+   stage_info->module = module;
+   stage_info->pName = name;
+   stage_info->pSpecializationInfo = NULL;
+   owned_stages |= stage;
+}
+
+void PipelineBuilder::add_vsfs(VkShaderModule vs, VkShaderModule fs)
+{
+   add_stage(VK_SHADER_STAGE_VERTEX_BIT, vs);
+   add_stage(VK_SHADER_STAGE_FRAGMENT_BIT, fs);
+}
+
+void PipelineBuilder::add_vsfs(QoShaderModuleCreateInfo vs, QoShaderModuleCreateInfo fs)
+{
+   add_vsfs(__qoCreateShaderModule(device, &vs), __qoCreateShaderModule(device, &fs));
+   add_resource_decls(&vs);
+   add_io_decls(&vs);
+   add_resource_decls(&fs);
+   add_io_decls(&fs);
+}
+
+void PipelineBuilder::add_cs(VkShaderModule cs)
+{
+   add_stage(VK_SHADER_STAGE_COMPUTE_BIT, cs);
+}
+
+void PipelineBuilder::add_cs(QoShaderModuleCreateInfo cs)
+{
+   add_cs(__qoCreateShaderModule(device, &cs));
+   add_resource_decls(&cs);
+}
+
+bool PipelineBuilder::is_compute() {
+   return gfx_pipeline_info.stageCount == 0;
+}
+
+void PipelineBuilder::create_compute_pipeline() {
+   VkComputePipelineCreateInfo create_info;
+   create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
+   create_info.pNext = NULL;
+   create_info.flags = VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR;
+   create_info.stage = stages[0];
+   create_info.layout = pipeline_layout;
+   create_info.basePipelineHandle = VK_NULL_HANDLE;
+   create_info.basePipelineIndex = 0;
+
+   VkResult result = CreateComputePipelines(device, VK_NULL_HANDLE, 1, &create_info, NULL, &pipeline);
+   assert(result == VK_SUCCESS);
+}
+
+void PipelineBuilder::create_graphics_pipeline() {
+   /* create the create infos */
+   if (!samples)
+      samples = VK_SAMPLE_COUNT_1_BIT;
+
+   unsigned num_color_attachments = 0;
+   VkPipelineColorBlendAttachmentState blend_attachment_states[16];
+   VkAttachmentReference color_attachments[16];
+   VkAttachmentDescription attachment_descs[17];
+   for (unsigned i = 0; i < 16; i++) {
+      if (color_outputs[i] == VK_FORMAT_UNDEFINED)
+         continue;
+
+      VkAttachmentDescription *desc = &attachment_descs[num_color_attachments];
+      desc->flags = 0;
+      desc->format = color_outputs[i];
+      desc->samples = samples;
+      desc->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+      desc->storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+      desc->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+      desc->stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
+      desc->initialLayout = VK_IMAGE_LAYOUT_GENERAL;
+      desc->finalLayout = VK_IMAGE_LAYOUT_GENERAL;
+
+      VkAttachmentReference *ref = &color_attachments[num_color_attachments];
+      ref->attachment = num_color_attachments;
+      ref->layout = VK_IMAGE_LAYOUT_GENERAL;
+
+      VkPipelineColorBlendAttachmentState *blend = &blend_attachment_states[num_color_attachments];
+      blend->blendEnable = false;
+      blend->colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
+                              VK_COLOR_COMPONENT_G_BIT |
+                              VK_COLOR_COMPONENT_B_BIT |
+                              VK_COLOR_COMPONENT_A_BIT;
+
+      num_color_attachments++;
+   }
+
+   unsigned num_attachments = num_color_attachments;
+   VkAttachmentReference ds_attachment;
+   if (ds_output != VK_FORMAT_UNDEFINED) {
+      VkAttachmentDescription *desc = &attachment_descs[num_attachments];
+      desc->flags = 0;
+      desc->format = ds_output;
+      desc->samples = samples;
+      desc->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+      desc->storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+      desc->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+      desc->stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
+      desc->initialLayout = VK_IMAGE_LAYOUT_GENERAL;
+      desc->finalLayout = VK_IMAGE_LAYOUT_GENERAL;
+
+      ds_attachment.attachment = num_color_attachments;
+      ds_attachment.layout = VK_IMAGE_LAYOUT_GENERAL;
+
+      num_attachments++;
+   }
+
+   vs_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+   vs_input.pNext = NULL;
+   vs_input.flags = 0;
+   vs_input.pVertexBindingDescriptions = vs_bindings;
+   vs_input.pVertexAttributeDescriptions = vs_attributes;
+
+   VkPipelineInputAssemblyStateCreateInfo assembly_state;
+   assembly_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+   assembly_state.pNext = NULL;
+   assembly_state.flags = 0;
+   assembly_state.topology = topology;
+   assembly_state.primitiveRestartEnable = false;
+
+   VkPipelineTessellationStateCreateInfo tess_state;
+   tess_state.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
+   tess_state.pNext = NULL;
+   tess_state.flags = 0;
+   tess_state.patchControlPoints = patch_size;
+
+   VkPipelineViewportStateCreateInfo viewport_state;
+   viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+   viewport_state.pNext = NULL;
+   viewport_state.flags = 0;
+   viewport_state.viewportCount = 1;
+   viewport_state.pViewports = NULL;
+   viewport_state.scissorCount = 1;
+   viewport_state.pScissors = NULL;
+
+   VkPipelineRasterizationStateCreateInfo rasterization_state;
+   rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+   rasterization_state.pNext = NULL;
+   rasterization_state.flags = 0;
+   rasterization_state.depthClampEnable = false;
+   rasterization_state.rasterizerDiscardEnable = false;
+   rasterization_state.polygonMode = VK_POLYGON_MODE_FILL;
+   rasterization_state.cullMode = VK_CULL_MODE_NONE;
+   rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+   rasterization_state.depthBiasEnable = false;
+   rasterization_state.lineWidth = 1.0;
+
+   VkPipelineMultisampleStateCreateInfo ms_state;
+   ms_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+   ms_state.pNext = NULL;
+   ms_state.flags = 0;
+   ms_state.rasterizationSamples = samples;
+   ms_state.sampleShadingEnable = sample_shading_enable;
+   ms_state.minSampleShading = min_sample_shading;
+   VkSampleMask sample_mask = 0xffffffff;
+   ms_state.pSampleMask = &sample_mask;
+   ms_state.alphaToCoverageEnable = false;
+   ms_state.alphaToOneEnable = false;
+
+   VkPipelineDepthStencilStateCreateInfo ds_state;
+   ds_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+   ds_state.pNext = NULL;
+   ds_state.flags = 0;
+   ds_state.depthTestEnable = ds_output != VK_FORMAT_UNDEFINED;
+   ds_state.depthWriteEnable = true;
+   ds_state.depthCompareOp = VK_COMPARE_OP_ALWAYS;
+   ds_state.depthBoundsTestEnable = false;
+   ds_state.stencilTestEnable = true;
+   ds_state.front.failOp = VK_STENCIL_OP_KEEP;
+   ds_state.front.passOp = VK_STENCIL_OP_REPLACE;
+   ds_state.front.depthFailOp = VK_STENCIL_OP_REPLACE;
+   ds_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
+   ds_state.front.compareMask = 0xffffffff,
+   ds_state.front.reference = 0;
+   ds_state.back = ds_state.front;
+
+   VkPipelineColorBlendStateCreateInfo color_blend_state;
+   color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+   color_blend_state.pNext = NULL;
+   color_blend_state.flags = 0;
+   color_blend_state.logicOpEnable = false;
+   color_blend_state.attachmentCount = num_color_attachments;
+   color_blend_state.pAttachments = blend_attachment_states;
+
+   VkDynamicState dynamic_states[9] = {
+      VK_DYNAMIC_STATE_VIEWPORT,
+      VK_DYNAMIC_STATE_SCISSOR,
+      VK_DYNAMIC_STATE_LINE_WIDTH,
+      VK_DYNAMIC_STATE_DEPTH_BIAS,
+      VK_DYNAMIC_STATE_BLEND_CONSTANTS,
+      VK_DYNAMIC_STATE_DEPTH_BOUNDS,
+      VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
+      VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
+      VK_DYNAMIC_STATE_STENCIL_REFERENCE
+   };
+
+   VkPipelineDynamicStateCreateInfo dynamic_state;
+   dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+   dynamic_state.pNext = NULL;
+   dynamic_state.flags = 0;
+   dynamic_state.dynamicStateCount = sizeof(dynamic_states) / sizeof(VkDynamicState);
+   dynamic_state.pDynamicStates = dynamic_states;
+
+   gfx_pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+   gfx_pipeline_info.pNext = NULL;
+   gfx_pipeline_info.flags = VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR;
+   gfx_pipeline_info.pVertexInputState = &vs_input;
+   gfx_pipeline_info.pInputAssemblyState = &assembly_state;
+   gfx_pipeline_info.pTessellationState = &tess_state;
+   gfx_pipeline_info.pViewportState = &viewport_state;
+   gfx_pipeline_info.pRasterizationState = &rasterization_state;
+   gfx_pipeline_info.pMultisampleState = &ms_state;
+   gfx_pipeline_info.pDepthStencilState = &ds_state;
+   gfx_pipeline_info.pColorBlendState = &color_blend_state;
+   gfx_pipeline_info.pDynamicState = &dynamic_state;
+   gfx_pipeline_info.subpass = 0;
+
+   /* create the objects used to create the pipeline */
+   VkSubpassDescription subpass;
+   subpass.flags = 0;
+   subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+   subpass.inputAttachmentCount = 0;
+   subpass.pInputAttachments = NULL;
+   subpass.colorAttachmentCount = num_color_attachments;
+   subpass.pColorAttachments = color_attachments;
+   subpass.pResolveAttachments = NULL;
+   subpass.pDepthStencilAttachment = ds_output == VK_FORMAT_UNDEFINED ? NULL : &ds_attachment;
+   subpass.preserveAttachmentCount = 0;
+   subpass.pPreserveAttachments = NULL;
+
+   VkRenderPassCreateInfo renderpass_info;
+   renderpass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+   renderpass_info.pNext = NULL;
+   renderpass_info.flags = 0;
+   renderpass_info.attachmentCount = num_attachments;
+   renderpass_info.pAttachments = attachment_descs;
+   renderpass_info.subpassCount = 1;
+   renderpass_info.pSubpasses = &subpass;
+   renderpass_info.dependencyCount = 0;
+   renderpass_info.pDependencies = NULL;
+
+   VkResult result = CreateRenderPass(device, &renderpass_info, NULL, &render_pass);
+   assert(result == VK_SUCCESS);
+
+   gfx_pipeline_info.layout = pipeline_layout;
+   gfx_pipeline_info.renderPass = render_pass;
+
+   /* create the pipeline */
+   gfx_pipeline_info.pStages = stages;
+
+   result = CreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gfx_pipeline_info, NULL, &pipeline);
+   assert(result == VK_SUCCESS);
+}
+
+void PipelineBuilder::create_pipeline() {
+   unsigned num_desc_layouts = 0;
+   for (unsigned i = 0; i < 64; i++) {
+      if (!(desc_layouts_used & (1ull << i)))
+         continue;
+
+      VkDescriptorSetLayoutCreateInfo desc_layout_info;
+      desc_layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+      desc_layout_info.pNext = NULL;
+      desc_layout_info.flags = 0;
+      desc_layout_info.bindingCount = num_desc_bindings[i];
+      desc_layout_info.pBindings = desc_bindings[i];
+
+      VkResult result = CreateDescriptorSetLayout(device, &desc_layout_info, NULL, &desc_layouts[num_desc_layouts]);
+      assert(result == VK_SUCCESS);
+      num_desc_layouts++;
+   }
+
+   VkPipelineLayoutCreateInfo pipeline_layout_info;
+   pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+   pipeline_layout_info.pNext = NULL;
+   pipeline_layout_info.flags = 0;
+   pipeline_layout_info.pushConstantRangeCount = 1;
+   pipeline_layout_info.pPushConstantRanges = &push_constant_range;
+   pipeline_layout_info.setLayoutCount = num_desc_layouts;
+   pipeline_layout_info.pSetLayouts = desc_layouts;
+
+   VkResult result = CreatePipelineLayout(device, &pipeline_layout_info, NULL, &pipeline_layout);
+   assert(result == VK_SUCCESS);
+
+   if (is_compute())
+      create_compute_pipeline();
+   else
+      create_graphics_pipeline();
+}
+
+void PipelineBuilder::print_ir(VkShaderStageFlagBits stages, const char *name, bool remove_encoding)
+{
+   if (!pipeline)
+      create_pipeline();
+   print_pipeline_ir(device, pipeline, stages, name, remove_encoding);
+}
diff --git a/src/amd/compiler/tests/helpers.h b/src/amd/compiler/tests/helpers.h
index 50f50497951..6fa8f41a5ee 100644
--- a/src/amd/compiler/tests/helpers.h
+++ b/src/amd/compiler/tests/helpers.h
@@ -25,6 +25,40 @@
 #define ACO_TEST_HELPERS_H
 
 #include "framework.h"
+#include "vulkan/vulkan.h"
+
+enum QoShaderDeclType {
+   QoShaderDeclType_ubo,
+   QoShaderDeclType_ssbo,
+   QoShaderDeclType_img_buf,
+   QoShaderDeclType_img,
+   QoShaderDeclType_tex_buf,
+   QoShaderDeclType_combined,
+   QoShaderDeclType_tex,
+   QoShaderDeclType_samp,
+   QoShaderDeclType_in,
+   QoShaderDeclType_out,
+};
+
+struct QoShaderDecl {
+   const char *name;
+   const char *type;
+   QoShaderDeclType decl_type;
+   //TODO: array size?
+   unsigned location;
+   unsigned component;
+   unsigned binding;
+   unsigned set;
+};
+
+struct QoShaderModuleCreateInfo {
+    void *pNext;
+    size_t spirvSize;
+    const void *pSpirv;
+    uint32_t declarationCount;
+    const QoShaderDecl *pDeclarations;
+    VkShaderStageFlagBits stage;
+};
 
 extern ac_shader_config config;
 extern radv_shader_info info;
@@ -47,4 +81,73 @@ void finish_assembler_test();
 
 void writeout(unsigned i, aco::Temp tmp=aco::Temp(0, aco::s1));
 
+/* vulkan helpers */
+VkDevice get_vk_device(enum chip_class chip_class);
+VkDevice get_vk_device(enum radeon_family family);
+
+void print_pipeline_ir(VkDevice device, VkPipeline pipeline, VkShaderStageFlagBits stages,
+                       const char *name, bool remove_encoding=false);
+
+VkShaderModule __qoCreateShaderModule(VkDevice dev, const QoShaderModuleCreateInfo *info);
+
+class PipelineBuilder {
+public:
+   /* inputs */
+   VkDevice device;
+   VkFormat color_outputs[16];
+   VkFormat ds_output;
+   VkPrimitiveTopology topology;
+   VkSampleCountFlagBits samples;
+   bool sample_shading_enable;
+   float min_sample_shading;
+   uint32_t patch_size;
+   VkPipelineVertexInputStateCreateInfo vs_input;
+   VkVertexInputBindingDescription vs_bindings[16];
+   VkVertexInputAttributeDescription vs_attributes[16];
+   VkPushConstantRange push_constant_range;
+   uint64_t desc_layouts_used;
+   unsigned num_desc_bindings[64];
+   VkDescriptorSetLayoutBinding desc_bindings[64][64];
+   VkPipelineShaderStageCreateInfo stages[5];
+   VkShaderStageFlags owned_stages;
+
+   /* outputs */
+   VkGraphicsPipelineCreateInfo gfx_pipeline_info;
+   VkComputePipelineCreateInfo cs_pipeline_info;
+   VkDescriptorSetLayout desc_layouts[64];
+   VkPipelineLayout pipeline_layout;
+   VkRenderPass render_pass;
+   VkPipeline pipeline;
+
+   PipelineBuilder(VkDevice dev);
+   ~PipelineBuilder();
+
+   PipelineBuilder(const PipelineBuilder&) = delete;
+   PipelineBuilder& operator = (const PipelineBuilder&) = delete;
+
+   void add_desc_binding(VkShaderStageFlags stage_flags, uint32_t layout,
+                         uint32_t binding, VkDescriptorType type, uint32_t count=1);
+
+   void add_vertex_binding(uint32_t binding, uint32_t stride, VkVertexInputRate rate=VK_VERTEX_INPUT_RATE_VERTEX);
+   void add_vertex_attribute(uint32_t location, uint32_t binding, VkFormat format, uint32_t offset);
+
+   void add_resource_decls(QoShaderModuleCreateInfo *module);
+   void add_io_decls(QoShaderModuleCreateInfo *module);
+
+   void add_stage(VkShaderStageFlagBits stage, VkShaderModule module, const char *name="main");
+   void add_vsfs(VkShaderModule vs, VkShaderModule fs);
+   void add_vsfs(QoShaderModuleCreateInfo vs, QoShaderModuleCreateInfo fs);
+   void add_cs(VkShaderModule cs);
+   void add_cs(QoShaderModuleCreateInfo cs);
+
+   bool is_compute();
+
+   void create_pipeline();
+
+   void print_ir(VkShaderStageFlagBits stages, const char *name, bool remove_encoding=false);
+private:
+   void create_compute_pipeline();
+   void create_graphics_pipeline();
+};
+
 #endif /* ACO_TEST_HELPERS_H */
diff --git a/src/amd/compiler/tests/meson.build b/src/amd/compiler/tests/meson.build
index c63b860630a..bb8e7ca9288 100644
--- a/src/amd/compiler/tests/meson.build
+++ b/src/amd/compiler/tests/meson.build
@@ -23,15 +23,26 @@ aco_tests_files = files(
   'helpers.h',
   'main.cpp',
   'test_assembler.cpp',
+  'test_isel.cpp',
   'test_optimizer.cpp',
   'test_tests.cpp',
 )
 
+spirv_files = files(
+  'test_isel.cpp',
+)
+
+gen_spirv = generator(prog_python,
+                      output    : '@BASENAME at -spirv.h',
+                      arguments : [join_paths(meson.current_source_dir(), 'glsl_scraper.py'),
+                                   '@INPUT@', '--with-glslang', prog_glslang.path(), '-o', '@OUTPUT@'])
+gen_spirv_files = gen_spirv.process(spirv_files)
+
 test(
   'aco_tests',
   executable(
     'aco_tests',
-    aco_tests_files,
+    [aco_tests_files, gen_spirv_files],
     cpp_args : ['-DACO_TEST_SOURCE_DIR="@0@"'.format(meson.current_source_dir()),
                 '-DACO_TEST_BUILD_ROOT="@0@"'.format(meson.build_root()),
                 '-DACO_TEST_PYTHON_BIN="@0@"'.format(prog_python.path())],
@@ -39,7 +50,7 @@ test(
       inc_include, inc_src, inc_gallium, inc_compiler, inc_mesa, inc_mapi, inc_amd, inc_amd_common, inc_amd_common_llvm,
     ],
     link_with : [
-      libamd_common, libamd_common_llvm
+      libamd_common, libamd_common_llvm, libvulkan_radeon,
     ],
     dependencies : [
       dep_llvm, dep_thread, idep_aco, idep_nir, idep_mesautil
diff --git a/src/amd/compiler/tests/test_isel.cpp b/src/amd/compiler/tests/test_isel.cpp
new file mode 100644
index 00000000000..f45fe4311c0
--- /dev/null
+++ b/src/amd/compiler/tests/test_isel.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2020 Valve Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+#include "helpers.h"
+#include "test_isel-spirv.h"
+
+using namespace aco;
+
+BEGIN_TEST(isel.interp.simple)
+   QoShaderModuleCreateInfo vs = qoShaderModuleCreateInfoGLSL(VERTEX,
+      layout(location = 0) in vec4 in_color;
+      layout(location = 0) out vec4 out_color;
+      void main() {
+         out_color = in_color;
+      }
+   );
+   QoShaderModuleCreateInfo fs = qoShaderModuleCreateInfoGLSL(FRAGMENT,
+      layout(location = 0) in vec4 in_color;
+      layout(location = 0) out vec4 out_color;
+      void main() {
+         //>> v1: %a_tmp = v_interp_p1_f32 %bx, %pm:m0 attr0.w
+         //! v1: %a = v_interp_p2_f32 %by, %pm:m0, %a_tmp attr0.w
+         //! v1: %b_tmp = v_interp_p1_f32 %bx, %pm:m0 attr0.z
+         //! v1: %b = v_interp_p2_f32 %by, %pm:m0, %b_tmp attr0.z
+         //! v1: %g_tmp = v_interp_p1_f32 %bx, %pm:m0 attr0.y
+         //! v1: %g = v_interp_p2_f32 %by, %pm:m0, %g_tmp attr0.y
+         //! v1: %r_tmp = v_interp_p1_f32 %bx, %pm:m0 attr0.x
+         //! v1: %r = v_interp_p2_f32 %by, %pm:m0, %r_tmp attr0.x
+         //! exp %r, %g, %b, %a mrt0
+         out_color = in_color;
+      }
+   );
+
+   PipelineBuilder bld(get_vk_device(GFX9));
+   bld.add_vsfs(vs, fs);
+   bld.print_ir(VK_SHADER_STAGE_FRAGMENT_BIT, "ACO IR");
+END_TEST
+
+BEGIN_TEST(isel.compute.simple)
+   for (unsigned i = GFX7; i <= GFX8; i++) {
+      if (!set_variant((chip_class)i))
+         continue;
+
+      QoShaderModuleCreateInfo cs = qoShaderModuleCreateInfoGLSL(COMPUTE,
+         layout(local_size_x=1) in;
+         layout(binding=0) buffer Buf {
+            uint res;
+         };
+         void main() {
+            //~gfx7>> v1: %data = v_mov_b32 42
+            //~gfx7>> buffer_store_dword %_, v1: undef, 0, %data disable_wqm storage:buffer semantics: scope:invocation
+            //~gfx8>> s1: %data = s_mov_b32 42
+            //~gfx8>> s_buffer_store_dword %_, 0, %data storage:buffer semantics: scope:invocation
+            res = 42;
+         }
+      );
+
+      PipelineBuilder bld(get_vk_device((chip_class)i));
+      bld.add_cs(cs);
+      bld.print_ir(VK_SHADER_STAGE_COMPUTE_BIT, "ACO IR", true);
+   }
+END_TEST
diff --git a/src/vulkan/overlay-layer/meson.build b/src/vulkan/overlay-layer/meson.build
index 73e1e8346d3..3225b3f512b 100644
--- a/src/vulkan/overlay-layer/meson.build
+++ b/src/vulkan/overlay-layer/meson.build
@@ -18,8 +18,6 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-glslang = find_program('glslangValidator')
-
 overlay_shaders = [
   'overlay.frag',
   'overlay.vert',
@@ -28,7 +26,7 @@ overlay_spv = []
 foreach s : ['overlay.frag', 'overlay.vert']
   overlay_spv += custom_target(
     s + '.spv.h', input : s, output : s + '.spv.h',
-    command : [glslang, '-V', '-x', '-o', '@OUTPUT@', '@INPUT@'])
+    command : [prog_glslang, '-V', '-x', '-o', '@OUTPUT@', '@INPUT@'])
 endforeach
 
 vklayer_files = files(



More information about the mesa-commit mailing list