Commit dd5fe2f3 authored by Erik Faye-Lund 's avatar Erik Faye-Lund Committed by Marge Bot
Browse files

microsoft/compiler: add dxil-util code

This is support-code to emit the DirectX Intermediate Language, which is
a dialect of LLVM 3.7 bitcode. Because modern versions of LLVM doesn't
support emitting bitcode for older versions, and we can't rely on an old
LLVM version because we need the OpenCL support from Clang later on, we
instead implement our own LLVM bitcode encoder as part of this work.

See the official DXIL documentation for more details on DXIL:
https://github.com/Microsoft/DirectXShaderCompiler/blob/master/docs/DXIL.rst



The reason this comes as a separate library, is because we're also using
this code as the basis for an OpenCL C compiler, which will follow as a
separate merge-request later.

This is the combination of more than 230 commits from our development
branch, including the work from several authors.
Co-authored-by: Bill Kristiansen's avatarBill Kristiansen <billkris@microsoft.com>
Co-authored-by: Boris Brezillon's avatarBoris Brezillon <boris.brezillon@collabora.com>
Co-authored-by: Daniel Stone's avatarDaniel Stone <daniels@collabora.com>
Co-authored...
parent 10dfd3cf
/*
* Copyright © Microsoft 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 "dxil_buffer.h"
#include <assert.h>
void
dxil_buffer_init(struct dxil_buffer *b, unsigned abbrev_width)
{
blob_init(&b->blob);
b->buf = 0;
b->buf_bits = 0;
b->abbrev_width = abbrev_width;
}
void
dxil_buffer_finish(struct dxil_buffer *b)
{
blob_finish(&b->blob);
}
static bool
flush_dword(struct dxil_buffer *b)
{
assert(b->buf_bits >= 32 && b->buf_bits < 64);
uint32_t lower_bits = b->buf & UINT32_MAX;
if (!blob_write_bytes(&b->blob, &lower_bits, sizeof(lower_bits)))
return false;
b->buf >>= 32;
b->buf_bits -= 32;
return true;
}
bool
dxil_buffer_emit_bits(struct dxil_buffer *b, uint32_t data, unsigned width)
{
assert(b->buf_bits < 32);
assert(width > 0 && width <= 32);
assert((data & ~((UINT64_C(1) << width) - 1)) == 0);
b->buf |= ((uint64_t)data) << b->buf_bits;
b->buf_bits += width;
if (b->buf_bits >= 32)
return flush_dword(b);
return true;
}
bool
dxil_buffer_emit_vbr_bits(struct dxil_buffer *b, uint64_t data,
unsigned width)
{
assert(width > 1 && width <= 32);
uint32_t tag = UINT32_C(1) << (width - 1);
uint32_t max = tag - 1;
while (data > max) {
uint32_t value = (data & max) | tag;
data >>= width - 1;
if (!dxil_buffer_emit_bits(b, value, width))
return false;
}
return dxil_buffer_emit_bits(b, data, width);
}
bool
dxil_buffer_align(struct dxil_buffer *b)
{
assert(b->buf_bits < 32);
if (b->buf_bits) {
b->buf_bits = 32;
return flush_dword(b);
}
return true;
}
/*
* Copyright © Microsoft 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.
*/
#ifndef DXIL_BUFFER_H
#define DXIL_BUFFER_H
#include "util/blob.h"
struct dxil_buffer {
struct blob blob;
uint64_t buf;
unsigned buf_bits;
unsigned abbrev_width;
};
void
dxil_buffer_init(struct dxil_buffer *b, unsigned abbrev_width);
void
dxil_buffer_finish(struct dxil_buffer *b);
bool
dxil_buffer_emit_bits(struct dxil_buffer *b, uint32_t data, unsigned width);
bool
dxil_buffer_emit_vbr_bits(struct dxil_buffer *b, uint64_t data,
unsigned width);
bool
dxil_buffer_align(struct dxil_buffer *b);
static bool
dxil_buffer_emit_abbrev_id(struct dxil_buffer *b, uint32_t id)
{
return dxil_buffer_emit_bits(b, id, b->abbrev_width);
}
#endif
/*
* Copyright © Microsoft 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 "dxil_buffer.h"
#include <assert.h>
#include <stdio.h>
static void
init()
{
struct dxil_buffer buf;
dxil_buffer_init(&buf, 2);
assert(!buf.buf);
assert(!buf.buf_bits);
}
static void
assert_blob_data(const struct dxil_buffer *m, const uint8_t *data,
size_t len)
{
if (m->blob.size != len) {
fprintf(stderr, "blob-size mismatch, expected %zd, got %zd",
len, m->blob.size);
abort();
}
for (size_t i = 0; i < len; ++i) {
if (m->blob.data[i] != data[i]) {
fprintf(stderr, "blob-data mismatch at index %zd, "
"expected 0x%02x, got 0x%02x", i,
data[i], m->blob.data[i]);
abort();
}
}
}
#define ASSERT_BLOB_DATA(m, data) \
assert_blob_data(m, data, sizeof(data))
static void
align()
{
struct dxil_buffer buf;
dxil_buffer_init(&buf, 2);
assert_blob_data(&buf, NULL, 0);
dxil_buffer_init(&buf, 2);
dxil_buffer_emit_bits(&buf, 0xbeef, 16);
dxil_buffer_align(&buf);
assert(!buf.buf);
assert(!buf.buf_bits);
uint8_t expected0[] = { 0xef, 0xbe, 0x00, 0x00 };
ASSERT_BLOB_DATA(&buf, expected0);
dxil_buffer_align(&buf);
ASSERT_BLOB_DATA(&buf, expected0);
}
static void
emit_bits()
{
struct dxil_buffer buf;
dxil_buffer_init(&buf, 2);
dxil_buffer_emit_bits(&buf, 0xbeef, 16);
dxil_buffer_align(&buf);
assert(!buf.buf);
assert(!buf.buf_bits);
uint8_t expected0[] = { 0xef, 0xbe, 0x00, 0x00 };
ASSERT_BLOB_DATA(&buf, expected0);
dxil_buffer_init(&buf, 2);
dxil_buffer_emit_bits(&buf, 0xdead, 16);
dxil_buffer_emit_bits(&buf, 0xbeef, 16);
assert(!buf.buf);
assert(!buf.buf_bits);
uint8_t expected1[] = { 0xad, 0xde, 0xef, 0xbe };
ASSERT_BLOB_DATA(&buf, expected1);
dxil_buffer_init(&buf, 2);
dxil_buffer_emit_bits(&buf, 0x1111111, 28);
dxil_buffer_emit_bits(&buf, 0x22222222, 32);
dxil_buffer_align(&buf);
uint8_t expected2[] = { 0x11, 0x11, 0x11, 0x21, 0x22, 0x22, 0x22, 0x02 };
ASSERT_BLOB_DATA(&buf, expected2);
}
static void
emit_vbr_bits()
{
struct dxil_buffer buf;
dxil_buffer_init(&buf, 2);
dxil_buffer_emit_vbr_bits(&buf, 0x1a, 8);
dxil_buffer_emit_vbr_bits(&buf, 0x1a, 6);
dxil_buffer_emit_vbr_bits(&buf, 0x00, 2);
dxil_buffer_emit_vbr_bits(&buf, 0x0a, 4);
dxil_buffer_emit_vbr_bits(&buf, 0x04, 2);
dxil_buffer_emit_vbr_bits(&buf, 0x00, 2);
uint8_t expected[] = { 0x1a, 0x1a, 0x1a, 0x1a };
ASSERT_BLOB_DATA(&buf, expected);
}
int
main()
{
init();
align();
emit_bits();
emit_vbr_bits();
return 0;
}
/*
* Copyright © Microsoft 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 "dxil_container.h"
#include "dxil_module.h"
#include "util/u_debug.h"
#include <assert.h>
const uint32_t DXIL_DXBC = DXIL_FOURCC('D', 'X', 'B', 'C');
void
dxil_container_init(struct dxil_container *c)
{
blob_init(&c->parts);
c->num_parts = 0;
}
void
dxil_container_finish(struct dxil_container *c)
{
blob_finish(&c->parts);
}
static bool
add_part_header(struct dxil_container *c,
enum dxil_part_fourcc fourcc,
uint32_t part_size)
{
assert(c->parts.size < UINT_MAX);
unsigned offset = (unsigned)c->parts.size;
if (!blob_write_bytes(&c->parts, &fourcc, sizeof(fourcc)) ||
!blob_write_bytes(&c->parts, &part_size, sizeof(part_size)))
return false;
assert(c->num_parts < DXIL_MAX_PARTS);
c->part_offsets[c->num_parts++] = offset;
return true;
}
static bool
add_part(struct dxil_container *c,
enum dxil_part_fourcc fourcc,
const void *part_data, uint32_t part_size)
{
return add_part_header(c, fourcc, part_size) &&
blob_write_bytes(&c->parts, part_data, part_size);
}
bool
dxil_container_add_features(struct dxil_container *c,
const struct dxil_features *features)
{
union {
struct dxil_features flags;
uint64_t bits;
} u = { .flags = *features };
return add_part(c, DXIL_SFI0, &u.bits, sizeof(u.bits));
}
typedef struct {
struct {
const char *name;
uint32_t offset;
} entries[DXIL_SHADER_MAX_IO_ROWS];
uint32_t num_entries;
} name_offset_cache_t;
static uint32_t
get_semantic_name_offset(name_offset_cache_t *cache, const char *name,
struct _mesa_string_buffer *buf, uint32_t buf_offset)
{
/* consider replacing this with a binary search using rb_tree */
for (unsigned i = 0; i < cache->num_entries; ++i) {
if (!strcmp(name, cache->entries[i].name))
return cache->entries[i].offset;
}
uint32_t offset = buf->length + buf_offset;
cache->entries[cache->num_entries].name = name;
cache->entries[cache->num_entries].offset = offset;
++cache->num_entries;
_mesa_string_buffer_append_len(buf, name, strlen(name) + 1);
return offset;
}
static uint32_t
collect_semantic_names(unsigned num_records,
struct dxil_signature_record *io_data,
struct _mesa_string_buffer *buf,
uint32_t buf_offset)
{
name_offset_cache_t cache;
cache.num_entries = 0;
for (unsigned i = 0; i < num_records; ++i) {
struct dxil_signature_record *io = &io_data[i];
uint32_t offset = get_semantic_name_offset(&cache, io->name, buf, buf_offset);
for (unsigned j = 0; j < io->num_elements; ++j)
io->elements[j].semantic_name_offset = offset;
}
return buf_offset + buf->length;
}
bool
dxil_container_add_io_signature(struct dxil_container *c,
enum dxil_part_fourcc part,
unsigned num_records,
struct dxil_signature_record *io_data)
{
struct {
uint32_t param_count;
uint32_t param_offset;
} header;
header.param_count = 0;
uint32_t fixed_size = sizeof(header);
header.param_offset = fixed_size;
bool retval = true;
for (unsigned i = 0; i < num_records; ++i) {
/* TODO:
* - Here we need to check whether the value is actually part of the
* signature */
fixed_size += sizeof(struct dxil_signature_element) * io_data[i].num_elements;
header.param_count += io_data[i].num_elements;
}
struct _mesa_string_buffer *names =
_mesa_string_buffer_create(NULL, 1024);
uint32_t last_offset = collect_semantic_names(num_records, io_data,
names, fixed_size);
if (!add_part_header(c, part, last_offset) ||
!blob_write_bytes(&c->parts, &header, sizeof(header))) {
retval = false;
goto cleanup;
}
/* write all parts */
for (unsigned i = 0; i < num_records; ++i)
for (unsigned j = 0; j < io_data[i].num_elements; ++j) {
if (!blob_write_bytes(&c->parts, &io_data[i].elements[j],
sizeof(io_data[i].elements[j]))) {
retval = false;
goto cleanup;
}
}
/* write all names */
if (!blob_write_bytes(&c->parts, names->buf, names->length))
retval = false;
cleanup:
_mesa_string_buffer_destroy(names);
return retval;
}
bool
dxil_container_add_state_validation(struct dxil_container *c,
const struct dxil_module *m,
struct dxil_validation_state *state)
{
uint32_t psv1_size = sizeof(struct dxil_psv_runtime_info_1);
uint32_t resource_bind_info_size = 4 * sizeof(uint32_t);
uint32_t dxil_pvs_sig_size = sizeof(struct dxil_psv_signature_element);
uint32_t resource_count = state->num_resources;
uint32_t size = psv1_size + 2 * sizeof(uint32_t);
if (resource_count > 0) {
size += sizeof (uint32_t) +
resource_bind_info_size * resource_count;
}
uint32_t string_table_size = (m->sem_string_table->length + 3) & ~3u;
size += sizeof(uint32_t) + string_table_size;
// Semantic index table size, currently always 0
size += sizeof(uint32_t) + m->sem_index_table.size * sizeof(uint32_t);
if (m->num_sig_inputs || m->num_sig_outputs) {
size += sizeof(uint32_t);
}
size += dxil_pvs_sig_size * m->num_sig_inputs;
size += dxil_pvs_sig_size * m->num_sig_outputs;
// size += dxil_pvs_sig_size * m->num_sig_patch_const...;
state->state.sig_input_vectors = (uint8_t)m->num_psv_inputs;
// TODO: check proper stream
state->state.sig_output_vectors[0] = (uint8_t)m->num_psv_outputs;
// TODO: Add viewID records size
// TODO: Add sig input output dependency table size
uint32_t dependency_table_size = 0;
if (state->state.sig_input_vectors > 0) {
for (unsigned i = 0; i < 4; ++i) {
if (state->state.sig_output_vectors[i] > 0)
dependency_table_size += sizeof(uint32_t) * ((state->state.sig_output_vectors[i] + 7) >> 3) *
state->state.sig_input_vectors * 4;
}
}
size += dependency_table_size;
// TODO: Domain shader table goes here
if (!add_part_header(c, DXIL_PSV0, size))
return false;
if (!blob_write_bytes(&c->parts, &psv1_size, sizeof(psv1_size)))
return false;
if (!blob_write_bytes(&c->parts, &state->state, psv1_size))
return false;
if (!blob_write_bytes(&c->parts, &resource_count, sizeof(resource_count)))
return false;
if (resource_count > 0) {
if (!blob_write_bytes(&c->parts, &resource_bind_info_size, sizeof(resource_bind_info_size)) ||
!blob_write_bytes(&c->parts, state->resources, resource_bind_info_size * state->num_resources))
return false;
}
uint32_t fill = 0;
if (!blob_write_bytes(&c->parts, &string_table_size, sizeof(string_table_size)) ||
!blob_write_bytes(&c->parts, m->sem_string_table->buf, m->sem_string_table->length) ||
!blob_write_bytes(&c->parts, &fill, string_table_size - m->sem_string_table->length))
return false;
// TODO: write the correct semantic index table. Currently it is empty
if (!blob_write_bytes(&c->parts, &m->sem_index_table.size, sizeof(uint32_t)))
return false;
if (m->sem_index_table.size > 0) {
if (!blob_write_bytes(&c->parts, m->sem_index_table.data,
m->sem_index_table.size * sizeof(uint32_t)))
return false;
}
if (m->num_sig_inputs || m->num_sig_outputs) {
if (!blob_write_bytes(&c->parts, &dxil_pvs_sig_size, sizeof(dxil_pvs_sig_size)))
return false;
if (!blob_write_bytes(&c->parts, &m->psv_inputs, dxil_pvs_sig_size * m->num_sig_inputs))
return false;
if (!blob_write_bytes(&c->parts, &m->psv_outputs, dxil_pvs_sig_size * m->num_sig_outputs))
return false;
}
// TODO: Write PatchConst...
// TODO: Handle case when ViewID is used
// TODO: Handle sig input output dependency table
for (uint32_t i = 0; i < dependency_table_size; ++i)
blob_write_uint8(&c->parts, 0);
return true;
}
bool
dxil_container_add_module(struct dxil_container *c,
const struct dxil_module *m)
{
assert(m->buf.buf_bits == 0); // make sure the module is fully flushed
uint32_t version = (m->shader_kind << 16) |
(m->major_version << 4) |
m->minor_version;
uint32_t size = 6 * sizeof(uint32_t) + m->buf.blob.size;
assert(size % sizeof(uint32_t) == 0);
uint32_t uint32_size = size / sizeof(uint32_t);