This commit is contained in:
David Gonzalez Martin 2025-04-15 18:15:00 -06:00
parent 0e789d3f13
commit e4e4b635c3
6 changed files with 321 additions and 8 deletions

View File

@ -808,6 +808,10 @@ pub const Module = opaque {
pub fn get_intrinsic_declaration(module: *Module, intrinsic_id: Intrinsic.Id, parameter_types: []const *Type) *Value { pub fn get_intrinsic_declaration(module: *Module, intrinsic_id: Intrinsic.Id, parameter_types: []const *Type) *Value {
return api.LLVMGetIntrinsicDeclaration(module, intrinsic_id, parameter_types.ptr, parameter_types.len); return api.LLVMGetIntrinsicDeclaration(module, intrinsic_id, parameter_types.ptr, parameter_types.len);
} }
pub fn get_named_function(module: *Module, name: [*:0]const u8) ?*Function {
return api.LLVMGetNamedFunction(module, name);
}
}; };
pub const VerifyResult = struct { pub const VerifyResult = struct {
@ -996,10 +1000,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 +1045,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 +1073,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 {
@ -507,6 +513,7 @@ pub const Type = struct {
.bits => |bits| bits.backing_type.get_bit_alignment(), .bits => |bits| bits.backing_type.get_bit_alignment(),
.array => |array| array.element_type.get_bit_alignment(), .array => |array| array.element_type.get_bit_alignment(),
.structure => |structure| structure.bit_alignment, .structure => |structure| structure.bit_alignment,
.enumerator => |enumerator| enumerator.backing_type.get_bit_alignment(),
else => @trap(), else => @trap(),
}; };
} }
@ -755,6 +762,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 +778,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 +797,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 +1025,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 +2785,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();
@ -5413,12 +5448,13 @@ pub const Module = struct {
module.report_error(); module.report_error();
} }
if (binary.left.bb == .constant_integer) { // TODO: better strategy
if (binary.left.bb == .constant_integer or binary.left.bb == .enum_literal) {
module.analyze_value_type(function, binary.right, .{}); module.analyze_value_type(function, binary.right, .{});
module.analyze_value_type(function, binary.left, .{ module.analyze_value_type(function, binary.left, .{
.type = binary.right.type, .type = binary.right.type,
}); });
} else if (binary.right.bb == .constant_integer) { } else if (binary.right.bb == .constant_integer or binary.right.bb == .enum_literal) {
module.analyze_value_type(function, binary.left, .{}); module.analyze_value_type(function, binary.left, .{});
module.analyze_value_type(function, binary.right, .{ module.analyze_value_type(function, binary.right, .{
.type = binary.left.type, .type = binary.left.type,
@ -5466,6 +5502,211 @@ 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 = string_to_enum.enum_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: {
if (module.llvm.module.get_named_function("memcmp")) |memcmp| {
module.llvm.memcmp = memcmp;
break :b memcmp;
} else {
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(module.llvm.pointer_type, element_pointer_pointer);
const memcmp_return_result = module.llvm.builder.create_call(memcmp.get_type(), memcmp.to_value(), &.{module.llvm.builder.create_int_to_ptr(arguments[0].to_value(), module.llvm.pointer_type), 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 +6127,15 @@ 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| blk: {
module.emit_value(function, string_to_enum.string_value);
const s2e = string_to_enum.enum_type.bb.enumerator.string_to_enum orelse unreachable;
const first_field = module.llvm.builder.create_extract_value(string_to_enum.string_value.llvm.?, 0);
const second_field = module.llvm.builder.create_extract_value(string_to_enum.string_value.llvm.?, 1);
const ptr_to_int = module.llvm.builder.create_ptr_to_int(first_field, module.integer_type(64, false).resolve(module).handle);
const call = module.llvm.builder.create_call(s2e.function.get_type(), s2e.function.to_value(), &.{ptr_to_int, second_field});
break :blk call;
},
.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;
@ -6461,9 +6711,7 @@ pub const Module = struct {
module.analyze(function, if_statement.condition, .{}); module.analyze(function, if_statement.condition, .{});
const llvm_condition = switch (if_statement.condition.type.?.bb) { const llvm_condition = switch (if_statement.condition.type.?.bb) {
.integer => |integer| if (integer.bit_count != 1) { .integer => |integer| if (integer.bit_count != 1) module.llvm.builder.create_integer_compare(.ne, if_statement.condition.llvm.?, if_statement.condition.type.?.llvm.handle.?.get_zero().to_value()) else if_statement.condition.llvm.?,
module.report_error();
} else if_statement.condition.llvm.?,
.pointer => module.llvm.builder.create_integer_compare(.ne, if_statement.condition.llvm.?, if_statement.condition.type.?.llvm.handle.?.get_zero().to_value()), .pointer => module.llvm.builder.create_integer_compare(.ne, if_statement.condition.llvm.?, if_statement.condition.type.?.llvm.handle.?.get_zero().to_value()),
else => @trap(), else => @trap(),
}; };
@ -6729,7 +6977,7 @@ pub const Module = struct {
.linkage = .InternalLinkage, .linkage = .InternalLinkage,
.name = "conststring", .name = "conststring",
.initial_value = constant_string, .initial_value = constant_string,
.type = u8_type.llvm.handle.?.get_array_type(string_literal.len + @intFromBool(null_terminate)).to_type(), .type = u8_type.resolve(module).handle.get_array_type(string_literal.len + @intFromBool(null_terminate)).to_type(),
}); });
global_variable.set_unnamed_address(.global); global_variable.set_unnamed_address(.global);
@ -6767,6 +7015,21 @@ pub const Module = struct {
} }
}, },
.intrinsic => |intrinsic| switch (intrinsic) { .intrinsic => |intrinsic| switch (intrinsic) {
.string_to_enum => |string_to_enum| {
module.emit_value(function, string_to_enum.string_value);
const s2e = string_to_enum.enum_type.bb.enumerator.string_to_enum orelse unreachable;
const first_field = module.llvm.builder.create_extract_value(string_to_enum.string_value.llvm.?, 0);
const second_field = module.llvm.builder.create_extract_value(string_to_enum.string_value.llvm.?, 1);
const ptr_to_int = module.llvm.builder.create_ptr_to_int(first_field, module.integer_type(64, false).resolve(module).handle);
const call = module.llvm.builder.create_call(s2e.function.get_type(), s2e.function.to_value(), &.{ptr_to_int, second_field});
_ = module.create_store(.{
.source_value = call,
.destination_value = left_llvm,
.source_type = s2e.struct_type,
.destination_type = s2e.struct_type,
.alignment = pointer_type.bb.pointer.alignment,
});
},
.va_start => { .va_start => {
assert(value_type == module.get_va_list_type()); assert(value_type == module.get_va_list_type());
assert(pointer_type.bb.pointer.type == module.get_va_list_type()); assert(pointer_type.bb.pointer.type == module.get_va_list_type());

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;
} }

View File

@ -124,6 +124,7 @@ pub extern fn llvm_value_is_instruction(value: *llvm.Value) bool;
// Intrinsics // Intrinsics
pub extern fn LLVMLookupIntrinsicID(name_pointer: [*]const u8, name_length: usize) llvm.Intrinsic.Id; pub extern fn LLVMLookupIntrinsicID(name_pointer: [*]const u8, name_length: usize) llvm.Intrinsic.Id;
pub extern fn LLVMGetNamedFunction(module: *llvm.Module, name: [*:0]const u8) *llvm.Function;
pub extern fn LLVMGetIntrinsicDeclaration(module: *llvm.Module, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Value; pub extern fn LLVMGetIntrinsicDeclaration(module: *llvm.Module, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Value;
pub extern fn LLVMIntrinsicGetType(context: *llvm.Context, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Type.Function; pub extern fn LLVMIntrinsicGetType(context: *llvm.Context, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Type.Function;

View File

@ -309,4 +309,5 @@ const names = &[_][]const u8{
"c_struct_with_array", "c_struct_with_array",
"c_function_pointer", "c_function_pointer",
"c_abi", "c_abi",
"string_to_enum",
}; };

20
tests/string_to_enum.bbb Normal file
View File

@ -0,0 +1,20 @@
E = enum
{
asd,
dsa,
gsa,
}
[export] main = fn [cc(c)] () s32
{
>e = "dsa";
>s2e = #string_to_enum(E, e);
>result: s32 = 1;
if (s2e.is_valid)
{
result = #extend(s2e.enum_value != .dsa);
}
return result;
}