SPIR-V: OpMemoryModel and basic capability generation

This commit is contained in:
Robin Voetter 2021-01-19 01:54:01 +01:00
parent 71ac82ecb0
commit 801732aebd
2 changed files with 55 additions and 18 deletions

View File

@ -3,6 +3,12 @@ const spec = @import("spirv/spec.zig");
const Module = @import("../Module.zig");
const Decl = Module.Decl;
pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void {
const word_count = @intCast(u32, args.len + 1);
try code.append((word_count << 16) | @enumToInt(instr));
try code.appendSlice(args);
}
pub const SPIRVModule = struct {
// TODO: Also use a free list.
next_id: u32 = 0,
@ -19,10 +25,4 @@ pub const SPIRVModule = struct {
pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void {
}
pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void {
const word_count = @intCast(u32, args.len + 1);
try code.append((word_count << 16) | @enumToInt(instr));
try code.appendSlice(args);
}
};

View File

@ -132,21 +132,24 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
defer tracy.end();
const module = self.base.options.module.?;
const target = comp.getTarget();
var binary = std.ArrayList(u32).init(self.base.allocator);
defer binary.deinit();
// Header
{
const header = [_]u32{
spec.magic_number,
(spec.version.major << 16) | (spec.version.minor << 8),
0, // TODO: Register Zig compiler magic number.
self.spirv_module.idBound(),
0, // Schema (currently reserved for future use in the SPIR-V spec).
};
try binary.appendSlice(&header);
}
// Note: The order of adding functions to the final binary
// follows the SPIR-V logical moduel format!
try binary.appendSlice(&[_]u32{
spec.magic_number,
(spec.version.major << 16) | (spec.version.minor << 8),
0, // TODO: Register Zig compiler magic number.
self.spirv_module.idBound(),
0, // Schema (currently reserved for future use in the SPIR-V spec).
});
try writeCapabilities(&binary, target);
try writeMemoryModel(&binary, target);
// Collect list of buffers to write.
// SPIR-V files support both little and big endian words. The actual format is
@ -160,7 +163,6 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items));
// Functions
for (module.decl_table.items()) |entry| {
const decl = entry.value;
switch (decl.typed_value) {
@ -183,6 +185,41 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
try file.pwritevAll(all_buffers.items, 0);
}
fn writeCapabilities(binary: *std.ArrayList(u32), target: std.Target) !void {
// TODO: Integrate with a hypothetical feature system
const cap: spec.Capability = switch (target.os.tag) {
.opencl => .Kernel,
.glsl450 => .Shader,
.vulkan => .VulkanMemoryModel,
else => unreachable, // TODO
};
try codegen.writeInstruction(binary, .OpCapability, &[_]u32{ @enumToInt(cap) });
}
fn writeMemoryModel(binary: *std.ArrayList(u32), target: std.Target) !void {
const addressing_model = switch (target.os.tag) {
.opencl => switch (target.cpu.arch) {
.spirv32 => spec.AddressingModel.Physical32,
.spirv64 => spec.AddressingModel.Physical64,
else => unreachable, // TODO
},
.glsl450, .vulkan => spec.AddressingModel.Logical,
else => unreachable, // TODO
};
const memory_model: spec.MemoryModel = switch (target.os.tag) {
.opencl => .OpenCL,
.glsl450 => .GLSL450,
.vulkan => .Vulkan,
else => unreachable,
};
try codegen.writeInstruction(binary, .OpMemoryModel, &[_]u32{
@enumToInt(addressing_model), @enumToInt(memory_model)
});
}
fn wordsToIovConst(words: []const u32) std.os.iovec_const {
const bytes = std.mem.sliceAsBytes(words);
return .{