From 03484afd758043ca4edaa6ebbf489a52282fbc32 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 15 Apr 2025 07:34:04 -0600 Subject: [PATCH] Remove unused code --- src/converter.zig | 8323 ---------------------------------------- src/converter_test.zig | 457 --- src/lib.zig | 4 - src/lib_test.zig | 44 - 4 files changed, 8828 deletions(-) delete mode 100644 src/converter.zig delete mode 100644 src/converter_test.zig delete mode 100644 src/lib_test.zig diff --git a/src/converter.zig b/src/converter.zig deleted file mode 100644 index 05c9ec4..0000000 --- a/src/converter.zig +++ /dev/null @@ -1,8323 +0,0 @@ -const lib = @import("lib.zig"); -const assert = lib.assert; -const os = lib.os; -const Arena = lib.Arena; -const llvm = @import("LLVM.zig"); - -test { - _ = @import("converter_test.zig"); -} - -const left_bracket = '['; -const right_bracket = ']'; -const left_brace = '{'; -const right_brace = '}'; -const left_parenthesis = '('; -const right_parenthesis = ')'; - -const max_argument_count = 64; - -fn array_type_name(arena: *Arena, array_type: ArrayType) [:0]const u8 { - var buffer: [256]u8 = undefined; - var i: u64 = 0; - buffer[i] = left_bracket; - i += 1; - i += lib.string_format.integer_decimal(buffer[i..], array_type.element_count.?); - buffer[i] = right_bracket; - i += 1; - const element_name = array_type.element_type.name.?; - @memcpy(buffer[i..][0..element_name.len], element_name); - i += element_name.len; - return arena.duplicate_string(buffer[0..i]); -} - -fn array_type_llvm(noalias module: *Module, array: ArrayType) Type.LLVM { - const element_count = array.element_count.?; - return .{ - .handle = array.element_type.llvm.handle.get_array_type(element_count).to_type(), - .debug = if (module.llvm.di_builder) |di_builder| di_builder.create_array_type(element_count, @intCast(array.element_type.get_bit_alignment()), array.element_type.llvm.debug, &.{}).to_type() else undefined, - }; -} - -fn is_identifier_start_ch(ch: u8) bool { - return (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z') or ch == '_'; -} - -fn is_decimal_ch(ch: u8) bool { - return ch >= '0' and ch <= '9'; -} - -fn is_identifier_ch(ch: u8) bool { - return is_identifier_start_ch(ch) or is_decimal_ch(ch); -} - -const GlobalKeyword = enum { - @"export", - @"extern", -}; - -const GlobalKind = enum { - @"fn", - @"struct", - bits, - @"enum", -}; - -const FunctionKeyword = enum { - cc, -}; - -const CallingConvention = enum { - c, - - pub fn to_llvm(calling_convention: CallingConvention) llvm.CallingConvention { - return switch (calling_convention) { - .c => .c, - }; - } - - pub fn resolve(calling_convention: CallingConvention, target: Target) ResolvedCallingConvention { - return switch (calling_convention) { - .c => switch (target.cpu) { - .x86_64 => switch (target.os) { - .linux => .system_v, - }, - }, - }; - } -}; - -pub const ResolvedCallingConvention = enum { - system_v, - win64, -}; - -const AttributeContainerType = enum { - call, - function, -}; - -fn llvm_add_function_attribute(value: *llvm.Value, attribute: *llvm.Attribute, container_type: AttributeContainerType) void { - switch (container_type) { - .call => { - const call = value.is_call_instruction() orelse unreachable; - call.add_attribute(.function, attribute); - }, - .function => { - const function = value.to_function(); - function.add_attribute(.function, attribute); - }, - } -} - -fn llvm_add_argument_attribute(value: *llvm.Value, attribute: *llvm.Attribute, index: c_uint, container_type: AttributeContainerType) void { - switch (container_type) { - .call => { - const call = value.is_call_instruction() orelse unreachable; - call.add_attribute(@enumFromInt(index), attribute); - }, - .function => { - const function = value.to_function(); - function.add_attribute(@enumFromInt(index), attribute); - }, - } -} - -pub const Function = struct { - return_alloca: *llvm.Value, - exit_block: ?*llvm.BasicBlock, - return_block: *llvm.BasicBlock, - current_scope: *llvm.DI.Scope, - return_pointer: *Value, - attributes: Attributes, - locals: Variable.Array = .{}, - arguments: Variable.Array = .{}, - - const Attributes = struct { - inline_behavior: enum { - default, - always_inline, - no_inline, - inline_hint, - } = .default, - naked: bool = false, - }; -}; - -pub const ConstantInteger = struct { - value: u64, - signed: bool, -}; - -pub const Value = struct { - bb: union(enum) { - function: Function, - local, - global, - argument, - instruction, - struct_initialization: struct { - is_constant: bool, - }, - bits_initialization, - infer_or_ignore, - constant_integer: ConstantInteger, - constant_array, - external_function, - @"unreachable", - string_literal_global, - }, - type: *Type, - llvm: *llvm.Value, - lvalue: bool, - dereference_to_assign: bool, - - const Array = struct { - buffer: [1024]Value = undefined, - count: u64 = 0, - - pub fn add(values: *Array) *Value { - const result = &values.buffer[values.count]; - values.count += 1; - return result; - } - }; - - pub fn is_constant(value: *Value) bool { - return switch (value.bb) { - .constant_integer, .constant_array => true, - .struct_initialization => |si| si.is_constant, - .instruction => false, - else => @trap(), - }; - } -}; - -const Field = struct { - name: []const u8, - type: *Type, - bit_offset: u64, - byte_offset: u64, -}; - -const FunctionType = struct { - return_type_abi: Abi.Information, - argument_type_abis: []const Abi.Information, - abi_return_type: *Type, - abi_argument_types: []const *Type, - calling_convention: CallingConvention, - available_registers: Abi.RegisterCount, - is_var_args: bool, - - fn get_abi_argument_types(function_type: *const FunctionType) []const *Type { - return function_type.abi_argument_types[0..function_type.abi_argument_count]; - } -}; - -const StructType = struct { - fields: []const Field, - is_slice: bool, - bit_size: u64, - byte_size: u64, - bit_alignment: u32, - byte_alignment: u32, -}; - -const Bits = struct { - fields: []const Field, - backing_type: *Type, - implicit_backing_type: bool, -}; - -pub const ArrayType = struct { - element_count: ?u64, - element_type: *Type, -}; - -pub const IntegerType = struct { - bit_count: u32, - signed: bool, -}; - -pub const FloatType = struct { - const Kind = enum { - half, - bfloat, - float, - double, - fp128, - }; - kind: Kind, -}; - -pub const Enumerator = struct { - fields: []const Enumerator.Field, - backing_type: *Type, - implicit_backing_type: bool, - - pub const Field = struct { - name: []const u8, - value: u64, - }; -}; - -pub const PointerType = struct { - type: *Type, - alignment: u32, -}; - -pub const SliceType = struct { - pointer_type: *Type, - alignment: u32, -}; - -pub const Type = struct { - bb: BB, - llvm: LLVM, - name: ?[]const u8, - - pub const EvaluationKind = enum { - scalar, - complex, - aggregate, - }; - - pub const BB = union(enum) { - void, - noreturn, - forward_declaration, - integer: IntegerType, - float: FloatType, - structure: StructType, - bits: Bits, - function: FunctionType, - array: ArrayType, - pointer: PointerType, - enumerator: Enumerator, - vector, - }; - - pub fn is_slice(ty: *const Type) bool { - return switch (ty.bb) { - .structure => |structure| structure.is_slice, - else => false, - }; - } - - pub fn is_aggregate_type_for_abi(ty: *Type) bool { - const ev_kind = ty.get_evaluation_kind(); - const is_member_function_pointer_type = false; // TODO - return ev_kind != .scalar or is_member_function_pointer_type; - } - - pub fn is_integer_backing(ty: *Type) bool { - return switch (ty.bb) { - .enumerator, .integer, .bits, .pointer => true, - else => false, - }; - } - - pub fn is_abi_equal(ty: *const Type, other: *const Type) bool { - return ty == other or ty.llvm.handle == other.llvm.handle; - } - - pub fn is_signed(ty: *const Type) bool { - return switch (ty.bb) { - .integer => |integer| integer.signed, - .bits => |bits| bits.backing_type.is_signed(), - else => @trap(), - }; - } - - pub fn is_integral_or_enumeration_type(ty: *Type) bool { - return switch (ty.bb) { - .integer => true, - .bits => true, - .structure => false, - // .integer => |integer| switch (integer.bit_count) { - // 1, 8, 16, 32, 64, 128 => true, - // else => false, - // }, - else => @trap(), - }; - } - - pub fn is_arbitrary_bit_integer(ty: *Type) bool { - return switch (ty.bb) { - .integer => |integer| switch (integer.bit_count) { - 8, 16, 32, 64, 128 => false, - else => true, - }, - .bits => |bits| bits.backing_type.is_arbitrary_bit_integer(), - else => false, - }; - } - - pub fn is_promotable_integer_type_for_abi(ty: *Type) bool { - return switch (ty.bb) { - .integer => |integer| integer.bit_count < 32, - .bits => |bits| bits.backing_type.is_promotable_integer_type_for_abi(), - else => @trap(), - }; - } - - pub fn get_evaluation_kind(ty: *const Type) EvaluationKind { - return switch (ty.bb) { - .structure, .array => .aggregate, - .integer, .bits, .pointer, .enumerator => .scalar, - else => @trap(), - }; - } - - pub fn get_byte_allocation_size(ty: *const Type) u64 { - return lib.align_forward_u64(ty.get_byte_size(), ty.get_byte_alignment()); - } - - pub fn get_bit_size(ty: *const Type) u64 { - return switch (ty.bb) { - .integer => |integer| integer.bit_count, - .structure => |struct_type| struct_type.bit_size, - .bits => |bits| bits.backing_type.get_bit_size(), - .void, .forward_declaration, .function, .noreturn => unreachable, - .array => |*array| array.element_type.get_bit_size() * array.element_count.?, - .pointer => 64, - .enumerator => |enumerator| enumerator.backing_type.get_bit_size(), - .float => @trap(), - .vector => @trap(), - }; - } - - 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), - .structure => |struct_type| struct_type.byte_size, - .bits => |bits| bits.backing_type.get_byte_size(), - .void, .forward_declaration, .function, .noreturn => unreachable, - .array => |*array| array.element_type.get_byte_size() * array.element_count.?, - .pointer => 8, - .enumerator => @trap(), - .float => @trap(), - .vector => @trap(), - }; - } - - pub fn get_bit_alignment(ty: *const Type) u32 { - return switch (ty.bb) { - .integer => |integer| integer.bit_count, - .structure => |struct_type| struct_type.bit_alignment, - .bits => |bits| bits.backing_type.get_bit_alignment(), - .void, .forward_declaration, .function, .noreturn => unreachable, - .array => |*array| array.element_type.get_bit_alignment(), - .pointer => 64, - .enumerator => @trap(), - .float => @trap(), - .vector => @trap(), - }; - } - - pub fn get_byte_alignment(ty: *const Type) u32 { - return switch (ty.bb) { - .integer => |integer| @as(u32, @intCast(@divExact(@max(8, lib.next_power_of_two(integer.bit_count)), 8))), - .structure => |struct_type| struct_type.byte_alignment, - .bits => |bits| bits.backing_type.get_byte_alignment(), - .function => 1, - .void, .forward_declaration, .noreturn => unreachable, - .array => |array| array.element_type.get_byte_alignment(), - .pointer => 8, - .enumerator => |enumerator| enumerator.backing_type.get_byte_alignment(), - .float => @trap(), - .vector => @trap(), - }; - } - - const Array = struct { - buffer: [1024]Type = undefined, - count: u64 = 0, - - const buffer_size = 1024; - - 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: [1024]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 Module = struct { - arena: *Arena, - content: []const u8, - offset: u64, - line_offset: u64, - line_character_offset: u64, - llvm: LLVM, - target: Target, - globals: Variable.Array = .{}, - types: Type.Array = .{}, - values: Value.Array = .{}, - current_function: ?*Variable = null, - debug_tag: c_uint = 0, - void_type: *Type = undefined, - noreturn_type: *Type = undefined, - va_list_type: ?*Type = null, - void_value: *Value = undefined, - unreachable_value: *Value = undefined, - anonymous_pair_type_buffer: [64]u32 = undefined, - pointer_type_buffer: [128]u32 = undefined, - pointer_type_count: u32 = 0, - slice_type_buffer: [128]u32 = undefined, - slice_type_count: u32 = 0, - anonymous_pair_type_count: u32 = 0, - arena_restore_position: u64, - silent: bool, - - fn get_zero_value(module: *Module, ty: *Type) *Value { - const value = module.values.add(); - value.* = switch (ty.bb) { - .bits => |bits| .{ - .llvm = bits.backing_type.llvm.handle.to_integer().get_constant(0, @intFromBool(false)).to_value(), - .lvalue = false, - .dereference_to_assign = false, - .type = ty, - .bb = .bits_initialization, - }, - .structure => str: { - const constant_struct = ty.llvm.handle.get_zero(); - const llvm_value = switch (module.current_function == null) { - true => constant_struct.to_value(), - false => blk: { - const global_variable = module.llvm.handle.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = module.arena.join_string(&.{ "__const.", module.current_function.?.name, if (ty.name) |n| n else "" }), - .initial_value = constant_struct, - .type = ty.llvm.handle, - }); - global_variable.set_unnamed_address(.global); - break :blk global_variable.to_value(); - }, - }; - - break :str .{ - .llvm = llvm_value, - .type = ty, - .bb = .{ - .struct_initialization = .{ - .is_constant = true, - }, - }, - .lvalue = true, - .dereference_to_assign = false, - }; - }, - .integer => .{ - .llvm = ty.llvm.handle.to_integer().get_constant(0, @intFromBool(false)).to_value(), - .lvalue = false, - .dereference_to_assign = false, - .type = ty, - .bb = .{ - .constant_integer = .{ - .value = 0, - .signed = false, - }, - }, - }, - else => @trap(), - }; - return value; - } - - pub fn emit_block(module: *Module, block: *llvm.BasicBlock) void { - const maybe_current_block = module.llvm.builder.get_insert_block(); - - var emit_branch = false; - if (maybe_current_block) |current_block| { - emit_branch = current_block.get_terminator() == null; - } - - if (emit_branch) { - _ = module.llvm.builder.create_branch(block); - } - - if (maybe_current_block != null and maybe_current_block.?.get_parent() != null) { - module.llvm.builder.insert_basic_block_after_insert_block(block); - } else { - module.current_function.?.value.llvm.to_function().append_basic_block(block); - } - - module.llvm.builder.position_at_end(block); - } - - pub fn dump(module: *Module) void { - lib.print_string(module.llvm.handle.to_string()); - } - - pub fn coerce_int_or_pointer_to_int_or_pointer(module: *Module, source: *llvm.Value, source_ty: *Type, destination_ty: *Type) *llvm.Value { - const source_type = source_ty; - var destination_type = destination_ty; - switch (source_type == destination_type) { - true => return source, - false => { - if (source_type.bb == .pointer and destination_type.bb == .pointer) { - @trap(); - } else { - if (source_type.bb == .pointer) { - @trap(); - } - - if (destination_type.bb == .pointer) { - destination_type = module.integer_type(64, false); - } - - if (source_type != destination_type) { - @trap(); - } - - // This is the original destination type - if (destination_ty.bb == .pointer) { - @trap(); - } - - @trap(); - } - }, - } - } - - pub fn create_coerced_load(module: *Module, source: *llvm.Value, source_ty: *Type, destination_type: *Type) *llvm.Value { - var source_pointer = source; - var source_type = source_ty; - - const result = switch (source_type.is_abi_equal(destination_type)) { - true => module.create_load(.{ - .type = destination_type, - .value = source_pointer, - }), - false => res: { - const destination_size = destination_type.get_byte_size(); - if (source_type.bb == .structure) { - const src = module.enter_struct_pointer_for_coerced_access(source_pointer, source_type, destination_size); - source_pointer = src.value; - source_type = src.type; - } - - if (source_type.is_integer_backing() and destination_type.is_integer_backing()) { - const load = module.create_load(.{ - .type = destination_type, - .value = source_pointer, - }); - const result = module.coerce_int_or_pointer_to_int_or_pointer(load, source_type, destination_type); - return result; - } else { - const source_size = source_type.get_byte_size(); - - const is_source_type_scalable = false; - const is_destination_type_scalable = false; - if (!is_source_type_scalable and !is_destination_type_scalable and source_size >= destination_size) { - const load = module.create_load(.{ .type = destination_type, .value = source, .alignment = source_type.get_byte_alignment() }); - break :res load; - } else { - const is_destination_scalable_vector_type = false; - if (is_destination_scalable_vector_type) { - @trap(); - } - - // Coercion through memory - const original_destination_alignment = destination_type.get_byte_alignment(); - const source_alignment = source_type.get_byte_alignment(); - const destination_alignment = @max(original_destination_alignment, source_alignment); - const destination_alloca = module.create_alloca(.{ .type = destination_type, .name = "coerce", .alignment = destination_alignment }); - _ = module.llvm.builder.create_memcpy(destination_alloca, destination_alignment, source, source_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(source_size, @intFromBool(false)).to_value()); - const load = module.create_load(.{ .type = destination_type, .value = destination_alloca, .alignment = destination_alignment }); - return load; - } - } - }, - }; - return result; - } - - pub fn create_coerced_store(module: *Module, source_value: *llvm.Value, source_type: *Type, destination: *llvm.Value, destination_ty: *Type, destination_size: u64, destination_volatile: bool) void { - _ = destination_volatile; - var destination_type = destination_ty; - var destination_pointer = destination; - const source_size = source_type.get_byte_size(); - if (!source_type.is_abi_equal(destination_type)) { - const r = module.enter_struct_pointer_for_coerced_access(destination_pointer, destination_type, source_size); - destination_pointer = r.value; - destination_type = r.type; - } - - const is_scalable = false; // TODO - if (is_scalable or source_size <= destination_size) { - const destination_alignment = destination_type.get_byte_alignment(); - if (source_type.bb == .integer and destination_type.bb == .pointer and source_size == lib.align_forward_u64(destination_size, destination_alignment)) { - @trap(); - } else if (source_type.bb == .structure) { - for (source_type.bb.structure.fields, 0..) |field, field_index| { - // TODO: volatile - const gep = module.llvm.builder.create_struct_gep(source_type.llvm.handle.to_struct(), destination_pointer, @intCast(field_index)); - const field_value = module.llvm.builder.create_extract_value(source_value, @intCast(field_index)); - _ = module.create_store(.{ - .source_value = field_value, - .source_type = field.type, - .destination_value = gep, - .destination_type = field.type, - .alignment = destination_alignment, - }); - } - } else { - _ = module.create_store(.{ - .source_value = source_value, - .source_type = source_type, - .destination_value = destination_pointer, - .destination_type = destination_type, - .alignment = destination_alignment, - }); - } - // TODO: is this valid for pointers too? - } else if (source_type.is_integer_backing()) { - @trap(); - } else { - // Coercion through memory - const original_destination_alignment = destination_type.get_byte_alignment(); - const source_alloca_alignment = @max(original_destination_alignment, source_type.get_byte_alignment()); - const source_alloca = module.create_alloca(.{ .type = source_type, .alignment = source_alloca_alignment, .name = "coerce" }); - _ = module.create_store(.{ - .source_value = source_value, - .destination_value = source_alloca, - .source_type = source_type, - .destination_type = source_type, - .alignment = source_alloca_alignment, - }); - _ = module.llvm.builder.create_memcpy(destination_pointer, original_destination_alignment, source_alloca, source_alloca_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(destination_size, @intFromBool(false)).to_value()); - } - } - - pub fn enter_struct_pointer_for_coerced_access(module: *Module, source_value: *llvm.Value, source_ty: *Type, destination_size: u64) struct { - value: *llvm.Value, - type: *Type, - } { - _ = module; - var source_pointer = source_value; - var source_type = source_ty; - assert(source_type.bb == .structure and source_type.bb.structure.fields.len > 0); - const first_field_type = source_type.bb.structure.fields[0].type; - const first_field_size = first_field_type.get_byte_size(); - const source_size = source_type.get_byte_size(); - - source_pointer = switch (first_field_size < destination_size and first_field_size < source_size) { - true => source_pointer, - false => @trap(), // TODO: make sure `source_type` is also updated here - }; - - return .{ .value = source_pointer, .type = source_type }; - } - - const AttributeBuildOptions = struct { - return_type_abi: Abi.Information, - abi_argument_types: []const *Type, - argument_type_abis: []const Abi.Information, - abi_return_type: *Type, - attributes: Function.Attributes, - call_site: bool, - }; - - pub fn build_attribute_list(module: *Module, options: AttributeBuildOptions) *llvm.Attribute.List { - const return_attributes = llvm.Attribute.Argument{ - .semantic_type = options.return_type_abi.semantic_type.llvm.handle, - .abi_type = options.abi_return_type.llvm.handle, - .dereferenceable_bytes = 0, - .alignment = 0, - .flags = .{ - .no_alias = false, - .non_null = false, - .no_undef = false, - .sign_extend = options.return_type_abi.flags.kind == .extend and options.return_type_abi.flags.sign_extension, - .zero_extend = options.return_type_abi.flags.kind == .extend and !options.return_type_abi.flags.sign_extension, - .in_reg = false, - .no_fp_class = .{}, - .struct_return = false, - .writable = false, - .dead_on_unwind = false, - .in_alloca = false, - .dereferenceable = false, - .dereferenceable_or_null = false, - .nest = false, - .by_value = false, - .by_reference = false, - .no_capture = false, - }, - }; - var argument_attribute_buffer: [128]llvm.Attribute.Argument = undefined; - const argument_attributes = argument_attribute_buffer[0..options.abi_argument_types.len]; - - if (options.return_type_abi.flags.kind == .indirect) { - const abi_index = @intFromBool(options.return_type_abi.flags.sret_after_this); - const argument_attribute = &argument_attributes[abi_index]; - argument_attribute.* = .{ - .semantic_type = options.return_type_abi.semantic_type.llvm.handle, - .abi_type = options.abi_argument_types[abi_index].llvm.handle, - .dereferenceable_bytes = 0, - .alignment = options.return_type_abi.semantic_type.get_byte_alignment(), - .flags = .{ - .no_alias = true, - .non_null = false, - .no_undef = false, - .sign_extend = false, - .zero_extend = false, - .in_reg = options.return_type_abi.flags.in_reg, - .no_fp_class = .{}, - .struct_return = true, - .writable = true, - .dead_on_unwind = true, - .in_alloca = false, - .dereferenceable = false, - .dereferenceable_or_null = false, - .nest = false, - .by_value = false, - .by_reference = false, - .no_capture = false, - }, - }; - } - - for (options.argument_type_abis) |argument_type_abi| { - for (argument_type_abi.abi_start..argument_type_abi.abi_start + argument_type_abi.abi_count) |abi_index| { - const argument_attribute = &argument_attributes[abi_index]; - argument_attribute.* = .{ - .semantic_type = argument_type_abi.semantic_type.llvm.handle, - .abi_type = options.abi_argument_types[abi_index].llvm.handle, - .dereferenceable_bytes = 0, - .alignment = if (argument_type_abi.flags.kind == .indirect) 8 else 0, - .flags = .{ - .no_alias = false, - .non_null = false, - .no_undef = false, - .sign_extend = argument_type_abi.flags.kind == .extend and argument_type_abi.flags.sign_extension, - .zero_extend = argument_type_abi.flags.kind == .extend and !argument_type_abi.flags.sign_extension, - .in_reg = argument_type_abi.flags.in_reg, - .no_fp_class = .{}, - .struct_return = false, - .writable = false, - .dead_on_unwind = false, - .in_alloca = false, - .dereferenceable = false, - .dereferenceable_or_null = false, - .nest = false, - .by_value = argument_type_abi.flags.indirect_by_value, - .by_reference = false, - .no_capture = false, - }, - }; - } - } - - return llvm.Attribute.List.build(module.llvm.context, llvm.Attribute.Function{ - .prefer_vector_width = llvm.String{}, - .stack_protector_buffer_size = llvm.String{}, - .definition_probe_stack = llvm.String{}, - .definition_stack_probe_size = llvm.String{}, - .flags0 = .{ - .noreturn = options.return_type_abi.semantic_type == module.noreturn_type, - .cmse_ns_call = false, - .returns_twice = false, - .cold = false, - .hot = false, - .no_duplicate = false, - .convergent = false, - .no_merge = false, - .will_return = false, - .no_caller_saved_registers = false, - .no_cf_check = false, - .no_callback = false, - .alloc_size = false, // TODO - .uniform_work_group_size = false, - .nounwind = true, - .aarch64_pstate_sm_body = false, - .aarch64_pstate_sm_enabled = false, - .aarch64_pstate_sm_compatible = false, - .aarch64_preserves_za = false, - .aarch64_in_za = false, - .aarch64_out_za = false, - .aarch64_inout_za = false, - .aarch64_preserves_zt0 = false, - .aarch64_in_zt0 = false, - .aarch64_out_zt0 = false, - .aarch64_inout_zt0 = false, - .optimize_for_size = false, - .min_size = false, - .no_red_zone = false, - .indirect_tls_seg_refs = false, - .no_implicit_floats = false, - .sample_profile_suffix_elision_policy = false, - .memory_none = false, - .memory_readonly = false, - .memory_inaccessible_or_arg_memory_only = false, - .memory_arg_memory_only = false, - .strict_fp = false, - .no_inline = options.attributes.inline_behavior == .no_inline, - .always_inline = options.attributes.inline_behavior == .always_inline, - .guard_no_cf = false, - // TODO: branch protection function attributes - // TODO: cpu features - - // CALL-SITE ATTRIBUTES - .call_no_builtins = false, - - // DEFINITION-SITE ATTRIBUTES - .definition_frame_pointer_kind = .none, - .definition_less_precise_fpmad = false, - .definition_null_pointer_is_valid = false, - .definition_no_trapping_fp_math = false, - .definition_no_infs_fp_math = false, - .definition_no_nans_fp_math = false, - .definition_approx_func_fp_math = false, - .definition_unsafe_fp_math = false, - .definition_use_soft_float = false, - .definition_no_signed_zeroes_fp_math = false, - .definition_stack_realignment = false, - .definition_backchain = false, - .definition_split_stack = false, - .definition_speculative_load_hardening = false, - .definition_zero_call_used_registers = .all, - // TODO: denormal builtins - .definition_non_lazy_bind = false, - .definition_cmse_nonsecure_entry = false, - .definition_unwind_table_kind = .none, - }, - .flags1 = .{ - .definition_disable_tail_calls = false, - .definition_stack_protect_strong = false, - .definition_stack_protect = false, - .definition_stack_protect_req = false, - .definition_aarch64_new_za = false, - .definition_aarch64_new_zt0 = false, - .definition_optimize_none = false, - .definition_naked = !options.call_site and options.attributes.naked, - .definition_inline_hint = !options.call_site and options.attributes.inline_behavior == .inline_hint, - }, - }, return_attributes, argument_attributes, options.call_site); - } - - pub fn get_va_list_type(module: *Module) *Type { - if (module.va_list_type) |va_list_type| { - @branchHint(.likely); - return va_list_type; - } else { - @branchHint(.unlikely); - const unsigned_int = module.integer_type(32, false); - const void_pointer = module.get_pointer_type(.{ - .type = module.integer_type(8, false), - }); - const llvm_parameter_types = [_]*llvm.Type{ - unsigned_int.llvm.handle, - unsigned_int.llvm.handle, - void_pointer.llvm.handle, - void_pointer.llvm.handle, - }; - const line = 1; - const bit_alignment = 0; // TODO - const flags = llvm.DI.Flags{}; - const llvm_member_types = [_]*llvm.DI.Type.Derived{ - if (module.llvm.di_builder) |di_builder| di_builder.create_member_type(module.llvm.global_scope, "gp_offset", module.llvm.file, line, 32, bit_alignment, 0, .{}, unsigned_int.llvm.debug) else undefined, - if (module.llvm.di_builder) |di_builder| di_builder.create_member_type(module.llvm.global_scope, "fp_offset", module.llvm.file, line, 32, bit_alignment, 32, .{}, unsigned_int.llvm.debug) else undefined, - if (module.llvm.di_builder) |di_builder| di_builder.create_member_type(module.llvm.global_scope, "overflow_arg_area", module.llvm.file, line, 64, bit_alignment, 64, .{}, void_pointer.llvm.debug) else undefined, - if (module.llvm.di_builder) |di_builder| di_builder.create_member_type(module.llvm.global_scope, "reg_save_area", module.llvm.file, line, 64, bit_alignment, 128, .{}, void_pointer.llvm.debug) else undefined, - }; - const llvm_handle = module.llvm.context.get_struct_type(&llvm_parameter_types); - const bit_size = 24 * 8; - const va_list_name = "va_list"; - const llvm_debug = if (module.llvm.di_builder) |di_builder| di_builder.create_struct_type(module.llvm.global_scope, va_list_name, module.llvm.file, line, bit_size, bit_alignment, flags, &llvm_member_types) else undefined; - - const field_buffer = [_]Field{ - .{ .name = "gp_offset", .type = unsigned_int, .bit_offset = 0, .byte_offset = 0 }, - .{ .name = "fp_offset", .type = unsigned_int, .bit_offset = 32, .byte_offset = 4 }, - .{ .name = "overflow_arg_area", .type = void_pointer, .bit_offset = 64, .byte_offset = 8 }, - .{ .name = "reg_save_area", .type = void_pointer, .bit_offset = 128, .byte_offset = 16 }, - }; - const fields = module.arena.allocate(Field, 4); - @memcpy(fields, &field_buffer); - - const result = module.types.add(.{ - .llvm = .{ - .handle = llvm_handle.to_type(), - .debug = llvm_debug.to_type(), - }, - .name = va_list_name, - .bb = .{ - .structure = .{ - .bit_alignment = 64, - .byte_alignment = 16, - .byte_size = 24, - .bit_size = 24 * 8, - .fields = fields, - .is_slice = false, - }, - }, - }); - - const array = ArrayType{ - .element_count = 1, - .element_type = result, - }; - const ty = module.types.add(.{ - .name = array_type_name(module.arena, array), - .llvm = array_type_llvm(module, array), - .bb = .{ - .array = array, - }, - }); - module.va_list_type = ty; - return ty; - } - } - - const AllocaOptions = struct { - type: *Type, - name: []const u8 = "", - alignment: ?c_uint = null, - }; - - pub fn create_alloca(module: *Module, options: AllocaOptions) *llvm.Value { - const abi_type = switch (options.type.is_arbitrary_bit_integer()) { - true => module.align_integer_type(options.type), - false => options.type, - }; - const alignment: c_uint = if (options.alignment) |a| a else @intCast(abi_type.get_byte_alignment()); - const v = module.llvm.builder.create_alloca(abi_type.llvm.handle, options.name); - v.set_alignment(alignment); - return v; - } - - const IntCast = struct { - source_type: *Type, - destination_type: *Type, - value: *llvm.Value, - }; - - pub fn raw_int_cast(module: *Module, options: IntCast) *llvm.Value { - assert(options.source_type != options.destination_type); - const source_size = options.source_type.get_bit_size(); - const destination_size = options.destination_type.get_bit_size(); - const result = switch (source_size < destination_size) { - true => switch (options.source_type.is_signed()) { - true => module.llvm.builder.create_sign_extend(options.value, options.destination_type.llvm.handle), - false => module.llvm.builder.create_zero_extend(options.value, options.destination_type.llvm.handle), - }, - false => module.llvm.builder.create_truncate(options.value, options.destination_type.llvm.handle), - }; - return result; - } - - const LoadOptions = struct { - type: *Type, - value: *llvm.Value, - alignment: ?c_uint = null, - }; - - pub fn create_load(module: *Module, options: LoadOptions) *llvm.Value { - switch (options.type.bb) { - .void, .noreturn, .forward_declaration => unreachable, - .array => unreachable, - .function => unreachable, - .vector => @trap(), - .bits, .float, .integer, .pointer, .enumerator, .structure => { - const storage_type = switch (options.type.is_arbitrary_bit_integer()) { - true => module.align_integer_type(options.type), - false => options.type, - }; - const alignment: c_uint = if (options.alignment) |a| a else @intCast(storage_type.get_byte_alignment()); - const v = module.llvm.builder.create_load(storage_type.llvm.handle, options.value); - v.set_alignment(alignment); - return switch (storage_type == options.type) { - true => v, - false => module.raw_int_cast(.{ .source_type = storage_type, .destination_type = options.type, .value = v }), - }; - }, - } - } - - const StoreOptions = struct { - source_value: *llvm.Value, - destination_value: *llvm.Value, - source_type: *Type, - destination_type: *Type, - alignment: ?c_uint = null, - }; - - pub fn create_store(module: *Module, options: StoreOptions) *llvm.Value { - const raw_store_type = switch (options.source_type.is_arbitrary_bit_integer()) { - true => module.align_integer_type(options.source_type), - false => options.source_type, - }; - const source_value = switch (raw_store_type == options.source_type) { - true => options.source_value, - false => module.raw_int_cast(.{ .source_type = options.source_type, .destination_type = raw_store_type, .value = options.source_value }), - }; - const alignment = if (options.alignment) |a| a else options.destination_type.get_byte_alignment(); - const v = module.llvm.builder.create_store(source_value, options.destination_value); - v.set_alignment(alignment); - return v; - } - - pub fn current_basic_block(module: *Module) *llvm.BasicBlock { - return module.llvm.builder.get_insert_block() orelse unreachable; - } - - const LLVM = struct { - context: *llvm.Context, - handle: *llvm.Module, - builder: *llvm.Builder, - di_builder: ?*llvm.DI.Builder, - global_scope: *llvm.DI.Scope, - file: *llvm.DI.File, - pointer_type: *llvm.Type, - slice_type: *llvm.Type, - intrinsic_table: IntrinsicTable, - - const IntrinsicTable = struct { - trap: llvm.Intrinsic.Id, - va_start: llvm.Intrinsic.Id, - va_end: llvm.Intrinsic.Id, - va_copy: llvm.Intrinsic.Id, - }; - }; - - pub fn get_anonymous_struct_pair(module: *Module, pair: [2]*Type) *Type { - for (module.anonymous_pair_type_buffer[0..module.anonymous_pair_type_count]) |anonymous_type_index| { - const anonymous_type = &module.types.get()[anonymous_type_index]; - const fields = anonymous_type.bb.structure.fields; - if (fields.len == 2 and pair[0] == fields[0].type and pair[1] == fields[1].type) { - return anonymous_type; - } - } else { - const llvm_pair_members = &.{ pair[0].llvm.handle, pair[1].llvm.handle }; - const llvm_pair = module.llvm.context.get_struct_type(llvm_pair_members); - const byte_alignment = @max(pair[0].get_byte_alignment(), pair[1].get_byte_alignment()); - const byte_size = lib.align_forward_u64(pair[0].get_byte_size() + pair[1].get_byte_size(), byte_alignment); - const fields = module.arena.allocate(Field, 2); - fields[0] = .{ - .bit_offset = 0, - .byte_offset = 0, - .type = pair[0], - .name = "", - }; - fields[1] = .{ - .bit_offset = pair[0].get_bit_size(), // TODO - .byte_offset = pair[0].get_byte_size(), // TODO - .type = pair[1], - .name = "", - }; - const pair_type = module.types.add(.{ - .name = "", - .bb = .{ - .structure = .{ - .bit_alignment = byte_alignment * 8, - .byte_alignment = byte_alignment, - .byte_size = byte_size, - .bit_size = byte_size * 8, - .fields = fields, - .is_slice = false, - }, - }, - .llvm = .{ - .handle = llvm_pair.to_type(), - .debug = undefined, - }, - }); - - module.anonymous_pair_type_buffer[module.anonymous_pair_type_count] = @intCast(pair_type - module.types.get().ptr); - module.anonymous_pair_type_count += 1; - - return pair_type; - } - } - - pub fn get_type(module: *Module, index: u64) *Type { - assert(index < module.types.count); - const result = &module.types.buffer[index]; - return result; - } - - pub fn integer_type(module: *Module, bit_count: u32, sign: bool) *Type { - switch (bit_count) { - 1...64 => { - const index = @as(u64, @intFromBool(sign)) * 64 + bit_count; - 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; - }, - 128 => @trap(), - else => @trap(), - } - } - - pub fn align_integer_type(module: *Module, ty: *Type) *Type { - assert(ty.bb == .integer); - const bit_count = ty.get_bit_size(); - const abi_bit_count: u32 = @intCast(@max(8, lib.next_power_of_two(bit_count))); - if (bit_count != abi_bit_count) { - const is_signed = ty.is_signed(); - return module.integer_type(abi_bit_count, is_signed); - } else { - return ty; - } - } - - pub fn load_arbitrary_integer_type(module: *Module, destination_type: *Type, value: *Value) *Value { - _ = module; - assert(value.type.bb == .pointer); - const appointee_type = value.type.bb.pointer.type; - assert(appointee_type != destination_type); - assert(destination_type.bb == .integer); - assert(appointee_type.bb == .integer); - assert(!appointee_type.is_arbitrary_bit_integer()); - assert(destination_type.is_arbitrary_bit_integer()); - // const bit_count = appointee_type.get_bit_size(); - // const abi_bit_count: u32 = @intCast(@max(8, lib.next_power_of_two(bit_count))); - // const is_signed = appointee_type.is_signed(); - _ = integer_type; - @trap(); - } - - pub fn store_arbitrary_integer_type(module: *Module) void { - _ = module; - @trap(); - } - - pub fn initialize(arena: *Arena, options: Options) *Module { - const arena_restore_position = arena.position; - const context = llvm.Context.create(); - const handle = context.create_module(options.name); - - var maybe_di_builder: ?*llvm.DI.Builder = null; - var global_scope: *llvm.DI.Scope = undefined; - var file: *llvm.DI.File = undefined; - - if (options.has_debug_info) { - const di_builder = handle.create_di_builder(); - maybe_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(); - } - file = di_builder.create_file(file_name, directory); - const compile_unit = di_builder.create_compile_unit(file, options.build_mode.is_optimized()); - global_scope = compile_unit.to_scope(); - } - - var llvm_integer_types: [64]*llvm.Type = undefined; - - for (1..64 + 1) |bit_count| { - llvm_integer_types[bit_count - 1] = context.get_integer_type(@intCast(bit_count)).to_type(); - } - - const llvm_i128 = context.get_integer_type(128).to_type(); - - const default_address_space = 0; - const pointer_type = context.get_pointer_type(default_address_space).to_type(); - const slice_type = context.get_struct_type(&.{ pointer_type, llvm_integer_types[64 - 1] }).to_type(); - - const module = arena.allocate_one(Module); - module.* = .{ - .content = options.content, - .offset = 0, - .line_offset = 0, - .line_character_offset = 0, - .arena = arena, - .target = options.target, - .llvm = .{ - .global_scope = global_scope, - .file = file, - .handle = handle, - .context = context, - .builder = context.create_builder(), - .di_builder = maybe_di_builder, - .pointer_type = pointer_type, - .slice_type = slice_type, - .intrinsic_table = .{ - .trap = llvm.lookup_intrinsic_id("llvm.trap"), - .va_start = llvm.lookup_intrinsic_id("llvm.va_start"), - .va_end = llvm.lookup_intrinsic_id("llvm.va_end"), - .va_copy = llvm.lookup_intrinsic_id("llvm.va_copy"), - }, - }, - .arena_restore_position = arena_restore_position, - .silent = options.silent, - }; - - module.void_type = module.types.add(.{ - .name = "void", - .llvm = .{ - .handle = context.get_void_type(), - .debug = if (maybe_di_builder) |di_builder| di_builder.create_basic_type("void", 0, .void, .{}) else undefined, - }, - .bb = .void, - }); - - for ([2]bool{ false, true }) |sign| { - for (1..64 + 1) |bit_count| { - const name_buffer = [3]u8{ if (sign) 's' else 'u', @intCast(if (bit_count < 10) bit_count % 10 + '0' else bit_count / 10 + '0'), if (bit_count > 9) @intCast(bit_count % 10 + '0') else 0 }; - const name_length = @as(u64, 2) + @intFromBool(bit_count > 9); - - const name = arena.duplicate_string(name_buffer[0..name_length]); - - _ = module.types.add(.{ - .name = name, - .bb = .{ - .integer = .{ - .bit_count = @intCast(bit_count), - .signed = sign, - }, - }, - .llvm = .{ - .handle = llvm_integer_types[bit_count - 1], - .debug = if (maybe_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, - }, - }); - } - } - - for ([2]bool{ false, true }) |sign| { - const name = if (sign) "s128" else "u128"; - _ = module.types.add(.{ - .name = name, - .bb = .{ - .integer = .{ - .bit_count = 128, - .signed = sign, - }, - }, - .llvm = .{ - .handle = llvm_i128, - .debug = if (maybe_di_builder) |di_builder| blk: { - const dwarf_type: llvm.Dwarf.Type = if (sign) .signed else .unsigned; - break :blk di_builder.create_basic_type(name, 128, dwarf_type, .{}); - } else undefined, - }, - }); - } - - module.noreturn_type = module.types.add(.{ - .name = "noreturn", - .llvm = .{ - .handle = context.get_void_type(), - .debug = if (maybe_di_builder) |di_builder| di_builder.create_basic_type("noreturn", 0, .void, .{ .no_return = true }) else undefined, - }, - .bb = .noreturn, - }); - - module.void_value = module.values.add(); - module.void_value.* = .{ - .llvm = undefined, - .bb = .infer_or_ignore, - .type = module.void_type, - .lvalue = false, - .dereference_to_assign = false, - }; - - module.unreachable_value = module.values.add(); - module.unreachable_value.* = .{ - .llvm = undefined, - .bb = .@"unreachable", - .type = module.noreturn_type, - .lvalue = false, - .dereference_to_assign = false, - }; - - return module; - } - - pub fn deinitialize(module: *Module) void { - const arena = module.arena; - const position = module.arena_restore_position; - defer arena.restore(position); - } - - const Pointer = struct { - type: *Type, - alignment: ?u32 = null, - }; - - pub fn get_pointer_type(module: *Module, pointer: Pointer) *Type { - const p = PointerType{ - .type = pointer.type, - .alignment = if (pointer.alignment) |a| a else pointer.type.get_byte_alignment(), - }; - const all_types = module.types.get(); - const pointer_type = for (module.pointer_type_buffer[0..module.pointer_type_count]) |pointer_type_index| { - const ty = &all_types[pointer_type_index]; - const pointer_type = &all_types[pointer_type_index].bb.pointer; - if (pointer_type.type == p.type and pointer_type.alignment == p.alignment) { - break ty; - } - } else blk: { - const pointer_name = if (p.type.name) |name| module.arena.join_string(&.{ "&", name }) else "unknownptr"; - const pointer_type = module.types.add(.{ - .name = pointer_name, - .llvm = .{ - .handle = module.llvm.pointer_type, - .debug = if (module.llvm.di_builder) |di_builder| di_builder.create_pointer_type(p.type.llvm.debug, 64, 64, 0, pointer_name).to_type() else undefined, - }, - .bb = .{ - .pointer = p, - }, - }); - - const index = pointer_type - module.types.get().ptr; - module.pointer_type_buffer[module.pointer_type_count] = @intCast(index); - module.pointer_type_count += 1; - break :blk pointer_type; - }; - - return pointer_type; - } - - const Slice = struct { - type: *Type, - alignment: ?u32 = null, - }; - - pub fn get_slice_type(module: *Module, slice: Slice) *Type { - const alignment = if (slice.alignment) |a| a else slice.type.get_byte_alignment(); - const all_types = module.types.get(); - - for (module.slice_type_buffer[0..module.slice_type_count]) |slice_type_index| { - const ty = &all_types[slice_type_index]; - const struct_type = &all_types[slice_type_index].bb.structure; - assert(struct_type.is_slice); - assert(struct_type.fields.len == 2); - const pointer_type = struct_type.fields[0].type; - if (pointer_type.bb.pointer.type == slice.type and pointer_type.bb.pointer.alignment == alignment) { - return ty; - } - } else { - const pointer_type = module.get_pointer_type(.{ - .type = slice.type, - .alignment = slice.alignment, - }); - const length_type = module.integer_type(64, false); - - const llvm_type = module.llvm.context.get_struct_type(&.{ pointer_type.llvm.handle, length_type.llvm.handle }).to_type(); - - const name = module.arena.join_string(&.{ "[]", slice.type.name.? }); - const debug_type = if (module.llvm.di_builder) |di_builder| blk: { - const bit_size = 64; - const bit_alignment = 64; - const llvm_member_types = [_]*llvm.DI.Type.Derived{ - di_builder.create_member_type(module.llvm.global_scope, "pointer", module.llvm.file, 0, bit_size, bit_alignment, 0, .{}, pointer_type.llvm.debug), - di_builder.create_member_type(module.llvm.global_scope, "length", module.llvm.file, 0, bit_size, bit_alignment, bit_size, .{}, length_type.llvm.debug), - }; - const flags = llvm.DI.Flags{}; - const struct_type = di_builder.create_struct_type(module.llvm.global_scope, name, module.llvm.file, 0, bit_size, bit_alignment, flags, &llvm_member_types).to_type(); - break :blk struct_type; - } else undefined; - - const fields = module.arena.allocate(Field, 2); - fields[0] = .{ - .bit_offset = 0, - .byte_offset = 0, - .type = pointer_type, - .name = "pointer", - }; - fields[1] = .{ - .bit_offset = 64, - .byte_offset = 8, - .type = length_type, - .name = "length", - }; - - const slice_type = module.types.add(.{ - .bb = .{ - .structure = .{ - .fields = fields, - .byte_size = 16, - .bit_size = 128, - .byte_alignment = 8, - .bit_alignment = 64, - .is_slice = true, - }, - }, - .llvm = .{ - .handle = llvm_type, - .debug = debug_type, - }, - .name = name, - }); - const index = slice_type - module.types.get().ptr; - module.slice_type_buffer[module.slice_type_count] = @intCast(index); - module.slice_type_count += 1; - return slice_type; - } - } - - fn negate_value_llvm(noalias module: *Module, value: *Value) *llvm.Value { - _ = module; - return switch (value.is_constant()) { - true => value.llvm.to_constant().negate().to_value(), - false => @trap(), - }; - } - - fn get_line(module: *const Module) u32 { - return @intCast(module.line_offset + 1); - } - - fn get_column(module: *const Module) u32 { - return @intCast(module.offset - module.line_character_offset + 1); - } - - fn report_error(noalias module: *Module) noreturn { - @branchHint(.cold); - _ = module; - lib.os.abort(); - } - - fn skip_space(noalias module: *Module) void { - while (true) { - const offset = module.offset; - while (module.offset < module.content.len and is_space(module.content[module.offset])) { - module.line_offset += @intFromBool(module.content[module.offset] == '\n'); - module.line_character_offset = if (module.content[module.offset] == '\n') module.offset else module.line_character_offset; - module.offset += 1; - } - - if (module.offset + 1 < module.content.len) { - const i = module.offset; - const is_comment = module.content[i] == '/' and module.content[i + 1] == '/'; - if (is_comment) { - while (module.offset < module.content.len and module.content[module.offset] != '\n') { - module.offset += 1; - } - - if (module.offset < module.content.len) { - module.line_offset += 1; - module.line_character_offset = module.offset; - module.offset += 1; - } - } - } - - if (module.offset - offset == 0) { - break; - } - } - } - - pub fn parse_condition_parenthesis(noalias module: *Module) *Value { - module.skip_space(); - - module.expect_character(left_parenthesis); - module.skip_space(); - - const condition = module.parse_condition_raw(); - - module.skip_space(); - module.expect_character(right_parenthesis); - - return condition; - } - - pub fn parse_condition_raw(noalias module: *Module) *Value { - const condition = module.parse_value(.{}); - const boolean_type = module.integer_type(1, false); - if (condition.type != boolean_type) { - const llvm_value = switch (condition.type.bb) { - .integer => module.llvm.builder.create_integer_compare(.ne, condition.llvm, condition.type.llvm.handle.to_integer().get_constant(0, 0).to_value()), - else => @trap(), - }; - - const value = module.values.add(); - value.* = .{ - .llvm = llvm_value, - .type = boolean_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - return value; - } else { - return condition; - } - } - - pub fn parse_type(noalias module: *Module) *Type { - switch (module.content[module.offset]) { - 'a'...'z', 'A'...'Z', '_' => { - const identifier = module.parse_identifier(); - var int_type = identifier.len > 1 and identifier[0] == 's' or identifier[0] == 'u'; - if (int_type) { - for (identifier[1..]) |ch| { - int_type = int_type and is_decimal_ch(ch); - } - } - - if (int_type) { - const signedness = switch (identifier[0]) { - 's' => true, - 'u' => false, - else => unreachable, - }; - const bit_count: u32 = @intCast(lib.parse.integer_decimal(identifier[1..])); - const ty = module.integer_type(bit_count, signedness); - return ty; - } else if (lib.string.equal(identifier, "noreturn")) { - return module.noreturn_type; - } else { - const ty = module.types.find(identifier) orelse @trap(); - return ty; - } - }, - left_bracket => { - module.offset += 1; - - module.skip_space(); - - const is_slice = module.consume_character_if_match(right_bracket); - if (is_slice) { - const element_type = module.parse_type(); - const slice_type = module.get_slice_type(.{ .type = element_type }); - return slice_type; - } else { - const length_expression = module.parse_value(ValueBuilder{ - .type = module.integer_type(64, false), - .kind = .value, - }); - module.skip_space(); - module.expect_character(right_bracket); - - const element_type = module.parse_type(); - - if (length_expression.bb == .infer_or_ignore) { - const array_type = module.types.add(.{ - .name = undefined, - .llvm = undefined, - .bb = .{ - .array = .{ - .element_count = null, - .element_type = element_type, - }, - }, - }); - return array_type; - } else { - const element_count = length_expression.bb.constant_integer.value; - const array = ArrayType{ - .element_count = element_count, - .element_type = element_type, - }; - const array_type = module.types.add(.{ - .name = array_type_name(module.arena, array), - .llvm = array_type_llvm(module, array), - .bb = .{ - .array = array, - }, - }); - return array_type; - } - } - }, - '&' => { - module.offset += 1; - - module.skip_space(); - - const element_type = module.parse_type(); - - return module.get_pointer_type(.{ - .type = element_type, - }); - }, - '#' => return module.parse_type_intrinsic(), - else => @trap(), - } - } - - pub fn parse_identifier(noalias module: *Module) []const u8 { - const start = module.offset; - - if (is_identifier_start_ch(module.content[start])) { - module.offset += 1; - - while (module.offset < module.content.len) { - if (is_identifier_ch(module.content[module.offset])) { - module.offset += 1; - } else { - break; - } - } - } - - if (module.offset - start == 0) { - module.report_error(); - } - - return module.content[start..module.offset]; - } - - fn consume_character_if_match(noalias module: *Module, expected_ch: u8) bool { - var is_ch = false; - if (module.offset < module.content.len) { - const ch = module.content[module.offset]; - is_ch = expected_ch == ch; - module.offset += @intFromBool(is_ch); - } - - return is_ch; - } - - fn expect_or_consume(noalias module: *Module, expected_ch: u8, is_required: bool) bool { - if (is_required) { - module.expect_character(expected_ch); - return true; - } else { - return module.consume_character_if_match(expected_ch); - } - } - - fn parse_decimal(noalias module: *Module) u64 { - var value: u64 = 0; - while (true) { - const ch = module.content[module.offset]; - if (!is_decimal_ch(ch)) { - break; - } - - module.offset += 1; - value = lib.parse.accumulate_decimal(value, ch); - } - - return value; - } - - fn parse_hexadecimal(noalias module: *Module) u64 { - var value: u64 = 0; - while (true) { - const ch = module.content[module.offset]; - if (!lib.is_hex_digit(ch)) { - break; - } - - module.offset += 1; - value = lib.parse.accumulate_hexadecimal(value, ch); - } - - return value; - } - - fn parse_integer_value(module: *Module, sign: bool) u64 { - const start = module.offset; - const integer_start_ch = module.content[start]; - assert(!is_space(integer_start_ch)); - assert(is_decimal_ch(integer_start_ch)); - - const absolute_value: u64 = switch (integer_start_ch) { - '0' => blk: { - module.offset += 1; - - const next_ch = module.content[module.offset]; - break :blk switch (sign) { - false => switch (next_ch) { - 'x' => b: { - module.offset += 1; - break :b module.parse_hexadecimal(); - }, - 'o' => { - // TODO: parse octal - module.report_error(); - }, - 'b' => { - // TODO: parse binary - module.report_error(); - }, - '0'...'9' => { - module.report_error(); - }, - // Zero literal - else => 0, - }, - true => switch (next_ch) { - 'x', 'o', 'b', '0' => module.report_error(), - '1'...'9' => module.parse_decimal(), - else => unreachable, - }, - }; - }, - '1'...'9' => module.parse_decimal(), - else => unreachable, - }; - - return absolute_value; - } - - fn parse_integer(noalias module: *Module, expected_type: *Type, sign: bool) *Value { - const absolute_value = module.parse_integer_value(sign); - - const value: u64 = switch (sign) { - true => @bitCast(-@as(i64, @intCast(absolute_value))), - false => absolute_value, - }; - - const int_type = switch (expected_type.bb) { - .integer => expected_type, - .pointer => module.integer_type(64, false), - else => @trap(), - }; - const llvm_integer_value = int_type.llvm.handle.to_integer().get_constant(value, @intFromBool(int_type.bb.integer.signed)); - const integer_value = module.values.add(); - integer_value.* = .{ - .llvm = llvm_integer_value.to_value(), - .type = int_type, - .bb = .{ - .constant_integer = .{ - .value = absolute_value, - .signed = sign, - }, - }, - .lvalue = false, - .dereference_to_assign = false, - }; - return integer_value; - } - - fn expect_character(noalias module: *Module, expected_ch: u8) void { - if (!module.consume_character_if_match(expected_ch)) { - module.report_error(); - } - } - - fn parse_call(noalias module: *Module, may_be_callable: *Value) *Value { - const child_type = may_be_callable.type.bb.pointer.type; - const pointer_type = switch (child_type.bb) { - .function => may_be_callable.type, - .pointer => |p| switch (p.type.bb) { - .function => child_type, - else => @trap(), - }, - else => @trap(), - }; - const raw_function_type = pointer_type.bb.pointer.type; - const llvm_callable = switch (child_type == raw_function_type) { - true => may_be_callable.llvm, - else => module.create_load(.{ .type = pointer_type, .value = may_be_callable.llvm }), - }; - - const function_type = &raw_function_type.bb.function; - const calling_convention = function_type.calling_convention; - const llvm_calling_convention = calling_convention.to_llvm(); - var llvm_abi_argument_value_buffer: [max_argument_count]*llvm.Value = undefined; - var llvm_abi_argument_type_buffer: [max_argument_count]*llvm.Type = undefined; - var abi_argument_type_buffer: [max_argument_count]*Type = undefined; - var argument_type_abi_buffer: [max_argument_count]Abi.Information = undefined; - - var abi_argument_count: u16 = 0; - var semantic_argument_count: u32 = 0; - const function_semantic_argument_count = function_type.argument_type_abis.len; - - // TODO - const uses_in_alloca = false; - if (uses_in_alloca) { - @trap(); - } - - const llvm_indirect_return_value: *llvm.Value = switch (function_type.return_type_abi.flags.kind) { - .indirect, .in_alloca, .coerce_and_expand => blk: { - // TODO: handle edge cases: - // - virtual function pointer thunk - // - return alloca already exists - - const temporal_alloca = module.create_alloca(.{ .type = function_type.return_type_abi.semantic_type, .name = "tmp" }); - const has_sret = function_type.return_type_abi.flags.kind == .indirect; - if (has_sret) { - llvm_abi_argument_value_buffer[abi_argument_count] = temporal_alloca; - abi_argument_type_buffer[abi_argument_count] = module.void_type; - llvm_abi_argument_type_buffer[abi_argument_count] = module.void_type.llvm.handle; - abi_argument_count += 1; - break :blk temporal_alloca; - } else if (function_type.return_type_abi.flags.kind == .in_alloca) { - @trap(); - } else { - @trap(); - } - }, - else => undefined, - }; - - var available_registers = function_type.available_registers; - - while (true) : (semantic_argument_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_parenthesis)) { - break; - } - - const semantic_argument_index = semantic_argument_count; - const is_named_argument = semantic_argument_index < function_semantic_argument_count; - if (is_named_argument or function_type.is_var_args) { - const expected_semantic_argument_type: ?*Type = if (is_named_argument) function_type.argument_type_abis[semantic_argument_index].semantic_type else null; - const semantic_argument_value = module.parse_value(module, expected_semantic_argument_type, .value); - - _ = module.consume_character_if_match(','); - - const semantic_argument_type = switch (is_named_argument) { - true => function_type.argument_type_abis[semantic_argument_index].semantic_type, - false => if (semantic_argument_value.lvalue and semantic_argument_value.dereference_to_assign) blk: { - const t = semantic_argument_value.type; - assert(t.bb == .pointer); - assert(t.bb.pointer.type.bb == .structure); - break :blk t.bb.pointer.type; - } else semantic_argument_value.type, - }; - const argument_abi = if (is_named_argument) function_type.argument_type_abis[semantic_argument_index] else Abi.SystemV.classify_argument(module, &available_registers, &llvm_abi_argument_type_buffer, &abi_argument_type_buffer, .{ - .type = semantic_argument_type, - .abi_start = abi_argument_count, - .is_named_argument = true, - }); - if (is_named_argument) { - for (llvm_abi_argument_type_buffer[argument_abi.abi_start..][0..argument_abi.abi_count], abi_argument_type_buffer[argument_abi.abi_start..][0..argument_abi.abi_count], function_type.abi_argument_types[argument_abi.abi_start..][0..argument_abi.abi_count]) |*llvm_t, *t, abi_argument_type| { - llvm_t.* = abi_argument_type.llvm.handle; - t.* = abi_argument_type; - } - } - argument_type_abi_buffer[semantic_argument_index] = argument_abi; - - if (argument_abi.padding.type) |padding_type| { - _ = padding_type; - @trap(); - } - assert(abi_argument_count == argument_abi.abi_start); - const argument_abi_kind = argument_abi.flags.kind; - switch (argument_abi_kind) { - .direct, .extend => { - const coerce_to_type = argument_abi.get_coerce_to_type(); - if (coerce_to_type.bb != .structure and semantic_argument_type.is_abi_equal(coerce_to_type) and argument_abi.attributes.direct.offset == 0) { - var v = switch (argument_abi.semantic_type.get_evaluation_kind()) { - .aggregate => @trap(), - else => semantic_argument_value, - }; - _ = &v; - - if (!coerce_to_type.is_abi_equal(v.type)) { - switch (v.type) { - else => @trap(), - } - } - - // TODO: bitcast - // if (argument_abi.abi_start < function_type.argument_type_abis.len and v.type.llvm.handle != abi_arguments - - // TODO: fill types - llvm_abi_argument_value_buffer[abi_argument_count] = v.llvm; - abi_argument_count += 1; - } else { - if (coerce_to_type.bb == .structure and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) { - @trap(); - } - - const evaluation_kind = semantic_argument_type.get_evaluation_kind(); - var src = switch (evaluation_kind) { - .aggregate => semantic_argument_value, - .scalar => { - @trap(); - }, - .complex => @trap(), - }; - - src = switch (argument_abi.attributes.direct.offset > 0) { - true => @trap(), - false => src, - }; - - if (coerce_to_type.bb == .structure and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) { - const source_type_size_is_scalable = false; // TODO - if (source_type_size_is_scalable) { - @trap(); - } else { - const destination_size = coerce_to_type.get_byte_size(); - const source_size = argument_abi.semantic_type.get_byte_size(); - - const alignment = argument_abi.semantic_type.get_byte_alignment(); - const source = switch (source_size < destination_size) { - true => blk: { - const temporal_alloca = module.create_alloca(.{ .type = coerce_to_type, .name = "coerce", .alignment = alignment }); - const destination = temporal_alloca; - const source = semantic_argument_value.llvm; - _ = module.llvm.builder.create_memcpy(destination, alignment, source, alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(semantic_argument_type.get_byte_size(), @intFromBool(false)).to_value()); - break :blk temporal_alloca; - }, - false => src.llvm, - }; - - // TODO: - assert(argument_abi.attributes.direct.offset == 0); - - switch (semantic_argument_value.lvalue) { - true => { - for (coerce_to_type.bb.structure.fields, 0..) |field, field_index| { - const gep = module.llvm.builder.create_struct_gep(coerce_to_type.llvm.handle.to_struct(), source, @intCast(field_index)); - const maybe_undef = false; - if (maybe_undef) { - @trap(); - } - const load = module.create_load(.{ .value = gep, .type = field.type, .alignment = alignment }); - - llvm_abi_argument_value_buffer[abi_argument_count] = load; - abi_argument_count += 1; - } - }, - false => { - for (0..coerce_to_type.bb.structure.fields.len) |field_index| { - const extract_value = module.llvm.builder.create_extract_value(source, @intCast(field_index)); - llvm_abi_argument_value_buffer[abi_argument_count] = extract_value; - abi_argument_count += 1; - } - }, - } - } - } else { - assert(argument_abi.abi_count == 1); - assert(src.type.bb == .pointer); - const source_type = src.type.bb.pointer.type; - assert(source_type == argument_abi.semantic_type); - const destination_type = argument_abi.get_coerce_to_type(); - const load = module.create_coerced_load(src.llvm, source_type, destination_type); - - const is_cmse_ns_call = false; - if (is_cmse_ns_call) { - @trap(); - } - const maybe_undef = false; - if (maybe_undef) { - @trap(); - } - - llvm_abi_argument_value_buffer[abi_argument_count] = load; - abi_argument_count += 1; - } - } - }, - .indirect, .indirect_aliased => indirect: { - if (semantic_argument_type.get_evaluation_kind() == .aggregate) { - const same_address_space = true; - assert(argument_abi.abi_start >= function_type.abi_argument_types.len or same_address_space); - const indirect_alignment = argument_abi.attributes.indirect.alignment; - const address_alignment = semantic_argument_type.get_byte_alignment(); - const get_or_enforce_known_alignment = indirect_alignment; - // llvm::getOrEnforceKnownAlignment(Addr.emitRawPointer(*this), - // Align.getAsAlign(), - // *TD) < Align.getAsAlign()) { - // TODO - const need_copy = switch (address_alignment < indirect_alignment and get_or_enforce_known_alignment < indirect_alignment) { - true => @trap(), - false => b: { - const is_lvalue = !(semantic_argument_value.type.bb == .pointer and semantic_argument_type == semantic_argument_value.type.bb.pointer.type); - if (is_lvalue) { - var need_copy = false; - const is_by_val_or_by_ref = argument_abi.flags.kind == .indirect_aliased or argument_abi.flags.indirect_by_value; - - const lv_alignment = semantic_argument_value.type.get_byte_alignment(); - const arg_type_alignment = argument_abi.semantic_type.get_byte_alignment(); - if (!is_by_val_or_by_ref or lv_alignment < arg_type_alignment) { - need_copy = true; - } - - break :b need_copy; - } else { - break :b false; - } - }, - }; - - if (!need_copy) { - llvm_abi_argument_value_buffer[abi_argument_count] = semantic_argument_value.llvm; - abi_argument_count += 1; - break :indirect; - } - } - - @trap(); - }, - .ignore => unreachable, - else => @trap(), - } - - assert(abi_argument_count == argument_abi.abi_start + argument_abi.abi_count); - } else { - module.report_error(); - } - } - - if (function_type.is_var_args) { - assert(abi_argument_count >= function_type.abi_argument_types.len); - } else { - // TODO - assert(abi_argument_count == function_type.abi_argument_types.len); - } - - const llvm_abi_argument_values = llvm_abi_argument_value_buffer[0..abi_argument_count]; - const llvm_call = module.llvm.builder.create_call(raw_function_type.llvm.handle.to_function(), llvm_callable, llvm_abi_argument_values); - - if (function_type.is_var_args) { - @breakpoint(); - } - - const attribute_list = module.build_attribute_list(.{ - .return_type_abi = function_type.return_type_abi, - .abi_return_type = function_type.abi_return_type, - .abi_argument_types = abi_argument_type_buffer[0..abi_argument_count], - .argument_type_abis = argument_type_abi_buffer[0..semantic_argument_count], - .attributes = .{}, - .call_site = true, - }); - - const call_base = llvm_call.to_instruction().to_call_base(); - call_base.set_calling_convention(llvm_calling_convention); - call_base.set_attributes(attribute_list); - - const return_type_abi = &function_type.return_type_abi; - const return_abi_kind = return_type_abi.flags.kind; - - switch (return_abi_kind) { - .ignore => { - assert(return_type_abi.semantic_type == module.noreturn_type or return_type_abi.semantic_type == module.void_type); - return module.void_value; - }, - .direct, .extend => { - const coerce_to_type = return_type_abi.get_coerce_to_type(); - - if (return_type_abi.semantic_type.is_abi_equal(coerce_to_type) and return_type_abi.attributes.direct.offset == 0) { - const coerce_to_type_kind = coerce_to_type.get_evaluation_kind(); - switch (coerce_to_type_kind) { - .aggregate => {}, - .complex => @trap(), - .scalar => { - const value = module.values.add(); - value.* = .{ - .llvm = llvm_call, - .bb = .instruction, - .type = return_type_abi.semantic_type, - .lvalue = false, - .dereference_to_assign = false, - }; - return value; - }, - } - } - - // TODO: if - const fixed_vector_type = false; - if (fixed_vector_type) { - @trap(); - } - - const coerce_alloca = module.create_alloca(.{ .type = return_type_abi.semantic_type, .name = "coerce" }); - var destination_pointer = switch (return_type_abi.attributes.direct.offset == 0) { - true => coerce_alloca, - false => @trap(), - }; - _ = &destination_pointer; - - if (return_type_abi.semantic_type.bb.structure.fields.len > 0) { - // CreateCoercedStore( - // CI, StorePtr, - // llvm::TypeSize::getFixed(DestSize - RetAI.getDirectOffset()), - // DestIsVolatile); - const source_value = llvm_call; - const source_type = function_type.abi_return_type; - // const source_size = source_type.get_byte_size(); - var destination_type = return_type_abi.semantic_type; - const destination_size = destination_type.get_byte_size(); - // const destination_alignment = destination_type.get_byte_alignment(); - const left_destination_size = destination_size - return_type_abi.attributes.direct.offset; - - const is_destination_volatile = false; // TODO - module.create_coerced_store(source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); - } else { - @trap(); - } - - const value = module.values.add(); - value.* = .{ - .llvm = destination_pointer, - .bb = .instruction, - .type = module.get_pointer_type(.{ .type = return_type_abi.semantic_type }), - .lvalue = true, - .dereference_to_assign = true, - }; - return value; - }, - .indirect => { - const value = module.values.add(); - value.* = .{ - .llvm = llvm_indirect_return_value, - .bb = .instruction, - .type = module.get_pointer_type(.{ .type = return_type_abi.semantic_type }), - .lvalue = true, - .dereference_to_assign = true, - }; - return value; - }, - else => @trap(), - } - } - - fn parse_block(noalias module: *Module) void { - module.skip_space(); - - const current_function_global = module.current_function orelse unreachable; - const current_function = ¤t_function_global.value.bb.function; - const current_function_type = ¤t_function_global.value.type.bb.pointer.type.bb.function; - const block_line = module.get_line(); - const block_column = module.get_column(); - - const current_scope = current_function.current_scope; - defer current_function.current_scope = current_scope; - - if (module.llvm.di_builder) |di_builder| { - const lexical_block = di_builder.create_lexical_block(current_scope, module.llvm.file, block_line, block_column); - current_function.current_scope = lexical_block.to_scope(); - } - - module.expect_character(left_brace); - - const local_offset = current_function.locals.count; - defer current_function.locals.count = local_offset; - - while (true) { - module.skip_space(); - - if (module.offset == module.content.len) { - break; - } - - if (module.content[module.offset] == right_brace) { - break; - } - - var require_semicolon = true; - - const line = module.get_line(); - const column = module.get_column(); - - var statement_debug_location: *llvm.DI.Location = undefined; - if (module.llvm.di_builder) |_| { - const inlined_at: ?*llvm.DI.Metadata = null; // TODO - statement_debug_location = llvm.DI.create_debug_location(module.llvm.context, line, column, current_function.current_scope, inlined_at); - module.llvm.builder.set_current_debug_location(statement_debug_location); - } - - const statement_start_ch = module.content[module.offset]; - if (statement_start_ch == '>') { - module.offset += 1; - - module.skip_space(); - - const local_name = module.parse_identifier(); - - module.skip_space(); - - const has_type = module.consume_character_if_match(':'); - - module.skip_space(); - - const local_type_stated: ?*Type = switch (has_type) { - true => module.parse_type(), - false => null, - }; - - module.skip_space(); - - module.expect_character('='); - - const value = module.parse_value(.{ - .type = local_type_stated, - .kind = .value, - }); - const local_storage = module.values.add(); - const is_inferred = local_type_stated == null; - const is_inferred_pointer = is_inferred and value.dereference_to_assign; - const local_type = switch (is_inferred_pointer) { - true => value.type.bb.pointer.type, - false => if (local_type_stated) |t| t else value.type, - }; - const local_pointer_type = switch (value.dereference_to_assign) { - true => b: { - assert(value.type != local_type); - assert(value.type.bb == .pointer); - break :b value.type; - }, - false => b: { - assert(value.type == local_type); - const pointer_type = module.get_pointer_type(.{ - .type = local_type, - }); - break :b pointer_type; - }, - }; - const local_alignment = local_pointer_type.bb.pointer.alignment; - const llvm_alloca = module.create_alloca(.{ .type = local_type, .name = local_name, .alignment = local_alignment }); - local_storage.* = .{ - .llvm = llvm_alloca, - .type = local_pointer_type, - .bb = .local, - .lvalue = true, - .dereference_to_assign = false, - }; - - if (module.llvm.di_builder) |di_builder| { - module.llvm.builder.set_current_debug_location(statement_debug_location); - 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(current_function.current_scope, local_name, module.llvm.file, line, debug_type, always_preserve, flags, alignment); - const inlined_at: ?*llvm.DI.Metadata = null; // TODO - const debug_location = llvm.DI.create_debug_location(module.llvm.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, module.current_basic_block()); - module.llvm.builder.set_current_debug_location(statement_debug_location); - } - - const alignment: u32 = @intCast(local_type.get_byte_alignment()); - const destination = local_storage.llvm; - const source = value.llvm; - switch (local_type.get_evaluation_kind()) { - .aggregate => { - switch (value.lvalue) { - true => { - _ = module.llvm.builder.create_memcpy(destination, alignment, source, alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(local_type.get_byte_size(), @intFromBool(false)).to_value()); - }, - false => { - _ = module.create_store(.{ .source_value = source, .destination_value = destination, .source_type = local_type, .destination_type = local_type }); - }, - } - }, - else => { - _ = module.create_store(.{ .source_value = source, .destination_value = destination, .source_type = local_type, .destination_type = local_type }); - }, - } - - const local = current_function.locals.add(); - local.* = .{ - .name = local_name, - .value = local_storage, - }; - } else if (statement_start_ch == '#') { - const intrinsic = module.parse_value_intrinsic(null); - switch (intrinsic.type.bb) { - .void, .noreturn => {}, - else => @trap(), - } - } else if (is_identifier_start_ch(statement_start_ch)) { - const statement_start_identifier = module.parse_identifier(); - - if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| { - switch (statement_start_keyword) { - ._ => { - module.skip_space(); - module.expect_character('='); - module.skip_space(); - _ = module.parse_value(.{ - .kind = .value, - }); - }, - .@"return" => { - module.skip_space(); - - const return_type_abi = ¤t_function_type.return_type_abi; - const returns_nothing = module.consume_character_if_match(';'); - if (returns_nothing) { - @trap(); - } else { - // TODO: take ABI into account - const return_value = module.parse_value(.{ - .kind = .value, - .type = return_type_abi.semantic_type, - }); - - if (module.llvm.di_builder) |_| { - module.llvm.builder.set_current_debug_location(statement_debug_location); - } - - // Clang equivalent: CodeGenFunction::EmitReturnStmt - const return_alloca = current_function.return_alloca; - switch (return_type_abi.semantic_type.get_evaluation_kind()) { - .scalar => { - switch (return_type_abi.flags.kind) { - .indirect => { - @trap(); - }, - else => { - assert(!return_value.lvalue); - assert(return_value.type.is_abi_equal(return_type_abi.semantic_type)); - _ = module.create_store(.{ - .source_value = return_value.llvm, - .destination_value = return_alloca, - .source_type = return_type_abi.semantic_type, - .destination_type = return_type_abi.semantic_type, - }); - }, - } - }, - .aggregate => { - // TODO: handcoded code, might be wrong - switch (return_value.lvalue) { - true => { - const abi_alignment = current_function_type.return_type_abi.semantic_type.get_byte_alignment(); - const abi_size = current_function_type.return_type_abi.semantic_type.get_byte_size(); - switch (return_type_abi.flags.kind) { - .indirect => { - _ = module.llvm.builder.create_memcpy(return_alloca, abi_alignment, return_value.llvm, abi_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(abi_size, @intFromBool(false)).to_value()); - }, - else => { - switch (return_type_abi.semantic_type.get_evaluation_kind()) { - .aggregate => { - // TODO: this is 100% wrong, fix - assert(abi_alignment == return_type_abi.semantic_type.get_byte_alignment()); - assert(abi_size == return_type_abi.semantic_type.get_byte_size()); - _ = module.llvm.builder.create_memcpy(return_alloca, abi_alignment, return_value.llvm, abi_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(abi_size, @intFromBool(false)).to_value()); - }, - .scalar => { - const destination_type = current_function_type.return_type_abi.semantic_type; - const source_type = current_function_type.return_type_abi.semantic_type; - assert(return_value.type == source_type); - const rv = switch (return_value.type.bb) { - .pointer => return_value.llvm, - // TODO: this feels hacky - else => switch (return_value.lvalue) { - true => module.create_load(.{ .type = return_value.type, .value = return_value.llvm }), - false => return_value.llvm, - }, - }; - _ = module.create_store(.{ .source_value = rv, .source_type = source_type, .destination_value = return_alloca, .destination_type = destination_type }); - }, - .complex => @trap(), - } - }, - } - }, - false => { - assert(!return_value.lvalue); - assert(return_value.type.is_abi_equal(return_type_abi.semantic_type)); - _ = module.create_store(.{ - .source_value = return_value.llvm, - .destination_value = return_alloca, - .source_type = return_type_abi.semantic_type, - .destination_type = return_type_abi.semantic_type, - }); - }, - } - }, - .complex => @trap(), - } - } - - _ = module.llvm.builder.create_branch(current_function.return_block); - _ = module.llvm.builder.clear_insertion_position(); - }, - .@"if" => { - const taken_block = module.llvm.context.create_basic_block("if.true", current_function_global.value.llvm.to_function()); - const not_taken_block = module.llvm.context.create_basic_block("if.false", current_function_global.value.llvm.to_function()); - const exit_block = module.llvm.context.create_basic_block("if.end", null); - - const condition = module.parse_condition_parenthesis(); - - _ = module.llvm.builder.create_conditional_branch(condition.llvm, taken_block, not_taken_block); - module.llvm.builder.position_at_end(taken_block); - - const previous_exit_block = current_function.exit_block; - defer current_function.exit_block = previous_exit_block; - - current_function.exit_block = exit_block; - - module.parse_block(); - - const if_final_block = module.llvm.builder.get_insert_block(); - - module.skip_space(); - - var is_else = false; - if (is_identifier_start_ch(module.content[module.offset])) { - const identifier = module.parse_identifier(); - is_else = lib.string.equal(identifier, "else"); - if (!is_else) { - module.offset -= identifier.len; - } - } - - var is_second_block_terminated = false; - module.llvm.builder.position_at_end(not_taken_block); - if (is_else) { - current_function.exit_block = exit_block; - module.parse_block(); - is_second_block_terminated = module.llvm.builder.get_insert_block() == null; - } else { - if (if_final_block) |final_block| { - const current_insert_block = module.llvm.builder.get_insert_block(); - defer if (current_insert_block) |block| { - module.llvm.builder.position_at_end(block); - }; - module.llvm.builder.position_at_end(final_block); - _ = module.llvm.builder.create_branch(not_taken_block); - module.llvm.builder.clear_insertion_position(); - } - - assert(exit_block.to_value().use_empty()); - not_taken_block.to_value().set_name("if.end"); - assert(exit_block.get_parent() == null); - exit_block.delete(); - } - - if (!(if_final_block == null and is_second_block_terminated)) { - if (if_final_block != null) { - // @trap(); - } - - if (!is_second_block_terminated) { - // if (is_else) { - // @trap(); - // } else {} - } - } else { - assert(exit_block.get_parent() == null); - // TODO: - // if call `exit_block.erase_from_paren()`, it crashes, investigate - exit_block.delete(); - } - - require_semicolon = false; - }, - .@"while" => { - const loop_entry_block = module.llvm.context.create_basic_block("while.entry", current_function_global.value.llvm.to_function()); - _ = module.llvm.builder.create_branch(loop_entry_block); - module.llvm.builder.position_at_end(loop_entry_block); - - const condition = module.parse_condition_parenthesis(); - - const loop_body_block = module.llvm.context.create_basic_block("while.body", current_function_global.value.llvm.to_function()); - const loop_end_block = module.llvm.context.create_basic_block("while.end", current_function_global.value.llvm.to_function()); - _ = module.llvm.builder.create_conditional_branch(condition.llvm, loop_body_block, loop_end_block); - module.llvm.builder.position_at_end(loop_body_block); - module.skip_space(); - module.parse_block(); - - if (module.llvm.builder.get_insert_block() != null) { - _ = module.llvm.builder.create_branch(loop_entry_block); - } - - if (loop_body_block.to_value().use_empty()) { - @trap(); - } - - if (loop_end_block.to_value().use_empty()) { - @trap(); - } - - module.llvm.builder.position_at_end(loop_end_block); - - require_semicolon = false; - }, - } - } else { - module.offset -= statement_start_identifier.len; - - const v = module.parse_value(.{ - .kind = .maybe_pointer, - }); - - module.skip_space(); - - if (module.consume_character_if_match(';')) { - const is_noreturn = v.type.bb == .noreturn; - const is_valid = v.type.bb == .void or is_noreturn; - if (!is_valid) { - module.report_error(); - } - - if (is_noreturn) { - _ = module.llvm.builder.create_unreachable(); - } - - require_semicolon = false; - } else { - const left = v; - if (left.type.bb != .pointer) { - module.report_error(); - } - const store_alignment = left.type.bb.pointer.alignment; - const store_type = left.type.bb.pointer.type; - - const AssignmentOperator = enum { - plain, - pointer_add, - pointer_sub, - integer_add, - integer_sub, - integer_mul, - integer_udiv, - integer_sdiv, - integer_urem, - integer_srem, - shl, - ashr, - lshr, - @"and", - @"or", - xor, - }; - - const assignment_operator: AssignmentOperator = switch (module.content[module.offset]) { - '=' => .plain, - '+' => switch (module.content[module.offset + 1]) { - '=' => switch (store_type.bb) { - .integer => .integer_add, - .pointer => .pointer_add, - else => @trap(), - }, - else => @trap(), - }, - '-' => switch (module.content[module.offset + 1]) { - '=' => switch (store_type.bb) { - .integer => .integer_sub, - .pointer => .pointer_sub, - else => @trap(), - }, - else => @trap(), - }, - '*' => switch (module.content[module.offset + 1]) { - '=' => switch (store_type.bb) { - .integer => .integer_mul, - else => @trap(), - }, - else => @trap(), - }, - '/' => switch (module.content[module.offset + 1]) { - '=' => switch (store_type.bb) { - .integer => |integer| switch (integer.signed) { - true => .integer_sdiv, - false => .integer_udiv, - }, - else => @trap(), - }, - else => @trap(), - }, - '%' => switch (module.content[module.offset + 1]) { - '=' => switch (store_type.bb) { - .integer => |integer| switch (integer.signed) { - true => .integer_srem, - false => .integer_urem, - }, - else => @trap(), - }, - else => @trap(), - }, - '&' => switch (module.content[module.offset + 1]) { - '=' => switch (store_type.bb) { - .integer => .@"and", - else => @trap(), - }, - else => @trap(), - }, - '|' => switch (module.content[module.offset + 1]) { - '=' => switch (store_type.bb) { - .integer => .@"or", - else => @trap(), - }, - else => @trap(), - }, - '^' => switch (module.content[module.offset + 1]) { - '=' => switch (store_type.bb) { - .integer => .xor, - else => @trap(), - }, - else => @trap(), - }, - '<' => switch (module.content[module.offset + 1]) { - '<' => switch (module.content[module.offset + 2]) { - '=' => switch (store_type.bb) { - .integer => .shl, - else => @trap(), - }, - else => @trap(), - }, - else => @trap(), - }, - '>' => switch (module.content[module.offset + 1]) { - '>' => switch (module.content[module.offset + 2]) { - '=' => switch (store_type.bb) { - .integer => |integer| switch (integer.signed) { - true => .ashr, - false => .lshr, - }, - else => @trap(), - }, - else => @trap(), - }, - else => @trap(), - }, - else => @trap(), - }; - - module.offset += switch (assignment_operator) { - .plain, - => 1, - .pointer_sub, - .pointer_add, - .integer_sub, - .integer_add, - .integer_mul, - .integer_udiv, - .integer_sdiv, - .integer_urem, - .integer_srem, - .@"and", - .@"or", - .xor, - => 2, - .shl, - .ashr, - .lshr, - => 3, - }; - - const right_side = module.parse_value(.{ - .type = store_type, - .kind = .value, - }); - const right_llvm = right_side.llvm; - - module.skip_space(); - - const right = switch (assignment_operator) { - .plain => right_side, - else => |op| b: { - const left_load = module.create_load(.{ .type = store_type, .value = left.llvm }); - const result = module.values.add(); - const llvm_value = switch (op) { - .plain => unreachable, - .pointer_add => switch (right_side.type.bb) { - .integer => module.llvm.builder.create_gep(.{ - .type = store_type.bb.pointer.type.llvm.handle, - .aggregate = left_load, - .indices = &.{right_llvm}, - }), - else => @trap(), - }, - .pointer_sub => switch (right_side.type.bb) { - .integer => module.llvm.builder.create_gep(.{ - .type = store_type.bb.pointer.type.llvm.handle, - .aggregate = left_load, - .indices = &.{module.negate_value_llvm(right_side)}, - }), - else => @trap(), - }, - .integer_add => module.llvm.builder.create_add(left_load, right_llvm), - .integer_sub => module.llvm.builder.create_sub(left_load, right_llvm), - .integer_mul => module.llvm.builder.create_mul(left_load, right_llvm), - .integer_udiv => module.llvm.builder.create_udiv(left_load, right_llvm), - .integer_sdiv => module.llvm.builder.create_udiv(left_load, right_llvm), - .integer_urem => module.llvm.builder.create_urem(left_load, right_llvm), - .integer_srem => module.llvm.builder.create_urem(left_load, right_llvm), - .lshr => module.llvm.builder.create_lshr(left_load, right_llvm), - .ashr => module.llvm.builder.create_ashr(left_load, right_llvm), - .shl => module.llvm.builder.create_shl(left_load, right_llvm), - .@"and" => module.llvm.builder.create_and(left_load, right_llvm), - .@"or" => module.llvm.builder.create_or(left_load, right_llvm), - .xor => module.llvm.builder.create_xor(left_load, right_llvm), - }; - result.* = .{ - .llvm = llvm_value, - .type = store_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b result; - }, - }; - - switch (store_type.get_evaluation_kind()) { - .aggregate => { - if (store_type != right.type) { - module.report_error(); - } - assert(right.lvalue); - _ = module.llvm.builder.create_memcpy(left.llvm, store_alignment, right.llvm, right.type.get_byte_alignment(), module.integer_type(64, false).llvm.handle.to_integer().get_constant(right.type.get_byte_size(), @intFromBool(false)).to_value()); - }, - else => _ = module.create_store(.{ .source_value = right.llvm, .destination_value = left.llvm, .source_type = store_type, .destination_type = store_type, .alignment = store_alignment }), - } - } - } - } else { - module.report_error(); - } - - module.skip_space(); - - if (require_semicolon) { - module.expect_character(';'); - } - } - - module.expect_character(right_brace); - } - - const ExpressionState = enum { - none, - integer_add, - integer_sub, - integer_mul, - integer_udiv, - integer_sdiv, - integer_urem, - integer_srem, - shl, - ashr, - lshr, - @"and", - @"or", - xor, - integer_compare_equal, - integer_compare_not_equal, - integer_compare_unsigned_greater_than, - integer_compare_unsigned_greater_equal, - integer_compare_unsigned_less_than, - integer_compare_unsigned_less_equal, - integer_compare_signed_greater_than, - integer_compare_signed_greater_equal, - integer_compare_signed_less_than, - integer_compare_signed_less_equal, - pointer_add, - - pub fn to_int_predicate(expression_state: ExpressionState) llvm.IntPredicate { - return switch (expression_state) { - .integer_compare_not_equal => .ne, - .integer_compare_equal => .eq, - - .integer_compare_unsigned_greater_than => .ugt, - .integer_compare_unsigned_greater_equal => .uge, - .integer_compare_unsigned_less_than => .ult, - .integer_compare_unsigned_less_equal => .ule, - - .integer_compare_signed_greater_than => .sgt, - .integer_compare_signed_greater_equal => .sge, - .integer_compare_signed_less_than => .slt, - .integer_compare_signed_less_equal => .sle, - else => unreachable, - }; - } - }; - - const ValueKind = enum { - pointer, - value, - maybe_pointer, - }; - - const Precedence = enum { - none, - assignment, - @"or", - @"and", - comparison, - bitwise, - shifting, - add_like, - div_like, - prefix, - aggregate_initialization, - postfix, - - pub fn increment(precedence: Precedence) Precedence { - return @enumFromInt(@intFromEnum(precedence) + 1); - } - }; - - const ExpressionOperator = enum {}; - - const Operator = enum { - none, - }; - - const ComparisonOperator = enum { - compare_equal, - compare_not_equal, - compare_less_than, - compare_less_equal, - compare_greater_than, - compare_greater_equal, - }; - - const AssignmentOp = enum(u8) { - @"=" = '=', - @"+=" = '+', - @"-=" = '-', - @"*=" = '*', - @"/=" = '/', - @"%=" = '%', - @"&=" = '&', - @"|=" = '|', - @"^=" = '^', - @"<<=" = '<', - @">>=" = '>', - }; - - const ArithmeticBinaryOperator = enum(u8) { - @"+" = '+', - @"-" = '-', - @"*" = '*', - @"/" = '/', - @"%" = '%', - @"&" = '&', - @"|" = '|', - @"^" = '^', - @"<<" = '<', - @">>" = '>', - }; - - const Rule = struct { - before: ?*const Rule.Function, - after: ?*const Rule.Function, - precedence: Precedence, - - const Function = fn (noalias module: *Module, value_builder: ValueBuilder) *Value; - }; - - fn rule_before_identifier(noalias module: *Module, value_builder: ValueBuilder) *Value { - const identifier = value_builder.token.identifier; - const current_function = module.current_function orelse module.report_error(); - const variable = if (current_function.value.bb.function.locals.find(identifier)) |local| local else if (current_function.value.bb.function.arguments.find(identifier)) |argument| argument else if (module.globals.find(identifier)) |global| global else module.report_error(); - const value = variable.value; - assert(value.type.bb == .pointer); - - return switch (value_builder.kind) { - .value => if (value_builder.type) |expected_type| switch (expected_type == value.type) { - true => value, - false => if (value.type.bb.pointer.type == expected_type) blk: { - const pointer = value.type.bb.pointer; - const element_type = pointer.type; - const element_alignment = pointer.alignment; - - switch (element_type.get_evaluation_kind()) { - .scalar => { - const load = module.create_load(.{ - .type = element_type, - .value = value.llvm, - .alignment = element_alignment, - }); - const result = module.values.add(); - result.* = .{ - .llvm = load, - .bb = .instruction, - .type = element_type, - .lvalue = false, - .dereference_to_assign = false, - }; - break :blk result; - }, - .aggregate => @trap(), - .complex => @trap(), - } - } else @trap(), - } else switch (value_builder.kind) { - .value => blk: { - const pointer = value.type.bb.pointer; - const element_type = pointer.type; - const element_alignment = pointer.alignment; - - switch (element_type.get_evaluation_kind()) { - .scalar => { - const load = module.create_load(.{ - .type = element_type, - .value = value.llvm, - .alignment = element_alignment, - }); - const result = module.values.add(); - result.* = .{ - .llvm = load, - .bb = .instruction, - .type = element_type, - .lvalue = false, - .dereference_to_assign = false, - }; - break :blk result; - }, - .aggregate => @trap(), - .complex => @trap(), - } - }, - .maybe_pointer, .pointer => value, - }, - .pointer, .maybe_pointer => if (value_builder.type) |expected_type| switch (expected_type == value.type) { - true => b: { - assert(value.lvalue); - break :b value; - }, - false => @trap(), - } else value, - }; - } - - fn rule_before_value_keyword(noalias module: *Module, value_builder: ValueBuilder) *Value { - _ = value_builder; - _ = module; - @trap(); - } - - fn rule_before_value_intrinsic(noalias module: *Module, value_builder: ValueBuilder) *Value { - const expected_type = value_builder.type; - const value_intrinsic = value_builder.token.value_intrinsic; - const has_parenthesis = switch (value_intrinsic) { - .byte_size, - .cast, - .cast_to, - .extend, - .integer_max, - .int_from_enum, - .int_from_pointer, - .pointer_cast, - .select, - .trap, - .truncate, - .va_start, - .va_end, - .va_copy, - .va_arg, - => true, - }; - - if (has_parenthesis) { - module.expect_character(left_parenthesis); - } - - const value = switch (value_intrinsic) { - .extend => blk: { - const source_value = module.parse_value(.{}); - const source_type = source_value.type; - const destination_type = expected_type orelse module.report_error(); - if (source_type.get_bit_size() > destination_type.get_bit_size()) { - module.report_error(); - } else if (source_type.get_bit_size() == destination_type.get_bit_size() and source_type.is_signed() == destination_type.is_signed()) { - module.report_error(); - } - - const extension_instruction = switch (source_type.bb.integer.signed) { - true => module.llvm.builder.create_sign_extend(source_value.llvm, destination_type.llvm.handle), - false => module.llvm.builder.create_zero_extend(source_value.llvm, destination_type.llvm.handle), - }; - const value = module.values.add(); - value.* = .{ - .llvm = extension_instruction, - .type = destination_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :blk value; - }, - .integer_max => blk: { - const ty = module.parse_type(); - if (ty.bb != .integer) { - module.report_error(); - } - const bit_count = ty.bb.integer.bit_count; - const max_value = if (bit_count == 64) ~@as(u64, 0) else (@as(u64, 1) << @intCast(bit_count - @intFromBool(ty.bb.integer.signed))) - 1; - const expected_ty = expected_type orelse ty; - if (ty.get_bit_size() > expected_ty.get_bit_size()) { - module.report_error(); - } - const constant_integer = expected_ty.llvm.handle.to_integer().get_constant(max_value, @intFromBool(false)); - const value = module.values.add(); - value.* = .{ - .llvm = constant_integer.to_value(), - .type = expected_ty, - .bb = .{ - .constant_integer = .{ - .value = max_value, - .signed = false, - }, - }, - .lvalue = false, - .dereference_to_assign = false, - }; - break :blk value; - }, - else => @trap(), - }; - - if (has_parenthesis) { - module.skip_space(); - module.expect_character(right_parenthesis); - } - - return value; - } - - const Unary = enum { - @"-", - @"+", - @"&", - }; - - fn rule_before_unary(noalias module: *Module, value_builder: ValueBuilder) *Value { - assert(value_builder.left == null); - const unary_token = value_builder.token; - const unary_expression: Unary = switch (unary_token) { - .none => unreachable, - .@"-" => .@"-", - .@"&" => .@"&", - else => |t| @panic(@tagName(t)), - }; - const right = module.parse_precedence(value_builder.with_precedence(.prefix).with_token(.none).with_kind(if (unary_expression == .@"&") .pointer else value_builder.kind)); - return switch (unary_expression) { - .@"+" => @trap(), - .@"-" => b: { - const value = module.values.add(); - value.* = .{ - .llvm = module.negate_value_llvm(right), - .bb = .instruction, - .type = right.type, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b value; - }, - .@"&" => right, - }; - } - - const BinaryOperationKind = enum { - integer_add, - integer_sub, - integer_mul, - integer_udiv, - integer_sdiv, - integer_urem, - integer_srem, - integer_and, - integer_or, - integer_xor, - integer_shl, - integer_ashr, - integer_lshr, - integer_compare_equal, - integer_compare_not_equal, - integer_compare_unsigned_less_than, - integer_compare_unsigned_less_equal, - integer_compare_unsigned_greater_than, - integer_compare_unsigned_greater_equal, - integer_compare_signed_less_than, - integer_compare_signed_less_equal, - integer_compare_signed_greater_than, - integer_compare_signed_greater_equal, - }; - - fn rule_before_parenthesis(noalias module: *Module, value_builder: ValueBuilder) *Value { - _ = module; - _ = value_builder; - @trap(); - } - - fn rule_after_call(noalias module: *Module, value_builder: ValueBuilder) *Value { - const may_be_callable = value_builder.left orelse module.report_error(); - const child_type = may_be_callable.type.bb.pointer.type; - const pointer_type = switch (child_type.bb) { - .function => may_be_callable.type, - .pointer => |p| switch (p.type.bb) { - .function => child_type, - else => @trap(), - }, - else => @trap(), - }; - const raw_function_type = pointer_type.bb.pointer.type; - const llvm_callable = switch (child_type == raw_function_type) { - true => may_be_callable.llvm, - else => module.create_load(.{ .type = pointer_type, .value = may_be_callable.llvm }), - }; - - const function_type = &raw_function_type.bb.function; - const calling_convention = function_type.calling_convention; - const llvm_calling_convention = calling_convention.to_llvm(); - var llvm_abi_argument_value_buffer: [max_argument_count]*llvm.Value = undefined; - var llvm_abi_argument_type_buffer: [max_argument_count]*llvm.Type = undefined; - var abi_argument_type_buffer: [max_argument_count]*Type = undefined; - var argument_type_abi_buffer: [max_argument_count]Abi.Information = undefined; - - var abi_argument_count: u16 = 0; - var semantic_argument_count: u32 = 0; - const function_semantic_argument_count = function_type.argument_type_abis.len; - - // TODO - const uses_in_alloca = false; - if (uses_in_alloca) { - @trap(); - } - - const llvm_indirect_return_value: *llvm.Value = switch (function_type.return_type_abi.flags.kind) { - .indirect, .in_alloca, .coerce_and_expand => blk: { - // TODO: handle edge cases: - // - virtual function pointer thunk - // - return alloca already exists - - const temporal_alloca = module.create_alloca(.{ .type = function_type.return_type_abi.semantic_type, .name = "tmp" }); - const has_sret = function_type.return_type_abi.flags.kind == .indirect; - if (has_sret) { - llvm_abi_argument_value_buffer[abi_argument_count] = temporal_alloca; - abi_argument_type_buffer[abi_argument_count] = module.void_type; - llvm_abi_argument_type_buffer[abi_argument_count] = module.void_type.llvm.handle; - abi_argument_count += 1; - break :blk temporal_alloca; - } else if (function_type.return_type_abi.flags.kind == .in_alloca) { - @trap(); - } else { - @trap(); - } - }, - else => undefined, - }; - - var available_registers = function_type.available_registers; - - while (true) : (semantic_argument_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_parenthesis)) { - break; - } - - const semantic_argument_index = semantic_argument_count; - const is_named_argument = semantic_argument_index < function_semantic_argument_count; - if (is_named_argument or function_type.is_var_args) { - const expected_semantic_argument_type: ?*Type = if (is_named_argument) function_type.argument_type_abis[semantic_argument_index].semantic_type else null; - const semantic_argument_value = module.parse_value(.{ - .type = expected_semantic_argument_type, - }); - - _ = module.consume_character_if_match(','); - - const semantic_argument_type = switch (is_named_argument) { - true => function_type.argument_type_abis[semantic_argument_index].semantic_type, - false => if (semantic_argument_value.lvalue and semantic_argument_value.dereference_to_assign) blk: { - const t = semantic_argument_value.type; - assert(t.bb == .pointer); - assert(t.bb.pointer.type.bb == .structure); - break :blk t.bb.pointer.type; - } else semantic_argument_value.type, - }; - const argument_abi = if (is_named_argument) function_type.argument_type_abis[semantic_argument_index] else Abi.SystemV.classify_argument(module, &available_registers, &llvm_abi_argument_type_buffer, &abi_argument_type_buffer, .{ - .type = semantic_argument_type, - .abi_start = abi_argument_count, - .is_named_argument = true, - }); - if (is_named_argument) { - for (llvm_abi_argument_type_buffer[argument_abi.abi_start..][0..argument_abi.abi_count], abi_argument_type_buffer[argument_abi.abi_start..][0..argument_abi.abi_count], function_type.abi_argument_types[argument_abi.abi_start..][0..argument_abi.abi_count]) |*llvm_t, *t, abi_argument_type| { - llvm_t.* = abi_argument_type.llvm.handle; - t.* = abi_argument_type; - } - } - argument_type_abi_buffer[semantic_argument_index] = argument_abi; - - if (argument_abi.padding.type) |padding_type| { - _ = padding_type; - @trap(); - } - assert(abi_argument_count == argument_abi.abi_start); - const argument_abi_kind = argument_abi.flags.kind; - switch (argument_abi_kind) { - .direct, .extend => { - const coerce_to_type = argument_abi.get_coerce_to_type(); - if (coerce_to_type.bb != .structure and semantic_argument_type.is_abi_equal(coerce_to_type) and argument_abi.attributes.direct.offset == 0) { - var v = switch (argument_abi.semantic_type.get_evaluation_kind()) { - .aggregate => @trap(), - else => semantic_argument_value, - }; - _ = &v; - - if (!coerce_to_type.is_abi_equal(v.type)) { - switch (v.type) { - else => @trap(), - } - } - - // TODO: bitcast - // if (argument_abi.abi_start < function_type.argument_type_abis.len and v.type.llvm.handle != abi_arguments - - // TODO: fill types - llvm_abi_argument_value_buffer[abi_argument_count] = v.llvm; - abi_argument_count += 1; - } else { - if (coerce_to_type.bb == .structure and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) { - @trap(); - } - - const evaluation_kind = semantic_argument_type.get_evaluation_kind(); - var src = switch (evaluation_kind) { - .aggregate => semantic_argument_value, - .scalar => { - @trap(); - }, - .complex => @trap(), - }; - - src = switch (argument_abi.attributes.direct.offset > 0) { - true => @trap(), - false => src, - }; - - if (coerce_to_type.bb == .structure and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) { - const source_type_size_is_scalable = false; // TODO - if (source_type_size_is_scalable) { - @trap(); - } else { - const destination_size = coerce_to_type.get_byte_size(); - const source_size = argument_abi.semantic_type.get_byte_size(); - - const alignment = argument_abi.semantic_type.get_byte_alignment(); - const source = switch (source_size < destination_size) { - true => blk: { - const temporal_alloca = module.create_alloca(.{ .type = coerce_to_type, .name = "coerce", .alignment = alignment }); - const destination = temporal_alloca; - const source = semantic_argument_value.llvm; - _ = module.llvm.builder.create_memcpy(destination, alignment, source, alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(semantic_argument_type.get_byte_size(), @intFromBool(false)).to_value()); - break :blk temporal_alloca; - }, - false => src.llvm, - }; - - // TODO: - assert(argument_abi.attributes.direct.offset == 0); - - switch (semantic_argument_value.lvalue) { - true => { - for (coerce_to_type.bb.structure.fields, 0..) |field, field_index| { - const gep = module.llvm.builder.create_struct_gep(coerce_to_type.llvm.handle.to_struct(), source, @intCast(field_index)); - const maybe_undef = false; - if (maybe_undef) { - @trap(); - } - const load = module.create_load(.{ .value = gep, .type = field.type, .alignment = alignment }); - - llvm_abi_argument_value_buffer[abi_argument_count] = load; - abi_argument_count += 1; - } - }, - false => { - for (0..coerce_to_type.bb.structure.fields.len) |field_index| { - const extract_value = module.llvm.builder.create_extract_value(source, @intCast(field_index)); - llvm_abi_argument_value_buffer[abi_argument_count] = extract_value; - abi_argument_count += 1; - } - }, - } - } - } else { - assert(argument_abi.abi_count == 1); - assert(src.type.bb == .pointer); - const source_type = src.type.bb.pointer.type; - assert(source_type == argument_abi.semantic_type); - const destination_type = argument_abi.get_coerce_to_type(); - const load = module.create_coerced_load(src.llvm, source_type, destination_type); - - const is_cmse_ns_call = false; - if (is_cmse_ns_call) { - @trap(); - } - const maybe_undef = false; - if (maybe_undef) { - @trap(); - } - - llvm_abi_argument_value_buffer[abi_argument_count] = load; - abi_argument_count += 1; - } - } - }, - .indirect, .indirect_aliased => indirect: { - if (semantic_argument_type.get_evaluation_kind() == .aggregate) { - const same_address_space = true; - assert(argument_abi.abi_start >= function_type.abi_argument_types.len or same_address_space); - const indirect_alignment = argument_abi.attributes.indirect.alignment; - const address_alignment = semantic_argument_type.get_byte_alignment(); - const get_or_enforce_known_alignment = indirect_alignment; - // llvm::getOrEnforceKnownAlignment(Addr.emitRawPointer(*this), - // Align.getAsAlign(), - // *TD) < Align.getAsAlign()) { - // TODO - const need_copy = switch (address_alignment < indirect_alignment and get_or_enforce_known_alignment < indirect_alignment) { - true => @trap(), - false => b: { - const is_lvalue = !(semantic_argument_value.type.bb == .pointer and semantic_argument_type == semantic_argument_value.type.bb.pointer.type); - if (is_lvalue) { - var need_copy = false; - const is_by_val_or_by_ref = argument_abi.flags.kind == .indirect_aliased or argument_abi.flags.indirect_by_value; - - const lv_alignment = semantic_argument_value.type.get_byte_alignment(); - const arg_type_alignment = argument_abi.semantic_type.get_byte_alignment(); - if (!is_by_val_or_by_ref or lv_alignment < arg_type_alignment) { - need_copy = true; - } - - break :b need_copy; - } else { - break :b false; - } - }, - }; - - if (!need_copy) { - llvm_abi_argument_value_buffer[abi_argument_count] = semantic_argument_value.llvm; - abi_argument_count += 1; - break :indirect; - } - } - - @trap(); - }, - .ignore => unreachable, - else => @trap(), - } - - assert(abi_argument_count == argument_abi.abi_start + argument_abi.abi_count); - } else { - module.report_error(); - } - } - - if (function_type.is_var_args) { - assert(abi_argument_count >= function_type.abi_argument_types.len); - } else { - // TODO - assert(abi_argument_count == function_type.abi_argument_types.len); - } - - const llvm_abi_argument_values = llvm_abi_argument_value_buffer[0..abi_argument_count]; - const llvm_call = module.llvm.builder.create_call(raw_function_type.llvm.handle.to_function(), llvm_callable, llvm_abi_argument_values); - - const attribute_list = module.build_attribute_list(.{ - .return_type_abi = function_type.return_type_abi, - .abi_return_type = function_type.abi_return_type, - .abi_argument_types = abi_argument_type_buffer[0..abi_argument_count], - .argument_type_abis = argument_type_abi_buffer[0..semantic_argument_count], - .attributes = .{}, - .call_site = true, - }); - - const call_base = llvm_call.to_instruction().to_call_base(); - call_base.set_calling_convention(llvm_calling_convention); - call_base.set_attributes(attribute_list); - - const return_type_abi = &function_type.return_type_abi; - const return_abi_kind = return_type_abi.flags.kind; - - switch (return_abi_kind) { - .ignore => { - assert(return_type_abi.semantic_type == module.noreturn_type or return_type_abi.semantic_type == module.void_type); - return module.void_value; - }, - .direct, .extend => { - const coerce_to_type = return_type_abi.get_coerce_to_type(); - - if (return_type_abi.semantic_type.is_abi_equal(coerce_to_type) and return_type_abi.attributes.direct.offset == 0) { - const coerce_to_type_kind = coerce_to_type.get_evaluation_kind(); - switch (coerce_to_type_kind) { - .aggregate => {}, - .complex => @trap(), - .scalar => { - const value = module.values.add(); - value.* = .{ - .llvm = llvm_call, - .bb = .instruction, - .type = return_type_abi.semantic_type, - .lvalue = false, - .dereference_to_assign = false, - }; - return value; - }, - } - } - - // TODO: if - const fixed_vector_type = false; - if (fixed_vector_type) { - @trap(); - } - - const coerce_alloca = module.create_alloca(.{ .type = return_type_abi.semantic_type, .name = "coerce" }); - var destination_pointer = switch (return_type_abi.attributes.direct.offset == 0) { - true => coerce_alloca, - false => @trap(), - }; - _ = &destination_pointer; - - if (return_type_abi.semantic_type.bb.structure.fields.len > 0) { - // CreateCoercedStore( - // CI, StorePtr, - // llvm::TypeSize::getFixed(DestSize - RetAI.getDirectOffset()), - // DestIsVolatile); - const source_value = llvm_call; - const source_type = function_type.abi_return_type; - // const source_size = source_type.get_byte_size(); - var destination_type = return_type_abi.semantic_type; - const destination_size = destination_type.get_byte_size(); - // const destination_alignment = destination_type.get_byte_alignment(); - const left_destination_size = destination_size - return_type_abi.attributes.direct.offset; - - const is_destination_volatile = false; // TODO - module.create_coerced_store(source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); - } else { - @trap(); - } - - const value = module.values.add(); - value.* = .{ - .llvm = destination_pointer, - .bb = .instruction, - .type = module.get_pointer_type(.{ .type = return_type_abi.semantic_type }), - .lvalue = true, - .dereference_to_assign = true, - }; - return value; - }, - .indirect => { - const value = module.values.add(); - value.* = .{ - .llvm = llvm_indirect_return_value, - .bb = .instruction, - .type = module.get_pointer_type(.{ .type = return_type_abi.semantic_type }), - .lvalue = true, - .dereference_to_assign = true, - }; - return value; - }, - else => @trap(), - } - @trap(); - //return value; - } - - fn rule_after_dereference(noalias module: *Module, value_builder: ValueBuilder) *Value { - const left = value_builder.left orelse unreachable; - switch (left.type.bb) { - .pointer => |pointer| { - const load = module.create_load(.{ - .type = pointer.type, - .value = left.llvm, - .alignment = pointer.alignment, - }); - - const value = module.values.add(); - value.* = .{ - .type = pointer.type, - .bb = .instruction, - .llvm = load, - .lvalue = true, - .dereference_to_assign = false, - }; - return value; - }, - else => module.report_error(), - } - } - - fn rule_after_binary(noalias module: *Module, value_builder: ValueBuilder) *Value { - const binary_operator_token = value_builder.token; - const binary_operator_token_precedence = rules[@intFromEnum(binary_operator_token)].precedence; - const left = value_builder.left orelse module.report_error(); - assert(binary_operator_token_precedence != .assignment); // TODO: this may be wrong. Assignment operator is not allowed in expressions - const right_precedence = if (binary_operator_token_precedence == .assignment) .assignment else binary_operator_token_precedence.increment(); - const right = module.parse_precedence(value_builder.with_precedence(right_precedence).with_token(.none).with_left(null).with_type(left.type)); - - const binary_operation_type = value_builder.type orelse left.type; - - const binary_operation_kind: BinaryOperationKind = switch (binary_operator_token) { - .none => unreachable, - .@"+" => switch (binary_operation_type.bb) { - .integer => .integer_add, - else => @trap(), - }, - .@"-" => switch (binary_operation_type.bb) { - .integer => .integer_sub, - else => @trap(), - }, - .@"*" => switch (binary_operation_type.bb) { - .integer => .integer_mul, - else => @trap(), - }, - .@"/" => switch (binary_operation_type.bb) { - .integer => |integer| switch (integer.signed) { - true => .integer_sdiv, - false => .integer_udiv, - }, - else => @trap(), - }, - .@"%" => switch (binary_operation_type.bb) { - .integer => |integer| switch (integer.signed) { - true => .integer_srem, - false => .integer_urem, - }, - else => @trap(), - }, - .@"&" => switch (binary_operation_type.bb) { - .integer => .integer_and, - else => @trap(), - }, - .@"|" => switch (binary_operation_type.bb) { - .integer => .integer_or, - else => @trap(), - }, - .@"^" => switch (binary_operation_type.bb) { - .integer => .integer_xor, - else => @trap(), - }, - .@"<<" => switch (binary_operation_type.bb) { - .integer => .integer_shl, - else => @trap(), - }, - .@">>" => switch (binary_operation_type.bb) { - .integer => |integer| switch (integer.signed) { - true => .integer_ashr, - false => .integer_lshr, - }, - else => @trap(), - }, - .@"==" => switch (binary_operation_type.bb) { - .integer => .integer_compare_equal, - else => @trap(), - }, - else => @trap(), - }; - - const result = module.values.add(); - - const llvm_value = switch (binary_operation_kind) { - .integer_add => module.llvm.builder.create_add(left.llvm, right.llvm), - .integer_sub => module.llvm.builder.create_sub(left.llvm, right.llvm), - .integer_mul => module.llvm.builder.create_mul(left.llvm, right.llvm), - .integer_udiv => module.llvm.builder.create_udiv(left.llvm, right.llvm), - .integer_sdiv => module.llvm.builder.create_sdiv(left.llvm, right.llvm), - .integer_urem => module.llvm.builder.create_urem(left.llvm, right.llvm), - .integer_srem => module.llvm.builder.create_srem(left.llvm, right.llvm), - .integer_and => module.llvm.builder.create_and(left.llvm, right.llvm), - .integer_or => module.llvm.builder.create_or(left.llvm, right.llvm), - .integer_xor => module.llvm.builder.create_xor(left.llvm, right.llvm), - .integer_shl => module.llvm.builder.create_shl(left.llvm, right.llvm), - .integer_ashr => module.llvm.builder.create_ashr(left.llvm, right.llvm), - .integer_lshr => module.llvm.builder.create_lshr(left.llvm, right.llvm), - .integer_compare_equal => module.llvm.builder.create_integer_compare(.eq, left.llvm, right.llvm), - .integer_compare_not_equal => module.llvm.builder.create_integer_compare(.ne, left.llvm, right.llvm), - .integer_compare_unsigned_less_than => module.llvm.builder.create_integer_compare(.ult, left.llvm, right.llvm), - .integer_compare_unsigned_less_equal => module.llvm.builder.create_integer_compare(.ule, left.llvm, right.llvm), - .integer_compare_unsigned_greater_than => module.llvm.builder.create_integer_compare(.ugt, left.llvm, right.llvm), - .integer_compare_unsigned_greater_equal => module.llvm.builder.create_integer_compare(.uge, left.llvm, right.llvm), - .integer_compare_signed_less_than => module.llvm.builder.create_integer_compare(.slt, left.llvm, right.llvm), - .integer_compare_signed_less_equal => module.llvm.builder.create_integer_compare(.sle, left.llvm, right.llvm), - .integer_compare_signed_greater_than => module.llvm.builder.create_integer_compare(.sgt, left.llvm, right.llvm), - .integer_compare_signed_greater_equal => module.llvm.builder.create_integer_compare(.sge, left.llvm, right.llvm), - }; - - result.* = .{ - .llvm = llvm_value, - .type = switch (binary_operation_kind) { - .integer_compare_equal, - .integer_compare_not_equal, - .integer_compare_unsigned_less_than, - .integer_compare_unsigned_less_equal, - .integer_compare_unsigned_greater_than, - .integer_compare_unsigned_greater_equal, - .integer_compare_signed_less_than, - .integer_compare_signed_less_equal, - .integer_compare_signed_greater_than, - .integer_compare_signed_greater_equal, - => module.integer_type(1, false), - else => binary_operation_type, - }, - .dereference_to_assign = false, - .lvalue = false, - .bb = .instruction, - }; - - return result; - } - - fn rule_before_integer(noalias module: *Module, value_builder: ValueBuilder) *Value { - const v = value_builder.token.integer.value; - const value = module.values.add(); - const expected_ty = value_builder.type orelse module.report_error(); - if (expected_ty.bb != .integer) { - module.report_error(); - } - value.* = .{ - .llvm = expected_ty.llvm.handle.to_integer().get_constant(v, @intFromBool(false)).to_value(), - .type = expected_ty, - .bb = .{ - .constant_integer = .{ - .value = v, - .signed = false, - }, - }, - .dereference_to_assign = false, - .lvalue = false, - }; - return value; - } - - const Token = union(Id) { - none, - end_of_statement, - integer: Integer, - identifier: []const u8, - value_keyword: ValueKeyword, - value_intrinsic: ValueIntrinsic, - // Assignment operators - @"=", - @"+=", - @"-=", - @"*=", - @"/=", - @"%=", - @"&=", - @"|=", - @"^=", - @"<<=", - @">>=", - // Comparison operators - @"==", - @"!=", - @"<", - @">", - @"<=", - @">=", - // Logical AND - @"and", - @"and?", - // Logical OR - @"or", - @"or?", - // Add-like operators - @"+", - @"-", - // Div-like operators - @"*", - @"/", - @"%", - // Bitwise operators - @"&", - @"|", - @"^", - // Shifting operators - @"<<", - @">>", - // Pointer dereference - @".&", - // Parenthesis - @"(", - @")", - - const Id = enum { - none, - end_of_statement, - integer, - identifier, - value_keyword, - value_intrinsic, - // Assignment operators - @"=", - @"+=", - @"-=", - @"*=", - @"/=", - @"%=", - @"&=", - @"|=", - @"^=", - @"<<=", - @">>=", - // Comparison operators - @"==", - @"!=", - @"<", - @">", - @"<=", - @">=", - // Logical AND - @"and", - @"and?", - // Logical OR - @"or", - @"or?", - // Add-like operators - @"+", - @"-", - // Div-like operators - @"*", - @"/", - @"%", - // Bitwise operators - @"&", - @"|", - @"^", - // Shifting operators - @"<<", - @">>", - // Pointer dereference - @".&", - // Parenthesis - @"(", - @")", - }; - - const Integer = struct { - value: u64, - kind: Kind, - - const Kind = enum { - hexadecimal, - decimal, - octal, - binary, - }; - }; - }; - - const rules = blk: { - var r: [@typeInfo(Token.Id).@"enum".fields.len]Rule = undefined; - var count: u32 = 0; - r[@intFromEnum(Token.Id.none)] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.end_of_statement)] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.identifier)] = .{ - .before = &rule_before_identifier, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.value_keyword)] = .{ - .before = &rule_before_value_keyword, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.value_intrinsic)] = .{ - .before = &rule_before_value_intrinsic, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.integer)] = .{ - .before = &rule_before_integer, - .after = null, - .precedence = .none, - }; - count += 1; - - const assignment_operators = [_]Token.Id{ - .@"=", - .@"+=", - .@"-=", - .@"*=", - .@"/=", - .@"%=", - .@"&=", - .@"|=", - .@"^=", - .@"<<=", - .@">>=", - }; - - for (assignment_operators) |assignment_operator| { - r[@intFromEnum(assignment_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .assignment, - }; - count += 1; - } - - const comparison_operators = [_]Token.Id{ - .@"==", - .@"!=", - .@"<", - .@">", - .@"<=", - .@">=", - }; - - for (comparison_operators) |comparison_operator| { - r[@intFromEnum(comparison_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .comparison, - }; - count += 1; - } - - const and_operators = [_]Token.Id{ - .@"and", - .@"and?", - }; - - for (and_operators) |and_operator| { - r[@intFromEnum(and_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .@"or", - }; - count += 1; - } - - const or_operators = [_]Token.Id{ - .@"or", - .@"or?", - }; - - for (or_operators) |or_operator| { - r[@intFromEnum(or_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .@"or", - }; - count += 1; - } - - const add_like_operators = [_]Token.Id{ - .@"+", - .@"-", - }; - - for (add_like_operators) |add_like_operator| { - r[@intFromEnum(add_like_operator)] = .{ - .before = rule_before_unary, - .after = rule_after_binary, - .precedence = .add_like, - }; - count += 1; - } - - const div_like_operators = [_]Token.Id{ - .@"*", - .@"/", - .@"%", - }; - - for (div_like_operators) |div_like_operator| { - r[@intFromEnum(div_like_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .div_like, - }; - count += 1; - } - - r[@intFromEnum(Token.Id.@"&")] = .{ - .before = rule_before_unary, - .after = rule_after_binary, - .precedence = .bitwise, - }; - count += 1; - - const bitwise_operators = [_]Token.Id{ - .@"|", - .@"^", - }; - - for (bitwise_operators) |bitwise_operator| { - r[@intFromEnum(bitwise_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .bitwise, - }; - count += 1; - } - - const shifting_operators = [_]Token.Id{ - .@"<<", - .@">>", - }; - - for (shifting_operators) |shifting_operator| { - r[@intFromEnum(shifting_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .shifting, - }; - count += 1; - } - - r[@intFromEnum(Token.Id.@".&")] = .{ - .before = null, - .after = rule_after_dereference, - .precedence = .postfix, - }; - count += 1; - - r[@intFromEnum(Token.Id.@"(")] = .{ - .before = rule_before_parenthesis, - .after = rule_after_call, - .precedence = .postfix, - }; - count += 1; - r[@intFromEnum(Token.Id.@")")] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - - assert(count == r.len); - break :blk r; - }; - - fn tokenize(module: *Module) Token { - module.skip_space(); - - const start_index = module.offset; - if (start_index == module.content.len) { - module.report_error(); - } - - const start_character = module.content[start_index]; - const result: Token = switch (start_character) { - ';' => blk: { - module.offset += 1; - break :blk .end_of_statement; - }, - 'a'...'z', 'A'...'Z', '_' => blk: { - assert(is_identifier_start_ch(start_character)); - const identifier = module.parse_identifier(); - const token: Token = if (lib.string.to_enum(ValueKeyword, identifier)) |value_keyword| .{ .value_keyword = value_keyword } else .{ .identifier = identifier }; - break :blk token; - }, - '#' => if (is_identifier_start_ch(module.content[module.offset + 1])) blk: { - module.offset += 1; - const value_intrinsic_identifier = module.parse_identifier(); - const value_intrinsic = lib.string.to_enum(ValueIntrinsic, value_intrinsic_identifier) orelse module.report_error(); - break :blk .{ - .value_intrinsic = value_intrinsic, - }; - } else { - @trap(); - }, - '0' => blk: { - const next_ch = module.content[start_index + 1]; - const token_integer_kind: Token.Integer.Kind = switch (next_ch) { - 'x' => .hexadecimal, - 'o' => .octal, - 'b' => .binary, - else => .decimal, - }; - const value: u64 = switch (token_integer_kind) { - .decimal => switch (next_ch) { - 0...9 => module.report_error(), - else => b: { - module.offset += 1; - break :b 0; - }, - }, - else => @trap(), - }; - - if (module.content[module.offset] == '.') { - @trap(); - } else { - break :blk .{ .integer = .{ .value = value, .kind = token_integer_kind } }; - } - }, - '1'...'9' => blk: { - const decimal = module.parse_decimal(); - if (module.content[module.offset] == '.') { - @trap(); - } else { - break :blk .{ .integer = .{ .value = decimal, .kind = .decimal } }; - } - }, - '+', '-', '*', '/', '%', '&', '|', '^' => |c| blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - '=' => @trap(), - else => switch (c) { - '+' => .@"+", - '-' => .@"-", - '*' => .@"*", - '/' => .@"/", - '%' => .@"%", - '&' => .@"&", - '|' => .@"|", - '^' => .@"^", - else => unreachable, - }, - }; - - const token = switch (token_id) { - else => unreachable, - inline .@"+", - .@"-", - .@"*", - .@"/", - .@"%", - .@"&", - .@"|", - .@"^", - => |tid| @unionInit(Token, @tagName(tid), {}), - }; - - module.offset += @as(u32, 1) + @intFromBool(next_ch == '='); - - break :blk token; - }, - '<' => blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - '<' => switch (module.content[start_index + 2]) { - '=' => .@"<<=", - else => .@"<<", - }, - '=' => .@"<=", - else => .@"<", - }; - - module.offset += switch (token_id) { - .@"<<=" => 3, - .@"<<", .@"<=" => 2, - .@"<" => 1, - else => unreachable, - }; - - const token = switch (token_id) { - else => unreachable, - inline .@"<<=", - .@"<<", - .@"<=", - .@"<", - => |tid| @unionInit(Token, @tagName(tid), {}), - }; - break :blk token; - }, - '>' => blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - '>' => switch (module.content[start_index + 2]) { - '=' => .@">>=", - else => .@">>", - }, - '=' => .@">=", - else => .@">", - }; - - module.offset += switch (token_id) { - .@">>=" => 3, - .@">>", .@">=" => 2, - .@">" => 1, - else => unreachable, - }; - - const token = switch (token_id) { - else => unreachable, - inline .@">>=", - .@">>", - .@">=", - .@">", - => |tid| @unionInit(Token, @tagName(tid), {}), - }; - break :blk token; - }, - '.' => blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - '&' => .@".&", - else => @trap(), - }; - - module.offset += switch (token_id) { - .@".&" => 2, - else => @trap(), - }; - const token = switch (token_id) { - else => unreachable, - inline .@".&", - => |tid| @unionInit(Token, @tagName(tid), {}), - }; - break :blk token; - }, - '=' => blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - '=' => .@"==", - else => .@"=", - }; - module.offset += switch (token_id) { - .@"==" => 2, - .@"=" => 1, - else => @trap(), - }; - const token = switch (token_id) { - else => unreachable, - inline .@"==", .@"=" => |tid| @unionInit(Token, @tagName(tid), {}), - }; - break :blk token; - }, - '(' => blk: { - module.offset += 1; - break :blk .@"("; - }, - ')' => blk: { - module.offset += 1; - break :blk .@")"; - }, - else => @trap(), - }; - - assert(start_index != module.offset); - - return result; - } - - fn parse_precedence(noalias module: *Module, value_builder: ValueBuilder) *Value { - assert(value_builder.token == .none); - const token = module.tokenize(); - const rule = &rules[@intFromEnum(token)]; - if (rule.before) |before| { - const left = before(module, value_builder.with_precedence(.none).with_token(token)); - - const result = module.parse_precedence_left(value_builder.with_left(left)); - return result; - } else { - module.report_error(); - } - } - - fn parse_precedence_left(noalias module: *Module, value_builder: ValueBuilder) *Value { - var result = value_builder.left; - const precedence = value_builder.precedence; - - while (true) { - const checkpoint = module.offset; - const token = module.tokenize(); - const token_rule = &rules[@intFromEnum(token)]; - const token_precedence: Precedence = switch (token_rule.precedence) { - .assignment => switch (value_builder.allow_assignment_operators) { - true => .assignment, - false => .none, - }, - else => |p| p, - }; - if (@intFromEnum(precedence) > @intFromEnum(token_precedence)) { - module.offset = checkpoint; - break; - } - - const after_rule = token_rule.after orelse module.report_error(); - const old = result; - const new = after_rule(module, value_builder.with_token(token).with_precedence(.none).with_left(old)); - result = new; - } - - return result.?; - } - - const parse_value = parse_value2; - fn parse_value2(noalias module: *Module, value_builder: ValueBuilder) *Value { - assert(value_builder.precedence == .none); - assert(value_builder.left == null); - const value = module.parse_precedence(value_builder.with_precedence(.assignment)); - - if (value_builder.type) |expected_type| { - if (expected_type != value.type) { - module.report_error(); - } - } - - return value; - } - - const ValueBuilder = struct { - kind: ValueKind = .value, - type: ?*Type = null, - precedence: Precedence = .none, - left: ?*Value = null, - token: Token = .none, - allow_assignment_operators: bool = false, - - fn with_token(vb: ValueBuilder, token: Token) ValueBuilder { - var v = vb; - v.token = token; - return v; - } - - fn with_precedence(vb: ValueBuilder, precedence: Precedence) ValueBuilder { - var v = vb; - v.precedence = precedence; - return v; - } - - fn with_left(vb: ValueBuilder, left: ?*Value) ValueBuilder { - var v = vb; - v.left = left; - return v; - } - - fn with_kind(vb: ValueBuilder, kind: ValueKind) ValueBuilder { - var v = vb; - v.kind = kind; - return v; - } - - fn with_type(vb: ValueBuilder, ty: ?*Type) ValueBuilder { - var v = vb; - v.type = ty; - return v; - } - }; - - fn parse_value1(noalias module: *Module, maybe_expected_type: ?*Type, value_kind: ValueKind) *Value { - module.skip_space(); - - var value_state = ExpressionState.none; - var previous_value: ?*Value = null; - var iterations: u64 = 0; - var iterative_expected_type = maybe_expected_type; - - const value: *Value = while (true) : (iterations += 1) { - if (iterations == 1) { - iterative_expected_type = previous_value.?.type; - } - - const old_iterative_expected_type = iterative_expected_type; - iterative_expected_type = switch (value_state) { - .pointer_add => module.integer_type(64, false), - else => iterative_expected_type, - }; - - const current_value = switch (module.consume_character_if_match(left_parenthesis)) { - true => blk: { - const r = module.parse_value(module, iterative_expected_type, value_kind); - module.skip_space(); - module.expect_character(right_parenthesis); - break :blk r; - }, - false => module.parse_single_value(module, iterative_expected_type, value_kind), - }; - - iterative_expected_type = old_iterative_expected_type; - - module.skip_space(); - - const left = switch (value_state) { - .none => undefined, - else => previous_value.?.llvm, - }; - const right = current_value.llvm; - const next_ty = if (previous_value) |pv| pv.type else current_value.type; - - const llvm_value = switch (value_state) { - .none => current_value.llvm, - .integer_sub => module.llvm.builder.create_sub(left, right), - .integer_add => module.llvm.builder.create_add(left, right), - .integer_mul => module.llvm.builder.create_mul(left, right), - .integer_sdiv => module.llvm.builder.create_sdiv(left, right), - .integer_udiv => module.llvm.builder.create_udiv(left, right), - .integer_srem => module.llvm.builder.create_srem(left, right), - .integer_urem => module.llvm.builder.create_urem(left, right), - .shl => module.llvm.builder.create_shl(left, right), - .ashr => module.llvm.builder.create_ashr(left, right), - .lshr => module.llvm.builder.create_lshr(left, right), - .@"and" => module.llvm.builder.create_and(left, right), - .@"or" => module.llvm.builder.create_or(left, right), - .xor => module.llvm.builder.create_xor(left, right), - .integer_compare_equal, - .integer_compare_not_equal, - .integer_compare_unsigned_greater_than, - .integer_compare_unsigned_greater_equal, - .integer_compare_unsigned_less_than, - .integer_compare_unsigned_less_equal, - .integer_compare_signed_greater_than, - .integer_compare_signed_greater_equal, - .integer_compare_signed_less_than, - .integer_compare_signed_less_equal, - => |icmp| module.llvm.builder.create_compare(icmp.to_int_predicate(), left, right), - .pointer_add => module.llvm.builder.create_gep(.{ - .type = next_ty.bb.pointer.type.llvm.handle, - .aggregate = left, - .indices = &.{right}, - .inbounds = false, - }), - }; - - switch (value_state) { - .none => previous_value = current_value, - else => { - previous_value = module.values.add(); - previous_value.?.* = .{ - .llvm = llvm_value, - .type = switch (value_state) { - .none => unreachable, - .integer_compare_equal, - .integer_compare_not_equal, - .integer_compare_unsigned_greater_than, - .integer_compare_unsigned_greater_equal, - .integer_compare_unsigned_less_than, - .integer_compare_unsigned_less_equal, - .integer_compare_signed_greater_than, - .integer_compare_signed_greater_equal, - .integer_compare_signed_less_than, - .integer_compare_signed_less_equal, - => module.integer_type(1, false), - .integer_sub, - .integer_add, - .integer_mul, - .integer_sdiv, - .integer_udiv, - .integer_srem, - .integer_urem, - .shl, - .ashr, - .lshr, - .@"and", - .@"or", - .xor, - => next_ty, - .pointer_add => next_ty, - }, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - }, - } - - const ch = module.content[module.offset]; - // If an assignment operator (it being simple or compound, like +=, -=, &=, etc.) is found, then we break - const new_value_state: ExpressionState = switch (ch) { - ',', ';', right_parenthesis, right_bracket, right_brace => .none, - '=' => switch (module.content[module.offset + 1]) { - '=' => .integer_compare_equal, - else => .none, - }, - '-' => switch (module.content[module.offset + 1]) { - '=' => .none, - else => .integer_sub, - }, - '+' => switch (module.content[module.offset + 1]) { - '=' => .none, - else => switch (next_ty.bb) { - .integer => .integer_add, - .pointer => .pointer_add, - else => @trap(), - }, - }, - '*' => switch (module.content[module.offset + 1]) { - '=' => .none, - else => switch (next_ty.bb) { - .integer => .integer_mul, - else => @trap(), - }, - }, - '/' => switch (module.content[module.offset + 1]) { - '=' => .none, - else => switch (next_ty.bb) { - .integer => |int| switch (int.signed) { - true => .integer_sdiv, - false => .integer_udiv, - }, - else => @trap(), - }, - }, - '%' => switch (module.content[module.offset + 1]) { - '=' => .none, - else => switch (next_ty.bb) { - .integer => |int| switch (int.signed) { - true => .integer_srem, - false => .integer_urem, - }, - else => @trap(), - }, - }, - '<' => switch (module.content[module.offset + 1]) { - '<' => switch (module.content[module.offset + 2]) { - '=' => .none, - else => .shl, - }, - '=' => switch (next_ty.bb) { - .integer => |int| switch (int.signed) { - true => .integer_compare_signed_less_equal, - false => .integer_compare_unsigned_less_equal, - }, - else => @trap(), - }, - else => switch (next_ty.bb) { - .integer => |int| switch (int.signed) { - true => .integer_compare_signed_less_than, - false => .integer_compare_unsigned_less_than, - }, - else => @trap(), - }, - }, - '>' => switch (module.content[module.offset + 1]) { - '>' => switch (module.content[module.offset + 2]) { - '=' => .none, - else => switch (next_ty.bb) { - .integer => |integer| switch (integer.signed) { - true => .ashr, - false => .lshr, - }, - else => @trap(), - }, - }, - '=' => switch (next_ty.bb) { - .integer => |int| switch (int.signed) { - true => .integer_compare_signed_greater_equal, - false => .integer_compare_unsigned_greater_equal, - }, - else => @trap(), - }, - else => switch (next_ty.bb) { - .integer => |int| switch (int.signed) { - true => .integer_compare_signed_greater_than, - false => .integer_compare_unsigned_greater_than, - }, - else => @trap(), - }, - }, - '&' => switch (module.content[module.offset + 1]) { - '=' => .none, - else => .@"and", - }, - '|' => switch (module.content[module.offset + 1]) { - '=' => .none, - else => .@"or", - }, - '^' => switch (module.content[module.offset + 1]) { - '=' => .none, - else => .xor, - }, - '!' => switch (module.content[module.offset + 1]) { - '=' => switch (next_ty.bb) { - .integer, .pointer => .integer_compare_not_equal, - else => @trap(), - }, - else => module.report_error(), - }, - '.' => switch (module.content[module.offset + 1]) { - '.' => .none, - else => @trap(), - }, - else => module.report_error(), - }; - - module.offset += switch (new_value_state) { - .none => break previous_value.?, - .pointer_add, - .integer_sub, - .integer_add, - .integer_mul, - .integer_udiv, - .integer_sdiv, - .integer_urem, - .integer_srem, - .integer_compare_unsigned_greater_than, - .integer_compare_unsigned_greater_equal, - .integer_compare_unsigned_less_than, - .integer_compare_unsigned_less_equal, - .integer_compare_signed_greater_than, - .integer_compare_signed_greater_equal, - .integer_compare_signed_less_than, - .integer_compare_signed_less_equal, - .@"and", - .@"or", - .xor, - => 1, - .integer_compare_equal, - .integer_compare_not_equal, - .ashr, - .lshr, - .shl, - => 2, - }; - value_state = new_value_state; - module.skip_space(); - }; - - return value; - } - - const Prefix = enum { - none, - negative, - not_zero, - }; - - const ValueIntrinsic = enum { - byte_size, - cast, - cast_to, - extend, - integer_max, - int_from_enum, - int_from_pointer, - pointer_cast, - select, - trap, - truncate, - va_start, - va_end, - va_copy, - va_arg, - }; - - fn parse_value_intrinsic(noalias module: *Module, expected_type: ?*Type) *Value { - module.expect_character('#'); - module.skip_space(); - const intrinsic_name = module.parse_identifier(); - const intrinsic_keyword = lib.string.to_enum(ValueIntrinsic, intrinsic_name) orelse module.report_error(); - module.skip_space(); - - module.expect_character(left_parenthesis); - - module.skip_space(); - - switch (intrinsic_keyword) { - .byte_size => { - const ty = module.parse_type(); - module.skip_space(); - module.expect_character(')'); - const byte_size = ty.get_byte_size(); - const destination_type = expected_type orelse module.report_error(); - if (destination_type.bb != .integer) { - module.report_error(); - } - const value = module.values.add(); - value.* = .{ - .llvm = destination_type.llvm.handle.to_integer().get_constant(byte_size, @intFromBool(false)).to_value(), - .bb = .{ - .constant_integer = .{ - .value = byte_size, - .signed = false, - }, - }, - .type = destination_type, - .lvalue = false, - .dereference_to_assign = false, - }; - return value; - }, - .cast => { - @trap(); - }, - .cast_to => { - const destination_type = module.parse_type(); - module.skip_space(); - module.expect_character(','); - const source_value = module.parse_value(.{}); - module.skip_space(); - module.expect_character(')'); - - if (source_value.type.bb == .pointer and destination_type.bb == .integer) { - const value = module.values.add(); - value.* = .{ - .llvm = module.llvm.builder.create_ptr_to_int(source_value.llvm, destination_type.llvm.handle), - .type = destination_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - return value; - } else { - @trap(); - } - }, - .extend => { - const source_value = module.parse_value(.{}); - module.skip_space(); - module.expect_character(right_parenthesis); - const source_type = source_value.type; - const destination_type = expected_type orelse module.report_error(); - if (source_type.get_bit_size() > destination_type.get_bit_size()) { - module.report_error(); - } else if (source_type.get_bit_size() == destination_type.get_bit_size() and source_type.is_signed() == destination_type.is_signed()) { - module.report_error(); - } - - const extension_instruction = switch (source_type.bb.integer.signed) { - true => module.llvm.builder.create_sign_extend(source_value.llvm, destination_type.llvm.handle), - false => module.llvm.builder.create_zero_extend(source_value.llvm, destination_type.llvm.handle), - }; - const value = module.values.add(); - value.* = .{ - .llvm = extension_instruction, - .type = destination_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - - return value; - }, - .integer_max => { - module.skip_space(); - const ty = module.parse_type(); - module.expect_character(right_parenthesis); - if (ty.bb != .integer) { - module.report_error(); - } - const bit_count = ty.bb.integer.bit_count; - const max_value = if (bit_count == 64) ~@as(u64, 0) else (@as(u64, 1) << @intCast(bit_count - @intFromBool(ty.bb.integer.signed))) - 1; - const expected_ty = expected_type orelse ty; - if (ty.get_bit_size() > expected_ty.get_bit_size()) { - module.report_error(); - } - const constant_integer = expected_ty.llvm.handle.to_integer().get_constant(max_value, @intFromBool(false)); - const value = module.values.add(); - value.* = .{ - .llvm = constant_integer.to_value(), - .type = expected_ty, - .bb = .{ - .constant_integer = .{ - .value = max_value, - .signed = false, - }, - }, - .lvalue = false, - .dereference_to_assign = false, - }; - - return value; - }, - .int_from_enum => { - const source_value = module.parse_value(.{}); - module.skip_space(); - module.expect_character(right_parenthesis); - if (source_value.type.bb != .enumerator) { - module.report_error(); - } - const original_target_type = source_value.type.bb.enumerator.backing_type; - const target_type = expected_type orelse original_target_type; - - if (target_type.bb != .integer) { - module.report_error(); - } - - if (target_type.get_bit_size() < original_target_type.get_bit_size()) { - module.report_error(); - } - - const value = module.values.add(); - value.* = source_value.*; - value.type = target_type; - return value; - }, - .int_from_pointer => { - const source_value = module.parse_value(.{}); - module.skip_space(); - module.expect_character(right_parenthesis); - if (source_value.type.bb != .pointer) { - module.report_error(); - } - const original_target_type = module.integer_type(64, false); - const target_type = expected_type orelse original_target_type; - - if (target_type.bb != .integer) { - module.report_error(); - } - - if (target_type.get_bit_size() < original_target_type.get_bit_size()) { - module.report_error(); - } - - const value = module.values.add(); - value.* = .{ - .llvm = module.llvm.builder.create_ptr_to_int(source_value.llvm, target_type.llvm.handle), - .type = target_type, - .lvalue = false, - .dereference_to_assign = false, - .bb = .instruction, - }; - return value; - }, - .pointer_cast => { - const ty = expected_type orelse module.report_error(); - if (ty.bb != .pointer) { - module.report_error(); - } - const source_value = module.parse_value(.{}); - module.skip_space(); - module.expect_character(right_parenthesis); - if (source_value.type.bb != .pointer) { - module.report_error(); - } - if (ty == source_value.type) { - module.report_error(); - } - const value = module.values.add(); - value.* = .{ - .llvm = module.llvm.builder.create_pointer_cast(source_value.llvm, ty.llvm.handle), - .type = ty, - .bb = .instruction, - .lvalue = true, - .dereference_to_assign = false, - }; - return value; - }, - .select => { - const condition_value = module.parse_condition_raw(); - - module.skip_space(); - module.expect_character(','); - module.skip_space(); - - const true_value = module.parse_value(.{ - .type = expected_type, - }); - - module.skip_space(); - module.expect_character(','); - module.skip_space(); - - const expected_ty = expected_type orelse true_value.type; - - const false_value = module.parse_value(.{ - .type = expected_ty, - }); - - module.skip_space(); - module.expect_character(right_parenthesis); - - if (true_value.type != expected_ty) { - module.report_error(); - } - - if (false_value.type != expected_ty) { - module.report_error(); - } - - const value = module.values.add(); - value.* = .{ - .llvm = module.llvm.builder.create_select(condition_value.llvm, true_value.llvm, false_value.llvm), - .bb = .instruction, - .type = expected_ty, - .lvalue = false, - .dereference_to_assign = false, - }; - return value; - }, - // .slice => { - // const value = module.parse_value(module, null, .value); - // const u64_type = module.integer_type(64, false); - // - // module.skip_space(); - // - // var found_right_parenthesis = false; - // const second_argument: ?*Value = if (module.consume_character_if_match(',')) b: { - // module.skip_space(); - // if (!module.consume_character_if_match(right_parenthesis)) { - // break :b module.parse_value(module, null, .value); - // } else { - // found_right_parenthesis = true; - // break :b null; - // } - // } else null; - // - // const parse_third_argument = if (!found_right_parenthesis) b: { - // module.skip_space(); - // const second_comma = module.consume_character_if_match(','); - // module.skip_space(); - // found_right_parenthesis = module.consume_character_if_match(right_parenthesis); - // if (second_comma and !found_right_parenthesis) { - // @trap(); - // } - // - // if (!found_right_parenthesis) { - // module.report_error(); - // } - // break :b false; - // } else false; - // const third_argument: ?*Value = if (parse_third_argument) module.parse_value(module, null, .value) else null; - // const element_count = @as(u32, 1) + @intFromBool(second_argument != null) + @intFromBool(third_argument != null); - // - // if (expected_type) |expected_ty| { - // if (!expected_ty.is_slice()) { - // module.report_error(); - // } - // - // const slice_type = expected_ty; - // const slice_pointer_type = slice_type.bb.structure.fields[0].type; - // const slice_element_type = slice_pointer_type.bb.pointer.type; - // - // assert(slice_type != value.type); - // - // switch (value.type.bb) { - // .pointer => |pointer| { - // const pointer_element_type = pointer.type; - // if (slice_type == pointer_element_type) switch (element_count) { - // 1 => @trap(), - // 2 => { - // // If a slice is found and two arguments are given, the second argument is a start - // @trap(); - // }, - // 3 => @trap(), - // else => unreachable, - // } else if (pointer_element_type == slice_element_type) switch (element_count) { - // 1 => @trap(), - // 2 => { - // // If a pointer is found and its element type matches the slice element type, then the second argument is the length of the slice - // const length = second_argument orelse unreachable; - // if (length.type.bb != .integer) { - // module.report_error(); - // } - // - // if (length.type != u64_type) { - // @trap(); - // } - // - // const slice_poison = slice_type.llvm.handle.get_poison(); - // const pointer_insert = module.llvm.builder.create_insert_value(slice_poison, value.llvm, 0); - // const length_insert = module.llvm.builder.create_insert_value(pointer_insert, length.llvm, 1); - // const slice_value = length_insert; - // const result = module.values.add(); - // result.* = .{ - // .llvm = slice_value, - // .type = slice_type, - // .bb = .instruction, - // .lvalue = false, - // .dereference_to_assign = false, - // }; - // return result; - // }, - // 3 => @trap(), - // else => unreachable, - // } else switch (pointer_element_type.bb) { - // .array => |array| { - // const array_element_type = array.element_type; - // if (array_element_type == slice_element_type) { - // assert(element_count == 1); - // const slice_poison = slice_type.llvm.handle.get_poison(); - // const pointer_insert = module.llvm.builder.create_insert_value(slice_poison, value.llvm, 0); - // const length_value = u64_type.llvm.handle.to_integer().get_constant(array.element_count.?, @intFromBool(false)); - // const length_insert = module.llvm.builder.create_insert_value(pointer_insert, length_value.to_value(), 1); - // const slice_value = length_insert; - // const result = module.values.add(); - // result.* = .{ - // .llvm = slice_value, - // .type = slice_type, - // .bb = .instruction, - // .lvalue = false, - // .dereference_to_assign = false, - // }; - // return result; - // } else { - // module.report_error(); - // } - // }, - // else => @trap(), - // } - // }, - // else => @trap(), - // } - // } else { - // @trap(); - // } - // }, - .trap => { - module.expect_character(right_parenthesis); - - // TODO: lookup in advance - const intrinsic_id = module.llvm.intrinsic_table.trap; - const argument_types: []const *llvm.Type = &.{}; - const argument_values: []const *llvm.Value = &.{}; - const intrinsic_function = module.llvm.handle.get_intrinsic_declaration(intrinsic_id, argument_types); - const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, argument_types); - const llvm_call = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, argument_values); - _ = module.llvm.builder.create_unreachable(); - module.llvm.builder.clear_insertion_position(); - - const value = module.values.add(); - value.* = .{ - .llvm = llvm_call, - .type = module.noreturn_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - - return value; - }, - .truncate => { - const source_value = module.parse_value(.{}); - module.skip_space(); - module.expect_character(right_parenthesis); - const destination_type = expected_type orelse module.report_error(); - const truncate = module.llvm.builder.create_truncate(source_value.llvm, destination_type.llvm.handle); - - const value = module.values.add(); - value.* = .{ - .llvm = truncate, - .type = destination_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - - return value; - }, - .va_start => { - module.expect_character(right_parenthesis); - - const va_list_type = module.get_va_list_type(); - const alloca = module.create_alloca(.{ .type = va_list_type }); - const intrinsic_id = module.llvm.intrinsic_table.va_start; - const argument_types: []const *llvm.Type = &.{module.llvm.pointer_type}; - const intrinsic_function = module.llvm.handle.get_intrinsic_declaration(intrinsic_id, argument_types); - const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, argument_types); - const argument_values: []const *llvm.Value = &.{alloca}; - _ = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, argument_values); - - const value = module.values.add(); - value.* = .{ - .llvm = alloca, - .type = module.get_pointer_type(.{ .type = va_list_type }), - .bb = .instruction, - .lvalue = true, - .dereference_to_assign = true, - }; - - return value; - }, - .va_end => { - const va_list = module.parse_value(.{ - .type = module.get_pointer_type(.{ .type = module.get_va_list_type() }), - .kind = .pointer, - }); - module.skip_space(); - module.expect_character(right_parenthesis); - const intrinsic_id = module.llvm.intrinsic_table.va_end; - const argument_types: []const *llvm.Type = &.{module.llvm.pointer_type}; - const intrinsic_function = module.llvm.handle.get_intrinsic_declaration(intrinsic_id, argument_types); - const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, argument_types); - const argument_values: []const *llvm.Value = &.{va_list.llvm}; - const llvm_value = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, argument_values); - const value = module.values.add(); - value.* = .{ - .llvm = llvm_value, - .type = module.void_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - - return value; - }, - .va_copy => @trap(), - .va_arg => { - const va_list_type = module.get_va_list_type(); - const raw_va_list = module.parse_value(.{ - .type = module.get_pointer_type(.{ .type = va_list_type }), - .kind = .pointer, - }); - const va_list = module.llvm.builder.create_gep(.{ - .type = va_list_type.llvm.handle, - .aggregate = raw_va_list.llvm, - .indices = &([1]*llvm.Value{module.integer_type(64, false).llvm.handle.to_integer().get_constant(0, @intFromBool(false)).to_value()} ** 2), - }); - - module.skip_space(); - - module.expect_character(','); - - module.skip_space(); - - const arg_type = module.parse_type(); - module.skip_space(); - - module.expect_character(right_parenthesis); - const r = Abi.SystemV.classify_argument_type(module, arg_type, .{ - .available_gpr = 0, - .is_named_argument = false, - .is_reg_call = false, - }); - const abi = r[0]; - const needed_register_count = r[1]; - - const abi_kind = abi.flags.kind; - assert(abi_kind != .ignore); - - const va_list_struct = va_list_type.bb.array.element_type; - const llvm_address = switch (needed_register_count.gpr == 0 and needed_register_count.sse == 0) { - true => Abi.SystemV.emit_va_arg_from_memory(module, va_list, va_list_struct, arg_type), - false => blk: { - const va_list_struct_llvm = va_list_struct.llvm.handle.to_struct(); - const gpr_offset_pointer = if (needed_register_count.gpr != 0) module.llvm.builder.create_struct_gep(va_list_struct_llvm, va_list, 0) else undefined; - const gpr_offset = if (needed_register_count.gpr != 0) module.create_load(.{ .type = va_list_struct.bb.structure.fields[0].type, .value = gpr_offset_pointer, .alignment = 16 }) else undefined; - const raw_in_regs = 48 - needed_register_count.gpr * 8; - const int32 = module.integer_type(32, false); - const int32_llvm = int32.llvm.handle.to_integer(); - var in_regs = if (needed_register_count.gpr != 0) int32_llvm.get_constant(raw_in_regs, @intFromBool(false)).to_value() else @trap(); - in_regs = if (needed_register_count.gpr != 0) module.llvm.builder.create_integer_compare(.ule, gpr_offset, in_regs) else in_regs; - - const fp_offset_pointer = if (needed_register_count.sse != 0) module.llvm.builder.create_struct_gep(va_list_struct_llvm, va_list, 1) else undefined; - const fp_offset = if (needed_register_count.sse != 0) module.create_load(.{ .type = va_list_struct.bb.structure.fields[1].type, .value = fp_offset_pointer }) else undefined; - const raw_fits_in_fp = 176 - needed_register_count.sse * 16; - var fits_in_fp = if (needed_register_count.sse != 0) int32_llvm.get_constant(raw_fits_in_fp, @intFromBool(false)).to_value() else undefined; - fits_in_fp = if (needed_register_count.sse != 0) module.llvm.builder.create_integer_compare(.ule, fp_offset, fits_in_fp) else undefined; - in_regs = if (needed_register_count.sse != 0 and needed_register_count.gpr != 0) @trap() else in_regs; - - const in_reg_block = module.llvm.context.create_basic_block("va_arg.in_reg", null); - const in_mem_block = module.llvm.context.create_basic_block("va_arg.in_mem", null); - const end_block = module.llvm.context.create_basic_block("va_arg.end", null); - _ = module.llvm.builder.create_conditional_branch(in_regs, in_reg_block, in_mem_block); - module.emit_block(in_reg_block); - - const reg_save_area = module.create_load(.{ .type = va_list_struct.bb.structure.fields[3].type, .value = module.llvm.builder.create_struct_gep(va_list_struct_llvm, va_list, 3), .alignment = 16 }); - - const register_address = if (needed_register_count.gpr != 0 and needed_register_count.sse != 0) { - @trap(); - } else if (needed_register_count.gpr != 0) b: { - const register_address = module.llvm.builder.create_gep(.{ - .type = va_list_struct.bb.structure.fields[3].type.bb.pointer.type.llvm.handle, - .aggregate = reg_save_area, - .indices = &.{gpr_offset}, - .inbounds = false, - }); - if (arg_type.get_byte_alignment() > 8) { - @trap(); - } - break :b register_address; - } else if (needed_register_count.sse == 1) { - @trap(); - } else { - assert(needed_register_count.sse == 2); - @trap(); - }; - - if (needed_register_count.gpr != 0) { - const raw_offset = needed_register_count.gpr * 8; - const new_offset = module.llvm.builder.create_add(gpr_offset, int32_llvm.get_constant(raw_offset, @intFromBool(false)).to_value()); - _ = module.create_store(.{ .destination_value = gpr_offset_pointer, .source_value = new_offset, .source_type = int32, .destination_type = int32, .alignment = 16 }); - } - - if (needed_register_count.sse != 0) { - @trap(); - } - - _ = module.llvm.builder.create_branch(end_block); - - module.emit_block(in_mem_block); - - const memory_address = Abi.SystemV.emit_va_arg_from_memory(module, va_list, va_list_struct, arg_type); - module.emit_block(end_block); - - const values = &.{ register_address, memory_address }; - const blocks = &.{ in_reg_block, in_mem_block }; - const phi = module.llvm.builder.create_phi(module.llvm.pointer_type); - phi.add_incoming(values, blocks); - break :blk phi.to_value(); - }, - }; - - switch (arg_type.get_evaluation_kind()) { - .aggregate => { - const result_type = module.get_pointer_type(.{ .type = arg_type }); - const value = module.values.add(); - value.* = .{ - .type = result_type, - .bb = .instruction, - .llvm = llvm_address, - .lvalue = true, - .dereference_to_assign = true, - }; - return value; - }, - .scalar => { - const value = module.values.add(); - const load = module.create_load(.{ .type = arg_type, .value = llvm_address }); - value.* = .{ - .type = arg_type, - .bb = .instruction, - .llvm = load, - .lvalue = false, - .dereference_to_assign = false, - }; - return value; - }, - .complex => @trap(), - } - }, - } - } - - const TypeIntrinsic = enum { - ReturnType, - }; - - fn parse_type_intrinsic(noalias module: *Module) *Type { - module.expect_character('#'); - module.skip_space(); - const intrinsic_name = module.parse_identifier(); - const intrinsic_keyword = lib.string.to_enum(TypeIntrinsic, intrinsic_name) orelse module.report_error(); - module.skip_space(); - - module.expect_character(left_parenthesis); - - module.skip_space(); - - switch (intrinsic_keyword) { - .ReturnType => { - module.skip_space(); - module.expect_character(right_parenthesis); - const current_function_variable = module.current_function orelse module.report_error(); - const return_type = current_function_variable.value.type.bb.pointer.type.bb.function.return_type_abi.semantic_type; - return return_type; - }, - } - - @trap(); - } - - const ValueKeyword = enum { - undefined, - @"unreachable", - zero, - }; - - fn parse_single_value(noalias module: *Module, expected_type: ?*Type, value_kind: ValueKind) *Value { - module.skip_space(); - - if (module.current_function) |function| { - if (module.llvm.di_builder) |_| { - const line = module.get_line(); - const column = module.get_column(); - const inlined_at: ?*llvm.DI.Metadata = null; // TODO - const debug_location = llvm.DI.create_debug_location(module.llvm.context, line, column, function.value.bb.function.current_scope, inlined_at); - module.llvm.builder.set_current_debug_location(debug_location); - } - } - - const prefix_offset = module.offset; - const prefix_ch = module.content[prefix_offset]; - const must_be_constant = module.current_function == null; - const prefix: Prefix = switch (prefix_ch) { - 'a'...'z', 'A'...'Z', '_', '0'...'9' => .none, - '-' => blk: { - module.offset += 1; - - // TODO: should we skip space here? - module.skip_space(); - break :blk .negative; - }, - left_brace => { - module.offset += 1; - - module.skip_space(); - - const ty = expected_type orelse module.report_error(); - - switch (ty.bb) { - .structure => |*struct_type| { - var field_count: u32 = 0; - - var field_value_buffer: [64]*Value = undefined; - var field_index_buffer: [64]u32 = undefined; - - var is_ordered = true; - var is_constant = true; - var zero = false; - - while (true) : (field_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } else if (module.consume_character_if_match('.')) { - const field_name = module.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 module.report_error(); - - is_ordered = is_ordered and field_index == field_count; - const field = struct_type.fields[field_index]; - - module.skip_space(); - - module.expect_character('='); - - module.skip_space(); - - const field_value = module.parse_value(module, field.type, .value); - if (field.type != field_value.type) { - @trap(); - } - if (field.type.llvm.handle != field_value.type.llvm.handle) { - @trap(); - } - is_constant = is_constant and field_value.is_constant(); - field_value_buffer[field_count] = field_value; - field_index_buffer[field_count] = field_index; - - module.skip_space(); - - _ = module.consume_character_if_match(','); - - module.skip_space(); - } else { - const identifier = module.parse_identifier(); - if (lib.string.to_enum(ValueKeyword, identifier)) |value_keyword| switch (value_keyword) { - ._ => module.report_error(), - .undefined => @trap(), - .@"unreachable" => @trap(), - .zero => { - zero = true; - module.skip_space(); - _ = module.consume_character_if_match(','); - module.skip_space(); - module.expect_character(right_brace); - // We need to break here otherwise `field_count` would be incremented - break; - }, - } else { - module.report_error(); - } - } - } - - if (must_be_constant and !is_constant) { - @trap(); - } - - var zero_until_end = false; - if (zero) { - if (field_count == struct_type.fields.len) { - module.report_error(); - } - - if (is_ordered and is_constant) { - const zero_fields = struct_type.fields[field_count..]; - const zero_field_values = field_value_buffer[field_count..][0..zero_fields.len]; - for (zero_fields, zero_field_values) |zero_field, *zero_field_value| { - zero_field_value.* = module.get_zero_value(zero_field.type); - field_count += 1; - } - } else if (is_ordered) { - zero_until_end = true; - } else { - @trap(); - } - } - - if (field_count != struct_type.fields.len) { - if (!zero_until_end) { - @trap(); - } - } - - const field_values = field_value_buffer[0..field_count]; - const field_indices = field_index_buffer[0..field_count]; - - const llvm_value = switch (is_constant and is_ordered) { - true => blk: { - var llvm_value_buffer: [64]*llvm.Constant = undefined; - var llvm_gc_value_buffer = [1]?*llvm.GlobalVariable{null} ** 64; - const llvm_values = llvm_value_buffer[0..field_count]; - const llvm_gc_values = llvm_gc_value_buffer[0..field_count]; - for (field_values, llvm_gc_values, llvm_values, struct_type.fields) |field_value, *llvm_gc_value, *llvm_field_value, *field| { - llvm_field_value.* = switch (field.type.llvm.handle == field_value.llvm.get_type()) { - true => field_value.llvm.to_constant(), - false => switch (field.type.bb) { - .array => b: { - const global_variable = field_value.llvm.to_global_variable(); - const initializer = global_variable.get_initializer(); - const use_empty = field_value.llvm.use_empty(); - if (use_empty) { - llvm_gc_value.* = global_variable; - } - break :b initializer; - }, - .structure => b: { - assert(field_value.lvalue); - assert(field.type == field_value.type); - const global_variable = field_value.llvm.to_global_variable(); - const initializer = global_variable.get_initializer(); - const use_empty = field_value.llvm.use_empty(); - if (use_empty) { - llvm_gc_value.* = global_variable; - } - break :b initializer; - }, - else => @trap(), - }, - }; - } - - const constant_struct = ty.llvm.handle.to_struct().get_constant(llvm_values); - const result = switch (module.current_function == null) { - true => constant_struct.to_value(), - false => b: { - const global_variable = module.llvm.handle.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = module.arena.join_string(&.{ "__const.", module.current_function.?.name, if (ty.name) |n| n else "" }), - .initial_value = constant_struct, - .type = ty.llvm.handle, - }); - global_variable.set_unnamed_address(.global); - break :b global_variable.to_value(); - }, - }; - - for (llvm_gc_values) |maybe_gc_value| { - if (maybe_gc_value) |gc_value| { - gc_value.erase_from_parent(); - // gc_value.delete(); - } - } - - break :blk result; - }, - false => blk: { - const alloca = module.create_alloca(.{ .type = ty, .name = "compound_literal" }); - const llvm_struct = ty.llvm.handle.to_struct(); - const fields = struct_type.fields[0..field_count]; - - for (fields, field_indices, field_values) |field, field_index, field_value| { - const gep = module.llvm.builder.create_struct_gep(llvm_struct, alloca, field_index); - assert(field_value.type == field.type); - // TODO: consider more store types - _ = module.create_store(.{ - .destination_type = field.type, - .source_type = field_value.type, - .source_value = field_value.llvm, - .destination_value = gep, - }); - } - - if (zero_until_end) { - // const zero_field_values = field_value_buffer[field_count..][0..zero_fields.len]; - const zero_gep = module.llvm.builder.create_struct_gep(llvm_struct, alloca, field_count); - const zero_value = module.integer_type(8, false).llvm.handle.to_integer().get_constant(0, @intFromBool(false)).to_value(); - const raw_byte_count = ty.get_byte_size() - struct_type.fields[field_count].byte_offset; - const byte_count = module.integer_type(64, false).llvm.handle.to_integer().get_constant(raw_byte_count, @intFromBool(false)).to_value(); - _ = module.llvm.builder.create_memset(zero_gep, zero_value, byte_count, 1); - } else { - assert(field_count == struct_type.fields.len); - } - - break :blk alloca; - }, - }; - - const value = module.values.add(); - value.* = .{ - .llvm = llvm_value, - .type = ty, - .bb = .{ - .struct_initialization = .{ - .is_constant = is_constant, - }, - }, - .lvalue = true, - .dereference_to_assign = false, - }; - - return value; - }, - .bits => |bits| { - var field_count: u64 = 0; - - var llvm_value = bits.backing_type.llvm.handle.to_integer().get_constant(0, @intFromBool(false)).to_value(); - - var zero = false; - while (true) : (field_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } else if (module.consume_character_if_match('.')) { - const field_name = module.parse_identifier(); - const field_index: u32 = for (bits.fields, 0..) |*field, field_index| { - if (lib.string.equal(field.name, field_name)) { - break @intCast(field_index); - } - } else module.report_error(); - - const field = bits.fields[field_index]; - - module.skip_space(); - - module.expect_character('='); - - module.skip_space(); - - const field_value = module.parse_value(module, field.type, .value); - - const extended_field_value = module.llvm.builder.create_zero_extend(field_value.llvm, bits.backing_type.llvm.handle); - const shifted_value = module.llvm.builder.create_shl(extended_field_value, bits.backing_type.llvm.handle.to_integer().get_constant(field.bit_offset, @intFromBool(false)).to_value()); - const or_value = module.llvm.builder.create_or(llvm_value, shifted_value); - llvm_value = or_value; - - module.skip_space(); - - _ = module.consume_character_if_match(','); - - module.skip_space(); - } else { - const identifier = module.parse_identifier(); - if (lib.string.to_enum(ValueKeyword, identifier)) |value_keyword| switch (value_keyword) { - ._ => module.report_error(), - .undefined => @trap(), - .zero => { - zero = true; - module.skip_space(); - _ = module.consume_character_if_match(','); - module.skip_space(); - module.expect_character(right_brace); - // We need to break here otherwise `field_count` would be incremented - break; - }, - .@"unreachable" => @trap(), - } else { - module.report_error(); - } - } - } - - if (field_count != bits.fields.len) { - // expect: 'zero' keyword - if (zero or bits.implicit_backing_type) { - // TODO: should we do anything? - } else { - @trap(); - } - } - - const value = module.values.add(); - value.* = .{ - .llvm = llvm_value, - .type = ty, - .bb = .bits_initialization, - .lvalue = false, - .dereference_to_assign = false, - }; - - return value; - }, - else => module.report_error(), - } - }, - left_bracket => { - module.offset += 1; - - const ty = expected_type orelse module.report_error(); - switch (ty.bb) { - .array => |*array| { - var element_count: u64 = 0; - var element_buffer: [64]*llvm.Value = undefined; - - var elements_are_constant = true; - - while (true) : (element_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_bracket)) { - break; - } - - const element_value = module.parse_value(module, array.element_type, .value); - elements_are_constant = elements_are_constant and element_value.is_constant(); - element_buffer[element_count] = element_value.llvm; - - module.skip_space(); - - _ = module.consume_character_if_match(','); - } - - if (array.element_count == null) { - array.element_count = element_count; - ty.llvm = array_type_llvm(module, array.*); - ty.name = array_type_name(module.arena, array.*); - } - - const array_elements = element_buffer[0..element_count]; - if (elements_are_constant) { - const constant_array = array.element_type.llvm.handle.get_constant_array(@ptrCast(array_elements)); - const global = switch (module.current_function == null) { - true => constant_array.to_value(), - false => b: { - const global_variable = module.llvm.handle.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = module.arena.join_string(&.{ "__const.", module.current_function.?.name, if (ty.name) |n| n else "" }), - .initial_value = constant_array, - .type = ty.llvm.handle, - }); - global_variable.set_unnamed_address(.global); - break :b global_variable.to_value(); - }, - }; - const value = module.values.add(); - value.* = .{ - .llvm = global, - .type = ty, - .bb = .constant_array, - .lvalue = true, - .dereference_to_assign = false, - }; - return value; - } else { - @trap(); - } - - @trap(); - }, - else => @trap(), - } - }, - '#' => return module.parse_value_intrinsic(module, expected_type), - '&' => { - module.offset += 1; - const value = module.parse_value(module, expected_type, .pointer); - - if (expected_type) |expected_ty| { - if (expected_ty.is_slice()) { - switch (value.type.bb) { - .pointer => |pointer| switch (pointer.type.bb) { - .array => |array| { - switch (value_kind) { - .value => { - const slice_poison = expected_ty.llvm.handle.get_poison(); - const pointer_insert = module.llvm.builder.create_insert_value(slice_poison, value.llvm, 0); - const length_value = module.integer_type(64, false).llvm.handle.to_integer().get_constant(array.element_count.?, @intFromBool(false)); - const length_insert = module.llvm.builder.create_insert_value(pointer_insert, length_value.to_value(), 1); - const result = module.values.add(); - result.* = .{ - .llvm = length_insert, - .type = expected_ty, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - return result; - }, - else => |t| @panic(@tagName(t)), - } - }, - else => @trap(), - }, - else => @trap(), - } - @trap(); - } - } - - return value; - }, - '!' => blk: { - module.offset += 1; - - // TODO: should we skip space here? - module.skip_space(); - break :blk .not_zero; - }, - '.' => { - const expected_ty = expected_type orelse module.report_error(); - if (expected_ty.bb != .enumerator) { - module.report_error(); - } - module.offset += 1; - - module.skip_space(); - const field_name = module.parse_identifier(); - const field_value = for (expected_ty.bb.enumerator.fields) |*field| { - if (lib.string.equal(field.name, field_name)) { - break field.value; - } - } else { - module.report_error(); - }; - const value = module.values.add(); - value.* = .{ - .bb = .{ - .constant_integer = .{ - .value = field_value, - .signed = false, - }, - }, - .llvm = expected_ty.llvm.handle.to_integer().get_constant(field_value, @intFromBool(false)).to_value(), - .type = expected_ty, - .lvalue = false, - .dereference_to_assign = false, - }; - - return value; - }, - '"' => { - module.offset += 1; - - const string_start = module.offset; - // TODO: better string handling (escape characters and such) - while (!module.consume_character_if_match('"')) { - module.offset += 1; - } - const string_end = module.offset - 1; - const string_length = string_end - string_start; - const string = module.content[string_start..][0..string_length]; - const null_terminate = true; - const constant_string = module.llvm.context.get_constant_string(string, null_terminate); - switch (module.current_function == null) { - true => @trap(), - false => { - const u8_type = module.integer_type(8, false); - const global_variable = module.llvm.handle.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = module.arena.join_string(&.{ "__const.", module.current_function.?.name, ".string" }), - .initial_value = constant_string, - .type = u8_type.llvm.handle.get_array_type(string.len + @intFromBool(null_terminate)).to_type(), - }); - global_variable.set_unnamed_address(.global); - - const slice_type = module.get_slice_type(.{ - .type = u8_type, - }); - - const slice_poison = slice_type.llvm.handle.get_poison(); - const slice_pointer = module.llvm.builder.create_insert_value(slice_poison, global_variable.to_value(), 0); - const slice_length = module.llvm.builder.create_insert_value(slice_pointer, module.integer_type(64, false).llvm.handle.to_integer().get_constant(string.len, @intFromBool(false)).to_value(), 1); - const slice = slice_length; - - const value = module.values.add(); - value.* = .{ - .type = slice_type, - .bb = .instruction, - .llvm = slice, - .lvalue = false, - .dereference_to_assign = false, - }; - return value; - }, - } - @trap(); - }, - '\'' => { - module.offset += 1; - // TODO: UTF-8 - const ch = module.content[module.offset]; - // TODO: escape character - assert(ch != '\\'); - module.offset += 1; - module.expect_character('\''); - const value = module.values.add(); - const u8_type = module.integer_type(8, false); - value.* = .{ - .llvm = u8_type.llvm.handle.to_integer().get_constant(ch, @intFromBool(false)).to_value(), - .type = u8_type, - .bb = .{ - .constant_integer = .{ - .value = ch, - .signed = false, - }, - }, - .lvalue = false, - .dereference_to_assign = false, - }; - - return value; - }, - else => os.abort(), - }; - - const value_offset = module.offset; - const value_start_ch = module.content[value_offset]; - var value = switch (value_start_ch) { - 'a'...'z', 'A'...'Z', '_' => b: { - const identifier = module.parse_identifier(); - - if (lib.string.to_enum(ValueKeyword, identifier)) |value_keyword| switch (value_keyword) { - ._ => return module.void_value, - .undefined => { - const expected_ty = expected_type orelse module.report_error(); - // TODO: cache poison - const value = module.values.add(); - value.* = .{ - .llvm = expected_ty.llvm.handle.get_poison(), - .type = expected_ty, - .bb = .instruction, // TODO - .lvalue = false, - .dereference_to_assign = false, - }; - return value; - }, - .zero => { - const ty = expected_type orelse module.report_error(); - - return module.get_zero_value(ty); - }, - .@"unreachable" => { - _ = module.llvm.builder.create_unreachable(); - module.llvm.builder.clear_insertion_position(); - return module.unreachable_value; - }, - }; - - if (module.current_function) |current_function| { - const variable = if (current_function.value.bb.function.locals.find(identifier)) |local| local else if (current_function.value.bb.function.arguments.find(identifier)) |argument| argument else if (module.globals.find(identifier)) |global| global else module.report_error(); - - module.skip_space(); - - assert(variable.value.type.bb == .pointer); - const appointee_type = variable.value.type.bb.pointer.type; - - if (module.consume_character_if_match(left_parenthesis)) { - const call = module.parse_call(module, variable.value); - break :b call; - } else if (module.consume_character_if_match('.')) { - module.skip_space(); - - switch (appointee_type.bb) { - .structure => |*struct_type| { - const field_name = module.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 module.report_error(); - const field = struct_type.fields[field_index]; - const gep = module.llvm.builder.create_struct_gep(appointee_type.llvm.handle.to_struct(), variable.value.llvm, field_index); - - switch (value_kind) { - .pointer, .maybe_pointer => { - @trap(); - }, - .value => { - const load = module.values.add(); - load.* = .{ - .llvm = module.create_load(.{ .type = field.type, .value = gep }), - .type = field.type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b load; - }, - } - }, - .bits => |*bits| { - const field_name = module.parse_identifier(); - const field_index: u32 = for (bits.fields, 0..) |field, field_index| { - if (lib.string.equal(field.name, field_name)) { - break @intCast(field_index); - } - } else module.report_error(); - const field = bits.fields[field_index]; - - const bitfield_load = module.create_load(.{ .type = bits.backing_type, .value = variable.value.llvm }); - const bitfield_shifted = module.llvm.builder.create_lshr(bitfield_load, bits.backing_type.llvm.handle.to_integer().get_constant(field.bit_offset, @intFromBool(false)).to_value()); - const bitfield_masked = module.llvm.builder.create_and(bitfield_shifted, bits.backing_type.llvm.handle.to_integer().get_constant((@as(u64, 1) << @intCast(field.type.get_bit_size())) - 1, @intFromBool(false)).to_value()); - - if (value_kind == .pointer) { - module.report_error(); - } - - const value = module.values.add(); - - value.* = .{ - .bb = .instruction, - .llvm = switch (bits.backing_type == field.type) { - true => bitfield_masked, - false => blk: { - assert(bits.backing_type.get_bit_size() > field.type.get_bit_size()); - const trunc = module.llvm.builder.create_truncate(bitfield_masked, field.type.llvm.handle); - break :blk trunc; - }, - }, - .type = field.type, - .lvalue = false, - .dereference_to_assign = false, - }; - - break :b value; - }, - .pointer => |pointer_type| { - const element_type = pointer_type.type; - if (module.consume_character_if_match('&')) { - const pointer_load = module.values.add(); - pointer_load.* = .{ - .llvm = module.create_load(.{ .type = appointee_type, .value = variable.value.llvm }), - .type = appointee_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - switch (value_kind) { - .value => { - if (expected_type) |expected_ty| { - if (expected_ty == appointee_type) { - @trap(); - } else { - assert(appointee_type.bb == .pointer); // TODO ????? - if (appointee_type.bb == .pointer and element_type == expected_ty) { - const load = module.values.add(); - load.* = .{ - .llvm = module.create_load(.{ .type = element_type, .value = pointer_load.llvm }), - .type = element_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b load; - } else { - module.report_error(); - } - } - } else { - const load = module.values.add(); - load.* = .{ - .llvm = module.create_load(.{ .type = element_type, .value = pointer_load.llvm }), - .type = element_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b load; - } - }, - .maybe_pointer, .pointer => { - if (expected_type) |expected_ty| { - _ = expected_ty; - @trap(); - } else { - break :b pointer_load; - } - }, - } - } else { - switch (element_type.bb) { - .structure => |*struct_type| { - const field_name = module.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 module.report_error(); - const field = struct_type.fields[field_index]; - const gep = module.llvm.builder.create_struct_gep(element_type.llvm.handle.to_struct(), variable.value.llvm, field_index); - switch (value_kind) { - .pointer, .maybe_pointer => { - @trap(); - }, - .value => { - const load = module.values.add(); - load.* = .{ - .llvm = module.create_load(.{ .type = field.type, .value = gep }), - .type = field.type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b load; - }, - } - }, - else => @trap(), - } - } - }, - else => @trap(), - } - } else if (module.consume_character_if_match(left_bracket)) { - module.skip_space(); - - const index_type = module.integer_type(64, false); - const index = module.parse_value(module, index_type, .value); - - const ArrayExpressionKind = enum { - array, - slice, - }; - const array_expression_kind: ArrayExpressionKind = if (module.consume_character_if_match(right_bracket)) .array else .slice; - switch (array_expression_kind) { - .array => { - const llvm_index_type = module.integer_type(64, false).llvm.handle.to_integer(); - const zero_index = llvm_index_type.get_constant(0, @intFromBool(false)).to_value(); - - switch (value_kind) { - .pointer, .maybe_pointer => { - @trap(); - }, - .value => { - switch (appointee_type.bb) { - .array => |array| { - const gep = module.llvm.builder.create_gep(.{ - .type = appointee_type.llvm.handle, - .aggregate = variable.value.llvm, - .indices = &.{ zero_index, index.llvm }, - }); - - const load_type = array.element_type; - const load = module.values.add(); - load.* = .{ - .llvm = module.create_load(.{ .type = load_type, .value = gep }), - .type = load_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b load; - }, - .pointer => |pointer| { - const pointer_load = module.create_load(.{ .type = appointee_type, .value = variable.value.llvm }); - const gep = module.llvm.builder.create_gep(.{ - .type = pointer.type.llvm.handle, - .aggregate = pointer_load, - .indices = &.{index.llvm}, - .inbounds = false, - }); - - const load_type = pointer.type; - const load = module.values.add(); - load.* = .{ - .llvm = module.create_load(.{ .type = load_type, .value = gep }), - .type = load_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b load; - }, - .structure => |structure| { - if (!structure.is_slice) { - module.report_error(); - } - - const gep_to_pointer_field = module.llvm.builder.create_struct_gep(appointee_type.llvm.handle.to_struct(), variable.value.llvm, 0); - const pointer_type = structure.fields[0].type; - const element_type = pointer_type.bb.pointer.type; - const pointer_load = module.create_load(.{ .type = pointer_type, .value = gep_to_pointer_field }); - const gep_to_element = module.llvm.builder.create_gep(.{ - .type = element_type.llvm.handle, - .aggregate = pointer_load, - .indices = &.{index.llvm}, - .inbounds = false, - }); - const element_load = module.create_load(.{ .type = element_type, .value = gep_to_element, .alignment = pointer_type.bb.pointer.alignment }); - const load = module.values.add(); - load.* = .{ - .llvm = element_load, - .type = element_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b load; - }, - else => module.report_error(), - } - }, - } - }, - .slice => { - const start_index = index; - module.expect_character('.'); - module.expect_character('.'); - module.skip_space(); - - if (module.consume_character_if_match(right_bracket)) { - switch (appointee_type.bb) { - .structure => |structure| { - if (!structure.is_slice) { - module.report_error(); - } - - const slice_type = appointee_type; - const slice_pointer_type = structure.fields[0].type; - const slice_element_type = slice_pointer_type.bb.pointer.type; - const slice_load = module.create_load(.{ .type = slice_type, .value = variable.value.llvm }); - const original_pointer_field = module.llvm.builder.create_extract_value(slice_load, 0); - const original_length_field = module.llvm.builder.create_extract_value(slice_load, 1); - const pointer_field = module.llvm.builder.create_gep(.{ .type = slice_element_type.llvm.handle, .aggregate = original_pointer_field, .indices = &.{start_index.llvm} }); - const length_field = module.llvm.builder.create_sub(original_length_field, start_index.llvm); - - const slice_poison = slice_type.llvm.handle.get_poison(); - const slice_pointer = module.llvm.builder.create_insert_value(slice_poison, pointer_field, 0); - const slice_length = module.llvm.builder.create_insert_value(slice_pointer, length_field, 1); - const slice_value = slice_length; - - const result = module.values.add(); - result.* = .{ - .llvm = slice_value, - .type = slice_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - return result; - }, - else => @trap(), - } - } else { - const end_index = module.parse_value(module, index_type, .value); - module.skip_space(); - module.expect_character(right_bracket); - - if (start_index.bb == .constant_integer and end_index.bb == .constant_integer) { - @trap(); - } else if (start_index.bb == .constant_integer) { - switch (appointee_type.bb) { - .pointer => { - const slice_type = module.get_slice_type(.{ .type = appointee_type }); - const pointer_load = module.create_load(.{ .type = appointee_type, .value = variable.value.llvm }); - const resulting_pointer = if (start_index.bb.constant_integer.value == 0) pointer_load else module.llvm.builder.create_gep(.{ - .type = variable.value.type.llvm.handle, - .aggregate = pointer_load, - .indices = &.{start_index.llvm}, - }); - const resulting_length = if (start_index.bb.constant_integer.value == 0) end_index.llvm else @trap(); - const slice_poison = slice_type.llvm.handle.get_poison(); - const pointer_insert = module.llvm.builder.create_insert_value(slice_poison, resulting_pointer, 0); - const length_insert = module.llvm.builder.create_insert_value(pointer_insert, resulting_length, 1); - const slice_value = length_insert; - - const result = module.values.add(); - result.* = .{ - .llvm = slice_value, - .type = slice_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - return result; - }, - else => @trap(), - } - @trap(); - } else if (end_index.bb == .constant_integer) { - @trap(); - } else { - @trap(); - } - } - }, - } - } else { - switch (value_kind) { - .pointer, .maybe_pointer => break :b variable.value, - .value => switch (appointee_type.get_evaluation_kind()) { - .aggregate => { - const value_address = module.values.add(); - value_address.* = .{ - .llvm = variable.value.llvm, - .type = variable.value.type, - .bb = .instruction, - .lvalue = true, - .dereference_to_assign = true, - }; - break :b value_address; - }, - else => { - const load = module.values.add(); - load.* = .{ - .llvm = module.create_load(.{ .type = appointee_type, .value = variable.value.llvm }), - .type = appointee_type, - .bb = .instruction, - .lvalue = false, - .dereference_to_assign = false, - }; - break :b load; - }, - }, - } - } - } else { - module.report_error(); - } - }, - '0'...'9' => module.parse_integer(module, expected_type.?, prefix == .negative), - else => os.abort(), - }; - - switch (prefix) { - .none, - .negative, // Already done in 'parse_integer' // TODO: - => {}, - .not_zero => { - const llvm_value = module.llvm.builder.create_compare(.eq, value.llvm, switch (value.type.bb) { - .integer => value.type.llvm.handle.to_integer().get_constant(0, 0).to_value(), - .pointer => value.type.llvm.handle.get_zero().to_value(), - else => @trap(), - }); - value.* = .{ - .llvm = llvm_value, - .bb = .instruction, - .type = module.integer_type(1, false), - .lvalue = false, - .dereference_to_assign = false, - }; - }, - } - - return value; - } - - pub fn create_coerced_store(module: *Module, source_value: *llvm.Value, source_type: *Type, destination: *llvm.Value, destination_ty: *Type, destination_size: u64, destination_volatile: bool) void { - _ = destination_volatile; - var destination_type = destination_ty; - var destination_pointer = destination; - const source_size = source_type.get_byte_size(); - if (!source_type.is_abi_equal(destination_type)) { - const r = module.enter_struct_pointer_for_coerced_access(destination_pointer, destination_type, source_size); - destination_pointer = r.value; - destination_type = r.type; - } - - const is_scalable = false; // TODO - if (is_scalable or source_size <= destination_size) { - const destination_alignment = destination_type.get_byte_alignment(); - if (source_type.bb == .integer and destination_type.bb == .pointer and source_size == lib.align_forward_u64(destination_size, destination_alignment)) { - @trap(); - } else if (source_type.bb == .structure) { - for (source_type.bb.structure.fields, 0..) |field, field_index| { - // TODO: volatile - const gep = module.llvm.builder.create_struct_gep(source_type.llvm.handle.to_struct(), destination_pointer, @intCast(field_index)); - const field_value = module.llvm.builder.create_extract_value(source_value, @intCast(field_index)); - _ = module.create_store(.{ - .source_value = field_value, - .source_type = field.type, - .destination_value = gep, - .destination_type = field.type, - .alignment = destination_alignment, - }); - } - } else { - _ = module.create_store(.{ - .source_value = source_value, - .source_type = source_type, - .destination_value = destination_pointer, - .destination_type = destination_type, - .alignment = destination_alignment, - }); - } - // TODO: is this valid for pointers too? - } else if (source_type.is_integer_backing()) { - @trap(); - } else { - // Coercion through memory - const original_destination_alignment = destination_type.get_byte_alignment(); - const source_alloca_alignment = @max(original_destination_alignment, source_type.get_byte_alignment()); - const source_alloca = module.create_alloca(.{ .type = source_type, .alignment = source_alloca_alignment, .name = "coerce" }); - _ = module.create_store(.{ - .source_value = source_value, - .destination_value = source_alloca, - .source_type = source_type, - .destination_type = source_type, - .alignment = source_alloca_alignment, - }); - _ = module.llvm.builder.create_memcpy(destination_pointer, original_destination_alignment, source_alloca, source_alloca_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(destination_size, @intFromBool(false)).to_value()); - } - } -}; - -fn is_space(ch: u8) bool { - return ((@intFromBool(ch == ' ') | @intFromBool(ch == '\n')) | ((@intFromBool(ch == '\t') | @intFromBool(ch == '\r')))) != 0; -} - -const StatementStartKeyword = enum { - @"_", - @"return", - @"if", - // TODO: make `unreachable` a statement start keyword? - @"while", -}; - -pub const BuildMode = enum { - debug_none, - debug_fast, - debug_size, - soft_optimize, - optimize_for_speed, - optimize_for_size, - aggressively_optimize_for_speed, - aggressively_optimize_for_size, - - fn is_optimized(build_mode: BuildMode) bool { - return @intFromEnum(build_mode) >= @intFromEnum(BuildMode.soft_optimize); - } - - fn to_llvm_ir(build_mode: BuildMode) llvm.OptimizationLevel { - return switch (build_mode) { - .debug_none => unreachable, - .debug_fast, .debug_size => .O0, - .soft_optimize => .O1, - .optimize_for_speed => .O2, - .optimize_for_size => .Os, - .aggressively_optimize_for_speed => .O3, - .aggressively_optimize_for_size => .Oz, - }; - } - - fn to_llvm_machine(build_mode: BuildMode) llvm.CodeGenerationOptimizationLevel { - return switch (build_mode) { - .debug_none => .none, - .debug_fast, .debug_size => .none, - .soft_optimize => .less, - .optimize_for_speed => .default, - .optimize_for_size => .default, - .aggressively_optimize_for_speed => .aggressive, - .aggressively_optimize_for_size => .aggressive, - }; - } -}; - -const CPUArchitecture = enum { - x86_64, -}; - -const OperatingSystem = enum { - linux, -}; - -pub const Target = struct { - cpu: CPUArchitecture, - os: OperatingSystem, - - pub fn get_native() Target { - const builtin = @import("builtin"); - return Target{ - .cpu = switch (builtin.cpu.arch) { - .x86_64 => .x86_64, - else => @compileError("CPU not supported"), - }, - .os = switch (builtin.os.tag) { - .linux => .linux, - else => @compileError("OS not supported"), - }, - }; - } -}; - -pub const Abi = struct { - const Kind = enum(u3) { - ignore, - direct, - extend, - indirect, - indirect_aliased, - expand, - coerce_and_expand, - in_alloca, - }; - - const RegisterCount = union { - system_v: Abi.SystemV.RegisterCount, - }; - - const Flags = packed struct { - kind: Kind, - padding_in_reg: bool = false, - in_alloca_sret: bool = false, - in_alloca_indirect: bool = false, - indirect_by_value: bool = false, - indirect_realign: bool = false, - sret_after_this: bool = false, - in_reg: bool = false, - can_be_flattened: bool = false, - sign_extension: bool = false, - }; - - const Information = struct { - semantic_type: *Type, - coerce_to_type: ?*Type = null, - padding: union { - type: ?*Type, - unpadded_coerce_and_expand_type: ?*Type, - } = .{ .type = null }, - padding_arg_index: u16 = 0, - attributes: union { - direct: DirectAttributes, - indirect: IndirectAttributes, - alloca_field_index: u32, - } = .{ - .direct = .{ - .offset = 0, - .alignment = 0, - }, - }, - flags: Abi.Flags, - abi_start: u16 = 0, - abi_count: u16 = 0, - - const DirectAttributes = struct { - offset: u32, - alignment: u32, - }; - - const IndirectAttributes = struct { - alignment: u32, - address_space: u32, - }; - - const Direct = struct { - semantic_type: *Type, - type: *Type, - padding: ?*Type = null, - offset: u32 = 0, - alignment: u32 = 0, - can_be_flattened: bool = true, - }; - - pub fn get_direct(direct: Direct) Information { - var result = Information{ - .semantic_type = direct.semantic_type, - .flags = .{ - .kind = .direct, - }, - }; - result.set_coerce_to_type(direct.type); - result.set_padding_type(direct.padding); - result.set_direct_offset(direct.offset); - result.set_direct_alignment(direct.alignment); - result.set_can_be_flattened(direct.can_be_flattened); - return result; - } - - pub const Ignore = struct { - semantic_type: *Type, - }; - - pub fn get_ignore(ignore: Ignore) Information { - return Information{ - .semantic_type = ignore.semantic_type, - .flags = .{ - .kind = .ignore, - }, - }; - } - - const Extend = struct { - semantic_type: *Type, - type: ?*Type = null, - sign: bool, - }; - - pub fn get_extend(extend: Extend) Information { - assert(extend.semantic_type.is_integral_or_enumeration_type()); - var result = Information{ - .semantic_type = extend.semantic_type, - .flags = .{ - .kind = .extend, - }, - }; - result.set_coerce_to_type(if (extend.type) |t| t else extend.semantic_type); - result.set_padding_type(null); - result.set_direct_offset(0); - result.set_direct_alignment(0); - result.flags.sign_extension = extend.sign; - return result; - } - - const NaturalAlignIndirect = struct { - semantic_type: *Type, - padding_type: ?*Type = null, - by_value: bool = true, - realign: bool = false, - }; - - pub fn get_natural_align_indirect(nai: NaturalAlignIndirect) Abi.Information { - const alignment = nai.semantic_type.get_byte_alignment(); - return get_indirect(.{ - .semantic_type = nai.semantic_type, - .alignment = alignment, - .by_value = nai.by_value, - .realign = nai.realign, - .padding_type = nai.padding_type, - }); - } - - pub const Indirect = struct { - semantic_type: *Type, - padding_type: ?*Type = null, - alignment: u32, - by_value: bool = true, - realign: bool = false, - }; - - pub fn get_indirect(indirect: Indirect) Abi.Information { - var result = Abi.Information{ - .semantic_type = indirect.semantic_type, - .attributes = .{ - .indirect = .{ - .address_space = 0, - .alignment = 0, - }, - }, - .flags = .{ - .kind = .indirect, - }, - }; - result.set_indirect_align(indirect.alignment); - result.set_indirect_by_value(indirect.by_value); - result.set_indirect_realign(indirect.realign); - result.set_sret_after_this(false); - result.set_padding_type(indirect.padding_type); - return result; - } - - fn set_sret_after_this(abi: *Abi.Information, sret_after_this: bool) void { - assert(abi.flags.kind == .indirect); - abi.flags.sret_after_this = sret_after_this; - } - - fn set_indirect_realign(abi: *Abi.Information, realign: bool) void { - assert(abi.flags.kind == .indirect); - abi.flags.indirect_realign = realign; - } - - fn set_indirect_by_value(abi: *Abi.Information, by_value: bool) void { - assert(abi.flags.kind == .indirect); - abi.flags.indirect_by_value = by_value; - } - - fn set_indirect_align(abi: *Abi.Information, alignment: u32) void { - assert(abi.flags.kind == .indirect or abi.flags.kind == .indirect_aliased); - abi.attributes.indirect.alignment = alignment; - } - - fn set_coerce_to_type(info: *Information, coerce_to_type: *Type) void { - assert(info.can_have_coerce_to_type()); - info.coerce_to_type = coerce_to_type; - } - - fn get_coerce_to_type(info: *const Information) *Type { - assert(info.can_have_coerce_to_type()); - return info.coerce_to_type.?; - } - - fn can_have_coerce_to_type(info: *const Information) bool { - return switch (info.flags.kind) { - .direct, .extend, .coerce_and_expand => true, - else => false, - }; - } - - fn set_padding_type(info: *Information, padding_type: ?*Type) void { - assert(info.can_have_padding_type()); - info.padding = .{ - .type = padding_type, - }; - } - - fn can_have_padding_type(info: *const Information) bool { - return switch (info.flags.kind) { - .direct, .extend, .indirect, .indirect_aliased, .expand => true, - else => false, - }; - } - - fn get_padding_type(info: *const Information) ?*Type { - return if (info.can_have_padding_type()) info.padding.type else null; - } - - fn set_direct_offset(info: *Information, offset: u32) void { - assert(info.flags.kind == .direct or info.flags.kind == .extend); - info.attributes.direct.offset = offset; - } - - fn set_direct_alignment(info: *Information, alignment: u32) void { - assert(info.flags.kind == .direct or info.flags.kind == .extend); - info.attributes.direct.alignment = alignment; - } - - fn set_can_be_flattened(info: *Information, can_be_flattened: bool) void { - assert(info.flags.kind == .direct); - info.flags.can_be_flattened = can_be_flattened; - } - - fn get_can_be_flattened(info: *const Information) bool { - assert(info.flags.kind == .direct); - return info.flags.can_be_flattened; - } - }; - - pub const SystemV = struct { - pub const RegisterCount = struct { - gpr: u32, - sse: u32, - }; - - pub const Class = enum { - integer, - sse, - sseup, - x87, - x87up, - complex_x87, - none, - memory, - - fn merge(accumulator: Class, field: Class) Class { - // AMD64-ABI 3.2.3p2: Rule 4. Each field of an object is - // classified recursively so that always two fields are - // considered. The resulting class is calculated according to - // the classes of the fields in the eightbyte: - // - // (a) If both classes are equal, this is the resulting class. - // - // (b) If one of the classes is NO_CLASS, the resulting class is - // the other class. - // - // (c) If one of the classes is MEMORY, the result is the MEMORY - // class. - // - // (d) If one of the classes is INTEGER, the result is the - // INTEGER. - // - // (e) If one of the classes is X87, X87UP, COMPLEX_X87 class, - // MEMORY is used as class. - // - // (f) Otherwise class SSE is used. - - // Accum should never be memory (we should have returned) or - // ComplexX87 (because this cannot be passed in a structure). - - assert(accumulator != .memory and accumulator != .complex_x87); - if (accumulator == field or field == .none) { - return accumulator; - } - - if (field == .memory) { - return .memory; - } - - if (accumulator == .none) { - return field; - } - - if (accumulator == .integer or field == .integer) { - return .integer; - } - - if (field == .x87 or field == .x87up or field == .complex_x87 or accumulator == .x87 or accumulator == .x87up) { - return .memory; - } - - return .sse; - } - }; - - const ClassifyOptions = struct { - base_offset: u64, - is_named_argument: bool, - is_register_call: bool = false, - }; - - fn classify(ty: *Type, options: ClassifyOptions) [2]Class { - var result = [2]Class{ .none, .none }; - - const is_memory = options.base_offset >= 8; - const current_index = @intFromBool(is_memory); - const not_current_index = @intFromBool(!is_memory); - assert(current_index != not_current_index); - result[current_index] = .memory; - - switch (ty.bb) { - .void, .noreturn => result[current_index] = .none, - .bits => result[current_index] = .integer, - .pointer => result[current_index] = .integer, - .integer => |integer| { - if (integer.bit_count <= 64) { - result[current_index] = .integer; - } else if (integer.bit_count == 128) { - @trap(); - } else { - @trap(); - } - }, - .structure => |struct_type| { - if (struct_type.byte_size <= 64) { - const has_variable_array = false; - if (!has_variable_array) { - // const struct_type = ty.get_payload(.@"struct"); - result[current_index] = .none; - const is_union = false; - var member_offset: u32 = 0; - for (struct_type.fields) |field| { - const offset = options.base_offset + member_offset; - const member_size = field.type.get_byte_size(); - const member_alignment = field.type.get_byte_alignment(); - member_offset = @intCast(lib.align_forward_u64(member_offset + member_size, ty.get_byte_alignment())); - const native_vector_size = 16; - if (ty.get_byte_size() > 16 and ((!is_union and ty.get_byte_size() != member_size) or ty.get_byte_size() > native_vector_size)) { - result[0] = .memory; - const r = classify_post_merge(ty.get_byte_size(), result); - return r; - } - - if (offset % member_alignment != 0) { - result[0] = .memory; - const r = classify_post_merge(ty.get_byte_size(), result); - return r; - } - - const member_classes = classify(field.type, .{ - .base_offset = offset, - .is_named_argument = false, - }); - for (&result, member_classes) |*r, m| { - const merge_result = r.merge(m); - r.* = merge_result; - } - - if (result[0] == .memory or result[1] == .memory) break; - } - - const final = classify_post_merge(ty.get_byte_size(), result); - result = final; - } - } - }, - .array => |*array_type| { - if (ty.get_byte_size() <= 64) { - if (options.base_offset % ty.get_byte_alignment() == 0) { - result[current_index] = .none; - - const vector_size = 16; - if (ty.get_byte_size() > 16 and (ty.get_byte_size() != array_type.element_type.get_byte_size() or ty.get_byte_size() > vector_size)) { - unreachable; - } else { - var offset = options.base_offset; - - for (0..array_type.element_count.?) |_| { - const element_classes = classify(array_type.element_type, .{ - .base_offset = offset, - .is_named_argument = false, - }); - offset += array_type.element_type.get_byte_size(); - const merge_result = [2]Class{ result[0].merge(element_classes[0]), result[1].merge(element_classes[1]) }; - result = merge_result; - if (result[0] == .memory or result[1] == .memory) { - break; - } - } - - const final_result = classify_post_merge(ty.get_byte_size(), result); - assert(final_result[1] != .sseup or final_result[0] != .sse); - result = final_result; - } - } - } - }, - else => @trap(), - } - - return result; - } - - fn classify_post_merge(aggregate_size: u64, classes: [2]Class) [2]Class { - // AMD64-ABI 3.2.3p2: Rule 5. Then a post merger cleanup is done: - // - // (a) If one of the classes is Memory, the whole argument is passed in - // memory. - // - // (b) If X87UP is not preceded by X87, the whole argument is passed in - // memory. - // - // (c) If the size of the aggregate exceeds two eightbytes and the first - // eightbyte isn't SSE or any other eightbyte isn't SSEUP, the whole - // argument is passed in memory. NOTE: This is necessary to keep the - // ABI working for processors that don't support the __m256 type. - // - // (d) If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE. - // - // Some of these are enforced by the merging logic. Others can arise - // only with unions; for example: - // union { _Complex double; unsigned; } - // - // Note that clauses (b) and (c) were added in 0.98. - - var result = classes; - if (result[1] == .memory) { - result[0] = .memory; - } - - if (result[1] == .x87up) { - @trap(); - } - - if (aggregate_size > 16 and (result[0] != .sse or result[1] != .sseup)) { - result[0] = .memory; - } - - if (result[1] == .sseup and result[0] != .sse) { - result[0] = .sse; - } - - return result; - } - - fn get_int_type_at_offset(module: *Module, ty: *Type, offset: u32, source_type: *Type, source_offset: u32) *Type { - switch (ty.bb) { - .bits => |bits| { - return get_int_type_at_offset(module, bits.backing_type, offset, if (source_type == ty) bits.backing_type else source_type, source_offset); - }, - .integer => |integer_type| { - switch (integer_type.bit_count) { - 64 => return ty, - 32, 16, 8 => { - if (offset != 0) unreachable; - const start = source_offset + ty.get_byte_size(); - const end = source_offset + 8; - if (contains_no_user_data(source_type, start, end)) { - return ty; - } - }, - else => return module.integer_type(@intCast(@min(ty.get_byte_size() - source_offset, 8) * 8), integer_type.signed), - } - }, - .pointer => return if (offset == 0) ty else @trap(), - .structure => { - if (get_member_at_offset(ty, offset)) |field| { - return get_int_type_at_offset(module, field.type, @intCast(offset - field.byte_offset), source_type, source_offset); - } - unreachable; - }, - .array => |array_type| { - const element_type = array_type.element_type; - const element_size = element_type.get_byte_size(); - const element_offset = (offset / element_size) * element_size; - return get_int_type_at_offset(module, element_type, @intCast(offset - element_offset), source_type, source_offset); - }, - else => |t| @panic(@tagName(t)), - } - - if (source_type.get_byte_size() - source_offset > 8) { - return module.integer_type(64, false); - } else { - const byte_count = source_type.get_byte_size() - source_offset; - const bit_count = byte_count * 8; - return module.integer_type(@intCast(bit_count), false); - } - } - - fn get_member_at_offset(ty: *Type, offset: u32) ?*const Field { - if (ty.get_byte_size() <= offset) { - return null; - } - - var offset_it: u32 = 0; - var last_match: ?*const Field = null; - - const struct_type = &ty.bb.structure; - for (struct_type.fields) |*field| { - if (offset_it > offset) { - break; - } - - last_match = field; - offset_it = @intCast(lib.align_forward_u64(offset_it + field.type.get_byte_size(), ty.get_byte_alignment())); - } - - assert(last_match != null); - return last_match; - } - - fn contains_no_user_data(ty: *Type, start: u64, end: u64) bool { - if (ty.get_byte_size() <= start) { - return true; - } - - switch (ty.bb) { - .structure => |*struct_type| { - var offset: u64 = 0; - - for (struct_type.fields) |field| { - if (offset >= end) break; - const field_start = if (offset < start) start - offset else 0; - if (!contains_no_user_data(field.type, field_start, end - offset)) return false; - offset += field.type.get_byte_size(); - } - - return true; - }, - .array => |array_type| { - for (0..array_type.element_count.?) |i| { - const offset = i * array_type.element_type.get_byte_size(); - if (offset >= end) break; - const element_start = if (offset < start) start - offset else 0; - if (!contains_no_user_data(array_type.element_type, element_start, end - offset)) return false; - } - - return true; - }, - else => return false, - } - } - - const ArgumentOptions = struct { - available_gpr: u32, - is_named_argument: bool, - is_reg_call: bool, - }; - - pub fn classify_argument_type(module: *Module, argument_type: *Type, options: ArgumentOptions) struct { Abi.Information, Abi.SystemV.RegisterCount } { - const classes = classify(argument_type, .{ - .base_offset = 0, - .is_named_argument = options.is_named_argument, - }); - assert(classes[1] != .memory or classes[0] == .memory); - assert(classes[1] != .sseup or classes[0] == .sse); - var needed_registers = Abi.SystemV.RegisterCount{ - .gpr = 0, - .sse = 0, - }; - - var low: ?*Type = null; - switch (classes[0]) { - .integer => { - needed_registers.gpr += 1; - - const low_ty = Abi.SystemV.get_int_type_at_offset(module, argument_type, 0, argument_type, 0); - low = low_ty; - - if (classes[1] == .none and low_ty.bb == .integer) { - if (argument_type.bb == .enumerator) { - @trap(); - } - - if (argument_type.is_integral_or_enumeration_type() and argument_type.is_promotable_integer_type_for_abi()) { - return .{ - Abi.Information.get_extend(.{ - .semantic_type = argument_type, - .sign = argument_type.is_signed(), - }), - needed_registers, - }; - } - } - }, - .memory, .x87, .complex_x87 => { - // TODO: CXX ABI: RAA_Indirect - return .{ get_indirect_result(argument_type, options.available_gpr), needed_registers }; - }, - else => @trap(), - } - - var high: ?*Type = null; - switch (classes[1]) { - .none => {}, - .integer => { - needed_registers.gpr += 1; - const high_ty = Abi.SystemV.get_int_type_at_offset(module, argument_type, 8, argument_type, 8); - high = high_ty; - - if (classes[0] == .none) { - @trap(); - } - }, - else => @trap(), - } - - const result_type = if (high) |hi| get_by_val_argument_pair(module, low orelse unreachable, hi) else low orelse unreachable; - return .{ - Abi.Information.get_direct(.{ - .semantic_type = argument_type, - .type = result_type, - }), - needed_registers, - }; - } - - const ClassifyArgument = struct { - type: *Type, - abi_start: u16, - is_reg_call: bool = false, - is_named_argument: bool, - }; - - pub fn classify_argument(module: *Module, available_registers: *Abi.RegisterCount, llvm_abi_argument_type_buffer: []*llvm.Type, abi_argument_type_buffer: []*Type, options: ClassifyArgument) Abi.Information { - const semantic_argument_type = options.type; - const result = if (options.is_reg_call) @trap() else Abi.SystemV.classify_argument_type(module, semantic_argument_type, .{ - .is_named_argument = options.is_named_argument, - .is_reg_call = options.is_reg_call, - .available_gpr = available_registers.system_v.gpr, - }); - const abi = result[0]; - const needed_registers = result[1]; - - var argument_type_abi = switch (available_registers.system_v.gpr >= needed_registers.gpr and available_registers.system_v.sse >= needed_registers.sse) { - true => blk: { - available_registers.system_v.gpr -= needed_registers.gpr; - available_registers.system_v.sse -= needed_registers.sse; - break :blk abi; - }, - false => Abi.SystemV.get_indirect_result(semantic_argument_type, available_registers.system_v.gpr), - }; - - if (argument_type_abi.get_padding_type() != null) { - @trap(); - } - - argument_type_abi.abi_start = options.abi_start; - - const count = switch (argument_type_abi.flags.kind) { - .direct, .extend => blk: { - const coerce_to_type = argument_type_abi.get_coerce_to_type(); - const flattened_struct = argument_type_abi.flags.kind == .direct and argument_type_abi.get_can_be_flattened() and coerce_to_type.bb == .structure; - - const count: u16 = switch (flattened_struct) { - false => 1, - true => @intCast(argument_type_abi.get_coerce_to_type().bb.structure.fields.len), - }; - - switch (flattened_struct) { - false => { - llvm_abi_argument_type_buffer[argument_type_abi.abi_start] = coerce_to_type.llvm.handle; - abi_argument_type_buffer[argument_type_abi.abi_start] = coerce_to_type; - }, - true => { - for (coerce_to_type.bb.structure.fields, 0..) |field, field_index| { - const index = argument_type_abi.abi_start + field_index; - llvm_abi_argument_type_buffer[index] = field.type.llvm.handle; - abi_argument_type_buffer[index] = field.type; - } - }, - } - - break :blk count; - }, - .indirect => blk: { - const indirect_type = module.get_pointer_type(.{ .type = argument_type_abi.semantic_type }); - abi_argument_type_buffer[argument_type_abi.abi_start] = indirect_type; - llvm_abi_argument_type_buffer[argument_type_abi.abi_start] = indirect_type.llvm.handle; - break :blk 1; - }, - else => |t| @panic(@tagName(t)), - }; - - argument_type_abi.abi_count = count; - - return argument_type_abi; - } - - pub fn get_by_val_argument_pair(module: *Module, low: *Type, high: *Type) *Type { - const low_size = low.get_byte_allocation_size(); - const high_alignment = high.get_byte_alignment(); - const high_start = lib.align_forward_u64(low_size, high_alignment); - assert(high_start != 0 and high_start <= 8); - - const new_low = if (high_start != 8) { - @trap(); - } else low; - const result = module.get_anonymous_struct_pair(.{ new_low, high }); - assert(result.bb.structure.fields[1].byte_offset == 8); - return result; - } - - pub fn classify_return_type(module: *Module, return_type: *Type) Abi.Information { - const classes = classify(return_type, .{ - .base_offset = 0, - .is_named_argument = true, - }); - assert(classes[1] != .memory or classes[0] == .memory); - assert(classes[1] != .sseup or classes[0] == .sse); - - var low: ?*Type = null; - - switch (classes[0]) { - .none => { - if (classes[1] == .none) { - return Abi.Information.get_ignore(.{ - .semantic_type = return_type, - }); - } - - @trap(); - }, - .integer => { - const low_ty = Abi.SystemV.get_int_type_at_offset(module, return_type, 0, return_type, 0); - low = low_ty; - - if (classes[1] == .none and low_ty.bb == .integer) { - if (return_type.bb == .enumerator) { - @trap(); - } - - if (return_type.is_integral_or_enumeration_type() and return_type.is_promotable_integer_type_for_abi()) { - return Abi.Information.get_extend(.{ - .semantic_type = return_type, - .sign = return_type.is_signed(), - }); - } - } - }, - .memory => { - return Abi.SystemV.get_indirect_return_result(.{ .type = return_type }); - }, - else => @trap(), - } - - var high: ?*Type = null; - - switch (classes[1]) { - .none => {}, - .integer => { - const high_offset = 8; - const high_ty = Abi.SystemV.get_int_type_at_offset(module, return_type, high_offset, return_type, high_offset); - high = high_ty; - if (classes[0] == .none) { - return Abi.Information.get_direct(.{ - .semantic_type = return_type, - .type = high_ty, - .offset = high_offset, - }); - } - }, - else => @trap(), - } - - if (high) |hi| { - low = Abi.SystemV.get_byval_argument_pair(module, .{ low orelse unreachable, hi }); - } - - return Abi.Information.get_direct(.{ - .semantic_type = return_type, - .type = low orelse unreachable, - }); - } - - pub fn get_byval_argument_pair(module: *Module, pair: [2]*Type) *Type { - const low_size = pair[0].get_byte_size(); - const high_alignment = pair[1].get_byte_alignment(); - const high_offset = lib.align_forward_u64(low_size, high_alignment); - assert(high_offset != 0 and high_offset <= 8); - const low = if (high_offset != 8) - if ((pair[0].bb == .float and pair[0].bb.float.kind == .half) or (pair[0].bb == .float and pair[0].bb.float.kind == .float)) { - @trap(); - } else { - assert(pair[0].is_integer_backing()); - @trap(); - } - else - pair[0]; - const high = pair[1]; - const struct_type = module.get_anonymous_struct_pair(.{ low, high }); - assert(struct_type.bb.structure.fields[1].byte_offset == 8); - - return struct_type; - } - - const IndirectReturn = struct { - type: *Type, - }; - - pub fn get_indirect_return_result(indirect: IndirectReturn) Abi.Information { - if (indirect.type.is_aggregate_type_for_abi()) { - return Abi.Information.get_natural_align_indirect(.{ - .semantic_type = indirect.type, - }); - } else { - @trap(); - } - } - - pub fn get_indirect_result(ty: *Type, free_gpr: u32) Abi.Information { - if (!ty.is_aggregate_type_for_abi() and !is_illegal_vector_type(ty) and !ty.is_arbitrary_bit_integer()) { - return switch (ty.is_promotable_integer_type_for_abi()) { - true => @trap(), - false => Abi.Information.get_direct(.{ - .semantic_type = ty, - .type = ty, - }), - }; - } else { - // TODO CXX ABI - const alignment = @max(ty.get_byte_alignment(), 8); - const size = ty.get_byte_size(); - return switch (free_gpr == 0 and alignment == 8 and size <= 8) { - true => @trap(), - false => Abi.Information.get_indirect(.{ - .semantic_type = ty, - .alignment = alignment, - }), - }; - } - } - - pub fn is_illegal_vector_type(ty: *Type) bool { - return switch (ty.bb) { - .vector => @trap(), - else => false, - }; - } - - pub fn emit_va_arg_from_memory(module: *Module, va_list_pointer: *llvm.Value, va_list_struct: *Type, arg_type: *Type) *llvm.Value { - const overflow_arg_area_pointer = module.llvm.builder.create_struct_gep(va_list_struct.llvm.handle.to_struct(), va_list_pointer, 2); - const overflow_arg_area_type = va_list_struct.bb.structure.fields[2].type; - const overflow_arg_area = module.create_load(.{ .type = overflow_arg_area_type, .value = overflow_arg_area_pointer }); - if (arg_type.get_byte_alignment() > 8) { - @trap(); - } - const arg_type_size = arg_type.get_byte_size(); - const raw_offset = lib.align_forward_u64(arg_type_size, 8); - const offset = module.integer_type(32, false).llvm.handle.to_integer().get_constant(raw_offset, @intFromBool(false)); - const new_overflow_arg_area = module.llvm.builder.create_gep(.{ - .type = module.integer_type(8, false).llvm.handle, - .aggregate = overflow_arg_area, - .indices = &.{offset.to_value()}, - .inbounds = false, - }); - _ = module.create_store(.{ .destination_type = overflow_arg_area_type, .source_type = overflow_arg_area_type, .source_value = new_overflow_arg_area, .destination_value = overflow_arg_area_pointer }); - return overflow_arg_area; - } - }; -}; - -pub const Options = struct { - content: []const u8, - path: [:0]const u8, - executable: [:0]const u8, - name: []const u8, - objects: []const [:0]const u8, - target: Target, - build_mode: BuildMode, - has_debug_info: bool, - silent: bool, -}; - -pub const convert = convert2; - -pub noinline fn convert2(arena: *Arena, options: Options) void { - llvm.default_initialize(); - - const module = Module.initialize(arena, options); - defer module.deinitialize(); - - while (true) { - module.skip_space(); - - if (module.offset == module.content.len) { - break; - } - - var is_export = false; - var is_extern = false; - - const global_line = module.get_line(); - const global_column = module.get_column(); - _ = global_column; - - if (module.content[module.offset] == left_bracket) { - module.offset += 1; - - while (module.offset < module.content.len) { - const global_keyword_string = module.parse_identifier(); - - const global_keyword = lib.string.to_enum(GlobalKeyword, global_keyword_string) orelse module.report_error(); - switch (global_keyword) { - .@"export" => is_export = true, - .@"extern" => is_extern = true, - } - - switch (module.content[module.offset]) { - right_bracket => break, - else => module.report_error(), - } - } - - module.expect_character(right_bracket); - - module.skip_space(); - } - - const global_name = module.parse_identifier(); - - if (module.types.find(global_name) != null) @trap(); - if (module.globals.find(global_name) != null) @trap(); - - module.skip_space(); - - var global_type: ?*Type = null; - if (module.consume_character_if_match(':')) { - module.skip_space(); - - global_type = module.parse_type(); - - module.skip_space(); - } - - module.expect_character('='); - - module.skip_space(); - - var global_keyword = false; - if (is_identifier_start_ch(module.content[module.offset])) { - const global_string = module.parse_identifier(); - module.skip_space(); - - if (lib.string.to_enum(GlobalKind, global_string)) |global_kind| { - global_keyword = true; - switch (global_kind) { - .@"fn" => { - var calling_convention = CallingConvention.c; - const function_attributes = Function.Attributes{}; - var is_var_args = false; - - if (module.consume_character_if_match(left_bracket)) { - while (module.offset < module.content.len) { - const function_identifier = module.parse_identifier(); - - const function_keyword = lib.string.to_enum(FunctionKeyword, function_identifier) orelse module.report_error(); - - module.skip_space(); - - switch (function_keyword) { - .cc => { - module.expect_character(left_parenthesis); - - module.skip_space(); - - const calling_convention_string = module.parse_identifier(); - - calling_convention = lib.string.to_enum(CallingConvention, calling_convention_string) orelse module.report_error(); - - module.skip_space(); - - module.expect_character(right_parenthesis); - }, - else => module.report_error(), - } - - module.skip_space(); - - switch (module.content[module.offset]) { - right_bracket => break, - else => module.report_error(), - } - } - - module.expect_character(right_bracket); - } - - module.skip_space(); - - module.expect_character(left_parenthesis); - - var argument_buffer: [max_argument_count]struct { - name: []const u8, - type: *Type, - line: u32, - column: u32, - } = undefined; - var semantic_argument_count: u32 = 0; - - while (module.offset < module.content.len and module.content[module.offset] != right_parenthesis) : (semantic_argument_count += 1) { - module.skip_space(); - - const argument_line = module.get_line(); - const argument_column = module.get_column(); - - if (module.consume_character_if_match('.')) { - if (module.consume_character_if_match('.')) { - module.expect_character('.'); - module.skip_space(); - - if (module.content[module.offset] == ')') { - if (calling_convention != .c) { - module.report_error(); - } - is_var_args = true; - break; - } else { - @trap(); - } - } else { - @trap(); - } - } - - const argument_name = module.parse_identifier(); - - module.skip_space(); - - module.expect_character(':'); - - module.skip_space(); - - const argument_type = module.parse_type(); - - module.skip_space(); - _ = module.consume_character_if_match(','); - - argument_buffer[semantic_argument_count] = .{ - .name = argument_name, - .type = argument_type, - .line = argument_line, - .column = argument_column, - }; - } - - module.expect_character(right_parenthesis); - - module.skip_space(); - - const semantic_return_type = module.parse_type(); - const linkage_name = global_name; - - const semantic_arguments = argument_buffer[0..semantic_argument_count]; - const argument_type_abis = module.arena.allocate(Abi.Information, semantic_arguments.len); - - var return_type_abi: Abi.Information = undefined; - - const resolved_calling_convention = calling_convention.resolve(module.target); - const is_reg_call = resolved_calling_convention == .system_v and false; // TODO: regcall calling_convention - - const function_type = switch (resolved_calling_convention) { - .system_v => ft: { - var available_registers: Abi.RegisterCount = switch (resolved_calling_convention) { - .system_v => .{ - .system_v = .{ - .gpr = if (is_reg_call) 11 else 6, - .sse = if (is_reg_call) 16 else 8, - }, - }, - .win64 => @trap(), - }; - var abi_return_type: *Type = undefined; - var abi_argument_type_count: u16 = 0; - var llvm_abi_argument_type_buffer: [max_argument_count]*llvm.Type = undefined; - var abi_argument_type_buffer: [max_argument_count]*Type = undefined; - - return_type_abi = Abi.SystemV.classify_return_type(module, semantic_return_type); - const return_abi_kind = return_type_abi.flags.kind; - abi_return_type = switch (return_abi_kind) { - .direct, .extend => return_type_abi.coerce_to_type.?, - .ignore, .indirect => module.void_type, - else => |t| @panic(@tagName(t)), - }; - - if (return_type_abi.flags.kind == .indirect) { - assert(!return_type_abi.flags.sret_after_this); - available_registers.system_v.gpr -= 1; - const indirect_type = module.get_pointer_type(.{ .type = return_type_abi.semantic_type }); - abi_argument_type_buffer[abi_argument_type_count] = indirect_type; - llvm_abi_argument_type_buffer[abi_argument_type_count] = indirect_type.llvm.handle; - abi_argument_type_count += 1; - } - - const required_arguments = semantic_argument_count; - - for (argument_type_abis, semantic_arguments, 0..) |*argument_type_abi, semantic_argument, semantic_argument_index| { - const semantic_argument_type = semantic_argument.type; - const is_named_argument = semantic_argument_index < required_arguments; - assert(is_named_argument); - - argument_type_abi.* = Abi.SystemV.classify_argument(module, &available_registers, &llvm_abi_argument_type_buffer, &abi_argument_type_buffer, .{ - .type = semantic_argument_type, - .abi_start = abi_argument_type_count, - .is_named_argument = is_named_argument, - }); - - abi_argument_type_count += argument_type_abi.abi_count; - } - - const abi_argument_types = module.arena.allocate(*Type, abi_argument_type_count); - @memcpy(abi_argument_types, abi_argument_type_buffer[0..abi_argument_types.len]); - - const llvm_abi_argument_types = llvm_abi_argument_type_buffer[0..abi_argument_type_count]; - const llvm_function_type = llvm.Type.Function.get(abi_return_type.llvm.handle, llvm_abi_argument_types, is_var_args); - - const subroutine_type_flags = llvm.DI.Flags{}; - const subroutine_type = if (module.llvm.di_builder) |di_builder| blk: { - var debug_argument_type_buffer: [max_argument_count + 1]*llvm.DI.Type = undefined; - const semantic_debug_argument_types = debug_argument_type_buffer[0 .. argument_type_abis.len + 1 + @intFromBool(is_var_args)]; - semantic_debug_argument_types[0] = return_type_abi.semantic_type.llvm.debug; - - for (argument_type_abis, semantic_debug_argument_types[1..][0..argument_type_abis.len]) |argument_abi, *debug_argument_type| { - debug_argument_type.* = argument_abi.semantic_type.llvm.debug; - } - - if (is_var_args) { - semantic_debug_argument_types[argument_type_abis.len + 1] = module.void_type.llvm.debug; - } - - const subroutine_type = di_builder.create_subroutine_type(module.llvm.file, semantic_debug_argument_types, subroutine_type_flags); - break :blk subroutine_type; - } else undefined; - - const result = module.types.add(.{ - .bb = .{ - .function = .{ - .return_type_abi = return_type_abi, - .calling_convention = calling_convention, - .is_var_args = is_var_args, - .argument_type_abis = argument_type_abis, - .abi_return_type = abi_return_type, - .abi_argument_types = abi_argument_types, - .available_registers = available_registers, - }, - }, - .llvm = .{ - .handle = llvm_function_type.to_type(), - .debug = subroutine_type.to_type(), - }, - .name = null, - }); - break :ft result; - }, - .win64 => { - @trap(); - }, - }; - - const llvm_handle = module.llvm.handle.create_function(.{ - .name = global_name, - .linkage = switch (is_export or is_extern) { - true => .ExternalLinkage, - false => .InternalLinkage, - }, - .type = function_type.llvm.handle.to_function(), - }); - - llvm_handle.set_calling_convention(calling_convention.to_llvm()); - const has_semicolon = module.consume_character_if_match(';'); - - const function_scope: *llvm.DI.Scope = if (module.llvm.di_builder) |di_builder| blk: { - const scope_line: u32 = @intCast(module.line_offset + 1); - const local_to_unit = !is_export and !is_extern; - const flags = llvm.DI.Flags{}; - const is_definition = !is_extern; - const subprogram = di_builder.create_function(module.llvm.global_scope, global_name, linkage_name, module.llvm.file, global_line, function_type.llvm.debug.to_subroutine(), local_to_unit, is_definition, scope_line, flags, options.build_mode.is_optimized()); - llvm_handle.set_subprogram(subprogram); - - break :blk @ptrCast(subprogram); - } else undefined; - - const value = module.values.add(); - value.* = .{ - .llvm = llvm_handle.to_value(), - .type = module.get_pointer_type(.{ .type = function_type }), - .bb = switch (has_semicolon) { - true => .external_function, - false => .{ - .function = .{ - .current_scope = function_scope, - .attributes = function_attributes, - .return_pointer = undefined, - .return_alloca = undefined, - .exit_block = null, - .return_block = undefined, - }, - }, - }, - .lvalue = true, - .dereference_to_assign = false, - }; - - const global = module.globals.add(); - global.* = .{ - .value = value, - .name = global_name, - }; - - const attribute_list = module.build_attribute_list(.{ - .abi_return_type = function_type.bb.function.abi_return_type, - .abi_argument_types = function_type.bb.function.abi_argument_types, - .argument_type_abis = function_type.bb.function.argument_type_abis, - .return_type_abi = function_type.bb.function.return_type_abi, - .attributes = function_attributes, - .call_site = false, - }); - - llvm_handle.set_attributes(attribute_list); - - if (!has_semicolon) { - const entry_block = module.llvm.context.create_basic_block("entry", llvm_handle); - value.bb.function.return_block = module.llvm.context.create_basic_block("ret_block", null); - - module.llvm.builder.position_at_end(entry_block); - module.llvm.builder.set_current_debug_location(null); - - // function prologue - - var llvm_abi_argument_buffer: [argument_buffer.len]*llvm.Argument = undefined; - llvm_handle.get_arguments(&llvm_abi_argument_buffer); - const llvm_abi_arguments = llvm_abi_argument_buffer[0..function_type.bb.function.abi_argument_types.len]; - - module.current_function = global; - defer module.current_function = null; - - switch (return_type_abi.flags.kind) { - .ignore => {}, - .indirect => { - const indirect_argument_index = @intFromBool(return_type_abi.flags.sret_after_this); - if (return_type_abi.flags.sret_after_this) { - @trap(); - } - value.bb.function.return_alloca = llvm_abi_arguments[indirect_argument_index].to_value(); - if (!return_type_abi.flags.indirect_by_value) { - @trap(); - } - }, - .in_alloca => { - @trap(); - }, - else => { - const alloca = module.create_alloca(.{ .type = return_type_abi.semantic_type, .name = "retval" }); - value.bb.function.return_alloca = alloca; - }, - } - - const argument_variables = global.value.bb.function.arguments.add_many(semantic_argument_count); - for (semantic_arguments, argument_type_abis, argument_variables, 0..) |semantic_argument, argument_abi, *argument_variable, argument_index| { - const abi_arguments = llvm_abi_arguments[argument_abi.abi_start..][0..argument_abi.abi_count]; - assert(argument_abi.flags.kind == .ignore or argument_abi.abi_count != 0); - const argument_abi_kind = argument_abi.flags.kind; - const semantic_argument_storage = switch (argument_abi_kind) { - .direct, .extend => blk: { - const first_argument = abi_arguments[0]; - const coerce_to_type = argument_abi.get_coerce_to_type(); - if (coerce_to_type.bb != .structure and coerce_to_type.is_abi_equal(argument_abi.semantic_type) and argument_abi.attributes.direct.offset == 0) { - assert(argument_abi.abi_count == 1); - const is_promoted = false; - var v = first_argument.to_value(); - v = switch (coerce_to_type.llvm.handle == v.get_type()) { - true => v, - false => @trap(), - }; - if (is_promoted) { - @trap(); - } - - switch (argument_abi.semantic_type.is_arbitrary_bit_integer()) { - true => { - const bit_count = argument_abi.semantic_type.get_bit_size(); - const abi_bit_count: u32 = @intCast(@max(8, lib.next_power_of_two(bit_count))); - const is_signed = argument_abi.semantic_type.is_signed(); - const destination_type = module.align_integer_type(argument_abi.semantic_type); - const alloca = module.create_alloca(.{ .type = destination_type, .name = semantic_argument.name }); - const result = switch (bit_count < abi_bit_count) { - true => switch (is_signed) { - true => module.llvm.builder.create_sign_extend(first_argument.to_value(), destination_type.llvm.handle), - false => module.llvm.builder.create_zero_extend(first_argument.to_value(), destination_type.llvm.handle), - }, - false => @trap(), - }; - _ = module.create_store(.{ .source_value = result, .destination_value = alloca, .source_type = destination_type, .destination_type = destination_type }); - break :blk alloca; - }, - false => { // TODO: ExtVectorBoolType - const alloca = module.create_alloca(.{ .type = argument_abi.semantic_type, .name = semantic_argument.name }); - _ = module.create_store(.{ .source_value = first_argument.to_value(), .destination_value = alloca, .source_type = argument_abi.semantic_type, .destination_type = argument_abi.semantic_type }); - break :blk alloca; - }, - } - } else { - const is_fixed_vector_type = false; - if (is_fixed_vector_type) { - @trap(); - } - - if (coerce_to_type.bb == .structure and coerce_to_type.bb.structure.fields.len > 1 and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) { - const contains_homogeneous_scalable_vector_types = false; - if (contains_homogeneous_scalable_vector_types) { - @trap(); - } - } - - const alloca = module.create_alloca(.{ .type = argument_abi.semantic_type }); - const pointer = switch (argument_abi.attributes.direct.offset > 0) { - true => @trap(), - false => alloca, - }; - const pointer_type = switch (argument_abi.attributes.direct.offset > 0) { - true => @trap(), - false => argument_abi.semantic_type, - }; - - if (coerce_to_type.bb == .structure and coerce_to_type.bb.structure.fields.len > 1 and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) { - const struct_size = coerce_to_type.get_byte_size(); - const pointer_element_size = pointer_type.get_byte_size(); // TODO: fix - const is_scalable = false; - - switch (is_scalable) { - true => @trap(), - false => { - const source_size = struct_size; - const destination_size = pointer_element_size; - const address_alignment = argument_abi.semantic_type.get_byte_alignment(); - const address = switch (source_size <= destination_size) { - true => alloca, - false => module.create_alloca(.{ .type = coerce_to_type, .alignment = address_alignment, .name = "coerce" }), - }; - assert(coerce_to_type.bb.structure.fields.len == argument_abi.abi_count); - for (coerce_to_type.bb.structure.fields, abi_arguments, 0..) |field, abi_argument, field_index| { - const gep = module.llvm.builder.create_struct_gep(coerce_to_type.llvm.handle.to_struct(), address, @intCast(field_index)); - // TODO: check if alignment is right - _ = module.create_store(.{ .source_value = abi_argument.to_value(), .destination_value = gep, .source_type = field.type, .destination_type = field.type }); - } - - if (source_size > destination_size) { - _ = module.llvm.builder.create_memcpy(pointer, pointer_type.get_byte_alignment(), address, address_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(destination_size, @intFromBool(false)).to_value()); - } - }, - } - } else { - assert(argument_abi.abi_count == 1); - const abi_argument_type = function_type.bb.function.abi_argument_types[argument_abi.abi_start]; - const destination_size = pointer_type.get_byte_size() - argument_abi.attributes.direct.offset; - const is_volatile = false; - module.create_coerced_store(abi_arguments[0].to_value(), abi_argument_type, pointer, pointer_type, destination_size, is_volatile); - } - - switch (argument_abi.semantic_type.get_evaluation_kind()) { - .scalar => @trap(), - else => { - // TODO - }, - } - - break :blk alloca; - } - }, - .indirect, .indirect_aliased => blk: { - assert(argument_abi.abi_count == 1); - switch (argument_abi.semantic_type.get_evaluation_kind()) { - .scalar => @trap(), - else => { - if (argument_abi.flags.indirect_realign or argument_abi.flags.kind == .indirect_aliased) { - @trap(); - } - - const use_indirect_debug_address = !argument_abi.flags.indirect_by_value; - if (use_indirect_debug_address) { - @trap(); - } - - const llvm_argument = abi_arguments[0]; - break :blk llvm_argument.to_value(); - }, - } - }, - else => @trap(), - }; - - const argument_value = module.values.add(); - argument_value.* = .{ - .llvm = semantic_argument_storage, - .type = module.get_pointer_type(.{ .type = semantic_argument.type }), - .bb = .argument, - .lvalue = true, - .dereference_to_assign = false, - }; - argument_variable.* = .{ - .value = argument_value, - .name = semantic_argument.name, - }; - - if (module.llvm.di_builder) |di_builder| { - const always_preserve = true; - const flags = llvm.DI.Flags{}; - const parameter_variable = di_builder.create_parameter_variable(function_scope, semantic_argument.name, @intCast(argument_index + 1), module.llvm.file, semantic_argument.line, semantic_argument.type.llvm.debug, always_preserve, flags); - const inlined_at: ?*llvm.DI.Metadata = null; // TODO - const debug_location = llvm.DI.create_debug_location(module.llvm.context, semantic_argument.line, semantic_argument.column, function_scope, inlined_at); - _ = di_builder.insert_declare_record_at_end(semantic_argument_storage, parameter_variable, di_builder.null_expression(), debug_location, module.current_basic_block()); - } - } - - module.parse_block(); - - // Handle jump to the return block - const return_block = value.bb.function.return_block; - - if (module.llvm.builder.get_insert_block()) |current_basic_block| { - assert(current_basic_block.get_terminator() == null); - - if (current_basic_block.is_empty() or current_basic_block.to_value().use_empty()) { - return_block.to_value().replace_all_uses_with(current_basic_block.to_value()); - return_block.delete(); - } else { - module.emit_block(return_block); - } - } else { - var is_reachable = false; - - if (return_block.to_value().has_one_use()) { - if (llvm.Value.to_branch(return_block.user_begin())) |branch| { - is_reachable = !branch.is_conditional() and branch.get_successor(0) == return_block; - - if (is_reachable) { - module.llvm.builder.position_at_end(branch.to_instruction().get_parent()); - branch.to_instruction().erase_from_parent(); - return_block.delete(); - } - } - } - - if (!is_reachable) { - module.emit_block(return_block); - } - } - - // End function debug info - if (module.llvm.di_builder) |di_builder| { - if (llvm_handle.get_subprogram()) |subprogram| { - di_builder.finalize_subprogram(subprogram); - } - } - - if (return_type_abi.semantic_type == module.noreturn_type or value.bb.function.attributes.naked) { - @trap(); - } else if (return_type_abi.semantic_type == module.void_type) { - module.llvm.builder.create_ret_void(); - } else { - const abi_kind = return_type_abi.flags.kind; - const return_value: ?*llvm.Value = switch (abi_kind) { - .direct, .extend => blk: { - const coerce_to_type = return_type_abi.get_coerce_to_type(); - const return_alloca = value.bb.function.return_alloca; - - if (return_type_abi.semantic_type.is_abi_equal(coerce_to_type) and return_type_abi.attributes.direct.offset == 0) { - if (module.llvm.builder.find_return_value_dominating_store(return_alloca, return_type_abi.semantic_type.llvm.handle)) |store| { - const store_instruction = store.to_instruction(); - const return_value = store_instruction.to_value().get_operand(0); - const alloca = store_instruction.to_value().get_operand(1); - assert(alloca == return_alloca); - store_instruction.erase_from_parent(); - assert(alloca.use_empty()); - alloca.to_instruction().erase_from_parent(); - break :blk return_value; - } else { - const load_value = module.create_load(.{ .type = return_type_abi.semantic_type, .value = return_alloca }); - break :blk load_value; - } - } else { - const source = switch (return_type_abi.attributes.direct.offset == 0) { - true => return_alloca, - false => @trap(), - }; - - const source_type = return_type_abi.semantic_type; - const destination_type = coerce_to_type; - const result = module.create_coerced_load(source, source_type, destination_type); - break :blk result; - } - }, - .indirect => switch (return_type_abi.semantic_type.get_evaluation_kind()) { - .complex => @trap(), - .aggregate => null, - .scalar => @trap(), - }, - else => @trap(), - }; - - if (return_value) |rv| { - module.llvm.builder.create_ret(rv); - } else { - module.llvm.builder.create_ret_void(); - } - } - } - - if (!has_semicolon and lib.optimization_mode == .Debug) { - const verify_result = llvm_handle.verify(); - if (!verify_result.success) { - lib.print_string(module.llvm.handle.to_string()); - lib.print_string("============================\n"); - lib.print_string(llvm_handle.to_string()); - lib.print_string("============================\n"); - lib.print_string(verify_result.error_message orelse unreachable); - lib.print_string("\n============================\n"); - os.abort(); - } - } - }, - .@"struct" => { - module.skip_space(); - - module.expect_character(left_brace); - - if (module.types.find(global_name) != null) { - @trap(); - } - - const struct_type = module.types.add(.{ - .name = global_name, - .bb = .forward_declaration, - .llvm = .{ - .handle = undefined, - .debug = if (module.llvm.di_builder) |di_builder| blk: { - const r = di_builder.create_replaceable_composite_type(module.debug_tag, global_name, module.llvm.global_scope, module.llvm.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: u64 = 0; - var byte_offset: u64 = 0; - var byte_alignment: u32 = 1; - var bit_alignment: u32 = 1; - - while (true) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } - - const field_line = module.get_line(); - const field_name = module.parse_identifier(); - - module.skip_space(); - - module.expect_character(':'); - - module.skip_space(); - - const field_type = module.parse_type(); - - const field_byte_alignment = field_type.get_byte_alignment(); - const field_bit_alignment = field_type.get_bit_alignment(); - const field_bit_size = field_type.get_bit_size(); - const field_byte_size = field_type.get_byte_size(); - - const field_byte_offset = lib.align_forward_u64(byte_offset, field_byte_alignment); - const field_bit_offset = field_byte_offset * 8; - - 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.llvm.di_builder) |di_builder| { - const member_type = di_builder.create_member_type(module.llvm.global_scope, field_name, module.llvm.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_offset + field_byte_size; - - field_count += 1; - - module.skip_space(); - - switch (module.content[module.offset]) { - ',' => module.offset += 1, - else => {}, - } - } - - module.skip_space(); - - _ = module.consume_character_if_match(';'); - - const byte_size = byte_offset; - const bit_size = byte_size * 8; - - const fields = module.arena.allocate(Field, field_count); - @memcpy(fields, field_buffer[0..field_count]); - - const element_types = llvm_field_type_buffer[0..field_count]; - struct_type.llvm.handle = module.llvm.context.get_struct_type(element_types).to_type(); - - if (module.llvm.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.llvm.global_scope, global_name, module.llvm.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 = .{ - .structure = .{ - .bit_size = byte_size * 8, - .byte_size = byte_size, - .bit_alignment = bit_alignment, - .byte_alignment = byte_alignment, - .fields = fields, - .is_slice = false, - }, - }; - }, - .bits => { - const is_implicit_type = module.content[module.offset] == left_brace; - const maybe_backing_type: ?*Type = switch (is_implicit_type) { - true => null, - false => module.parse_type(), - }; - - module.skip_space(); - - module.expect_character(left_brace); - - var field_buffer: [128]Field = undefined; - var field_line_buffer: [128]u32 = undefined; - var field_count: u64 = 0; - - var field_bit_offset: u64 = 0; - - while (true) : (field_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } - - const field_line = module.get_line(); - field_line_buffer[field_count] = field_line; - - const field_name = module.parse_identifier(); - - module.skip_space(); - - module.expect_character(':'); - - module.skip_space(); - - const field_type = module.parse_type(); - - field_buffer[field_count] = .{ - .name = field_name, - .type = field_type, - .bit_offset = field_bit_offset, - .byte_offset = 0, - }; - - const field_bit_size = field_type.get_bit_size(); - - // if (module.llvm.di_builder) |di_builder| { - // llvm_debug_field_buffer[field_count] = member_type; - // } - - field_bit_offset += field_bit_size; - - module.skip_space(); - - _ = module.consume_character_if_match(','); - } - - _ = module.consume_character_if_match(';'); - - const fields = module.arena.allocate(Field, field_count); - @memcpy(fields, field_buffer[0..field_count]); - - const field_lines = field_line_buffer[0..field_count]; - - const backing_type = if (maybe_backing_type) |bt| bt else module.integer_type(@intCast(@max(8, lib.next_power_of_two(field_bit_offset))), false); - if (backing_type.bb != .integer) { - module.report_error(); - } - - if (backing_type.get_bit_size() > 64) { - module.report_error(); - } - - const bit_size = backing_type.get_bit_size(); - const bit_alignment = backing_type.get_bit_alignment(); - - var llvm_debug_field_buffer: [128]*llvm.DI.Type.Derived = undefined; - const debug_member_types = llvm_debug_field_buffer[0..field_count]; - - if (module.llvm.di_builder) |di_builder| { - for (fields, debug_member_types, field_lines) |field, *debug_member_type, field_line| { - debug_member_type.* = di_builder.create_bit_field_member_type(module.llvm.global_scope, field.name, module.llvm.file, field_line, field.type.get_bit_size(), field_bit_offset, 0, .{}, backing_type.llvm.debug); - } - } - - _ = module.types.add(.{ - .name = global_name, - .llvm = .{ - .handle = backing_type.llvm.handle, - .debug = if (module.llvm.di_builder) |di_builder| di_builder.create_struct_type(module.llvm.global_scope, global_name, module.llvm.file, global_line, bit_size, @intCast(bit_alignment), .{}, debug_member_types).to_type() else undefined, - }, - .bb = .{ - .bits = .{ - .fields = fields, - .backing_type = backing_type, - .implicit_backing_type = is_implicit_type, - }, - }, - }); - }, - .@"enum" => { - const is_implicit_type = module.content[module.offset] == left_brace; - const maybe_backing_type: ?*Type = switch (is_implicit_type) { - true => null, - false => module.parse_type(), - }; - - module.skip_space(); - - module.expect_character(left_brace); - - var highest_value: u64 = 0; - var lowest_value = ~@as(u64, 0); - - var field_buffer: [64]Enumerator.Field = undefined; - var field_count: u64 = 0; - - while (true) : (field_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } - - const field_index = field_count; - const field_name = module.parse_identifier(); - module.skip_space(); - - const field_value = if (module.consume_character_if_match('=')) blk: { - module.skip_space(); - const field_value = module.parse_integer_value(false); - break :blk field_value; - } else { - @trap(); - }; - - field_buffer[field_index] = .{ - .name = field_name, - .value = field_value, - }; - - highest_value = @max(highest_value, field_value); - lowest_value = @min(lowest_value, field_value); - - module.skip_space(); - module.expect_character(','); - } - - module.skip_space(); - - _ = module.consume_character_if_match(';'); - - const backing_type = maybe_backing_type orelse blk: { - const bits_needed = 64 - @clz(highest_value); - const int_type = module.integer_type(bits_needed, false); - break :blk int_type; - }; - - if (maybe_backing_type) |bt| { - const bits_needed = 64 - @clz(highest_value); - if (bits_needed > bt.get_bit_size()) { - module.report_error(); - } - } - - const fields = arena.allocate(Enumerator.Field, field_count); - @memcpy(fields, field_buffer[0..field_count]); - - const debug_type = if (module.llvm.di_builder) |di_builder| blk: { - var enumerator_buffer: [64]*llvm.DI.Enumerator = undefined; - const enumerators = enumerator_buffer[0..field_count]; - for (enumerators, fields) |*enumerator_pointer, *field| { - enumerator_pointer.* = di_builder.create_enumerator(field.name, @bitCast(field.value), false); - } - const alignment = 0; // TODO - const enumeration_type = di_builder.create_enumeration_type(module.llvm.global_scope, global_name, module.llvm.file, global_line, backing_type.get_bit_size(), alignment, enumerators, backing_type.llvm.debug); - break :blk enumeration_type.to_type(); - } else undefined; - - _ = module.types.add(.{ - .bb = .{ - .enumerator = .{ - .backing_type = backing_type, - .fields = fields, - .implicit_backing_type = is_implicit_type, - }, - }, - .llvm = .{ - .handle = backing_type.llvm.handle, - .debug = debug_type, - }, - .name = global_name, - }); - }, - } - } else { - module.offset -= global_string.len; - } - } - - if (!global_keyword) { - const value = module.parse_value(.{ - .type = global_type, - }); - const expected_type = global_type orelse value.type; - module.skip_space(); - - module.expect_character(';'); - - const global_variable = module.llvm.handle.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, - }); - global_variable.to_value().set_alignment(@intCast(expected_type.get_byte_alignment())); - - if (module.llvm.di_builder) |di_builder| { - const linkage_name = global_name; - const local_to_unit = !(is_export or is_extern); - const alignment = 0; // TODO - const global_variable_expression = di_builder.create_global_variable(module.llvm.global_scope, global_name, linkage_name, module.llvm.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 = module.get_pointer_type(.{ .type = expected_type }), - .bb = .global, - .lvalue = true, - .dereference_to_assign = false, - }; - - const global = module.globals.add(); - global.* = .{ - .name = global_name, - .value = global_value, - }; - } - } - - if (module.llvm.di_builder) |di_builder| { - di_builder.finalize(); - } - - const verify_result = module.llvm.handle.verify(); - if (!verify_result.success) { - lib.print_string(module.llvm.handle.to_string()); - lib.print_string("============================\n"); - lib.print_string(verify_result.error_message orelse unreachable); - os.abort(); - } - - if (!module.silent) { - const module_string = module.llvm.handle.to_string(); - lib.print_string_stderr(module_string); - } - - var error_message: llvm.String = undefined; - const target_machine = llvm.Target.Machine.create(.{ - .target_options = blk: { - var target_options = llvm.Target.Options.default(); - target_options.flags0.trap_unreachable = switch (options.build_mode) { - .debug_none, .debug_fast, .debug_size => true, - else => false, - }; - break :blk target_options; - }, - .cpu_triple = llvm.String.from_slice(llvm.global.host_triple), - .cpu_model = llvm.String.from_slice(llvm.global.host_cpu_model), - .cpu_features = llvm.String.from_slice(llvm.global.host_cpu_features), - .optimization_level = options.build_mode.to_llvm_machine(), - .relocation_model = .default, - .code_model = .none, - .jit = false, - }, &error_message) orelse { - os.abort(); - }; - - const object_generate_result = llvm.object_generate(module.llvm.handle, target_machine, .{ - .optimize_when_possible = @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, - .path = options.objects[0], - }); - - switch (object_generate_result) { - .success => { - const result = llvm.link(module.arena, .{ - .output_path = options.executable, - .objects = options.objects, - }); - - switch (result.success) { - true => {}, - false => os.abort(), - } - }, - else => os.abort(), - } -} diff --git a/src/converter_test.zig b/src/converter_test.zig deleted file mode 100644 index 74f9841..0000000 --- a/src/converter_test.zig +++ /dev/null @@ -1,457 +0,0 @@ -const lib = @import("lib.zig"); -const Arena = lib.Arena; -const assert = lib.assert; -const std = @import("std"); -const configuration = @import("configuration"); - -const converter = @import("converter.zig"); -const BuildMode = converter.BuildMode; - -fn invoke(name: []const u8) !void { - if (!lib.GlobalState.initialized) { - lib.GlobalState.initialize(); - } - - comptime assert(lib.is_test); - const allocator = std.testing.allocator; - const arena = lib.global.arena; - const arena_position = arena.position; - defer arena.restore(arena_position); - - const c_abi_object_path = arena.duplicate_string(configuration.c_abi_object_path); - const file_path = arena.join_string(&.{ "tests/", name, ".bbb" }); - - inline for (@typeInfo(BuildMode).@"enum".fields) |f| { - const build_mode = @field(BuildMode, f.name); - inline for ([2]bool{ true, false }) |has_debug_info| { - // Bootstrap - { - var tmp_dir = std.testing.tmpDir(.{}); - defer tmp_dir.cleanup(); - const base_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path, "/", name }); - const executable_path = base_path; - const directory_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path }); - const object_path = arena.join_string(&.{ base_path, ".o" }); - try unit_test(arena, allocator, .{ - .object_paths = if (lib.string.equal(name, "c_abi")) &.{ object_path, c_abi_object_path } else &.{object_path}, - .executable_path = executable_path, - .file_path = file_path, - .name = name, - .directory_path = directory_path, - .build_mode = build_mode, - .has_debug_info = has_debug_info, - .self_hosted_path = null, - .run = true, - }); - } - - // Self-hosted - { - var tmp_dir = std.testing.tmpDir(.{}); - defer tmp_dir.cleanup(); - const base_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path, "/", name }); - const executable_path = base_path; - const directory_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path }); - const object_path = arena.join_string(&.{ base_path, ".o" }); - try unit_test(arena, allocator, .{ - .object_paths = if (lib.string.equal(name, "c_abi")) &.{ object_path, c_abi_object_path } else &.{object_path}, - .executable_path = executable_path, - .file_path = file_path, - .name = name, - .directory_path = directory_path, - .build_mode = build_mode, - .has_debug_info = has_debug_info, - .self_hosted_path = arena.join_string(&.{ "bb-cache/", compiler_basename(arena, build_mode, has_debug_info) }), - .run = true, - }); - } - } - } -} - -fn compiler_basename(arena: *Arena, build_mode: BuildMode, has_debug_info: bool) [:0]const u8 { - return arena.join_string(&.{ "compiler_", @tagName(build_mode), if (has_debug_info) "_di" else "_nodi" }); -} - -var compiler_compiled = false; -fn compile_the_compiler() !void { - if (!compiler_compiled) { - defer compiler_compiled = true; - - if (!lib.GlobalState.initialized) { - lib.GlobalState.initialize(); - } - - comptime assert(lib.is_test); - const allocator = std.testing.allocator; - const arena = lib.global.arena; - const arena_position = arena.position; - defer arena.restore(arena_position); - - inline for (@typeInfo(BuildMode).@"enum".fields) |f| { - const build_mode = @field(BuildMode, f.name); - inline for ([2]bool{ false, true }) |has_debug_info| { - var tmp_dir = std.testing.tmpDir(.{}); - defer tmp_dir.cleanup(); - const base_path = arena.join_string(&.{ "bb-cache/", compiler_basename(arena, build_mode, has_debug_info) }); - const executable_path = base_path; - const directory_path = "bb-cache"; - const object_path = arena.join_string(&.{ base_path, ".o" }); - - try unit_test(arena, allocator, .{ - .object_paths = &.{object_path}, - .executable_path = executable_path, - .file_path = arena.join_string(&.{"src/compiler.bbb"}), - .name = "compiler", - .directory_path = directory_path, - .build_mode = build_mode, - .has_debug_info = has_debug_info, - .self_hosted_path = null, - .run = false, - }); - } - } - } -} - -const InvokeWrapper = struct { - executable_path: [:0]const u8, - object_paths: []const [:0]const u8, - file_path: [:0]const u8, - name: []const u8, - build_mode: BuildMode, - has_debug_info: bool, - directory_path: [:0]const u8, - self_hosted_path: ?[]const u8, - run: bool, -}; - -fn unit_test(arena: *Arena, allocator: std.mem.Allocator, options: InvokeWrapper) anyerror!void { - const position = arena.position; - defer arena.restore(position); - - const file_content = lib.file.read(arena, options.file_path); - - if (options.self_hosted_path) |self_hosted_path| { - try compile_the_compiler(); - const argv = [_][]const u8{ - self_hosted_path, - options.file_path, - }; - const run_result = try std.process.Child.run(.{ - .allocator = allocator, - .argv = &argv, - }); - const success = switch (run_result.term) { - .Exited => |exit_code| exit_code == 0, - else => false, - }; - if (!success) { - std.debug.print("{s}\n{}\n{}\n", .{ argv, run_result, options }); - return error.compiler_failed_to_run_successfully; - } - } else { - converter.convert(arena, .{ - .path = options.file_path, - .content = file_content, - .objects = options.object_paths, - .executable = options.executable_path, - .build_mode = options.build_mode, - .name = options.name, - .has_debug_info = options.has_debug_info, - .target = converter.Target.get_native(), - }); - - if (options.run) { - const argv = [_][]const u8{options.executable_path}; - const run_result = std.process.Child.run(.{ - .allocator = allocator, - .argv = &argv, - }) catch |err| { - std.debug.print("error: {}\n", .{err}); - const r = try std.process.Child.run(.{ - .allocator = allocator, - .argv = &.{ "/usr/bin/ls", "-lasR", options.directory_path }, - .max_output_bytes = std.math.maxInt(usize), - }); - defer allocator.free(r.stdout); - defer allocator.free(r.stderr); - std.debug.print("ls {s} {s}\n", .{ options.directory_path, r.stdout }); - return err; - }; - - const success = switch (run_result.term) { - .Exited => |exit_code| exit_code == 0, - else => false, - }; - if (!success) { - std.debug.print("{s} {}\n{}\n", .{ argv, run_result, options }); - return error.executable_failed_to_run_successfully; - } - } - } -} - -fn invsrc(src: std.builtin.SourceLocation) !void { - try invoke(src.fn_name[std.mem.lastIndexOfScalar(u8, src.fn_name, '.').? + 1 ..]); -} - -test "minimal" { - try invsrc(@src()); -} - -test "constant_add" { - try invsrc(@src()); -} - -test "constant_sub" { - try invsrc(@src()); -} - -test "constant_mul" { - try invsrc(@src()); -} - -test "constant_div" { - try invsrc(@src()); -} - -test "constant_rem" { - try invsrc(@src()); -} - -test "constant_shift_left" { - try invsrc(@src()); -} - -test "constant_shift_right" { - try invsrc(@src()); -} - -test "constant_and" { - try invsrc(@src()); -} - -test "constant_or" { - try invsrc(@src()); -} - -test "constant_xor" { - try invsrc(@src()); -} - -test "minimal_stack" { - try invsrc(@src()); -} - -test "stack_add" { - try invsrc(@src()); -} - -test "stack_sub" { - try invsrc(@src()); -} - -test "global" { - try invsrc(@src()); -} - -test "simple_branch" { - try invsrc(@src()); -} - -test "basic_call" { - try invsrc(@src()); -} - -test "struct" { - try invsrc(@src()); -} - -test "extend" { - try invsrc(@src()); -} - -test "bits" { - try invsrc(@src()); -} - -test "basic_array" { - try invsrc(@src()); -} - -test "extern" { - try invsrc(@src()); -} - -test "pointer" { - try invsrc(@src()); -} - -test "if_no_else" { - try invsrc(@src()); -} - -test "comments" { - try invsrc(@src()); -} - -test "local_type_inference" { - try invsrc(@src()); -} - -test "if_no_else_void" { - try invsrc(@src()); -} - -test "c_abi0" { - try invsrc(@src()); -} - -test "c_abi1" { - try invsrc(@src()); -} - -test "return_u64_u64" { - try invsrc(@src()); -} - -test "struct_u64_u64" { - try invsrc(@src()); -} - -test "ret_c_bool" { - try invsrc(@src()); -} - -test "c_split_struct_ints" { - try invsrc(@src()); -} - -test "c_ret_struct_array" { - try invsrc(@src()); -} - -test "function_pointer" { - try invsrc(@src()); -} - -test "c_struct_with_array" { - try invsrc(@src()); -} - -test "indirect" { - try invsrc(@src()); -} - -test "indirect_struct" { - try invsrc(@src()); -} - -test "u1_return" { - try invsrc(@src()); -} - -test "small_struct_ints" { - try invsrc(@src()); -} - -test "c_med_struct_ints" { - try invsrc(@src()); -} - -test "c_abi" { - try invsrc(@src()); -} - -test "basic_varargs" { - try invsrc(@src()); -} - -test "struct_varargs" { - try invsrc(@src()); -} - -test "indirect_varargs" { - try invsrc(@src()); -} - -test "varargs" { - try invsrc(@src()); -} - -test "byte_size" { - try invsrc(@src()); -} - -test "bits_no_backing_type" { - try invsrc(@src()); -} - -test "basic_enum" { - try invsrc(@src()); -} - -test "return_type_builtin" { - try invsrc(@src()); -} - -test "bits_zero" { - try invsrc(@src()); -} - -test "struct_zero" { - try invsrc(@src()); -} - -test "select" { - try invsrc(@src()); -} - -test "bits_return_u1" { - try invsrc(@src()); -} - -test "integer_max" { - try invsrc(@src()); -} - -test "unreachable" { - try invsrc(@src()); -} - -test "pointer_cast" { - try invsrc(@src()); -} - -test "struct_assignment" { - try invsrc(@src()); -} - -test "global_struct" { - try invsrc(@src()); -} - -test "basic_slice" { - try invsrc(@src()); -} - -test "basic_string" { - try invsrc(@src()); -} - -test "argv" { - try invsrc(@src()); -} - -test "basic_while" { - try invsrc(@src()); -} - -test "c_string_to_slice" { - try invsrc(@src()); -} - -test "assignment_operators" { - try invsrc(@src()); -} diff --git a/src/lib.zig b/src/lib.zig index 4821a35..0d7115e 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -10,10 +10,6 @@ pub const VariableArguments = extern struct { extern "c" fn IsDebuggerPresent() bool; extern "c" fn __errno_location() *c_int; -test { - _ = @import("lib_test.zig"); -} - const CSlice = extern struct { pointer: ?*anyopaque, length: usize, diff --git a/src/lib_test.zig b/src/lib_test.zig deleted file mode 100644 index 707ba94..0000000 --- a/src/lib_test.zig +++ /dev/null @@ -1,44 +0,0 @@ -const lib = @import("lib.zig"); - -test "value_from_flag" { - const std = @import("std"); - const expect = std.testing.expect; - const value_from_flag = lib.value_from_flag; - - try expect(value_from_flag(1, 1) == 1); - try expect(value_from_flag(2, true) == 2); - try expect(value_from_flag(3, false) == 0); - try expect(value_from_flag(3, true) == 3); - try expect(value_from_flag(3, 1) == 3); - - try expect(value_from_flag(0xffff, 1) == 0xffff); - try expect(value_from_flag(0xffff, 0) == 0); - try expect(value_from_flag(0xffff, true) == 0xffff); - try expect(value_from_flag(0xffff, false) == 0); - - try expect(value_from_flag(0xffffffff, 1) == 0xffffffff); - try expect(value_from_flag(0xffffffff, 0) == 0); - try expect(value_from_flag(0xffffffff, true) == 0xffffffff); - try expect(value_from_flag(0xffffffff, false) == 0); - - try expect(value_from_flag(0xffffffffffffffff, 1) == 0xffffffffffffffff); - try expect(value_from_flag(0xffffffffffffffff, 0) == 0); - try expect(value_from_flag(0xffffffffffffffff, true) == 0xffffffffffffffff); - try expect(value_from_flag(0xffffffffffffffff, false) == 0); - - const a: u32 = 1235; - const b_true: bool = true; - const b_false: bool = false; - const u_true: u1 = 1; - const u_false: u1 = 0; - try expect(value_from_flag(a, b_true) == a); - try expect(value_from_flag(a, b_false) == 0); - try expect(value_from_flag(a, u_true) == a); - try expect(value_from_flag(a, u_false) == 0); - - const b: u64 = 0xffffffffffffffff; - try expect(value_from_flag(b, b_true) == b); - try expect(value_from_flag(b, b_false) == 0); - try expect(value_from_flag(b, u_true) == b); - try expect(value_from_flag(b, u_false) == 0); -}