This commit is contained in:
David Gonzalez Martin 2025-04-15 18:15:00 -06:00
parent 0e789d3f13
commit a251084524
4 changed files with 278 additions and 2 deletions

View File

@ -996,10 +996,15 @@ pub const GlobalVariable = opaque {
pub const set_initializer = api.LLVMSetInitializer; pub const set_initializer = api.LLVMSetInitializer;
pub const erase_from_parent = api.LLVMDeleteGlobal; pub const erase_from_parent = api.LLVMDeleteGlobal;
pub const delete = api.llvm_global_variable_delete; pub const delete = api.llvm_global_variable_delete;
pub fn to_value(global_variable: *GlobalVariable) *Value { pub fn to_value(global_variable: *GlobalVariable) *Value {
return @ptrCast(global_variable); return @ptrCast(global_variable);
} }
pub fn to_constant(global_variable: *GlobalVariable) *Constant {
return @ptrCast(global_variable);
}
pub const UnnamedAddress = enum(c_uint) { pub const UnnamedAddress = enum(c_uint) {
none, none,
local, local,
@ -1036,6 +1041,10 @@ pub const Function = opaque {
return api.llvm_function_to_string(function).to_slice().?; return api.llvm_function_to_string(function).to_slice().?;
} }
pub fn dump(function: *Function) void {
return lib.print_string(function.to_string());
}
pub const set_calling_convention = api.LLVMSetFunctionCallConv; pub const set_calling_convention = api.LLVMSetFunctionCallConv;
pub const get_calling_convention = api.LLVMGetFunctionCallConv; pub const get_calling_convention = api.LLVMGetFunctionCallConv;
@ -1060,6 +1069,10 @@ pub const Constant = opaque {
pub fn to_value(constant: *Constant.Integer) *Value { pub fn to_value(constant: *Constant.Integer) *Value {
return @ptrCast(constant); return @ptrCast(constant);
} }
pub fn to_constant(constant: *Constant.Integer) *Constant {
return @ptrCast(constant);
}
}; };
pub const get_sign_extended_value = api.LLVMConstIntGetSExtValue; pub const get_sign_extended_value = api.LLVMConstIntGetSExtValue;

View File

@ -206,6 +206,7 @@ pub const ResolvedType = struct {
pub const Enumerator = struct { pub const Enumerator = struct {
fields: []const Enumerator.Field, fields: []const Enumerator.Field,
string_to_enum: ?StringToEnum = null,
backing_type: *Type, backing_type: *Type,
line: u32, line: u32,
implicit_backing_type: bool, implicit_backing_type: bool,
@ -214,6 +215,11 @@ pub const Enumerator = struct {
name: []const u8, name: []const u8,
value: u64, value: u64,
}; };
pub const StringToEnum = struct {
function: *llvm.Function,
struct_type: *Type,
};
}; };
pub const Type = struct { pub const Type = struct {
@ -755,6 +761,7 @@ pub const Value = struct {
int_from_pointer: *Value, int_from_pointer: *Value,
pointer_cast: *Value, pointer_cast: *Value,
select: Select, select: Select,
string_to_enum: StringToEnum,
trap, trap,
truncate: *Value, truncate: *Value,
va_start, va_start,
@ -770,6 +777,7 @@ pub const Value = struct {
int_from_pointer, int_from_pointer,
pointer_cast, pointer_cast,
select, select,
string_to_enum,
trap, trap,
truncate, truncate,
va_start, va_start,
@ -788,6 +796,11 @@ pub const Value = struct {
list: *Value, list: *Value,
type: *Type, type: *Type,
}; };
const StringToEnum = struct {
enum_type: *Type,
string_value: *Value,
};
}; };
fn is_constant(value: *Value) bool { fn is_constant(value: *Value) bool {
@ -1011,6 +1024,7 @@ pub const Module = struct {
pointer_type: *llvm.Type, pointer_type: *llvm.Type,
void_type: *llvm.Type, void_type: *llvm.Type,
intrinsic_table: IntrinsicTable, intrinsic_table: IntrinsicTable,
memcmp: ?*llvm.Function = null,
debug_tag: u32, debug_tag: u32,
const IntrinsicTable = struct { const IntrinsicTable = struct {
@ -2770,7 +2784,27 @@ pub const Module = struct {
.false_value = false_value, .false_value = false_value,
}, },
}, },
} }; }, };
},
.string_to_enum => blk: {
module.skip_space();
module.expect_character(left_parenthesis);
module.skip_space();
const enum_type = module.parse_type(function);
module.expect_character(',');
module.skip_space();
const string_value = module.parse_value(function, .{});
module.skip_space();
module.expect_character(right_parenthesis);
break :blk .{ .bb = .{
.intrinsic = .{
.string_to_enum = .{
.enum_type = enum_type,
.string_value = string_value,
},
},
}, };
}, },
.trap => blk: { .trap => blk: {
module.skip_space(); module.skip_space();
@ -5466,6 +5500,206 @@ pub const Module = struct {
const int_ty = module.integer_type(64, false); const int_ty = module.integer_type(64, false);
break :blk int_ty; break :blk int_ty;
}, },
.string_to_enum => |string_to_enum| blk: {
if (string_to_enum.enum_type.bb != .enumerator) {
module.report_error();
}
if (string_to_enum.enum_type.bb.enumerator.string_to_enum == null) {
const fields = string_to_enum.enum_type.bb.enumerator.fields;
const array_element_count = fields.len;
const insert_block = module.llvm.builder.get_insert_block();
defer module.llvm.builder.position_at_end(insert_block.?);
const uint8 = module.integer_type(8, false);
const uint8_llvm = uint8.resolve(module).handle;
const aligned_backing_type = module.align_integer_type(string_to_enum.enum_type.bb.enumerator.backing_type);
const alignment = aligned_backing_type.get_byte_alignment();
const byte_size = lib.align_forward_u64(aligned_backing_type.get_byte_size() + 1, alignment);
const struct_fields = module.arena.allocate(Field, 2);
struct_fields[0] = .{
.bit_offset = 0,
.line = 0,
.type = aligned_backing_type,
.name = "enum_value",
.byte_offset = 0,
};
struct_fields[1] = .{
.bit_offset = aligned_backing_type.get_byte_size() * 8,
.line = 0,
.type = uint8,
.name = "is_valid",
.byte_offset = aligned_backing_type.get_byte_size(),
};
const struct_type = module.types.append(.{
.name = "string_to_enum",
.bb = .{
.structure = .{
.fields = struct_fields,
.byte_size = byte_size,
.bit_size = byte_size * 8,
.byte_alignment = alignment,
.bit_alignment = alignment * 8,
.line = 0,
.is_slice = false,
},
},
});
const uint64 = module.integer_type(64, false).resolve(module).handle;
const llvm_function_type = llvm.Type.Function.get(struct_type.resolve(module).handle, &.{uint64, uint64}, false);
const slice_struct_type = module.llvm.context.get_struct_type(&.{module.llvm.pointer_type, uint64});
const llvm_function_value = module.llvm.module.create_function(.{
.name = module.arena.join_string(&.{ "string_to_enum.", string_to_enum.enum_type.name }),
.linkage = .InternalLinkage,
.type = llvm_function_type,
});
var name_before: ?*llvm.GlobalVariable = null;
var value_constant_buffer: [64]*llvm.Constant = undefined;
var name_constant_buffer: [64]*llvm.Constant = undefined;
for (string_to_enum.enum_type.bb.enumerator.fields, 0..) |field, field_index| {
const value_global = aligned_backing_type.llvm.handle.?.to_integer().get_constant(field.value, 0);
value_constant_buffer[field_index] = value_global.to_constant();
const null_terminate = true;
const name_global = module.llvm.module.create_global_variable(.{
.type = uint8_llvm.get_array_type(field.name.len + @intFromBool(null_terminate)).to_type(),
.linkage = .InternalLinkage,
.name = module.arena.join_string(&.{"string.", string_to_enum.enum_type.name, ".", field.name}),
.initial_value = module.llvm.context.get_constant_string(field.name, null_terminate),
.is_constant = true,
.before = name_before,
});
name_before = name_global;
const slice_constant = module.llvm.context.get_anonymous_constant_struct(&.{
name_global.to_constant(),
uint64.to_integer().get_constant(field.name.len, 0).to_constant(),
}, false);
name_constant_buffer[field_index] = slice_constant;
}
const value_array = aligned_backing_type.llvm.handle.?.get_constant_array(value_constant_buffer[0..array_element_count]);
const name_array = slice_struct_type.to_type().get_constant_array(name_constant_buffer[0..array_element_count]);
const value_array_variable_type = aligned_backing_type.resolve(module).handle.get_array_type(array_element_count);
const value_array_variable = module.llvm.module.create_global_variable(.{
.type = value_array_variable_type.to_type(),
.linkage = .InternalLinkage,
.initial_value = value_array,
.name = "value.array.enum",
});
const name_array_variable_type = slice_struct_type.to_type().get_array_type(array_element_count);
const name_array_variable = module.llvm.module.create_global_variable(.{
.type = name_array_variable_type.to_type(),
.linkage = .InternalLinkage,
.initial_value = name_array,
.name = "name.array.enum",
});
const function_entry_block = module.llvm.context.create_basic_block("entry", llvm_function_value);
const return_block = module.llvm.context.create_basic_block("return_block", llvm_function_value);
const loop_entry_block = module.llvm.context.create_basic_block("loop.entry", llvm_function_value);
const loop_body_block = module.llvm.context.create_basic_block("loop.body", llvm_function_value);
const loop_exit_block = module.llvm.context.create_basic_block("loop.exit", llvm_function_value);
module.llvm.builder.position_at_end(function_entry_block);
const return_alloca = module.llvm.builder.create_alloca(struct_type.llvm.handle.?, "retval");
const index_alloca = module.llvm.builder.create_alloca(uint64, "idx");
_ = module.llvm.builder.create_store(uint64.get_zero().to_value(), index_alloca);
_ = module.llvm.builder.create_branch(loop_entry_block);
module.llvm.builder.position_at_end(loop_entry_block);
const index_load = module.llvm.builder.create_load(uint64, index_alloca);
const loop_cmp = module.llvm.builder.create_integer_compare(.ult, index_load, uint64.to_integer().get_constant(array_element_count, 0).to_value());
_ = module.llvm.builder.create_conditional_branch(loop_cmp, loop_body_block, loop_exit_block);
module.llvm.builder.position_at_end(loop_body_block);
var arguments: [2]*llvm.Argument = undefined;
llvm_function_value.get_arguments(&arguments);
const body_index_load = module.llvm.builder.create_load(uint64, index_alloca);
const array_element_pointer = module.llvm.builder.create_gep(.{
.type = name_array_variable_type.to_type(),
.aggregate = name_array_variable.to_value(),
.indices = &.{body_index_load},
});
const element_length_pointer = module.llvm.builder.create_struct_gep(slice_struct_type, array_element_pointer, 1);
const element_length = module.llvm.builder.create_load(uint64, element_length_pointer);
const length_comparison = module.llvm.builder.create_integer_compare(.eq, arguments[1].to_value(), element_length);
const length_match_block = module.llvm.context.create_basic_block("length.match", llvm_function_value);
const length_mismatch_block = module.llvm.context.create_basic_block("length.mismatch", llvm_function_value);
_ = module.llvm.builder.create_conditional_branch(length_comparison, length_match_block, length_mismatch_block);
module.llvm.builder.position_at_end(length_match_block);
const memcmp = if (module.llvm.memcmp) |memcmp| {
_ = memcmp;
@trap();
} else b: {
const memcmp = module.llvm.module.create_function(.{
.name = "memcmp",
.linkage = .ExternalLinkage,
.type = llvm.Type.Function.get(module.integer_type(32, true).resolve(module).handle, &.{module.llvm.pointer_type, module.llvm.pointer_type, uint64}, false),
});
module.llvm.memcmp = memcmp;
break :b memcmp;
};
const element_pointer_pointer = module.llvm.builder.create_struct_gep(slice_struct_type, array_element_pointer, 0);
const element_pointer = module.llvm.builder.create_load(uint64, element_pointer_pointer);
const memcmp_return_result = module.llvm.builder.create_call(memcmp.get_type(), memcmp.to_value(), &.{arguments[0].to_value(), element_pointer, element_length});
const content_comparison = module.llvm.builder.create_integer_compare(.eq, memcmp_return_result, module.integer_type(32, true).resolve(module).handle.get_zero().to_value());
const content_match_block = module.llvm.context.create_basic_block("content.match", llvm_function_value);
_ = module.llvm.builder.create_conditional_branch(content_comparison, content_match_block, length_mismatch_block);
module.llvm.builder.position_at_end(content_match_block);
const value_array_element_pointer = module.llvm.builder.create_gep(.{
.type = value_array_variable_type.to_type(),
.aggregate = value_array_variable.to_value(),
.indices = &.{body_index_load},
});
const enum_value_load = module.llvm.builder.create_load(aligned_backing_type.resolve(module).handle, value_array_element_pointer);
const ret_result_enum = module.llvm.builder.create_struct_gep(struct_type.llvm.handle.?.to_struct(), return_alloca, 0);
_ = module.llvm.builder.create_store(enum_value_load, ret_result_enum);
const ret_result_bool = module.llvm.builder.create_struct_gep(struct_type.llvm.handle.?.to_struct(), return_alloca, 1);
_ = module.llvm.builder.create_store(uint8_llvm.to_integer().get_constant(1, 0).to_value(), ret_result_bool);
_ = module.llvm.builder.create_branch(return_block);
module.llvm.builder.position_at_end(length_mismatch_block);
const inc = module.llvm.builder.create_add(body_index_load, uint64.to_integer().get_constant(1, 0).to_value());
_ = module.llvm.builder.create_store(inc, index_alloca);
_ = module.llvm.builder.create_branch(loop_entry_block);
module.llvm.builder.position_at_end(loop_exit_block);
_ = module.llvm.builder.create_store(struct_type.llvm.handle.?.get_zero().to_value(), return_alloca);
_ = module.llvm.builder.create_branch(return_block);
module.llvm.builder.position_at_end(return_block);
const ret_load = module.llvm.builder.create_load(struct_type.llvm.handle.?, return_alloca);
module.llvm.builder.create_ret(ret_load);
string_to_enum.enum_type.bb.enumerator.string_to_enum = .{
.function = llvm_function_value,
.struct_type = struct_type,
};
}
const s2e = string_to_enum.enum_type.bb.enumerator.string_to_enum orelse unreachable;
module.analyze_value_type(function, string_to_enum.string_value, .{ .type = module.get_slice_type(.{ .type = module.integer_type(8, false) }) });
break :blk s2e.struct_type;
},
.trap => module.noreturn_type, .trap => module.noreturn_type,
.va_start => module.get_va_list_type(), .va_start => module.get_va_list_type(),
.va_end => |va_list| blk: { .va_end => |va_list| blk: {
@ -5886,6 +6120,11 @@ pub const Module = struct {
const result = module.llvm.builder.create_select(condition, select.true_value.llvm.?, select.false_value.llvm.?); const result = module.llvm.builder.create_select(condition, select.true_value.llvm.?, select.false_value.llvm.?);
break :blk result; break :blk result;
}, },
.string_to_enum => |string_to_enum| {
_ = string_to_enum;
@trap();
},
.trap => blk: { .trap => blk: {
// TODO: lookup in advance // TODO: lookup in advance
const intrinsic_id = module.llvm.intrinsic_table.trap; const intrinsic_id = module.llvm.intrinsic_table.trap;

View File

@ -225,13 +225,25 @@ global_state_initialize = fn () void
}; };
} }
CompilerCommand = enum
{
compile,
test,
}
[export] main = fn [cc(c)] (argument_count: u32, argv: &&u8) s32 [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8) s32
{ {
global_state_initialize();
if (argument_count < 2) if (argument_count < 2)
{ {
return 1; return 1;
} }
>command_string = c_string_to_slice(argv[1]);
> a = #string_to_enum(CompilerCommand, command_string);
>relative_file_path_pointer = argv[2]; >relative_file_path_pointer = argv[2];
if (!relative_file_path_pointer) if (!relative_file_path_pointer)
{ {
@ -256,6 +268,5 @@ global_state_initialize = fn () void
return 1; return 1;
} }
global_state_initialize();
return 0; return 0;
} }

13
tests/string_to_enum.bbb Normal file
View File

@ -0,0 +1,13 @@
E = enum
{
asd,
dsa,
gsa,
}
[export] main = fn [cc(c)] () s32
{
>e = "dsa";
#string_to_enum(E, e);
return 0;
}