Basic enum support

This commit is contained in:
David Gonzalez Martin 2025-03-23 19:40:55 +01:00
parent c1f0c64757
commit 0a778aa94f
7 changed files with 946 additions and 737 deletions

View File

@ -1245,6 +1245,14 @@ pub const DI = struct {
pub fn create_pointer_type(builder: *DI.Builder, element_type: *DI.Type, bit_size: u64, align_in_bits: u32, address_space: c_uint, name: []const u8) *DI.Type.Derived {
return api.LLVMDIBuilderCreatePointerType(builder, element_type, bit_size, align_in_bits, address_space, name.ptr, name.len);
}
pub fn create_enumerator(builder: *DI.Builder, name: []const u8, value: i64, is_signed: bool) *DI.Enumerator {
return api.LLVMDIBuilderCreateEnumerator(builder, name.ptr, name.len, value, @intFromBool(is_signed));
}
pub fn create_enumeration_type(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, enumeration_types: []const *DI.Enumerator, backing_type: *DI.Type) *DI.Type.Composite {
return api.LLVMDIBuilderCreateEnumerationType(builder, scope, name.ptr, name.len, file, line, bit_size, align_in_bits, enumeration_types.ptr, @intCast(enumeration_types.len), backing_type);
}
};
pub const create_debug_location = api.LLVMDIBuilderCreateDebugLocation;
@ -1293,6 +1301,8 @@ pub const DI = struct {
};
};
pub const Enumerator = opaque {};
pub const Flags = packed struct(u32) {
visibility: Visibility = .none,
forward_declaration: bool = false,

View File

@ -1,3 +1,12 @@
Linux_PROT = bits u32
{
read: u1,
write: u1,
exec: u1,
sem: u1,
_: u28,
}
OSProtectionFlags = bits
{
read: u1,
@ -13,6 +22,10 @@ OSMapFlags = bits
populate: u1,
}
os_reserve = fn (base: u64, protection: OSProtectionFlags, map: OSMapFlags) &u8
{
}
Arena = struct
{
reserved_size: u64,

View File

@ -68,6 +68,7 @@ const GlobalKind = enum {
@"fn",
@"struct",
bits,
@"enum",
};
const FunctionKeyword = enum {
@ -1112,7 +1113,15 @@ pub const FloatType = struct {
kind: Kind,
};
pub const Enumerator = struct {};
pub const Enumerator = struct {
fields: []const Enumerator.Field,
backing_type: *Type,
pub const Field = struct {
name: []const u8,
value: u64,
};
};
pub const PointerType = struct {
type: *Type,
@ -1205,7 +1214,7 @@ pub const Type = struct {
pub fn get_evaluation_kind(ty: *const Type) EvaluationKind {
return switch (ty.bb) {
.structure, .array => .aggregate,
.integer, .bits, .pointer => .scalar,
.integer, .bits, .pointer, .enumerator => .scalar,
else => @trap(),
};
}
@ -1263,9 +1272,9 @@ pub const Type = struct {
.bits => |bits| bits.backing_type.get_byte_alignment(),
.function => 1,
.void, .forward_declaration, .noreturn => unreachable,
.array => |*array| array.element_type.get_byte_alignment(),
.array => |array| array.element_type.get_byte_alignment(),
.pointer => 8,
.enumerator => @trap(),
.enumerator => |enumerator| enumerator.backing_type.get_byte_alignment(),
.float => @trap(),
.vector => @trap(),
};
@ -1547,7 +1556,7 @@ const Converter = struct {
return value;
}
fn parse_integer(noalias converter: *Converter, noalias module: *Module, expected_type: *Type, sign: bool) *Value {
fn parse_integer_value(converter: *Converter, sign: bool) u64 {
const start = converter.offset;
const integer_start_ch = converter.content[start];
assert(!is_space(integer_start_ch));
@ -1589,6 +1598,12 @@ const Converter = struct {
else => unreachable,
};
return absolute_value;
}
fn parse_integer(noalias converter: *Converter, noalias module: *Module, expected_type: *Type, sign: bool) *Value {
const absolute_value = converter.parse_integer_value(sign);
const value: u64 = switch (sign) {
true => @bitCast(-@as(i64, @intCast(absolute_value))),
false => absolute_value,
@ -2565,6 +2580,7 @@ const Converter = struct {
cast,
cast_to,
extend,
int_from_enum,
trap,
truncate,
va_start,
@ -2661,6 +2677,29 @@ const Converter = struct {
return value;
},
.int_from_enum => {
const source_value = converter.parse_value(module, null, .value);
converter.skip_space();
converter.expect_character(right_parenthesis);
if (source_value.type.bb != .enumerator) {
converter.report_error();
}
const original_target_type = source_value.type.bb.enumerator.backing_type;
const target_type = expected_type orelse original_target_type;
if (target_type.bb != .integer) {
converter.report_error();
}
if (target_type.get_bit_size() < original_target_type.get_bit_size()) {
converter.report_error();
}
const value = module.values.add();
value.* = source_value.*;
value.type = target_type;
return value;
},
.trap => {
converter.expect_character(right_parenthesis);
@ -3184,6 +3223,38 @@ const Converter = struct {
converter.skip_space();
break :blk .not_zero;
},
'.' => {
const expected_ty = expected_type orelse converter.report_error();
if (expected_ty.bb != .enumerator) {
converter.report_error();
}
converter.offset += 1;
converter.skip_space();
const field_name = converter.parse_identifier();
const field_value = for (expected_ty.bb.enumerator.fields) |*field| {
if (lib.string.equal(field.name, field_name)) {
break field.value;
}
} else {
converter.report_error();
};
const value = module.values.add();
value.* = .{
.bb = .{
.constant_integer = .{
.value = field_value,
.signed = false,
},
},
.llvm = expected_ty.llvm.handle.to_integer().get_constant(field_value, @intFromBool(false)).to_value(),
.type = expected_ty,
.lvalue = false,
.dereference_to_assign = false,
};
return value;
},
else => os.abort(),
};
@ -4459,8 +4530,7 @@ pub noinline fn convert(arena: *Arena, options: ConvertOptions) void {
const global_string = converter.parse_identifier();
converter.skip_space();
if (string_to_enum(GlobalKind, global_string)) |global_kind| {
switch (global_kind) {
if (string_to_enum(GlobalKind, global_string)) |global_kind| switch (global_kind) {
.@"fn" => {
var calling_convention = CallingConvention.c;
const function_attributes = Function.Attributes{};
@ -5151,8 +5221,8 @@ pub noinline fn convert(arena: *Arena, options: ConvertOptions) void {
};
},
.bits => {
const allow_implicit_type = converter.content[converter.offset] == left_brace;
const maybe_backing_type: ?*Type = switch (allow_implicit_type) {
const is_implicit_type = converter.content[converter.offset] == left_brace;
const maybe_backing_type: ?*Type = switch (is_implicit_type) {
true => null,
false => converter.parse_type(module),
};
@ -5249,7 +5319,99 @@ pub noinline fn convert(arena: *Arena, options: ConvertOptions) void {
},
});
},
.@"enum" => {
const is_implicit_type = converter.content[converter.offset] == left_brace;
const maybe_backing_type: ?*Type = switch (is_implicit_type) {
true => null,
false => converter.parse_type(module),
};
converter.skip_space();
converter.expect_character(left_brace);
var highest_value: u64 = 0;
var lowest_value = ~@as(u64, 0);
var field_buffer: [64]Enumerator.Field = undefined;
var field_count: u64 = 0;
while (true) : (field_count += 1) {
converter.skip_space();
if (converter.consume_character_if_match(right_brace)) {
break;
}
const field_index = field_count;
const field_name = converter.parse_identifier();
converter.skip_space();
const field_value = if (converter.consume_character_if_match('=')) blk: {
converter.skip_space();
const field_value = converter.parse_integer_value(false);
break :blk field_value;
} else {
@trap();
};
field_buffer[field_index] = .{
.name = field_name,
.value = field_value,
};
highest_value = @max(highest_value, field_value);
lowest_value = @min(lowest_value, field_value);
converter.skip_space();
converter.expect_character(',');
}
converter.skip_space();
_ = converter.consume_character_if_match(';');
const backing_type = maybe_backing_type orelse blk: {
const bits_needed = 64 - @clz(highest_value);
const int_type = module.integer_type(bits_needed, false);
break :blk int_type;
};
if (maybe_backing_type) |bt| {
const bits_needed = 64 - @clz(highest_value);
if (bits_needed > bt.get_bit_size()) {
converter.report_error();
}
}
const fields = arena.allocate(Enumerator.Field, field_count);
@memcpy(fields, field_buffer[0..field_count]);
const debug_type = if (module.llvm.di_builder) |di_builder| blk: {
var enumerator_buffer: [64]*llvm.DI.Enumerator = undefined;
const enumerators = enumerator_buffer[0..field_count];
for (enumerators, fields) |*enumerator_pointer, *field| {
enumerator_pointer.* = di_builder.create_enumerator(field.name, @bitCast(field.value), false);
}
const alignment = 0; // TODO
const enumeration_type = di_builder.create_enumeration_type(module.llvm.global_scope, global_name, module.llvm.file, global_line, backing_type.get_bit_size(), alignment, enumerators, backing_type.llvm.debug);
break :blk enumeration_type.to_type();
} else undefined;
_ = module.types.add(.{
.bb = .{
.enumerator = .{
.backing_type = backing_type,
.fields = fields,
},
},
.llvm = .{
.handle = backing_type.llvm.handle,
.debug = debug_type,
},
.name = global_name,
});
},
} else {
converter.report_error();
}

View File

@ -376,3 +376,7 @@ test "byte_size" {
test "bits_no_backing_type" {
try invsrc(@src());
}
test "basic_enum" {
try invsrc(@src());
}

View File

@ -216,6 +216,8 @@ pub extern fn LLVMDIBuilderCreateStructType(builder: *llvm.DI.Builder, scope: *l
pub extern fn LLVMDIBuilderCreateMemberType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, bit_offset: u64, flags: llvm.DI.Flags, member_type: *llvm.DI.Type) *llvm.DI.Type.Derived;
pub extern fn LLVMDIBuilderCreateBitFieldMemberType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, bit_offset: u64, bit_storage_offset: u64, flags: llvm.DI.Flags, member_type: *llvm.DI.Type) *llvm.DI.Type.Derived;
pub extern fn LLVMDIBuilderCreatePointerType(builder: *llvm.DI.Builder, element_type: *llvm.DI.Type, bit_size: u64, align_in_bits: u32, address_space: c_uint, name_pointer: [*]const u8, name_length: usize) *llvm.DI.Type.Derived;
pub extern fn LLVMDIBuilderCreateEnumerator(builder: *llvm.DI.Builder, name_pointer: [*]const u8, name_length: usize, value: i64, is_unsigned: Bool) *llvm.DI.Enumerator;
pub extern fn LLVMDIBuilderCreateEnumerationType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, enumerator_pointer: [*]const *llvm.DI.Enumerator, enumerator_count: c_uint, backing_type: *llvm.DI.Type) *llvm.DI.Type.Composite;
pub extern fn LLVMMetadataReplaceAllUsesWith(forward: *llvm.DI.Type.Composite, complete: *llvm.DI.Type.Composite) void;

View File

@ -45,7 +45,7 @@ pub fn main(argc: c_int, argv: [*:null]const ?[*:0]const u8) callconv(.C) c_int
.build_mode = .debug_none,
.content = file_content,
.path = file_path,
.has_debug_info = false,
.has_debug_info = true,
.target = converter.Target.get_native(),
});
return 0;

18
tests/basic_enum.bbb Normal file
View File

@ -0,0 +1,18 @@
E = enum
{
zero = 0,
one = 1,
two = 2,
three = 3,
}
[export] main = fn [cc(c)] () s32
{
>a: E = .three;
>b: E = .two;
>c: E = .one;
>a_int: s32 = #extend(#int_from_enum(a));
>b_int: s32 = #extend(#int_from_enum(b));
>c_int: s32 = #extend(#int_from_enum(c));
return a_int - (b_int + c_int);
}