539 lines
14 KiB
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);
|
|
}
|