diff --git a/src/LLVM.zig b/src/LLVM.zig index f9c62c2..b0856d8 100644 --- a/src/LLVM.zig +++ b/src/LLVM.zig @@ -497,6 +497,16 @@ pub const Context = opaque { pub fn create_basic_block(context: *Context, name: []const u8, parent: *Function) *BasicBlock { return api.llvm_context_create_basic_block(context, String.from_slice(name), parent); } + + pub fn create_forward_declared_struct_type(context: *Context, name: []const u8) *Type.Struct { + return api.llvm_context_create_forward_declared_struct_type(context, String.from_slice(name)); + } + pub fn create_struct_type(context: *Context, element_types: []const *Type, name: []const u8) *Type.Struct { + const is_packed = false; + return api.llvm_context_create_struct_type(context, element_types.ptr, @intCast(element_types.len), String.from_slice(name), is_packed); + } + + pub const get_struct_type = api.llvm_context_get_struct_type; }; pub const BasicBlock = opaque { @@ -640,6 +650,14 @@ pub const Builder = opaque { pub fn create_call(builder: *Builder, function_type: *Type.Function, function_value: *Value, arguments: []const *Value) *Value { return api.LLVMBuildCall2(builder, function_type, function_value, arguments.ptr, @intCast(arguments.len), ""); } + + pub fn create_struct_gep(builder: *Builder, struct_type: *Type.Struct, pointer: *Value, index: c_uint) *Value { + return api.LLVMBuildStructGEP2(builder, struct_type, pointer, index, ""); + } + + pub fn create_insert_value(builder: *Builder, aggregate: *Value, element: *Value, index: c_uint) *Value { + return api.LLVMBuildInsertValue(builder, aggregate, element, index, ""); + } }; pub const GlobalValue = opaque { @@ -682,6 +700,8 @@ pub const Function = opaque { pub const set_calling_convention = api.LLVMSetFunctionCallConv; pub const get_calling_convention = api.LLVMGetFunctionCallConv; + + pub const get_arguments = api.LLVMGetParams; }; pub const Constant = opaque { @@ -696,34 +716,84 @@ pub const Constant = opaque { }; }; +pub const Argument = opaque { + pub fn to_value(argument: *Argument) *Value { + return @ptrCast(argument); + } +}; + pub const Value = opaque { pub const get_type = api.LLVMTypeOf; + pub const get_kind = api.LLVMGetValueKind; pub fn is_constant(value: *Value) bool { return api.LLVMIsConstant(value) != 0; } - pub const is_instruction = api.llvm_value_is_instruction; - pub fn to_constant(value: *Value) *Constant { assert(value.is_constant()); return @ptrCast(value); } pub fn to_instruction(value: *Value) *Instruction { - assert(value.is_instruction()); + assert(value.get_kind() == .Instruction); + return @ptrCast(value); + } + + pub fn to_function(value: *Value) *Function { + assert(value.get_kind() == .Function); return @ptrCast(value); } pub fn get_calling_convention(value: *Value) CallingConvention { - if (value.is_instruction()) { - const instruction = value.to_instruction(); - return instruction.get_calling_convention(); - } else { - const function = @as(*Function, @ptrCast(value)); - return function.get_calling_convention(); + const kind = value.get_kind(); + switch (kind) { + .Instruction => { + const instruction = value.to_instruction(); + return instruction.get_calling_convention(); + }, + .Function => { + const function = value.to_function(); + return function.get_calling_convention(); + }, + else => unreachable, } } + + pub const Kind = enum(c_uint) { + Argument, + BasicBlock, + MemoryUse, + MemoryDef, + MemoryPhi, + + Function, + GlobalAlias, + GlobalIFunc, + GlobalVariable, + BlockAddress, + ConstantExpr, + ConstantArray, + ConstantStruct, + ConstantVector, + + UndefValue, + ConstantAggregateZero, + ConstantDataArray, + ConstantDataVector, + ConstantInt, + ConstantFP, + ConstantPointerNull, + ConstantTokenNone, + + MetadataAsValue, + InlineAsm, + + Instruction, + PoisonValue, + ConstantTargetNone, + ConstantPtrAuth, + }; }; pub const Instruction = opaque { @@ -769,6 +839,10 @@ pub const DI = struct { return api.LLVMDIBuilderCreateAutoVariable(builder, scope, name.ptr, name.len, file, line, auto_type, @intFromBool(always_preserve), flags, alignment_in_bits); } + pub fn create_parameter_variable(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, argument_number: c_uint, file: *DI.File, line: c_uint, parameter_type: *DI.Type, always_preserve: bool, flags: DI.Flags) *DI.LocalVariable { + return api.LLVMDIBuilderCreateParameterVariable(builder, scope, name.ptr, name.len, argument_number, file, line, parameter_type, @intFromBool(always_preserve), flags); + } + pub const insert_declare_record_at_end = api.LLVMDIBuilderInsertDeclareRecordAtEnd; pub fn create_global_variable(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, linkage_name: []const u8, file: *DI.File, line: c_uint, global_type: *DI.Type, local_to_unit: bool, expression: *DI.Expression, align_in_bits: u32) *DI.GlobalVariableExpression { @@ -777,6 +851,23 @@ pub const DI = struct { } pub const create_lexical_block = api.LLVMDIBuilderCreateLexicalBlock; + + pub fn create_replaceable_composite_type(builder: *DI.Builder, tag: c_uint, name: []const u8, scope: *DI.Scope, file: *DI.File, line: c_uint) *DI.Type.Composite { + return api.LLVMDIBuilderCreateReplaceableCompositeType(builder, tag, name.ptr, name.len, scope, file, line, 0, 0, 0, .{}, null, 0); + } + + pub fn create_struct_type(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, flags: DI.Flags, members: []const *DI.Type.Derived) *DI.Type.Composite { + const derived_from: ?*DI.Type = null; + const runtime_language: c_uint = 0; + const vtable_holder: ?*DI.Metadata = null; + const unique_id_pointer: ?[*]const u8 = null; + const unique_id_length: usize = 0; + return api.LLVMDIBuilderCreateStructType(builder, scope, name.ptr, name.len, file, line, bit_size, align_in_bits, flags, derived_from, members.ptr, @intCast(members.len), runtime_language, vtable_holder, unique_id_pointer, unique_id_length); + } + + pub fn create_member_type(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, bit_offset: u64, flags: DI.Flags, member_type: *DI.Type) *DI.Type.Derived { + return api.LLVMDIBuilderCreateMemberType(builder, scope, name.ptr, name.len, file, line, bit_size, align_in_bits, bit_offset, flags, member_type); + } }; pub const create_debug_location = api.LLVMDIBuilderCreateDebugLocation; @@ -802,7 +893,19 @@ pub const DI = struct { pub const Record = opaque {}; pub const Type = opaque { - pub const Subroutine = opaque {}; + pub const Subroutine = opaque { + pub fn to_type(subroutine: *Subroutine) *DI.Type { + return @ptrCast(subroutine); + } + }; + pub const Composite = opaque { + pub fn to_type(composite: *Composite) *DI.Type { + return @ptrCast(composite); + } + + pub const replace_all_uses_with = api.LLVMMetadataReplaceAllUsesWith; + }; + pub const Derived = opaque {}; }; pub const Flags = packed struct(u32) { @@ -851,17 +954,46 @@ pub const DI = struct { }; pub const Type = opaque { - pub const is_function = api.llvm_type_is_function; - pub const is_integer = api.llvm_type_is_integer; + pub const Kind = enum(c_uint) { + Void, + Half, + Float, + Double, + X86_FP80, + FP128, + PPC_FP128, + Label, + Integer, + Function, + Struct, + Array, + Pointer, + Vector, + Metadata, + X86_MMX, + Token, + ScalableVector, + BFloat, + X86_AMX, + TargetExt, + }; - pub fn to_function(t: *Type) *Type.Function { - assert(t.is_function()); - return @ptrCast(t); + pub const get_kind = api.LLVMGetTypeKind; + pub const get_poison = api.LLVMGetPoison; + + pub fn to_integer(ty: *Type) *Type.Integer { + assert(ty.get_kind() == .Integer); + return @ptrCast(ty); } - pub fn to_integer(t: *Type) *Type.Integer { - assert(t.is_integer()); - return @ptrCast(t); + pub fn to_function(ty: *Type) *Type.Function { + assert(ty.get_kind() == .Function); + return @ptrCast(ty); + } + + pub fn to_struct(ty: *Type) *Type.Struct { + assert(ty.get_kind() == .Struct); + return @ptrCast(ty); } pub const Function = opaque { @@ -882,6 +1014,17 @@ pub const Type = opaque { } pub const get_bit_count = api.llvm_integer_type_get_bit_count; }; + + pub const Struct = opaque { + pub fn to_type(struct_type: *Type.Struct) *Type { + return @ptrCast(struct_type); + } + + pub fn set_body(struct_type: *Type.Struct, element_types: []const *Type) void { + const is_packed = false; + api.LLVMStructSetBody(struct_type, element_types.ptr, @intCast(element_types.len), @intFromBool(is_packed)); + } + }; }; pub const Dwarf = struct { diff --git a/src/converter.zig b/src/converter.zig index 2d09c06..3920b0e 100644 --- a/src/converter.zig +++ b/src/converter.zig @@ -50,7 +50,7 @@ const GlobalKeyword = enum { const GlobalKind = enum { @"fn", - foo, + @"struct", }; const FunctionKeyword = enum { @@ -70,80 +70,286 @@ const CallingConvention = enum { } }; -const Variable = struct { - name: []const u8, - storage: *llvm.Value, - type: Type, -}; +const Module = struct { + llvm: *llvm.Module, + di_builder: ?*llvm.DI.Builder, + global_scope: *llvm.DI.Scope = undefined, + file: *llvm.DI.File = undefined, + debug_integer_types: [8]*llvm.DI.Type = undefined, + globals: Variable.Array = .{}, + types: Type.Array = .{}, + values: Value.Array = .{}, + debug_tag: c_uint = 0, + current_function: ?*Variable = null, -const VariableArray = struct { - buffer: [64]Variable = undefined, - count: u32 = 0, - - pub fn get(variables: *VariableArray) []Variable { - return variables.buffer[0..variables.count]; - } - - pub fn add(variables: *VariableArray) *Variable { - const result = &variables.buffer[variables.count]; - variables.count += 1; + pub fn get_type(module: *Module, index: usize) *Type { + assert(index < module.types.count); + const result = &module.types.buffer[index]; return result; } - pub fn find(variables: *VariableArray, name: []const u8) ?*Variable { - for (variables.get()) |*variable| { - if (lib.string.equal(variable.name, name)) { - return variable; + pub fn integer_type(module: *Module, bit_count: u32, sign: bool) *Type { + assert(lib.is_power_of_two(bit_count)); + const index = @as(usize, @intFromBool(sign)) * 4 + @ctz(bit_count) - 3; + const result = module.get_type(index); + assert(result.bb == .integer); + assert(result.bb.integer.bit_count == bit_count); + assert(result.bb.integer.signed == sign); + return result; + } + + pub fn void_type(module: *Module) *Type { + const index = 8; + const result = module.get_type(index); + assert(result.bb == .void); + return result; + } + + pub fn initialize(arena: *Arena, thread: *llvm.Thread, options: ConvertOptions) *Module { + const module = arena.allocate_one(Module); + const m = thread.context.create_module(options.name); + module.* = Module{ + .llvm = m, + .di_builder = if (options.has_debug_info) m.create_di_builder() else null, + }; + + if (module.di_builder) |di_builder| { + var directory: []const u8 = undefined; + var file_name: []const u8 = undefined; + if (lib.string.last_character(options.path, '/')) |index| { + directory = options.path[0..index]; + file_name = options.path[index + 1 ..]; + } else { + os.abort(); } - } else { - return null; + const file = di_builder.create_file(file_name, directory); + const compile_unit = di_builder.create_compile_unit(file, options.build_mode.is_optimized()); + module.global_scope = compile_unit.to_scope(); + module.file = file; } + + for ([2]bool{ false, true }) |sign| { + for (0..4) |i| { + var name_buffer = [3]u8{ if (sign) 's' else 'u', 0, 0 }; + const bit_count = @as(u32, 1) << @intCast(3 + i); + switch (bit_count) { + 8 => name_buffer[1] = '8', + 16 => { + name_buffer[1] = '1'; + name_buffer[2] = '6'; + }, + 32 => { + name_buffer[1] = '3'; + name_buffer[2] = '2'; + }, + 64 => { + name_buffer[1] = '6'; + name_buffer[2] = '4'; + }, + else => unreachable, + } + const name_length = @as(usize, 2) + @intFromBool(bit_count > 9); + const name = arena.duplicate_string(name_buffer[0..name_length]); + _ = module.types.add(.{ + .name = name, + .bb = .{ + .integer = .{ + .bit_count = bit_count, + .signed = sign, + }, + }, + .llvm = .{ + .handle = switch (bit_count) { + 1 => thread.i1.type.to_type(), + 8 => thread.i8.type.to_type(), + 16 => thread.i16.type.to_type(), + 32 => thread.i32.type.to_type(), + 64 => thread.i64.type.to_type(), + else => unreachable, + }, + .debug = if (module.di_builder) |di_builder| blk: { + const dwarf_type: llvm.Dwarf.Type = if (bit_count == 8 and !sign) .unsigned_char else if (sign) .signed else .unsigned; + break :blk di_builder.create_basic_type(name, bit_count, dwarf_type, .{}); + } else undefined, + }, + }); + } + } + + return module; } }; -const ModuleBuilder = struct { - handle: *llvm.Module, - di_builder: ?*llvm.DI.Builder, - global_scope: *llvm.DI.Scope, - file: *llvm.DI.File, - integer_types: [8]*llvm.DI.Type, - globals: VariableArray = .{}, -}; - -pub const FunctionBuilder = struct { - handle: *llvm.Function, +pub const Function = struct { current_basic_block: *llvm.BasicBlock, current_scope: *llvm.DI.Scope, - return_type: Type, - locals: VariableArray = .{}, + locals: Variable.Array = .{}, + arguments: Variable.Array = .{}, + calling_convention: CallingConvention, }; -const Type = packed struct(u64) { - llvm: u48, - signedness: bool, - reserved: u15 = 0, +pub const Value = struct { + bb: union(enum) { + function: Function, + local, + global, + argument, + instruction, + constant_integer, + struct_initialization, + }, + type: *Type, + llvm: *llvm.Value, - pub fn new(llvm_type: *llvm.Type, signedness: bool) Type { - return .{ - .llvm = @intCast(@intFromPtr(llvm_type)), - .signedness = signedness, + const Array = struct { + buffer: [64]Value = undefined, + count: usize = 0, + + pub fn add(values: *Array) *Value { + const result = &values.buffer[values.count]; + values.count += 1; + return result; + } + }; +}; + +const Field = struct { + name: []const u8, + type: *Type, + bit_offset: usize, + byte_offset: usize, +}; + +const StructType = struct { + fields: []const Field, + bit_size: u64, + byte_size: u64, + bit_alignment: u64, + byte_alignment: u64, +}; + +const FunctionType = struct { + semantic_argument_types: []const *Type, + semantic_return_type: *Type, + calling_convention: CallingConvention, +}; + +pub const Type = struct { + bb: BB, + llvm: LLVM, + name: ?[]const u8, + + pub const BB = union(enum) { + void, + forward_declaration, + integer: struct { + bit_count: u32, + signed: bool, + }, + @"struct": StructType, + function: FunctionType, + }; + + pub fn get_bit_size(ty: *const Type) u64 { + return switch (ty.bb) { + .integer => |integer| integer.bit_count, + .@"struct" => |struct_type| struct_type.bit_size, + .void, .forward_declaration, .function => unreachable, }; } - pub fn get(t: Type) *llvm.Type { - return @ptrFromInt(t.llvm); + pub fn get_byte_size(ty: *const Type) u64 { + return switch (ty.bb) { + .integer => |integer| @divExact(@max(8, lib.next_power_of_two(integer.bit_count)), 8), + .@"struct" => |struct_type| struct_type.byte_size, + .void, .forward_declaration, .function => unreachable, + }; } - pub fn to_debug_type(ty: Type, module: *ModuleBuilder) *llvm.DI.Type { - if (ty.get().is_integer()) { - const integer_type = ty.get().to_integer(); - const bit_count = integer_type.get_bit_count(); - const index = (@ctz(bit_count) - 3) + (@as(u8, 4) * @intFromBool(ty.signedness)); - return module.integer_types[index]; - } else { - os.abort(); - } + pub fn get_bit_alignment(ty: *const Type) u64 { + return switch (ty.bb) { + .integer => |integer| integer.bit_count, + .@"struct" => |struct_type| struct_type.bit_alignment, + .void, .forward_declaration, .function => unreachable, + }; } + + pub fn get_byte_alignment(ty: *const Type) u64 { + return switch (ty.bb) { + .integer => |integer| @divExact(@max(8, lib.next_power_of_two(integer.bit_count)), 8), + .@"struct" => |struct_type| struct_type.bit_alignment, + .void, .forward_declaration, .function => unreachable, + }; + } + + const Array = struct { + buffer: [64]Type = undefined, + count: usize = 0, + + pub fn get(types: *Array) []Type { + return types.buffer[0..types.count]; + } + + pub fn find(types: *Array, name: []const u8) ?*Type { + for (types.get()) |*ty| { + if (ty.name) |type_name| { + if (lib.string.equal(type_name, name)) { + return ty; + } + } + } else { + return null; + } + } + + fn add(types: *Array, ty: Type) *Type { + const result = &types.buffer[types.count]; + types.count += 1; + result.* = ty; + return result; + } + }; + + pub const LLVM = struct { + handle: *llvm.Type, + debug: *llvm.DI.Type, + }; +}; + +pub const Variable = struct { + value: *Value, + name: []const u8, + + const Array = struct { + buffer: [64]Variable = undefined, + count: u32 = 0, + + pub fn get(variables: *Array) []Variable { + return variables.buffer[0..variables.count]; + } + + pub fn add(variables: *Array) *Variable { + const result = &variables.buffer[variables.count]; + variables.count += 1; + return result; + } + + pub fn add_many(variables: *Array, count: u32) []Variable { + const result = variables.buffer[variables.count .. variables.count + count]; + variables.count += count; + return result; + } + + pub fn find(variables: *Array, name: []const u8) ?*Variable { + for (variables.get()) |*variable| { + if (lib.string.equal(variable.name, name)) { + return variable; + } + } else { + return null; + } + } + }; }; const Converter = struct { @@ -174,7 +380,7 @@ const Converter = struct { } } - pub fn parse_type(noalias converter: *Converter, noalias thread: *llvm.Thread) Type { + pub fn parse_type(noalias converter: *Converter, noalias module: *Module) *Type { const identifier = converter.parse_identifier(); var integer_type = identifier.len > 1 and identifier[0] == 's' or identifier[0] == 'u'; if (integer_type) { @@ -189,20 +395,12 @@ const Converter = struct { 'u' => false, else => unreachable, }; - const bit_count = lib.parse.integer_decimal(identifier[1..]); - const llvm_int_type = switch (bit_count) { - // TODO: consider u1? - 0 => converter.report_error(), - 8 => thread.i8.type, - 16 => thread.i16.type, - 32 => thread.i32.type, - 64 => thread.i64.type, - else => converter.report_error(), - }; - const llvm_type = llvm_int_type.to_type(); - return Type.new(llvm_type, signedness); + const bit_count: u32 = @intCast(lib.parse.integer_decimal(identifier[1..])); + const ty = module.integer_type(bit_count, signedness); + return ty; } else { - os.abort(); + const ty = module.types.find(identifier) orelse @trap(); + return ty; } } @@ -263,7 +461,7 @@ const Converter = struct { return value; } - fn parse_integer(noalias converter: *Converter, expected_type: Type, sign: bool) *llvm.Value { + fn parse_integer(noalias converter: *Converter, noalias module: *Module, expected_type: *Type, sign: bool) *Value { const start = converter.offset; const integer_start_ch = converter.content[start]; assert(!is_space(integer_start_ch)); @@ -308,9 +506,15 @@ const Converter = struct { else => unreachable, }; - const integer_type = expected_type.get().to_integer(); - const integer_value = integer_type.get_constant(value, @intFromBool(expected_type.signedness)); - return integer_value.to_value(); + const integer_type = expected_type.llvm.handle.to_integer(); + const llvm_integer_value = integer_type.get_constant(value, @intFromBool(expected_type.bb.integer.signed)); + const integer_value = module.values.add(); + integer_value.* = .{ + .llvm = llvm_integer_value.to_value(), + .type = expected_type, + .bb = .constant_integer, + }; + return integer_value; } fn expect_character(noalias converter: *Converter, expected_ch: u8) void { @@ -319,24 +523,26 @@ const Converter = struct { } } - fn parse_block(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *ModuleBuilder, noalias function: *FunctionBuilder) void { + fn parse_block(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *Module) void { converter.skip_space(); + const current_function_global = module.current_function orelse unreachable; + const current_function = ¤t_function_global.value.bb.function; const block_line = converter.get_line(); const block_column = converter.get_column(); - const current_scope = function.current_scope; - defer function.current_scope = current_scope; + const current_scope = current_function.current_scope; + defer current_function.current_scope = current_scope; if (module.di_builder) |di_builder| { const lexical_block = di_builder.create_lexical_block(current_scope, module.file, block_line, block_column); - function.current_scope = lexical_block.to_scope(); + current_function.current_scope = lexical_block.to_scope(); } converter.expect_character(left_brace); - const local_offset = function.locals.count; - defer function.locals.count = local_offset; + const local_offset = current_function.locals.count; + defer current_function.locals.count = local_offset; while (true) { converter.skip_space(); @@ -357,7 +563,7 @@ const Converter = struct { var statement_debug_location: *llvm.DI.Location = undefined; if (module.di_builder) |_| { const inlined_at: ?*llvm.DI.Metadata = null; // TODO - statement_debug_location = llvm.DI.create_debug_location(thread.context, line, column, function.current_scope, inlined_at); + statement_debug_location = llvm.DI.create_debug_location(thread.context, line, column, current_function.current_scope, inlined_at); thread.builder.set_current_debug_location(statement_debug_location); } @@ -374,7 +580,7 @@ const Converter = struct { if (converter.consume_character_if_match(':')) { converter.skip_space(); - const local_type = converter.parse_type(thread); + const local_type = converter.parse_type(module); converter.skip_space(); @@ -385,30 +591,35 @@ const Converter = struct { if (module.di_builder) |_| { thread.builder.clear_current_debug_location(); } - const alloca = thread.builder.create_alloca(local_type.get(), local_name); - const value = converter.parse_value(thread, module, function, local_type); + const local_storage = module.values.add(); + local_storage.* = .{ + .llvm = thread.builder.create_alloca(local_type.llvm.handle, local_name), + .type = local_type, + .bb = .local, + }; + + const value = converter.parse_value(thread, module, local_type); if (module.di_builder) |di_builder| { thread.builder.set_current_debug_location(statement_debug_location); - const debug_type = local_type.to_debug_type(module); + const debug_type = local_type.llvm.debug; const always_preserve = true; // TODO: const alignment = 0; const flags = llvm.DI.Flags{}; - const local_variable = di_builder.create_auto_variable(function.current_scope, local_name, module.file, line, debug_type, always_preserve, flags, alignment); + const local_variable = di_builder.create_auto_variable(current_function.current_scope, local_name, module.file, line, debug_type, always_preserve, flags, alignment); const inlined_at: ?*llvm.DI.Metadata = null; // TODO - const debug_location = llvm.DI.create_debug_location(thread.context, line, column, function.current_scope, inlined_at); - _ = di_builder.insert_declare_record_at_end(alloca, local_variable, di_builder.null_expression(), debug_location, function.current_basic_block); + const debug_location = llvm.DI.create_debug_location(thread.context, line, column, current_function.current_scope, inlined_at); + _ = di_builder.insert_declare_record_at_end(local_storage.llvm, local_variable, di_builder.null_expression(), debug_location, current_function.current_basic_block); thread.builder.set_current_debug_location(statement_debug_location); } - _ = thread.builder.create_store(value, alloca); + _ = thread.builder.create_store(value.llvm, local_storage.llvm); - const local = function.locals.add(); + const local = current_function.locals.add(); local.* = .{ .name = local_name, - .storage = alloca, - .type = local_type, + .value = local_storage, }; } else { converter.report_error(); @@ -419,29 +630,29 @@ const Converter = struct { if (string_to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| { switch (statement_start_keyword) { .@"return" => { - const return_value = converter.parse_value(thread, module, function, function.return_type); - thread.builder.create_ret(return_value); + const return_value = converter.parse_value(thread, module, current_function_global.value.type.bb.function.semantic_return_type); + thread.builder.create_ret(return_value.llvm); }, .@"if" => { - const taken_block = thread.context.create_basic_block("", function.handle); - const not_taken_block = thread.context.create_basic_block("", function.handle); + const taken_block = thread.context.create_basic_block("", current_function_global.value.llvm.to_function()); + const not_taken_block = thread.context.create_basic_block("", current_function_global.value.llvm.to_function()); converter.skip_space(); converter.expect_character(left_parenthesis); converter.skip_space(); - const condition = converter.parse_value(thread, module, function, null); + const condition = converter.parse_value(thread, module, null); converter.skip_space(); converter.expect_character(right_parenthesis); - _ = thread.builder.create_conditional_branch(condition, taken_block, not_taken_block); + _ = thread.builder.create_conditional_branch(condition.llvm, taken_block, not_taken_block); thread.builder.position_at_end(taken_block); - converter.parse_block(thread, module, function); + converter.parse_block(thread, module); - const is_first_block_terminated = function.current_basic_block.get_terminator() != null; + const is_first_block_terminated = current_function.current_basic_block.get_terminator() != null; if (!is_first_block_terminated) { @trap(); } @@ -460,8 +671,8 @@ const Converter = struct { var is_second_block_terminated = false; if (is_else) { thread.builder.position_at_end(not_taken_block); - converter.parse_block(thread, module, function); - is_second_block_terminated = function.current_basic_block.get_terminator() != null; + converter.parse_block(thread, module); + is_second_block_terminated = current_function.current_basic_block.get_terminator() != null; } else { @trap(); } @@ -478,10 +689,10 @@ const Converter = struct { if (converter.consume_character_if_match(left_parenthesis)) { // This is a call - const variable = if (function.locals.find(statement_start_identifier)) |local| local else if (module.globals.find(statement_start_identifier)) |global| global else { + const variable = if (current_function.locals.find(statement_start_identifier)) |local| local else if (module.globals.find(statement_start_identifier)) |global| global else { converter.report_error(); }; - const call = thread.builder.create_call(variable.type.get().to_function(), variable.storage, &.{}); + const call = thread.builder.create_call(variable.value.type.llvm.handle.to_function(), variable.value.llvm, &.{}); _ = call; @trap(); } else { @@ -527,50 +738,60 @@ const Converter = struct { } }; - fn parse_value(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *ModuleBuilder, noalias function: ?*FunctionBuilder, maybe_expected_type: ?Type) *llvm.Value { + fn parse_value(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *Module, maybe_expected_type: ?*Type) *Value { converter.skip_space(); var value_state = ExpressionState.none; - var previous_value: *llvm.Value = undefined; + var previous_value: ?*Value = null; var iterations: usize = 0; - var iterative_expected_type: ?Type = maybe_expected_type; + var iterative_expected_type = maybe_expected_type; - const value = while (true) : (iterations += 1) { + const value: *Value = while (true) : (iterations += 1) { if (iterations == 1 and iterative_expected_type == null) { - iterative_expected_type = Type.new(previous_value.get_type(), false); + iterative_expected_type = previous_value.?.type; } const current_value = switch (converter.content[converter.offset] == left_parenthesis) { true => os.abort(), - false => converter.parse_single_value(thread, module, function, iterative_expected_type), + false => converter.parse_single_value(thread, module, iterative_expected_type), }; converter.skip_space(); const left = previous_value; const right = current_value; + const next_ty = if (previous_value) |pv| pv.type else current_value.type; + // _ = left; + // _ = right; - previous_value = switch (value_state) { - .none => current_value, - .sub => thread.builder.create_sub(left, right), - .add => thread.builder.create_add(left, right), - .mul => thread.builder.create_mul(left, right), - .sdiv => thread.builder.create_sdiv(left, right), - .udiv => thread.builder.create_udiv(left, right), - .srem => thread.builder.create_srem(left, right), - .urem => thread.builder.create_urem(left, right), - .shl => thread.builder.create_shl(left, right), - .ashr => thread.builder.create_ashr(left, right), - .lshr => thread.builder.create_lshr(left, right), - .@"and" => thread.builder.create_and(left, right), - .@"or" => thread.builder.create_or(left, right), - .xor => thread.builder.create_xor(left, right), - .icmp_ne => |icmp| thread.builder.create_compare(icmp.to_int_predicate(), left, right), + const llvm_value = switch (value_state) { + .none => current_value.llvm, + .sub => thread.builder.create_sub(left.?.llvm, right.llvm), + .add => thread.builder.create_add(left.?.llvm, right.llvm), + .mul => thread.builder.create_mul(left.?.llvm, right.llvm), + .sdiv => thread.builder.create_sdiv(left.?.llvm, right.llvm), + .udiv => thread.builder.create_udiv(left.?.llvm, right.llvm), + .srem => thread.builder.create_srem(left.?.llvm, right.llvm), + .urem => thread.builder.create_urem(left.?.llvm, right.llvm), + .shl => thread.builder.create_shl(left.?.llvm, right.llvm), + .ashr => thread.builder.create_ashr(left.?.llvm, right.llvm), + .lshr => thread.builder.create_lshr(left.?.llvm, right.llvm), + .@"and" => thread.builder.create_and(left.?.llvm, right.llvm), + .@"or" => thread.builder.create_or(left.?.llvm, right.llvm), + .xor => thread.builder.create_xor(left.?.llvm, right.llvm), + .icmp_ne => |icmp| thread.builder.create_compare(icmp.to_int_predicate(), left.?.llvm, right.llvm), + }; + + previous_value = module.values.add(); + previous_value.?.* = .{ + .llvm = llvm_value, + .type = next_ty, + .bb = .instruction, }; const ch = converter.content[converter.offset]; value_state = switch (ch) { - ';', right_parenthesis => break previous_value, + ',', ';', right_parenthesis => break previous_value.?, '-' => blk: { converter.offset += 1; break :blk .sub; @@ -585,17 +806,25 @@ const Converter = struct { }, '/' => blk: { converter.offset += 1; - break :blk switch (iterative_expected_type.?.signedness) { - true => .sdiv, - false => .udiv, + const ty = iterative_expected_type orelse unreachable; + break :blk switch (ty.bb) { + .integer => |int| switch (int.signed) { + true => .sdiv, + false => .udiv, + }, + else => unreachable, }; }, '%' => blk: { converter.offset += 1; - switch (iterative_expected_type.?.signedness) { - true => break :blk .srem, - false => break :blk .urem, - } + const ty = iterative_expected_type orelse unreachable; + break :blk switch (ty.bb) { + .integer => |int| switch (int.signed) { + true => .srem, + false => .urem, + }, + else => unreachable, + }; }, '<' => blk: { converter.offset += 1; @@ -614,9 +843,13 @@ const Converter = struct { break :blk switch (converter.content[converter.offset]) { '>' => b: { converter.offset += 1; - break :b switch (iterative_expected_type.?.signedness) { - true => .ashr, - false => .lshr, + const ty = iterative_expected_type orelse unreachable; + break :b switch (ty.bb) { + .integer => |int| switch (int.signed) { + true => .ashr, + false => .lshr, + }, + else => unreachable, }; }, else => os.abort(), @@ -658,15 +891,15 @@ const Converter = struct { negative, }; - fn parse_single_value(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *ModuleBuilder, noalias maybe_function: ?*FunctionBuilder, expected_type: ?Type) *llvm.Value { + fn parse_single_value(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *Module, expected_type: ?*Type) *Value { converter.skip_space(); - if (maybe_function) |function| { + if (module.current_function) |function| { if (module.di_builder) |_| { const line = converter.get_line(); const column = converter.get_column(); const inlined_at: ?*llvm.DI.Metadata = null; // TODO - const debug_location = llvm.DI.create_debug_location(thread.context, line, column, function.current_scope, inlined_at); + const debug_location = llvm.DI.create_debug_location(thread.context, line, column, function.value.bb.function.current_scope, inlined_at); thread.builder.set_current_debug_location(debug_location); } } @@ -682,6 +915,80 @@ const Converter = struct { converter.skip_space(); break :blk .negative; }, + left_brace => { + converter.offset += 1; + + converter.skip_space(); + + const ty = expected_type orelse converter.report_error(); + const must_be_constant = module.current_function == null; + + switch (ty.bb) { + .@"struct" => |*struct_type| { + var field_count: usize = 0; + + var llvm_value = switch (must_be_constant) { + true => @trap(), + false => ty.llvm.handle.get_poison(), + }; + + while (converter.consume_character_if_match('.')) : (field_count += 1) { + converter.skip_space(); + + const field_name = converter.parse_identifier(); + const field_index: u32 = for (struct_type.fields, 0..) |*field, field_index| { + if (lib.string.equal(field.name, field_name)) { + break @intCast(field_index); + } + } else converter.report_error(); + + const field = struct_type.fields[field_index]; + + converter.skip_space(); + + converter.expect_character('='); + + converter.skip_space(); + + const field_value = converter.parse_value(thread, module, field.type); + + if (must_be_constant) { + if (field_index != field_count) { + converter.report_error(); + } + @trap(); + } else { + llvm_value = thread.builder.create_insert_value(llvm_value, field_value.llvm, field_index); + } + + converter.skip_space(); + + _ = converter.consume_character_if_match(','); + + converter.skip_space(); + } + + if (field_count != struct_type.fields.len) { + // expect: 'zero' keyword + @trap(); + } + + converter.expect_character(right_brace); + + const value = module.values.add(); + value.* = .{ + .llvm = llvm_value, + .type = ty, + .bb = .struct_initialization, + }; + + return value; + }, + else => converter.report_error(), + } + + @trap(); + }, else => os.abort(), }; @@ -689,11 +996,13 @@ const Converter = struct { const value_start_ch = converter.content[value_offset]; const value = switch (value_start_ch) { 'a'...'z', 'A'...'Z', '_' => b: { - if (maybe_function) |function| { + if (module.current_function) |current_function| { const identifier = converter.parse_identifier(); const variable = blk: { - if (function.locals.find(identifier)) |local| { + if (current_function.value.bb.function.locals.find(identifier)) |local| { break :blk local; + } else if (current_function.value.bb.function.arguments.find(identifier)) |argument| { + break :blk argument; } else if (module.globals.find(identifier)) |global| { break :blk global; } else { @@ -704,21 +1013,74 @@ const Converter = struct { converter.skip_space(); if (converter.consume_character_if_match(left_parenthesis)) { - // TODO: arguments - converter.skip_space(); - converter.expect_character(right_parenthesis); - const call = thread.builder.create_call(variable.type.get().to_function(), variable.storage, &.{}); - call.to_instruction().set_calling_convention(variable.storage.get_calling_convention()); + var llvm_arguments: [64]*llvm.Value = undefined; + var argument_count: usize = 0; + while (true) : (argument_count += 1) { + converter.skip_space(); + + if (converter.consume_character_if_match(right_parenthesis)) { + break; + } + + switch (variable.value.type.bb.function.calling_convention) { + .c => { + @trap(); + }, + .unknown => { + const argument_value = converter.parse_value(thread, module, variable.value.type.bb.function.semantic_argument_types[argument_count]); + llvm_arguments[argument_count] = argument_value.llvm; + }, + } + } + + const llvm_argument_values = llvm_arguments[0..argument_count]; + const llvm_call = thread.builder.create_call(variable.value.type.llvm.handle.to_function(), variable.value.llvm, llvm_argument_values); + llvm_call.to_instruction().set_calling_convention(variable.value.llvm.get_calling_convention()); + const call = module.values.add(); + call.* = .{ + .llvm = llvm_call, + .type = variable.value.type, + .bb = .instruction, + }; break :b call; + } else if (converter.consume_character_if_match('.')) { + converter.skip_space(); + + switch (variable.value.type.bb) { + .@"struct" => |*struct_type| { + const field_name = converter.parse_identifier(); + const field_index: u32 = for (struct_type.fields, 0..) |field, field_index| { + if (lib.string.equal(field.name, field_name)) { + break @intCast(field_index); + } + } else converter.report_error(); + const field = struct_type.fields[field_index]; + const gep = thread.builder.create_struct_gep(variable.value.type.llvm.handle.to_struct(), variable.value.llvm, field_index); + const load = module.values.add(); + // const llvm_field_index = thread.llvminteger_type.get_constant(field_index, @intFromBool(false)); + load.* = .{ + .llvm = thread.builder.create_load(field.type.llvm.handle, gep), + .type = field.type, + .bb = .instruction, + }; + break :b load; + }, + else => @trap(), + } } else { - const load = thread.builder.create_load(variable.type.get(), variable.storage); + const load = module.values.add(); + load.* = .{ + .llvm = thread.builder.create_load(variable.value.type.llvm.handle, variable.value.llvm), + .type = variable.value.type, + .bb = .instruction, + }; break :b load; } } else { converter.report_error(); } }, - '0'...'9' => converter.parse_integer(expected_type.?, prefix == .negative), + '0'...'9' => converter.parse_integer(module, expected_type.?, prefix == .negative), else => os.abort(), }; @@ -794,56 +1156,7 @@ pub noinline fn convert(options: ConvertOptions) void { const thread = llvm.default_initialize(); - const m = thread.context.create_module(options.name); - var module = ModuleBuilder{ - .handle = m, - .di_builder = if (options.has_debug_info) m.create_di_builder() else null, - .global_scope = undefined, - .file = undefined, - .integer_types = undefined, - }; - - if (module.di_builder) |di_builder| { - var directory: []const u8 = undefined; - var file_name: []const u8 = undefined; - if (lib.string.last_character(options.path, '/')) |index| { - directory = options.path[0..index]; - file_name = options.path[index + 1 ..]; - } else { - os.abort(); - } - const file = di_builder.create_file(file_name, directory); - const compile_unit = di_builder.create_compile_unit(file, options.build_mode.is_optimized()); - module.global_scope = compile_unit.to_scope(); - module.file = file; - - for ([2]bool{ false, true }) |sign| { - for (0..4) |i| { - var name_buffer = [3]u8{ if (sign) 's' else 'u', 0, 0 }; - const bit_count = @as(u64, 1) << @intCast(3 + i); - switch (bit_count) { - 8 => name_buffer[1] = '8', - 16 => { - name_buffer[1] = '1'; - name_buffer[2] = '6'; - }, - 32 => { - name_buffer[1] = '3'; - name_buffer[2] = '2'; - }, - 64 => { - name_buffer[1] = '6'; - name_buffer[2] = '4'; - }, - else => unreachable, - } - const name_length = @as(usize, 2) + @intFromBool(bit_count > 9); - const name = name_buffer[0..name_length]; - const dwarf_type: llvm.Dwarf.Type = if (bit_count == 8 and !sign) .unsigned_char else if (sign) .signed else .unsigned; - module.integer_types[i + @as(usize, 4) * @intFromBool(sign)] = di_builder.create_basic_type(name, bit_count, dwarf_type, .{}); - } - } - } + const module = Module.initialize(lib.global.arena, thread, options); while (true) { converter.skip_space(); @@ -883,13 +1196,16 @@ pub noinline fn convert(options: ConvertOptions) void { const global_name = converter.parse_identifier(); + if (module.types.find(global_name) != null) @trap(); + if (module.globals.find(global_name) != null) @trap(); + converter.skip_space(); - var global_type: ?Type = null; + var global_type: ?*Type = null; if (converter.consume_character_if_match(':')) { converter.skip_space(); - global_type = converter.parse_type(thread); + global_type = converter.parse_type(module); converter.skip_space(); } @@ -947,109 +1263,358 @@ pub noinline fn convert(options: ConvertOptions) void { converter.expect_character(left_parenthesis); - while (converter.offset < converter.content.len and converter.content[converter.offset] != right_parenthesis) { - // TODO: arguments - converter.report_error(); + const Argument = struct { + name: []const u8, + type: *Type, + line: u32, + column: u32, + }; + var argument_buffer: [64]Argument = undefined; + var argument_count: u32 = 0; + + while (converter.offset < converter.content.len and converter.content[converter.offset] != right_parenthesis) : (argument_count += 1) { + converter.skip_space(); + + const argument_line = converter.get_line(); + const argument_column = converter.get_column(); + + const argument_name = converter.parse_identifier(); + + converter.skip_space(); + + converter.expect_character(':'); + + converter.skip_space(); + + const argument_type = converter.parse_type(module); + + converter.skip_space(); + + _ = converter.consume_character_if_match(','); + + argument_buffer[argument_count] = .{ + .name = argument_name, + .type = argument_type, + .line = argument_line, + .column = argument_column, + }; } converter.expect_character(right_parenthesis); converter.skip_space(); - const return_type = converter.parse_type(thread); - const function_type = llvm.Type.Function.get(return_type.get(), &.{}, false); + const return_type = converter.parse_type(module); + const linkage_name = global_name; - const handle = module.handle.create_function(.{ + var argument_type_buffer: [argument_buffer.len]*llvm.Type = undefined; + var debug_argument_type_buffer: [argument_buffer.len + 1]*llvm.DI.Type = undefined; + + const arguments = argument_buffer[0..argument_count]; + const argument_types = argument_type_buffer[0..argument_count]; + const debug_argument_types = debug_argument_type_buffer[0 .. argument_count + 1]; + + debug_argument_types[0] = return_type.llvm.debug; + + if (argument_count > 0) { + switch (calling_convention) { + .unknown => { + for (arguments, argument_types, debug_argument_types[1..]) |*argument, *argument_type, *debug_argument_type| { + argument_type.* = argument.type.llvm.handle; + debug_argument_type.* = argument.type.llvm.debug; + if (module.di_builder) |_| { + assert(@intFromPtr(argument.type.llvm.debug) != 0xaaaa_aaaa_aaaa_aaaa); + } + } + }, + // TODO: C calling convention + .c => @trap(), + } + } + + const llvm_function_type = llvm.Type.Function.get(return_type.llvm.handle, argument_types, false); + const llvm_handle = module.llvm.create_function(.{ .name = global_name, .linkage = switch (is_export) { true => .ExternalLinkage, false => .InternalLinkage, }, - .type = function_type, + .type = llvm_function_type, }); - handle.set_calling_convention(calling_convention.to_llvm()); + llvm_handle.set_calling_convention(calling_convention.to_llvm()); - const global = module.globals.add(); - global.* = .{ - .type = Type.new(function_type.to_type(), false), - .storage = handle.to_value(), - .name = global_name, - }; - - const entry_block = thread.context.create_basic_block("entry", handle); + const entry_block = thread.context.create_basic_block("entry", llvm_handle); thread.builder.position_at_end(entry_block); - var function = FunctionBuilder{ - .handle = handle, - .current_basic_block = entry_block, - .return_type = return_type, - .current_scope = undefined, - }; + const global = module.globals.add(); - if (module.di_builder) |di_builder| { - const debug_return_type = return_type.to_debug_type(&module); - const subroutine_type = di_builder.create_subroutine_type(module.file, &.{debug_return_type}, .{}); - const linkage_name = global_name; + var subroutine_type: *llvm.DI.Type.Subroutine = undefined; + const current_scope: *llvm.DI.Scope = if (module.di_builder) |di_builder| blk: { + const subroutine_type_flags = llvm.DI.Flags{}; + subroutine_type = di_builder.create_subroutine_type(module.file, debug_argument_types, subroutine_type_flags); const scope_line: u32 = @intCast(converter.line_offset + 1); const local_to_unit = !is_export; const flags = llvm.DI.Flags{}; const is_definition = true; const subprogram = di_builder.create_function(module.global_scope, global_name, linkage_name, module.file, global_line, subroutine_type, local_to_unit, is_definition, scope_line, flags, options.build_mode.is_optimized()); - handle.set_subprogram(subprogram); + llvm_handle.set_subprogram(subprogram); - function.current_scope = @ptrCast(subprogram); + break :blk @ptrCast(subprogram); + } else undefined; + + const function_type = module.types.add(.{ + .name = null, + .llvm = .{ + .handle = llvm_function_type.to_type(), + .debug = subroutine_type.to_type(), + }, + .bb = .{ + .function = .{ + .calling_convention = calling_convention, + .semantic_return_type = return_type, + .semantic_argument_types = blk: { + const semantic_argument_types = lib.global.arena.allocate(*Type, argument_count); + for (arguments, semantic_argument_types) |argument, *argument_type| { + argument_type.* = argument.type; + } + + break :blk semantic_argument_types; + }, + }, + }, + }); + + const value = module.values.add(); + value.* = .{ + .llvm = llvm_handle.to_value(), + .type = function_type, + .bb = .{ + .function = .{ + .current_basic_block = entry_block, + .calling_convention = calling_convention, + .current_scope = current_scope, + }, + }, + }; + + global.* = .{ + .value = value, + .name = global_name, + }; + + module.current_function = global; + defer module.current_function = null; + + const argument_variables = global.value.bb.function.arguments.add_many(argument_count); + + for (argument_variables, arguments) |*argument_variable, *argument| { + const argument_alloca = thread.builder.create_alloca(argument.type.llvm.handle, argument.name); + const argument_value = module.values.add(); + argument_value.* = .{ + .llvm = argument_alloca, + .type = argument.type, + .bb = .argument, + }; + argument_variable.* = .{ + .value = argument_value, + .name = argument.name, + }; } - converter.parse_block(thread, &module, &function); + var llvm_argument_buffer: [argument_buffer.len]*llvm.Argument = undefined; + llvm_handle.get_arguments(&llvm_argument_buffer); + const llvm_arguments = llvm_argument_buffer[0..argument_count]; + + if (argument_count > 0) { + switch (calling_convention) { + .unknown => { + for (argument_variables, llvm_arguments) |*argument_variable, llvm_argument| { + _ = thread.builder.create_store(llvm_argument.to_value(), argument_variable.value.llvm); + } + }, + .c => @trap(), + } + + if (module.di_builder) |di_builder| { + for (argument_variables, arguments, 0..) |argument_variable, argument, argument_number| { + const always_preserve = true; + const flags = llvm.DI.Flags{}; + const parameter_variable = di_builder.create_parameter_variable(global.value.bb.function.current_scope, argument_variable.name, @intCast(argument_number + 1), module.file, argument.line, argument.type.llvm.debug, always_preserve, flags); + const inlined_at: ?*llvm.DI.Metadata = null; // TODO + const debug_location = llvm.DI.create_debug_location(thread.context, argument.line, argument.column, global.value.bb.function.current_scope, inlined_at); + _ = di_builder.insert_declare_record_at_end(argument_variable.value.llvm, parameter_variable, di_builder.null_expression(), debug_location, global.value.bb.function.current_basic_block); + } + } + } + + converter.parse_block(thread, module); if (module.di_builder) |di_builder| { - di_builder.finalize_subprogram(handle.get_subprogram()); + di_builder.finalize_subprogram(llvm_handle.get_subprogram()); } if (lib.optimization_mode == .Debug and module.di_builder == null) { - const verify_result = handle.verify(); + const verify_result = llvm_handle.verify(); if (!verify_result.success) { os.abort(); } } }, - else => converter.report_error(), + .@"struct" => { + converter.skip_space(); + + converter.expect_character(left_brace); + + if (module.types.find(global_name) != null) { + @trap(); + } + + const llvm_struct_type = thread.context.create_forward_declared_struct_type(global_name); + const struct_type = module.types.add(.{ + .name = global_name, + .bb = .forward_declaration, + .llvm = .{ + .handle = llvm_struct_type.to_type(), + .debug = if (module.di_builder) |di_builder| blk: { + const r = di_builder.create_replaceable_composite_type(module.debug_tag, global_name, module.global_scope, module.file, global_line); + module.debug_tag += 1; + break :blk r.to_type(); + } else undefined, + }, + }); + + var field_buffer: [256]Field = undefined; + var llvm_field_type_buffer: [field_buffer.len]*llvm.Type = undefined; + var llvm_debug_member_type_buffer: [field_buffer.len]*llvm.DI.Type.Derived = undefined; + var field_count: usize = 0; + var byte_offset: u64 = 0; + var byte_alignment: u64 = 1; + var bit_alignment: u64 = 1; + + while (true) { + converter.skip_space(); + + if (converter.consume_character_if_match(right_brace)) { + break; + } + + const field_line = converter.get_line(); + const field_name = converter.parse_identifier(); + + converter.skip_space(); + + converter.expect_character(':'); + + converter.skip_space(); + + const field_type = converter.parse_type(module); + + const field_bit_offset = byte_offset * 8; + const field_byte_offset = byte_offset; + const field_bit_size = field_type.get_bit_size(); + const field_byte_size = field_type.get_byte_size(); + const field_byte_alignment = field_type.get_byte_alignment(); + const field_bit_alignment = field_type.get_bit_alignment(); + field_buffer[field_count] = .{ + .byte_offset = field_byte_offset, + .bit_offset = field_bit_offset, + .type = field_type, + .name = field_name, + }; + llvm_field_type_buffer[field_count] = field_type.llvm.handle; + + if (module.di_builder) |di_builder| { + const member_type = di_builder.create_member_type(module.global_scope, field_name, module.file, field_line, field_bit_size, @intCast(field_bit_alignment), field_bit_offset, .{}, field_type.llvm.debug); + llvm_debug_member_type_buffer[field_count] = member_type; + } + + byte_alignment = @max(byte_alignment, field_byte_alignment); + bit_alignment = @max(bit_alignment, field_bit_alignment); + byte_offset += field_byte_size; + + field_count += 1; + + converter.skip_space(); + + switch (converter.content[converter.offset]) { + ',' => converter.offset += 1, + else => {}, + } + } + + converter.skip_space(); + + _ = converter.consume_character_if_match(';'); + + const byte_size = byte_offset; + const bit_size = byte_size * 8; + + const fields = lib.global.arena.allocate(Field, field_count); + @memcpy(fields, field_buffer[0..field_count]); + + const element_types = llvm_field_type_buffer[0..field_count]; + llvm_struct_type.set_body(element_types); + + if (module.di_builder) |di_builder| { + const member_types = llvm_debug_member_type_buffer[0..field_count]; + const debug_struct_type = di_builder.create_struct_type(module.global_scope, global_name, module.file, global_line, bit_size, @intCast(bit_alignment), .{}, member_types); + const forward_declared: *llvm.DI.Type.Composite = @ptrCast(struct_type.llvm.debug); + forward_declared.replace_all_uses_with(debug_struct_type); + struct_type.llvm.debug = debug_struct_type.to_type(); + } + + struct_type.bb = .{ + .@"struct" = .{ + .bit_size = byte_size * 8, + .byte_size = byte_size, + .bit_alignment = bit_alignment, + .byte_alignment = byte_alignment, + .fields = fields, + }, + }; + }, } } else { converter.report_error(); } } else { if (global_type) |expected_type| { - const value = converter.parse_value(thread, &module, null, expected_type); - const global_variable = module.handle.create_global_variable(.{ - .linkage = switch (is_export) { - true => .ExternalLinkage, - false => .InternalLinkage, - }, - .name = global_name, - .initial_value = value.to_constant(), - .type = expected_type.get(), - }); - - const global = module.globals.add(); - global.* = .{ - .name = global_name, - .storage = global_variable.to_value(), - .type = expected_type, - }; + const value = converter.parse_value(thread, module, expected_type); converter.skip_space(); converter.expect_character(';'); + const global_variable = module.llvm.create_global_variable(.{ + .linkage = switch (is_export) { + true => .ExternalLinkage, + false => .InternalLinkage, + }, + .name = global_name, + .initial_value = value.llvm.to_constant(), + .type = expected_type.llvm.handle, + }); + if (module.di_builder) |di_builder| { - const debug_type = expected_type.to_debug_type(&module); const linkage_name = global_name; const local_to_unit = is_export; // TODO: extern const alignment = 0; // TODO - const global_variable_expression = di_builder.create_global_variable(module.global_scope, global_name, linkage_name, module.file, global_line, debug_type, local_to_unit, di_builder.null_expression(), alignment); + const global_variable_expression = di_builder.create_global_variable(module.global_scope, global_name, linkage_name, module.file, global_line, expected_type.llvm.debug, local_to_unit, di_builder.null_expression(), alignment); global_variable.add_debug_info(global_variable_expression); } + + const global_value = module.values.add(); + global_value.* = .{ + .llvm = global_variable.to_value(), + .type = expected_type, + .bb = .global, + }; + + const global = module.globals.add(); + global.* = .{ + .name = global_name, + .value = global_value, + }; } else { converter.report_error(); } @@ -1061,16 +1626,16 @@ pub noinline fn convert(options: ConvertOptions) void { } if (lib.optimization_mode == .Debug) { - const verify_result = module.handle.verify(); + const verify_result = module.llvm.verify(); if (!verify_result.success) { - lib.print_string(module.handle.to_string()); + lib.print_string(module.llvm.to_string()); lib.print_string("============================\n"); lib.print_string(verify_result.error_message orelse unreachable); os.abort(); } if (!lib.is_test) { - const module_string = module.handle.to_string(); + const module_string = module.llvm.to_string(); lib.print_string_stderr(module_string); } } @@ -1089,7 +1654,7 @@ pub noinline fn convert(options: ConvertOptions) void { os.abort(); }; - const object_generate_result = llvm.object_generate(module.handle, target_machine, .{ + const object_generate_result = llvm.object_generate(module.llvm, target_machine, .{ .optimize_when_possible = @intFromBool(@intFromEnum(options.build_mode) > @intFromEnum(BuildMode.soft_optimize)), .debug_info = options.has_debug_info, .optimization_level = if (options.build_mode != .debug_none) options.build_mode.to_llvm_ir() else null, diff --git a/src/converter_test.zig b/src/converter_test.zig index b4a678d..aa0a8fb 100644 --- a/src/converter_test.zig +++ b/src/converter_test.zig @@ -156,3 +156,7 @@ test "simple_branch" { test "basic_call" { try invsrc(@src()); } + +test "struct" { + try invsrc(@src()); +} diff --git a/src/lib.zig b/src/lib.zig index 44524e4..7858eb5 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -39,6 +39,23 @@ pub fn align_forward_u32(value: u32, alignment: u32) u32 { return align_forward(u32, value, alignment); } +pub fn is_power_of_two(value: anytype) bool { + return (value & (value - 1)) == 0; +} + +pub fn next_power_of_two(n: u64) u64 { + var result = n; + result -= 1; + result |= result >> 1; + result |= result >> 2; + result |= result >> 4; + result |= result >> 8; + result |= result >> 16; + result |= result >> 32; + result += 1; + return result; +} + const ValueFromFlag = enum { sub, cmov, diff --git a/src/llvm.cpp b/src/llvm.cpp index d8939db..3736d45 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -53,24 +53,6 @@ EXPORT Module* llvm_context_create_module(LLVMContext& context, BBLLVMString nam return new Module(name.string_ref(), context); } -EXPORT bool llvm_value_is_instruction(Value* value) -{ - auto result = isa(value); - return result; -} - -EXPORT bool llvm_type_is_function(const Type& type) -{ - auto result = type.isFunctionTy(); - return result; -} - -EXPORT bool llvm_type_is_integer(const Type& type) -{ - auto result = type.isIntegerTy(); - return result; -} - EXPORT unsigned llvm_integer_type_get_bit_count(const IntegerType& integer_type) { auto result = integer_type.getBitWidth(); @@ -94,6 +76,12 @@ EXPORT Function* llvm_module_create_function(Module* module, FunctionType* funct return function; } +EXPORT StructType* llvm_context_create_forward_declared_struct_type(LLVMContext& context, BBLLVMString name) +{ + auto* struct_type = StructType::create(context, name.string_ref()); + return struct_type; +} + EXPORT StructType* llvm_context_create_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, BBLLVMString name, bool is_packed) { auto types = ArrayRef(type_pointer, type_count); diff --git a/src/llvm_api.zig b/src/llvm_api.zig index 4c0dc10..aef20af 100644 --- a/src/llvm_api.zig +++ b/src/llvm_api.zig @@ -20,6 +20,8 @@ pub extern fn LLVMGetFunctionCallConv(function: *llvm.Function) llvm.CallingConv pub extern fn LLVMSetInstructionCallConv(instruction: *llvm.Instruction, calling_convention: llvm.CallingConvention) void; pub extern fn LLVMGetInstructionCallConv(instruction: *llvm.Instruction) llvm.CallingConvention; +pub extern fn LLVMGetParams(function: *llvm.Function, argument_buffer: [*]*llvm.Argument) void; + pub extern fn llvm_function_to_string(function: *llvm.Function) *llvm.String; pub extern fn llvm_function_verify(function: *llvm.Function, error_message: *llvm.String) bool; pub extern fn llvm_module_verify(module: *llvm.Module, error_message: *llvm.String) bool; @@ -49,6 +51,8 @@ pub extern fn llvm_builder_create_alloca(builder: *llvm.Builder, ty: *llvm.Type, pub extern fn LLVMBuildStore(builder: *llvm.Builder, value: *llvm.Value, pointer: *llvm.Value) *llvm.Value; pub extern fn LLVMBuildLoad2(builder: *llvm.Builder, ty: *llvm.Type, pointer: *llvm.Value, name: [*:0]const u8) *llvm.Value; pub extern fn LLVMBuildCall2(builder: *llvm.Builder, ty: *llvm.Type.Function, pointer: *llvm.Value, argument_pointer: [*]const *llvm.Value, argument_count: c_uint, name: [*:0]const u8) *llvm.Value; +pub extern fn LLVMBuildStructGEP2(builder: *llvm.Builder, struct_type: *llvm.Type.Struct, pointer: *llvm.Value, index: c_uint, name: [*:0]const u8) *llvm.Value; +pub extern fn LLVMBuildInsertValue(builder: *llvm.Builder, aggregate: *llvm.Value, element: *llvm.Value, index: c_uint, name: [*:0]const u8) *llvm.Value; pub extern fn LLVMSetCurrentDebugLocation2(builder: *llvm.Builder, location: ?*llvm.DI.Location) void; @@ -83,6 +87,8 @@ pub extern fn LLVMCountParamTypes(function_type: *llvm.Type.Function) c_uint; pub extern fn LLVMGetParamTypes(function_type: *llvm.Type.Function, types: [*]*llvm.Type) void; // Types: struct +pub extern fn LLVMStructSetBody(struct_type: *llvm.Type.Struct, element_type_pointer: [*]const *llvm.Type, element_type_count: c_uint, is_packed: Bool) void; +pub extern fn llvm_context_create_forward_declared_struct_type(context: *llvm.Context, name: llvm.String) *llvm.Type.Struct; pub extern fn llvm_context_create_struct_type(context: *llvm.Context, element_types_pointer: [*]const *llvm.Type, element_type_count: usize, name: llvm.String, is_packed: bool) *llvm.Type.Struct; pub extern fn llvm_context_get_struct_type(context: *llvm.Context, element_types_pointer: [*]const *llvm.Type, element_type_count: usize, is_packed: bool) *llvm.Type.Struct; @@ -96,14 +102,15 @@ pub extern fn LLVMPointerTypeInContext(context: *llvm.Context, address_space: c_ pub extern fn LLVMVectorType(element_type: *llvm.Type, element_count: c_uint) *llvm.Type.FixedVector; pub extern fn LLVMScalableVectorType(element_type: *llvm.Type, element_count: c_uint) *llvm.Type.ScalableVector; -pub extern fn llvm_type_is_function(ty: *llvm.Type) bool; -pub extern fn llvm_type_is_integer(ty: *llvm.Type) bool; +pub extern fn LLVMGetTypeKind(ty: *llvm.Type) llvm.Type.Kind; pub extern fn llvm_integer_type_get_bit_count(integer_type: *llvm.Type.Integer) c_uint; // VALUES +pub extern fn LLVMGetPoison(type: *llvm.Type) *llvm.Value; pub extern fn LLVMConstInt(type: *llvm.Type.Integer, value: c_ulonglong, sign_extend: Bool) *llvm.Constant.Integer; +pub extern fn LLVMGetValueKind(value: *llvm.Value) llvm.Value.Kind; pub extern fn LLVMIsConstant(value: *llvm.Value) Bool; // Debug info API @@ -119,9 +126,15 @@ pub extern fn LLVMDIBuilderCreateDebugLocation(context: *llvm.Context, line: c_u pub extern fn LLVMDIBuilderCreateBasicType(builder: *llvm.DI.Builder, name_pointer: [*]const u8, name_length: usize, bit_count: u64, dwarf_type: llvm.Dwarf.Type, flags: llvm.DI.Flags) *llvm.DI.Type; pub extern fn LLVMDIBuilderCreateAutoVariable(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, type: *llvm.DI.Type, always_preserve: Bool, flags: llvm.DI.Flags, align_in_bits: u32) *llvm.DI.LocalVariable; pub extern fn LLVMDIBuilderInsertDeclareRecordAtEnd(builder: *llvm.DI.Builder, storage: *llvm.Value, local_variable: *llvm.DI.LocalVariable, expression: *llvm.DI.Expression, debug_location: *llvm.DI.Location, basic_block: *llvm.BasicBlock) *llvm.DI.Record; +pub extern fn LLVMDIBuilderCreateParameterVariable(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, argument_number: c_uint, file: *llvm.DI.File, line: c_uint, type: *llvm.DI.Type, always_preserve: Bool, flags: llvm.DI.Flags) *llvm.DI.LocalVariable; pub extern fn LLVMDIBuilderCreateGlobalVariableExpression(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, linkage_name_pointer: [*]const u8, linkage_name_length: usize, file: *llvm.DI.File, line: c_uint, global_type: *llvm.DI.Type, local_to_unit: Bool, expression: *llvm.DI.Expression, declaration: ?*llvm.DI.Metadata, align_in_bits: u32) *llvm.DI.GlobalVariableExpression; pub extern fn llvm_global_variable_add_debug_info(global_variable: *llvm.GlobalVariable, debug_global_variable: *llvm.DI.GlobalVariableExpression) void; pub extern fn LLVMDIBuilderCreateLexicalBlock(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, file: *llvm.DI.File, line: c_uint, column: c_uint) *llvm.DI.LexicalBlock; +pub extern fn LLVMDIBuilderCreateReplaceableCompositeType(builder: *llvm.DI.Builder, tag: c_uint, name_pointer: [*]const u8, name_length: usize, scope: *llvm.DI.Scope, file: *llvm.DI.File, line: c_uint, runtime_language: c_uint, bit_size: u64, align_in_bits: u32, flags: llvm.DI.Flags, unique_identifier_pointer: ?[*]const u8, unique_identifier_length: usize) *llvm.DI.Type.Composite; +pub extern fn LLVMDIBuilderCreateStructType(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, flags: llvm.DI.Flags, derived_from: ?*llvm.DI.Type, member_pointer: [*]const *llvm.DI.Type.Derived, member_length: c_uint, runtime_language: c_uint, vtable_holder: ?*llvm.DI.Metadata, unique_id_pointer: ?[*]const u8, unique_id_length: usize) *llvm.DI.Type.Composite; +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 LLVMMetadataReplaceAllUsesWith(forward: *llvm.DI.Type.Composite, complete: *llvm.DI.Type.Composite) void; // Target pub extern fn llvm_default_target_triple() llvm.String; diff --git a/src/main.nat b/src/main.nat deleted file mode 100644 index d6fbf49..0000000 --- a/src/main.nat +++ /dev/null @@ -1,4 +0,0 @@ -fn main() s32 -{ - return 0; -} diff --git a/tests/struct.bbb b/tests/struct.bbb new file mode 100644 index 0000000..43bf668 --- /dev/null +++ b/tests/struct.bbb @@ -0,0 +1,20 @@ +Foo = struct { + x: s32, + y: s32, + z: s32, +}; + +foo = fn(arg: Foo) s32 { + return arg.z; +} + +[export] main = fn [cc(c)] () s32 +{ + >a: Foo = { + .x = 2, + .y = 1, + .z = 0, + }; + + return foo(a); +}