diff --git a/src/LLVM.zig b/src/LLVM.zig index 54c6448..ab1c97c 100644 --- a/src/LLVM.zig +++ b/src/LLVM.zig @@ -808,6 +808,10 @@ pub const Module = opaque { 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); } + + pub fn get_named_function(module: *Module, name: [*:0]const u8) ?*Function { + return api.LLVMGetNamedFunction(module, name); + } }; pub const VerifyResult = struct { @@ -996,10 +1000,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, @@ -1036,6 +1045,10 @@ pub const Function = opaque { 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 get_calling_convention = api.LLVMGetFunctionCallConv; @@ -1060,6 +1073,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..8ab8fd7 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: ?StringToEnum = null, backing_type: *Type, line: u32, implicit_backing_type: bool, @@ -214,6 +215,11 @@ pub const Enumerator = struct { name: []const u8, value: u64, }; + + pub const StringToEnum = struct { + function: *llvm.Function, + struct_type: *Type, + }; }; pub const Type = struct { @@ -507,6 +513,7 @@ pub const Type = struct { .bits => |bits| bits.backing_type.get_bit_alignment(), .array => |array| array.element_type.get_bit_alignment(), .structure => |structure| structure.bit_alignment, + .enumerator => |enumerator| enumerator.backing_type.get_bit_alignment(), else => @trap(), }; } @@ -755,6 +762,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 +778,7 @@ pub const Value = struct { int_from_pointer, pointer_cast, select, + string_to_enum, trap, truncate, va_start, @@ -788,6 +797,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 +1025,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 +2785,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(); @@ -5413,12 +5448,13 @@ pub const Module = struct { 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.left, .{ .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.right, .{ .type = binary.left.type, @@ -5466,6 +5502,211 @@ pub const Module = struct { const int_ty = module.integer_type(64, false); 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, .va_start => module.get_va_list_type(), .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.?); 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: { // TODO: lookup in advance const intrinsic_id = module.llvm.intrinsic_table.trap; @@ -6461,9 +6711,7 @@ pub const Module = struct { module.analyze(function, if_statement.condition, .{}); const llvm_condition = switch (if_statement.condition.type.?.bb) { - .integer => |integer| if (integer.bit_count != 1) { - module.report_error(); - } else if_statement.condition.llvm.?, + .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.?, .pointer => module.llvm.builder.create_integer_compare(.ne, if_statement.condition.llvm.?, if_statement.condition.type.?.llvm.handle.?.get_zero().to_value()), else => @trap(), }; @@ -6729,7 +6977,7 @@ pub const Module = struct { .linkage = .InternalLinkage, .name = "conststring", .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); @@ -6767,6 +7015,21 @@ pub const Module = struct { } }, .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 => { assert(value_type == module.get_va_list_type()); assert(pointer_type.bb.pointer.type == module.get_va_list_type()); 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/src/llvm_api.zig b/src/llvm_api.zig index 7b8d0a8..5a8c340 100644 --- a/src/llvm_api.zig +++ b/src/llvm_api.zig @@ -124,6 +124,7 @@ pub extern fn llvm_value_is_instruction(value: *llvm.Value) bool; // Intrinsics 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 LLVMIntrinsicGetType(context: *llvm.Context, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Type.Function; diff --git a/src/main.zig b/src/main.zig index 2c6decd..31e27d2 100644 --- a/src/main.zig +++ b/src/main.zig @@ -309,4 +309,5 @@ const names = &[_][]const u8{ "c_struct_with_array", "c_function_pointer", "c_abi", + "string_to_enum", }; diff --git a/tests/string_to_enum.bbb b/tests/string_to_enum.bbb new file mode 100644 index 0000000..558384f --- /dev/null +++ b/tests/string_to_enum.bbb @@ -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; +}