diff --git a/src/LLVM.zig b/src/LLVM.zig index 54c6448..ae18a12 100644 --- a/src/LLVM.zig +++ b/src/LLVM.zig @@ -996,10 +996,15 @@ pub const GlobalVariable = opaque { pub const set_initializer = api.LLVMSetInitializer; pub const erase_from_parent = api.LLVMDeleteGlobal; pub const delete = api.llvm_global_variable_delete; + pub fn to_value(global_variable: *GlobalVariable) *Value { return @ptrCast(global_variable); } + pub fn to_constant(global_variable: *GlobalVariable) *Constant { + return @ptrCast(global_variable); + } + pub const UnnamedAddress = enum(c_uint) { none, local, @@ -1060,6 +1065,10 @@ pub const Constant = opaque { pub fn to_value(constant: *Constant.Integer) *Value { return @ptrCast(constant); } + + pub fn to_constant(constant: *Constant.Integer) *Constant { + return @ptrCast(constant); + } }; pub const get_sign_extended_value = api.LLVMConstIntGetSExtValue; diff --git a/src/bootstrap.zig b/src/bootstrap.zig index 5646ff9..babbf31 100644 --- a/src/bootstrap.zig +++ b/src/bootstrap.zig @@ -206,6 +206,7 @@ pub const ResolvedType = struct { pub const Enumerator = struct { fields: []const Enumerator.Field, + string_to_enum: ?*llvm.Function = null, backing_type: *Type, line: u32, implicit_backing_type: bool, @@ -755,6 +756,7 @@ pub const Value = struct { int_from_pointer: *Value, pointer_cast: *Value, select: Select, + string_to_enum: StringToEnum, trap, truncate: *Value, va_start, @@ -770,6 +772,7 @@ pub const Value = struct { int_from_pointer, pointer_cast, select, + string_to_enum, trap, truncate, va_start, @@ -788,6 +791,11 @@ pub const Value = struct { list: *Value, type: *Type, }; + + const StringToEnum = struct { + enum_type: *Type, + string_value: *Value, + }; }; fn is_constant(value: *Value) bool { @@ -1011,6 +1019,7 @@ pub const Module = struct { pointer_type: *llvm.Type, void_type: *llvm.Type, intrinsic_table: IntrinsicTable, + memcmp: ?*llvm.Function = null, debug_tag: u32, const IntrinsicTable = struct { @@ -2770,7 +2779,27 @@ pub const Module = struct { .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: { module.skip_space(); @@ -5466,6 +5495,13 @@ pub const Module = struct { const int_ty = module.integer_type(64, false); break :blk int_ty; }, + .string_to_enum => |string_to_enum| blk: { + module.analyze_value_type(function, string_to_enum.string_value, .{ .type = module.get_slice_type(.{ .type = module.integer_type(8, false) }) }); + if (string_to_enum.enum_type.bb != .enumerator) { + module.report_error(); + } + break :blk string_to_enum.enum_type; + }, .trap => module.noreturn_type, .va_start => module.get_va_list_type(), .va_end => |va_list| blk: { @@ -5886,6 +5922,165 @@ pub const Module = struct { const result = module.llvm.builder.create_select(condition, select.true_value.llvm.?, select.false_value.llvm.?); break :blk result; }, + .string_to_enum => |string_to_enum| { + const fields = string_to_enum.enum_type.bb.enumerator.fields; + const array_element_count = fields.len; + const string_to_enum_function = if (string_to_enum.enum_type.bb.enumerator.string_to_enum) |string_to_enum_function| { + _ = string_to_enum_function; + @trap(); + } else string_to_enum: { + 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).resolve(module).handle; + const aligned_backing_type = module.align_integer_type(string_to_enum.enum_type.bb.enumerator.backing_type); + const return_struct_type = module.llvm.context.get_struct_type(&.{aligned_backing_type.resolve(module).handle, uint8 }); + const uint64 = module.integer_type(64, false).resolve(module).handle; + const llvm_function_type = llvm.Type.Function.get(return_struct_type.to_type(), &.{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 (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.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", + }); + _ = value_array_variable; + + 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(return_struct_type.to_type(), "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 = name_array_variable_type.to_type(), + .aggregate = name_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(return_struct_type, return_alloca, 0); + _ = module.llvm.builder.create_store(enum_value_load, ret_result_enum); + const ret_result_bool = module.llvm.builder.create_struct_gep(return_struct_type, return_alloca, 1); + _ = module.llvm.builder.create_store(uint8.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(return_struct_type.to_type().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(return_struct_type.to_type(), return_alloca); + module.llvm.builder.create_ret(ret_load); + + lib.print_string(llvm_function_value.to_string()); + + break :string_to_enum llvm_function_value; + }; + _ = string_to_enum_function; + @trap(); + }, .trap => blk: { // TODO: lookup in advance const intrinsic_id = module.llvm.intrinsic_table.trap; diff --git a/src/compiler.bbb b/src/compiler.bbb index 269b96a..5b5cdd1 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -225,13 +225,25 @@ global_state_initialize = fn () void }; } +CompilerCommand = enum +{ + compile, + test, +} + [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8) s32 { + global_state_initialize(); + if (argument_count < 2) { return 1; } + >command_string = c_string_to_slice(argv[1]); + + > a = #string_to_enum(CompilerCommand, command_string); + >relative_file_path_pointer = argv[2]; if (!relative_file_path_pointer) { @@ -256,6 +268,5 @@ global_state_initialize = fn () void return 1; } - global_state_initialize(); return 0; } diff --git a/tests/string_to_enum.bbb b/tests/string_to_enum.bbb new file mode 100644 index 0000000..01c2df2 --- /dev/null +++ b/tests/string_to_enum.bbb @@ -0,0 +1,13 @@ +E = enum +{ + asd, + dsa, + gsa, +} + +[export] main = fn [cc(c)] () s32 +{ + >e = "dsa"; + #string_to_enum(E, e); + return 0; +}