mach/libs/dusk/src/IR.zig

539 lines
14 KiB
Zig

const std = @import("std");
const AstGen = @import("AstGen.zig");
const Ast = @import("Ast.zig");
const ErrorList = @import("ErrorList.zig");
const IR = @This();
allocator: std.mem.Allocator,
globals_index: u32,
instructions: []const Inst,
refs: []const Inst.Ref,
strings: []const u8,
errors: ErrorList,
pub fn deinit(self: *IR) void {
self.allocator.free(self.instructions);
self.allocator.free(self.refs);
self.allocator.free(self.strings);
self.errors.deinit();
self.* = undefined;
}
pub fn generate(allocator: std.mem.Allocator, tree: *const Ast) error{OutOfMemory}!IR {
var astgen = AstGen{
.allocator = allocator,
.tree = tree,
.errors = try ErrorList.init(allocator),
.scope_pool = std.heap.MemoryPool(AstGen.Scope).init(allocator),
};
defer {
astgen.scope_pool.deinit();
astgen.scratch.deinit(allocator);
}
errdefer {
astgen.instructions.deinit(allocator);
astgen.refs.deinit(allocator);
astgen.strings.deinit(allocator);
}
const globals_index = try astgen.genTranslationUnit();
return .{
.allocator = allocator,
.globals_index = globals_index,
.instructions = try astgen.instructions.toOwnedSlice(allocator),
.refs = try astgen.refs.toOwnedSlice(allocator),
.strings = try astgen.strings.toOwnedSlice(allocator),
.errors = astgen.errors,
};
}
pub fn getStr(self: IR, index: u32) []const u8 {
return std.mem.sliceTo(self.strings[index..], 0);
}
pub const Inst = struct {
tag: Tag,
data: Data,
pub const Index = u32;
const ref_start_index = @typeInfo(Ref).Enum.fields.len;
pub fn toRef(index: Inst.Index) Ref {
return @intToEnum(Ref, ref_start_index + index);
}
pub const Ref = enum(u32) {
none,
bool_type,
i32_type,
u32_type,
f32_type,
f16_type,
sampler_type,
comparison_sampler_type,
external_sampled_texture_type,
true_literal,
false_literal,
_,
pub fn toIndex(inst: Ref) ?Inst.Index {
const ref_int = @enumToInt(inst);
if (ref_int >= ref_start_index) {
return @intCast(Inst.Index, ref_int - ref_start_index);
} else {
return null;
}
}
};
pub const Tag = enum(u6) {
/// data is global_variable_decl
global_variable_decl,
/// data is struct_decl
struct_decl,
/// data is struct_member
struct_member,
/// data is attr_simple
attr_simple,
/// data is attr_expr
attr_expr,
/// data is attr_builtin
attr_builtin,
/// data is attr_workgroup
attr_workgroup,
/// data is attr_interpolate
attr_interpolate,
/// data is vector_type
vector_type,
/// data is matrix_type
matrix_type,
/// data is atomic_type
atomic_type,
/// data is array_type
array_type,
/// data is ptr_type
ptr_type,
/// data is sampled_texture_type
sampled_texture_type,
/// data is multisampled_texture_type
multisampled_texture_type,
/// data is storage_texture_type
storage_texture_type,
/// data is depth_texture_type
depth_texture_type,
/// data is integer_literal
integer_literal,
/// data is float_literal
float_literal,
/// data is ref
not,
/// data is ref
negate,
/// data is ref
deref,
/// data is ref
addr_of,
/// data is binary
mul,
/// data is binary
div,
/// data is binary
mod,
/// data is binary
add,
/// data is binary
sub,
/// data is binary
shift_left,
/// data is binary
shift_right,
/// data is binary
binary_and,
/// data is binary
binary_or,
/// data is binary
binary_xor,
/// data is binary
circuit_and,
/// data is binary
circuit_or,
/// data is binary
equal,
/// data is binary
not_equal,
/// data is binary
less,
/// data is binary
less_equal,
/// data is binary
greater,
/// data is binary
greater_equal,
/// data is binary
index,
/// data is member_access
member_access,
/// data is binary (lhs is expr, rhs is type)
bitcast,
pub fn isDecl(self: Tag) bool {
return switch (self) {
.global_variable_decl, .struct_decl => true,
else => false,
};
}
};
pub const Data = union {
ref: Ref,
global_variable_decl: GlobalVariableDecl,
struct_decl: StructDecl,
struct_member: StructMember,
/// attributes with no argument.
attr_simple: AttrSimple,
/// attributes with an expression argument.
attr_expr: AttrExpr,
/// @builtin attribute which accepts a BuiltinValue argument.
attr_builtin: BuiltinValue,
/// @workgroup attribute. accepts at laest 1 argument.
attr_workgroup: AttrWorkgroup,
/// @interpolate attribute. accepts 2 arguments.
attr_interpolate: AttrInterpolate,
vector_type: VectorType,
matrix_type: MatrixType,
atomic_type: AtomicType,
array_type: ArrayType,
ptr_type: PointerType,
sampled_texture_type: SampledTextureType,
multisampled_texture_type: MultisampledTextureType,
storage_texture_type: StorageTextureType,
depth_texture_type: DepthTextureType,
integer_literal: i64,
float_literal: f64,
/// meaning of LHS and RHS depends on the corresponding Tag.
binary: BinaryExpr,
member_access: MemberAccess,
};
pub const GlobalVariableDecl = struct {
/// index to null-terminated string in `strings`
name: u32,
type: Ref,
addr_space: AddressSpace,
access_mode: AccessMode,
/// length of attributes
attrs: u4 = 0,
pub const AddressSpace = enum {
none,
function,
private,
workgroup,
uniform,
storage,
};
pub const AccessMode = enum {
none,
read,
write,
read_write,
};
};
pub const StructDecl = struct {
/// index to null-terminated string in `strings`
name: u32,
/// length of the member Ref's which comes after this
members: u32,
};
pub const StructMember = struct {
/// index to null-terminated string in `strings`
name: u32,
type: Ref,
@"align": u29, // 0 means null
};
pub const BuiltinValue = enum {
vertex_index,
instance_index,
position,
front_facing,
frag_depth,
local_invocation_id,
local_invocation_index,
global_invocation_id,
workgroup_id,
num_workgroups,
sample_index,
sample_mask,
};
pub const AttrSimple = enum {
invariant,
@"const",
vertex,
fragment,
compute,
};
pub const AttrExpr = struct {
kind: Kind,
expr: Ref,
pub const Kind = enum {
@"align",
binding,
group,
id,
location,
size,
};
};
pub const AttrWorkgroup = struct {
expr0: Ref,
expr1: Ref = .none,
expr2: Ref = .none,
};
pub const AttrInterpolate = struct {
type: InterpolationType,
sample: InterpolationSample,
pub const InterpolationType = enum {
perspective,
linear,
flat,
};
pub const InterpolationSample = enum {
center,
centroid,
sample,
};
};
pub const VectorType = struct {
component_type: Ref,
size: Size,
pub const Size = enum { two, three, four };
};
pub const MatrixType = struct {
component_type: Ref,
cols: VectorType.Size,
rows: VectorType.Size,
};
pub const AtomicType = struct { component_type: Ref };
pub const ArrayType = struct {
component_type: Ref,
size: Ref = .none,
};
pub const PointerType = struct {
component_type: Ref,
addr_space: AddressSpace,
access_mode: AccessMode,
pub const AddressSpace = enum {
function,
private,
workgroup,
uniform,
storage,
};
pub const AccessMode = enum {
read,
write,
read_write,
};
};
pub const SampledTextureType = struct {
kind: Kind,
component_type: Ref,
pub const Kind = enum {
@"1d",
@"2d",
@"2d_array",
@"3d",
cube,
cube_array,
};
};
pub const MultisampledTextureType = struct {
kind: Kind,
component_type: Ref,
pub const Kind = enum { @"2d" };
};
pub const StorageTextureType = struct {
kind: Kind,
texel_format: TexelFormat,
access_mode: AccessMode,
pub const Kind = enum {
@"1d",
@"2d",
@"2d_array",
@"3d",
};
pub const TexelFormat = enum {
rgba8unorm,
rgba8snorm,
rgba8uint,
rgba8sint,
rgba16uint,
rgba16sint,
rgba16float,
r32uint,
r32sint,
r32float,
rg32uint,
rg32sint,
rg32float,
rgba32uint,
rgba32sint,
rgba32float,
bgra8unorm,
};
pub const AccessMode = enum { write };
};
pub const DepthTextureType = enum {
@"2d",
@"2d_array",
cube,
cube_array,
multisampled_2d,
};
pub const BinaryExpr = struct {
lhs: Ref,
rhs: Ref,
};
pub const MemberAccess = struct {
base: Ref,
/// index to null-terminated string in `strings`
name: u32,
};
comptime {
std.debug.assert(@sizeOf(Inst) <= 24);
}
};
pub fn print(self: IR, writer: anytype) !void {
const globals = std.mem.sliceTo(self.refs[self.globals_index..], .none);
for (globals) |ref| {
try self.printInst(writer, 0, ref, false);
}
}
pub fn printInst(self: IR, writer: anytype, indention: u16, ref: Inst.Ref, as_ref: bool) !void {
switch (ref) {
.none,
.bool_type,
.i32_type,
.u32_type,
.f32_type,
.f16_type,
.sampler_type,
.comparison_sampler_type,
.external_sampled_texture_type,
.true_literal,
.false_literal,
=> {
try writer.print("{s}()", .{@tagName(ref)});
},
_ => {
const index = ref.toIndex().?;
const inst = self.instructions[index];
if (as_ref and inst.tag.isDecl()) {
try writer.print("%{d}", .{index});
return;
}
try writer.print("%{d} = {s}{{", .{ index, @tagName(inst.tag) });
switch (inst.tag) {
.global_variable_decl => {
try writer.writeByte('\n');
try printIndent(writer, indention + 1);
try writer.writeAll(".type = ");
try self.printInst(writer, indention + 2, inst.data.global_variable_decl.type, true);
try writer.writeAll(",\n");
try printIndent(writer, indention);
try writer.writeAll("},\n");
},
.struct_decl => {
try writer.writeByte('\n');
try printIndent(writer, indention + 1);
try writer.print(".name = \"{s}\",\n", .{self.getStr(inst.data.struct_decl.name)});
const members = std.mem.sliceTo(self.refs[inst.data.struct_decl.members..], .none);
try printIndent(writer, indention + 1);
try writer.writeAll(".members = [\n");
for (members) |member| {
try printIndent(writer, indention + 2);
try self.printInst(writer, indention + 2, member, false);
}
try printIndent(writer, indention + 1);
try writer.writeAll("],\n");
try printIndent(writer, indention);
try writer.writeAll("},\n");
},
.struct_member => {
try writer.writeByte('\n');
try printIndent(writer, indention + 1);
try writer.print(".name = \"{s}\",\n", .{self.getStr(inst.data.struct_member.name)});
try printIndent(writer, indention + 1);
try writer.writeAll(".type = ");
try self.printInst(writer, indention + 2, inst.data.struct_member.type, true);
try writer.writeAll(",\n");
try printIndent(writer, indention);
try writer.writeAll("},\n");
},
else => {
try writer.print("TODO", .{});
try writer.writeAll("}");
},
}
},
}
}
const indention_size = 2;
pub fn printIndent(writer: anytype, indent: u16) !void {
try writer.writeByteNTimes(' ', indent * indention_size);
}