Basic pointer

This commit is contained in:
David Gonzalez Martin 2025-02-26 20:26:19 -06:00
parent f67fdaa5f7
commit 1ad1121073
5 changed files with 301 additions and 79 deletions

View File

@ -510,10 +510,14 @@ pub const Context = opaque {
return api.llvm_context_create_struct_type(context, element_types.ptr, @intCast(element_types.len), String.from_slice(name), is_packed);
}
pub const get_struct_type = api.llvm_context_get_struct_type;
pub fn get_struct_type(context: *Context, element_types: []const *Type) *Type.Struct {
const is_packed = false;
return api.llvm_context_get_struct_type(context, element_types.ptr, element_types.len, is_packed);
}
pub const get_void_type = api.LLVMVoidTypeInContext;
pub const get_integer_type = api.LLVMIntTypeInContext;
pub const get_pointer_type = api.LLVMPointerTypeInContext;
};
pub const BasicBlock = opaque {
@ -903,6 +907,10 @@ pub const DI = struct {
pub fn create_bit_field_member_type(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, bit_size: u64, bit_offset: u64, bit_storage_offset: u64, flags: DI.Flags, member_type: *DI.Type) *DI.Type.Derived {
return api.LLVMDIBuilderCreateBitFieldMemberType(builder, scope, name.ptr, name.len, file, line, bit_size, bit_offset, bit_storage_offset, flags, member_type);
}
pub fn create_pointer_type(builder: *DI.Builder, element_type: *DI.Type, bit_size: u64, align_in_bits: u32, address_space: c_uint, name: []const u8) *DI.Type.Derived {
return api.LLVMDIBuilderCreatePointerType(builder, element_type, bit_size, align_in_bits, address_space, name.ptr, name.len);
}
};
pub const create_debug_location = api.LLVMDIBuilderCreateDebugLocation;
@ -940,7 +948,11 @@ pub const DI = struct {
pub const replace_all_uses_with = api.LLVMMetadataReplaceAllUsesWith;
};
pub const Derived = opaque {};
pub const Derived = opaque {
pub fn to_type(derived: *Derived) *DI.Type {
return @ptrCast(derived);
}
};
};
pub const Flags = packed struct(u32) {
@ -1067,6 +1079,12 @@ pub const Type = opaque {
}
};
pub const Pointer = opaque {
pub fn to_type(pointer_type: *Type.Pointer) *Type {
return @ptrCast(pointer_type);
}
};
pub fn get_array_type(element_type: *Type, element_count: u64) *Type.Array {
return api.LLVMArrayType2(element_type, element_count);
}

View File

@ -102,6 +102,10 @@ const Module = struct {
debug_tag: c_uint = 0,
void_type: *Type = undefined,
noreturn_type: *Type = undefined,
anonymous_pair_type_buffer: [64]u32 = undefined,
pointer_type_buffer: [64]u32 = undefined,
pointer_type_count: u32 = 0,
anonymous_pair_type_count: u32 = 0,
const LLVM = struct {
context: *llvm.Context,
@ -110,6 +114,7 @@ const Module = struct {
di_builder: ?*llvm.DI.Builder = null,
global_scope: *llvm.DI.Scope,
file: *llvm.DI.File,
pointer_type: *llvm.Type,
};
pub fn get_infer_or_ignore_value(module: *Module) *Value {
@ -162,6 +167,7 @@ const Module = struct {
}
const module = arena.allocate_one(Module);
const default_address_space = 0;
module.* = .{
.llvm = .{
.global_scope = global_scope,
@ -169,6 +175,7 @@ const Module = struct {
.handle = handle,
.context = context,
.builder = context.create_builder(),
.pointer_type = context.get_pointer_type(default_address_space).to_type(),
},
};
@ -380,6 +387,7 @@ pub const Type = struct {
bits: Bits,
function: FunctionType,
array: ArrayType,
pointer: *Type,
};
pub fn is_aggregate(ty: *const Type) bool {
@ -396,6 +404,7 @@ pub const Type = struct {
.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,
};
}
@ -406,6 +415,7 @@ pub const Type = struct {
.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,
};
}
@ -416,6 +426,7 @@ pub const Type = struct {
.bits => |bits| bits.backing_type.get_bit_alignment(),
.void, .forward_declaration, .function, .noreturn => unreachable,
.array => |*array| array.element_type.get_bit_alignment(),
.pointer => 64,
};
}
@ -426,6 +437,7 @@ pub const Type = struct {
.bits => |bits| bits.backing_type.get_byte_alignment(),
.void, .forward_declaration, .function, .noreturn => unreachable,
.array => |*array| array.element_type.get_byte_alignment(),
.pointer => 8,
};
}
@ -561,7 +573,7 @@ const Converter = struct {
converter.skip_space();
const length_expression = converter.parse_value(module, module.integer_type(64, false));
const length_expression = converter.parse_value(module, module.integer_type(64, false), .value);
converter.skip_space();
converter.expect_character(right_bracket);
@ -593,6 +605,36 @@ const Converter = struct {
return ty;
}
},
'&' => {
converter.offset += 1;
converter.skip_space();
const element_type = converter.parse_type(module);
const all_types = module.types.get();
const pointer_type = for (module.pointer_type_buffer[0..module.pointer_type_count]) |pointer_type_index| {
const pointer_type = &all_types[pointer_type_index];
if (pointer_type.bb.pointer == element_type) {
break pointer_type;
}
} else blk: {
const pointer_name = lib.global.arena.join_string(&.{ "&", element_type.name.? });
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(element_type.llvm.debug, 64, 64, 0, pointer_name).to_type() else undefined,
},
.bb = .{
.pointer = element_type,
},
});
break :blk pointer_type;
};
return pointer_type;
},
else => @trap(),
}
}
@ -749,7 +791,7 @@ const Converter = struct {
converter.report_error();
}
const semantic_argument_value = converter.parse_value(module, function_type.bb.function.semantic_argument_types[semantic_argument_index]);
const semantic_argument_value = converter.parse_value(module, function_type.bb.function.semantic_argument_types[semantic_argument_index], .value);
const argument_abi = function_type.bb.function.argument_type_abis[semantic_argument_index];
@ -851,7 +893,7 @@ const Converter = struct {
module.llvm.builder.clear_current_debug_location();
}
const value = converter.parse_value(module, local_type);
const value = converter.parse_value(module, local_type, .value);
const local_storage = module.values.add();
local_storage.* = .{
@ -889,7 +931,7 @@ const Converter = struct {
if (string_to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| {
switch (statement_start_keyword) {
.@"return" => {
const return_value = converter.parse_value(module, current_function_global.value.type.bb.function.semantic_return_type);
const return_value = converter.parse_value(module, current_function_global.value.type.bb.function.semantic_return_type, .value);
module.llvm.builder.create_ret(return_value.llvm);
},
.@"if" => {
@ -901,7 +943,7 @@ const Converter = struct {
converter.expect_character(left_parenthesis);
converter.skip_space();
const condition = converter.parse_value(module, null);
const condition = converter.parse_value(module, null, .value);
converter.skip_space();
converter.expect_character(right_parenthesis);
@ -944,25 +986,40 @@ const Converter = struct {
},
}
} else {
converter.offset -= statement_start_identifier.len;
const v = converter.parse_value(module, null, .maybe_pointer);
converter.skip_space();
if (converter.consume_character_if_match(left_parenthesis)) {
// This is a call
const variable = if (current_function.locals.find(statement_start_identifier)) |local| local else if (module.globals.find(statement_start_identifier)) |global| global else {
converter.report_error();
};
const return_value = converter.parse_call(module, variable.value);
const is_noreturn = return_value.type.bb == .noreturn;
const is_valid = return_value.type.bb == .void or is_noreturn;
if (!is_valid) {
converter.report_error();
}
switch (converter.content[converter.offset]) {
'=' => {
// const left = v;
converter.expect_character('=');
if (is_noreturn) {
_ = module.llvm.builder.create_unreachable();
}
} else {
converter.report_error();
converter.skip_space();
const left = v;
if (left.type.bb != .pointer) {
converter.report_error();
}
const load_type = left.type.bb.pointer;
const right = converter.parse_value(module, load_type, .value);
_ = module.llvm.builder.create_store(right.llvm, left.llvm);
},
';' => {
const is_noreturn = v.type.bb == .noreturn;
const is_valid = v.type.bb == .void or is_noreturn;
if (!is_valid) {
converter.report_error();
}
if (is_noreturn) {
_ = module.llvm.builder.create_unreachable();
}
},
else => @trap(),
}
}
} else {
@ -994,17 +1051,25 @@ const Converter = struct {
@"and",
@"or",
xor,
icmp_eq,
icmp_ne,
pub fn to_int_predicate(expression_state: ExpressionState) llvm.IntPredicate {
return switch (expression_state) {
.icmp_ne => .ne,
else => unreachable,
.icmp_eq => .eq,
else => @trap(),
};
}
};
fn parse_value(noalias converter: *Converter, noalias module: *Module, maybe_expected_type: ?*Type) *Value {
const ValueKind = enum {
pointer,
value,
maybe_pointer,
};
fn parse_value(noalias converter: *Converter, noalias module: *Module, maybe_expected_type: ?*Type, value_kind: ValueKind) *Value {
converter.skip_space();
var value_state = ExpressionState.none;
@ -1019,12 +1084,12 @@ const Converter = struct {
const current_value = switch (converter.consume_character_if_match(left_parenthesis)) {
true => blk: {
const r = converter.parse_value(module, iterative_expected_type);
const r = converter.parse_value(module, iterative_expected_type, value_kind);
converter.skip_space();
converter.expect_character(right_parenthesis);
break :blk r;
},
false => converter.parse_single_value(module, iterative_expected_type),
false => converter.parse_single_value(module, iterative_expected_type, value_kind),
};
converter.skip_space();
@ -1048,7 +1113,7 @@ const Converter = struct {
.@"and" => module.llvm.builder.create_and(left.?.llvm, right.llvm),
.@"or" => module.llvm.builder.create_or(left.?.llvm, right.llvm),
.xor => module.llvm.builder.create_xor(left.?.llvm, right.llvm),
.icmp_ne => |icmp| module.llvm.builder.create_compare(icmp.to_int_predicate(), left.?.llvm, right.llvm),
.icmp_ne, .icmp_eq => |icmp| module.llvm.builder.create_compare(icmp.to_int_predicate(), left.?.llvm, right.llvm),
};
switch (value_state) {
@ -1057,7 +1122,24 @@ const Converter = struct {
previous_value = module.values.add();
previous_value.?.* = .{
.llvm = llvm_value,
.type = next_ty,
.type = switch (value_state) {
.none => unreachable,
.icmp_eq, .icmp_ne => module.integer_type(1, false),
.sub,
.add,
.mul,
.sdiv,
.udiv,
.srem,
.urem,
.shl,
.ashr,
.lshr,
.@"and",
.@"or",
.xor,
=> next_ty,
},
.bb = .instruction,
};
},
@ -1066,6 +1148,13 @@ const Converter = struct {
const ch = converter.content[converter.offset];
value_state = switch (ch) {
',', ';', right_parenthesis, right_bracket => break previous_value.?,
'=' => switch (converter.content[converter.offset + 1]) {
'=' => blk: {
converter.offset += 2;
break :blk .icmp_eq;
},
else => break previous_value.?,
},
'-' => blk: {
converter.offset += 1;
break :blk .sub;
@ -1183,7 +1272,7 @@ const Converter = struct {
switch (intrinsic_keyword) {
.extend => {
const source_value = converter.parse_value(module, null);
const source_value = converter.parse_value(module, null, .value);
converter.skip_space();
converter.expect_character(right_parenthesis);
const source_type = source_value.type;
@ -1209,7 +1298,7 @@ const Converter = struct {
}
}
fn parse_single_value(noalias converter: *Converter, noalias module: *Module, expected_type: ?*Type) *Value {
fn parse_single_value(noalias converter: *Converter, noalias module: *Module, expected_type: ?*Type, value_kind: ValueKind) *Value {
converter.skip_space();
if (module.current_function) |function| {
@ -1268,7 +1357,7 @@ const Converter = struct {
converter.skip_space();
const field_value = converter.parse_value(module, field.type);
const field_value = converter.parse_value(module, field.type, .value);
if (must_be_constant) {
if (field_index != field_count) {
@ -1325,7 +1414,7 @@ const Converter = struct {
converter.skip_space();
const field_value = converter.parse_value(module, field.type);
const field_value = converter.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());
@ -1378,7 +1467,7 @@ const Converter = struct {
break;
}
const element_value = converter.parse_value(module, array.element_type);
const element_value = converter.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;
@ -1416,6 +1505,10 @@ const Converter = struct {
}
},
'#' => return converter.parse_intrinsic(module, expected_type),
'&' => {
converter.offset += 1;
return converter.parse_value(module, expected_type, .pointer);
},
else => os.abort(),
};
@ -1443,6 +1536,9 @@ const Converter = struct {
converter.skip_space();
if (converter.consume_character_if_match(left_parenthesis)) {
if (value_kind == .pointer) {
converter.report_error();
}
const call = converter.parse_call(module, variable.value);
break :b call;
} else if (converter.consume_character_if_match('.')) {
@ -1458,13 +1554,21 @@ const Converter = struct {
} else converter.report_error();
const field = struct_type.fields[field_index];
const gep = module.llvm.builder.create_struct_gep(variable.value.type.llvm.handle.to_struct(), variable.value.llvm, field_index);
const load = module.values.add();
load.* = .{
.llvm = module.llvm.builder.create_load(field.type.llvm.handle, gep),
.type = field.type,
.bb = .instruction,
};
break :b load;
switch (value_kind) {
.pointer, .maybe_pointer => {
@trap();
},
.value => {
const load = module.values.add();
load.* = .{
.llvm = module.llvm.builder.create_load(field.type.llvm.handle, gep),
.type = field.type,
.bb = .instruction,
};
break :b load;
},
}
},
.bits => |*bits| {
const field_name = converter.parse_identifier();
@ -1479,6 +1583,10 @@ const Converter = struct {
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) {
converter.report_error();
}
const value = module.values.add();
value.* = .{
.type = bits.backing_type,
@ -1488,6 +1596,24 @@ const Converter = struct {
break :b value;
},
.pointer => {
converter.expect_character('&');
switch (value_kind) {
.pointer, .maybe_pointer => {
break :b variable.value;
},
.value => {
const load = module.values.add();
load.* = .{
.llvm = module.llvm.builder.create_load(variable.value.type.llvm.handle, variable.value.llvm),
.type = variable.value.type,
.bb = .instruction,
};
break :b load;
},
}
},
else => @trap(),
}
} else if (converter.consume_character_if_match(left_bracket)) {
@ -1496,28 +1622,43 @@ const Converter = struct {
const index_type = module.integer_type(64, false);
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();
const index = converter.parse_value(module, index_type);
const index = converter.parse_value(module, index_type, .value);
converter.skip_space();
converter.expect_character(right_bracket);
const gep = module.llvm.builder.create_gep(variable.value.type.llvm.handle, variable.value.llvm, &.{ zero_index, index.llvm });
const load = module.values.add();
const load_type = variable.value.type.bb.array.element_type;
load.* = .{
.llvm = module.llvm.builder.create_load(load_type.llvm.handle, gep),
.type = load_type,
.bb = .instruction,
};
break :b load;
switch (value_kind) {
.pointer, .maybe_pointer => {
@trap();
},
.value => {
const load = module.values.add();
const load_type = variable.value.type.bb.array.element_type;
load.* = .{
.llvm = module.llvm.builder.create_load(load_type.llvm.handle, gep),
.type = load_type,
.bb = .instruction,
};
break :b load;
},
}
} else {
const load = module.values.add();
load.* = .{
.llvm = module.llvm.builder.create_load(variable.value.type.llvm.handle, variable.value.llvm),
.type = variable.value.type,
.bb = .instruction,
};
break :b load;
switch (value_kind) {
.pointer, .maybe_pointer => {
break :b variable.value;
},
.value => {
const load = module.values.add();
load.* = .{
.llvm = module.llvm.builder.create_load(variable.value.type.llvm.handle, variable.value.llvm),
.type = variable.value.type,
.bb = .instruction,
};
break :b load;
},
}
}
}
} else {
@ -1835,31 +1976,26 @@ pub const Abi = struct {
unreachable;
}
fn get_member_at_offset(ty: *Type, offset: u32) ?*Field {
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: ?*Field = null;
var last_match: ?*const Field = null;
_ = &offset_it;
_ = &last_match;
const struct_type = &ty.bb.@"struct";
for (struct_type.fields) |*field| {
if (offset_it > offset) {
break;
}
@trap();
last_match = field;
offset_it = @intCast(lib.align_forward_u64(offset_it + field.type.get_byte_size(), ty.get_byte_alignment()));
}
// const struct_type = ty.get_payload(.@"struct");
// for (struct_type.fields) |field| {
// if (offset_it > offset) {
// break;
// }
//
// last_match = field;
// offset_it = @intCast(lib.align_forward(offset_it + field.type.size, ty.alignment));
// }
//
// assert(last_match != null);
// return last_match;
assert(last_match != null);
return last_match;
}
fn contains_no_user_data(ty: *Type, start: u64, end: u64) bool {
@ -2226,9 +2362,7 @@ pub noinline fn convert(options: ConvertOptions) void {
};
if (high_part) |hp| {
_ = hp;
@trap();
// break :rta Abi.SystemV.get_argument_pair(.{ result_type, hp });
break :ret_ty_abi Abi.SystemV.get_argument_pair(.{ result_type, hp });
} else {
// TODO
const is_type = true;
@ -2369,7 +2503,51 @@ pub noinline fn convert(options: ConvertOptions) void {
// }));
// break :b &thread.void;
// },
// .direct_pair => |pair| get_anonymous_two_field_struct(thread, pair),
.direct_pair => |pair| b: {
for (module.anonymous_pair_type_buffer[0..module.anonymous_pair_type_count]) |*anonymous_type| {
_ = anonymous_type;
@trap();
} 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 = lib.global.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 = .{
.@"struct" = .{
.bit_alignment = byte_alignment * 8,
.byte_alignment = byte_alignment,
.byte_size = byte_size,
.bit_size = byte_size * 8,
.fields = fields,
},
},
.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;
break :b pair_type;
}
},
else => |t| @panic(@tagName(t)),
};
@ -2562,6 +2740,16 @@ pub noinline fn convert(options: ConvertOptions) void {
}
converter.parse_block(module);
const is_final_block_terminated = global.value.bb.function.current_basic_block.get_terminator() != null;
if (!is_final_block_terminated) {
switch (abi_return_type.bb) {
.void => {
module.llvm.builder.create_ret_void();
},
else => @trap(),
}
}
}
if (module.llvm.di_builder) |di_builder| {
@ -2771,7 +2959,7 @@ pub noinline fn convert(options: ConvertOptions) void {
}
} else {
if (global_type) |expected_type| {
const value = converter.parse_value(module, expected_type);
const value = converter.parse_value(module, expected_type, .value);
converter.skip_space();

View File

@ -177,3 +177,7 @@ test "basic_array" {
test "extern" {
try invsrc(@src());
}
test "pointer" {
try invsrc(@src());
}

View File

@ -142,6 +142,7 @@ pub extern fn LLVMDIBuilderCreateArrayType(builder: *llvm.DI.Builder, element_co
pub extern fn LLVMDIBuilderCreateStructType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, flags: llvm.DI.Flags, derived_from: ?*llvm.DI.Type, member_pointer: [*]const *llvm.DI.Type.Derived, member_length: c_uint, runtime_language: c_uint, vtable_holder: ?*llvm.DI.Metadata, unique_id_pointer: ?[*]const u8, unique_id_length: usize) *llvm.DI.Type.Composite;
pub extern fn LLVMDIBuilderCreateMemberType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, bit_offset: u64, flags: llvm.DI.Flags, member_type: *llvm.DI.Type) *llvm.DI.Type.Derived;
pub extern fn LLVMDIBuilderCreateBitFieldMemberType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, bit_offset: u64, bit_storage_offset: u64, flags: llvm.DI.Flags, member_type: *llvm.DI.Type) *llvm.DI.Type.Derived;
pub extern fn LLVMDIBuilderCreatePointerType(builder: *llvm.DI.Builder, element_type: *llvm.DI.Type, bit_size: u64, align_in_bits: u32, address_space: c_uint, name_pointer: [*]const u8, name_length: usize) *llvm.DI.Type.Derived;
pub extern fn LLVMMetadataReplaceAllUsesWith(forward: *llvm.DI.Type.Composite, complete: *llvm.DI.Type.Composite) void;

11
tests/pointer.bbb Normal file
View File

@ -0,0 +1,11 @@
modify = fn (v: &s32) void
{
v.& = 1;
}
[export] main = fn [cc(c)] () s32
{
>value: s32 = 0;
modify(&value);
return #extend(value == 1);
}