Skip to content

Instantly share code, notes, and snippets.

@HurricanKai
Created June 26, 2023 09:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HurricanKai/3e08373cd4023869015c1507afbdf102 to your computer and use it in GitHub Desktop.
Save HurricanKai/3e08373cd4023869015c1507afbdf102 to your computer and use it in GitHub Desktop.
pub const Instruction = MergeUnions(FillTemplatedAll(union(enum) {
I__Const: struct { value: u0 },
F__Const: struct { value: u0 },
}), IUnOp);
pub const IUnOp = FillTemplatedAll(union(enum) { I__Clz, I__Ctz, I__PopCnt });
const std = @import("std");
const BitWidth = enum { B32, B64 };
fn FillTemplatedAll(comptime Template: type) type {
return MergeUnions(FillTemplatedBitWidth(BitWidth.B32, Template), FillTemplatedBitWidth(BitWidth.B64, Template));
}
fn TransformTemplatedType(comptime bw: BitWidth, comptime is_float: bool, comptime T: type) type {
comptime {
return switch (@typeInfo(T)) {
.Struct => |s| if (s.fields.len == 1) {
var new_fields: [s.fields.len]std.builtin.Type.StructField = undefined;
for (&new_fields, s.fields) |*new_field, field| {
var new_type = TransformTemplatedType(bw, is_float, field.type);
new_field.* = .{
.type = new_type,
.name = field.name,
.default_value = field.default_value,
.is_comptime = field.is_comptime,
.alignment = @alignOf(new_type),
};
}
return @Type(.{ .Struct = .{ .layout = s.layout, .backing_integer = s.backing_integer, .fields = &new_fields, .decls = s.decls, .is_tuple = s.is_tuple } });
} else T,
.Int => |int| if (int.bits == 0) switch (bw) {
BitWidth.B32 => if (is_float) f32 else u32,
BitWidth.B64 => if (is_float) f64 else u64,
} else T,
else => T,
};
}
}
fn FillTemplatedBitWidth(comptime bw: BitWidth, comptime Template: type) type {
comptime {
var info = @typeInfo(Template).Union;
var new_fields: [info.fields.len]std.builtin.Type.UnionField = undefined;
for (&new_fields, info.fields) |*new_field, field| {
var new_type = field.type;
var new_align = field.alignment;
var new_name: [field.name.len]u8 = undefined;
@memset(&new_name, 0);
if (std.mem.replace(u8, field.name, "I__", switch (bw) {
BitWidth.B32 => "I32",
BitWidth.B64 => "I64",
}, &new_name) > 0) {
new_type = TransformTemplatedType(bw, false, new_type);
new_align = @alignOf(new_type);
} else if (std.mem.replace(u8, field.name, "F__", switch (bw) {
BitWidth.B32 => "F32",
BitWidth.B64 => "F64",
}, &new_name) > 0) {
new_type = TransformTemplatedType(bw, true, new_type);
new_align = @alignOf(new_type);
}
new_field.* = .{
.name = &new_name,
.type = new_type,
.alignment = new_align,
};
}
info.fields = &new_fields;
info.tag_type = RebuildTagType(info.fields);
const final_type = @Type(.{ .Union = info });
return final_type;
}
}
fn MergeUnions(comptime First: type, comptime Second: type) type {
comptime {
var fields = @typeInfo(First).Union.fields ++ @typeInfo(Second).Union.fields;
var decls: [0]std.builtin.Type.Declaration = undefined;
return @Type(.{ .Union = .{
.layout = std.builtin.Type.ContainerLayout.Auto,
.tag_type = RebuildTagType(fields),
.fields = fields,
.decls = &decls,
} });
}
}
fn RebuildTagType(comptime fields: []const std.builtin.Type.UnionField) type {
var tag_fields: [fields.len]std.builtin.Type.EnumField = undefined;
for (&tag_fields, fields, 0..) |*tag_field, field, value| {
tag_field.* = .{
.name = field.name,
.value = value,
};
}
return @Type(.{ .Enum = .{
.tag_type = std.math.IntFittingRange(0, tag_fields.len - 1),
.fields = &tag_fields,
.decls = &.{},
.is_exhaustive = true,
} });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment