adding backend agnostic IR
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
const spirv = @import("spirv.zig");
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const Error = error{
|
||||
HeaderTooShort,
|
||||
InvalidMagic,
|
||||
ByteSwappedModule,
|
||||
InvalidVersion,
|
||||
InvalidIdBound,
|
||||
InvalidSchema,
|
||||
ZeroWordInstruction,
|
||||
TruncatedInstruction,
|
||||
UnterminatedString,
|
||||
};
|
||||
|
||||
pub const Header = struct {
|
||||
version: u32,
|
||||
generator: u32,
|
||||
bound: u32,
|
||||
schema: u32,
|
||||
|
||||
pub inline fn major(self: Header) u8 {
|
||||
return @truncate(self.version >> 16);
|
||||
}
|
||||
|
||||
pub inline fn minor(self: Header) u8 {
|
||||
return @truncate(self.version >> 8);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Instruction = struct {
|
||||
opcode: spirv.Opcode,
|
||||
operands: []const u32,
|
||||
word_offset: usize,
|
||||
|
||||
pub fn operand(self: Instruction, index: usize) ?u32 {
|
||||
return if (index < self.operands.len) self.operands[index] else null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Iterator = struct {
|
||||
words: []const u32,
|
||||
cursor: usize = spirv.header_word_count,
|
||||
|
||||
pub fn next(self: *Iterator) Error!?Instruction {
|
||||
if (self.cursor == self.words.len)
|
||||
return null;
|
||||
|
||||
const first_word = self.words[self.cursor];
|
||||
const word_count: usize = first_word >> 16;
|
||||
|
||||
if (word_count == 0)
|
||||
return error.ZeroWordInstruction;
|
||||
if (word_count > self.words.len - self.cursor)
|
||||
return error.TruncatedInstruction;
|
||||
|
||||
const instruction: Instruction = .{
|
||||
.opcode = @enumFromInt(@as(u16, @truncate(first_word))),
|
||||
.operands = self.words[self.cursor + 1 .. self.cursor + word_count],
|
||||
.word_offset = self.cursor,
|
||||
};
|
||||
self.cursor += word_count;
|
||||
return instruction;
|
||||
}
|
||||
};
|
||||
|
||||
words: []const u32,
|
||||
header: Header,
|
||||
|
||||
pub fn init(words: []const u32) Error!Self {
|
||||
if (words.len < spirv.header_word_count)
|
||||
return error.HeaderTooShort;
|
||||
if (words[0] == spirv.byte_swapped_magic_number)
|
||||
return error.ByteSwappedModule;
|
||||
if (words[0] != spirv.magic_number)
|
||||
return error.InvalidMagic;
|
||||
|
||||
const header: Header = .{
|
||||
.version = words[1],
|
||||
.generator = words[2],
|
||||
.bound = words[3],
|
||||
.schema = words[4],
|
||||
};
|
||||
|
||||
if (header.major() != 1 or header.minor() > 6 or (header.version & 0xff00_00ff) != 0)
|
||||
return error.InvalidVersion;
|
||||
if (header.bound == 0)
|
||||
return error.InvalidIdBound;
|
||||
if (header.schema != 0)
|
||||
return error.InvalidSchema;
|
||||
|
||||
var self: Self = .{ .words = words, .header = header };
|
||||
var instruction_iterator = self.iterator();
|
||||
while (try instruction_iterator.next()) |_| {}
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn iterator(self: Self) Iterator {
|
||||
return .{ .words = self.words };
|
||||
}
|
||||
|
||||
pub fn literalStringWordCount(words: []const u32) Error!usize {
|
||||
for (words, 0..) |word, word_index| {
|
||||
inline for (0..4) |byte_index| {
|
||||
if (@as(u8, @truncate(word >> (byte_index * 8))) == 0)
|
||||
return word_index + 1;
|
||||
}
|
||||
}
|
||||
return error.UnterminatedString;
|
||||
}
|
||||
|
||||
pub fn literalStringEquals(words: []const u32, expected: []const u8) Error!bool {
|
||||
var byte_cursor: usize = 0;
|
||||
for (words) |word| {
|
||||
inline for (0..4) |byte_index| {
|
||||
const byte: u8 = @truncate(word >> (byte_index * 8));
|
||||
|
||||
if (byte == 0)
|
||||
return byte_cursor == expected.len;
|
||||
if (byte_cursor >= expected.len or byte != expected[byte_cursor])
|
||||
return false;
|
||||
|
||||
byte_cursor += 1;
|
||||
}
|
||||
}
|
||||
return error.UnterminatedString;
|
||||
}
|
||||
|
||||
pub fn copyLiteralString(allocator: anytype, words: []const u32) ![]u8 {
|
||||
const word_count = try literalStringWordCount(words);
|
||||
var byte_count: usize = 0;
|
||||
outer: for (words[0..word_count]) |word| {
|
||||
inline for (0..4) |byte_index| {
|
||||
if (@as(u8, @truncate(word >> (byte_index * 8))) == 0)
|
||||
break :outer;
|
||||
|
||||
byte_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const result = try allocator.alloc(u8, byte_count);
|
||||
var cursor: usize = 0;
|
||||
outer: for (words[0..word_count]) |word| {
|
||||
inline for (0..4) |byte_index| {
|
||||
const byte: u8 = @truncate(word >> (byte_index * 8));
|
||||
if (byte == 0)
|
||||
break :outer;
|
||||
|
||||
result[cursor] = byte;
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//! ## SPIR-V frontend
|
||||
//!
|
||||
//! This namespace contains the SPIR-V parser and translator used to import shader
|
||||
//! modules into the compiler IR.
|
||||
//!
|
||||
//! `Parser` validates the SPIR-V header and iterates over binary instructions.
|
||||
//! `spec` exposes a minimalistic SPIR-V header translation.
|
||||
//!
|
||||
//! The main entry point is `translator.translate`, which finds the requested entry
|
||||
//! point, maps its execution model to an IR shader stage, lowers supported types,
|
||||
//! constants, interfaces, instructions, and structured control flow, then validates
|
||||
//! the generated IR module.
|
||||
|
||||
pub const Parser = @import("Parser.zig");
|
||||
pub const translator = @import("translator.zig");
|
||||
pub const spec = @import("spirv.zig");
|
||||
@@ -0,0 +1,169 @@
|
||||
pub const magic_number: u32 = 0x07230203;
|
||||
pub const byte_swapped_magic_number: u32 = 0x03022307;
|
||||
pub const header_word_count: usize = 5;
|
||||
|
||||
pub const Opcode = enum(u32) {
|
||||
nop = 0,
|
||||
undef = 1,
|
||||
name = 5,
|
||||
member_name = 6,
|
||||
string = 7,
|
||||
line = 8,
|
||||
extension = 10,
|
||||
ext_inst_import = 11,
|
||||
ext_inst = 12,
|
||||
memory_model = 14,
|
||||
entry_point = 15,
|
||||
execution_mode = 16,
|
||||
capability = 17,
|
||||
|
||||
type_void = 19,
|
||||
type_bool = 20,
|
||||
type_int = 21,
|
||||
type_float = 22,
|
||||
type_vector = 23,
|
||||
type_matrix = 24,
|
||||
type_image = 25,
|
||||
type_sampler = 26,
|
||||
type_sampled_image = 27,
|
||||
type_array = 28,
|
||||
type_runtime_array = 29,
|
||||
type_struct = 30,
|
||||
type_opaque = 31,
|
||||
type_pointer = 32,
|
||||
type_function = 33,
|
||||
|
||||
constant_true = 41,
|
||||
constant_false = 42,
|
||||
constant = 43,
|
||||
constant_composite = 44,
|
||||
constant_null = 46,
|
||||
spec_constant_true = 48,
|
||||
spec_constant_false = 49,
|
||||
spec_constant = 50,
|
||||
spec_constant_composite = 51,
|
||||
spec_constant_op = 52,
|
||||
|
||||
function = 54,
|
||||
function_parameter = 55,
|
||||
function_end = 56,
|
||||
function_call = 57,
|
||||
variable = 59,
|
||||
load = 61,
|
||||
store = 62,
|
||||
access_chain = 65,
|
||||
|
||||
decorate = 71,
|
||||
member_decorate = 72,
|
||||
vector_shuffle = 79,
|
||||
composite_construct = 80,
|
||||
composite_extract = 81,
|
||||
composite_insert = 82,
|
||||
copy_object = 83,
|
||||
|
||||
convert_f_to_u = 109,
|
||||
convert_f_to_s = 110,
|
||||
convert_s_to_f = 111,
|
||||
convert_u_to_f = 112,
|
||||
u_convert = 113,
|
||||
s_convert = 114,
|
||||
f_convert = 115,
|
||||
bitcast = 124,
|
||||
|
||||
s_negate = 126,
|
||||
f_negate = 127,
|
||||
i_add = 128,
|
||||
f_add = 129,
|
||||
i_sub = 130,
|
||||
f_sub = 131,
|
||||
i_mul = 132,
|
||||
f_mul = 133,
|
||||
u_div = 134,
|
||||
s_div = 135,
|
||||
f_div = 136,
|
||||
u_mod = 137,
|
||||
s_rem = 138,
|
||||
s_mod = 139,
|
||||
f_rem = 140,
|
||||
f_mod = 141,
|
||||
shift_right_logical = 194,
|
||||
shift_right_arithmetic = 195,
|
||||
shift_left_logical = 196,
|
||||
bitwise_or = 197,
|
||||
bitwise_xor = 198,
|
||||
bitwise_and = 199,
|
||||
logical_equal = 164,
|
||||
logical_not_equal = 165,
|
||||
logical_or = 166,
|
||||
logical_and = 167,
|
||||
logical_not = 168,
|
||||
select = 169,
|
||||
i_equal = 170,
|
||||
i_not_equal = 171,
|
||||
u_less_than = 176,
|
||||
s_less_than = 177,
|
||||
f_ord_equal = 180,
|
||||
f_unord_equal = 181,
|
||||
f_ord_not_equal = 182,
|
||||
f_unord_not_equal = 183,
|
||||
f_ord_less_than = 184,
|
||||
f_unord_less_than = 185,
|
||||
|
||||
phi = 245,
|
||||
loop_merge = 246,
|
||||
selection_merge = 247,
|
||||
label = 248,
|
||||
branch = 249,
|
||||
branch_conditional = 250,
|
||||
@"switch" = 251,
|
||||
kill = 252,
|
||||
return_ = 253,
|
||||
return_value = 254,
|
||||
@"unreachable" = 255,
|
||||
no_line = 317,
|
||||
|
||||
_,
|
||||
};
|
||||
|
||||
pub const ExecutionModel = enum(u32) {
|
||||
vertex = 0,
|
||||
tessellation_control = 1,
|
||||
tessellation_evaluation = 2,
|
||||
geometry = 3,
|
||||
fragment = 4,
|
||||
gl_compute = 5,
|
||||
kernel = 6,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const StorageClass = enum(u32) {
|
||||
uniform_constant = 0,
|
||||
input = 1,
|
||||
uniform = 2,
|
||||
output = 3,
|
||||
workgroup = 4,
|
||||
cross_workgroup = 5,
|
||||
private = 6,
|
||||
function = 7,
|
||||
generic = 8,
|
||||
push_constant = 9,
|
||||
atomic_counter = 10,
|
||||
image = 11,
|
||||
storage_buffer = 12,
|
||||
physical_storage_buffer = 5349,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const ExecutionMode = enum(u32) {
|
||||
early_fragment_tests = 9,
|
||||
local_size = 17,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const Decoration = enum(u32) {
|
||||
built_in = 11,
|
||||
location = 30,
|
||||
component = 31,
|
||||
index = 32,
|
||||
_,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user