Basic pointer
All checks were successful
CI / ci (ReleaseFast, ubuntu-latest) (push) Successful in 30s
CI / ci (ReleaseSmall, ubuntu-latest) (push) Successful in 32s
CI / ci (ReleaseSafe, ubuntu-latest) (push) Successful in 38s
CI / ci (Debug, ubuntu-latest) (push) Successful in 51s

This commit is contained in:
David Gonzalez Martin 2025-04-06 15:22:46 +02:00
parent dd8bfda870
commit 0d910fd58d
6 changed files with 148 additions and 74 deletions

View File

@ -974,6 +974,10 @@ pub const Builder = opaque {
pub fn create_select(builder: *Builder, condition: *Value, true_value: *Value, false_value: *Value) *Value {
return api.LLVMBuildSelect(builder, condition, true_value, false_value, "");
}
pub fn create_neg(builder: *Builder, value: *Value) *Value {
return api.LLVMBuildNeg(builder, value, "");
}
};
pub const GlobalValue = opaque {

View File

@ -185,6 +185,11 @@ pub const Global = struct {
};
};
pub const ResolvedType = struct {
handle: *llvm.Type,
debug: *llvm.DI.Type,
};
pub const Type = struct {
bb: union(enum) {
void,
@ -205,6 +210,58 @@ pub const Type = struct {
debug: ?*llvm.DI.Type = null,
} = .{},
fn resolve(ty: *Type, module: *Module) ResolvedType {
if (ty.llvm.handle) |llvm_handle| {
return .{
.handle = llvm_handle,
.debug = ty.llvm.debug orelse undefined,
};
} else {
const llvm_type = switch (ty.bb) {
.void, .noreturn => module.llvm.void_type,
.integer => |integer| module.llvm.context.get_integer_type(integer.bit_count).to_type(),
// Consider function types later since we need to deal with ABI
.function => null,
.pointer => module.llvm.pointer_type,
else => @trap(),
};
ty.llvm.handle = llvm_type;
const debug_type = if (module.has_debug_info) switch (ty.bb) {
.void => module.llvm.di_builder.create_basic_type(ty.name, 0, .void, .{}),
.noreturn => module.llvm.di_builder.create_basic_type(ty.name, 0, .void, .{ .no_return = true }),
.integer => |integer| module.llvm.di_builder.create_basic_type(ty.name, integer.bit_count, switch (integer.signed) {
true => .signed,
false => .unsigned,
}, .{}),
.function => |function| b: {
var debug_argument_type_buffer: [64]*llvm.DI.Type = undefined;
const semantic_debug_argument_types = debug_argument_type_buffer[0 .. function.semantic_argument_types.len + 1 + @intFromBool(function.is_var_args)];
semantic_debug_argument_types[0] = function.semantic_return_type.llvm.debug.?;
for (function.semantic_argument_types, semantic_debug_argument_types[1..][0..function.semantic_argument_types.len]) |semantic_type, *debug_argument_type| {
debug_argument_type.* = semantic_type.llvm.debug.?;
}
if (function.is_var_args) {
semantic_debug_argument_types[function.semantic_argument_types.len + 1] = module.void_type.llvm.debug.?;
}
const subroutine_type = module.llvm.di_builder.create_subroutine_type(module.llvm.file, semantic_debug_argument_types, .{});
break :b subroutine_type.to_type();
},
.pointer => |pointer| module.llvm.di_builder.create_pointer_type(pointer.type.llvm.debug.?, 64, 64, 0, ty.name).to_type(),
else => @trap(),
} else null;
ty.llvm.debug = debug_type;
return .{
.handle = if (llvm_type) |lt| lt else undefined,
.debug = if (debug_type) |dt| dt else undefined,
};
}
}
pub const Integer = struct {
bit_count: u32,
signed: bool,
@ -428,13 +485,6 @@ const Binary = struct {
};
};
fn negate_llvm_value(value: *llvm.Value, is_constant: bool) *llvm.Value {
return switch (is_constant) {
true => value.to_constant().negate().to_value(),
false => @trap(),
};
}
pub const Value = struct {
bb: union(enum) {
function: Function,
@ -444,9 +494,11 @@ pub const Value = struct {
variable_reference: *Variable,
local,
intrinsic: Intrinsic,
dereference: *Value,
},
type: ?*Type = null,
llvm: ?*llvm.Value = null,
kind: Kind = .right,
const Intrinsic = union(Id) {
byte_size,
@ -487,6 +539,7 @@ pub const Value = struct {
fn is_constant(value: *Value) bool {
return switch (value.bb) {
.constant_integer => true,
.variable_reference => false,
else => @trap(),
};
}
@ -683,6 +736,8 @@ pub const Module = struct {
di_builder: *llvm.DI.Builder,
file: *llvm.DI.File,
compile_unit: *llvm.DI.CompileUnit,
pointer_type: *llvm.Type,
void_type: *llvm.Type,
};
pub fn integer_type(module: *Module, bit_count: u32, sign: bool) *Type {
@ -1653,6 +1708,7 @@ pub const Module = struct {
.bb = .{
.variable_reference = variable,
},
.kind = value_builder.kind,
};
return value;
}
@ -1747,34 +1803,32 @@ pub const Module = struct {
.none => unreachable,
.@"-" => .@"-",
.@"+" => .@"+",
.@"&" => .@"&",
else => @trap(),
};
const right = module.parse_precedence(value_builder.with_precedence(.prefix).with_token(.none).with_kind(if (unary_id == .@"&") .left else value_builder.kind));
const value = switch (unary_id) {
.@"+" => @trap(),
.@"-" => blk: {
const value = module.values.add();
value.* = .{
.bb = .{
.unary = .{
.id = unary_id,
.value = right,
},
},
};
break :blk value;
const value = module.values.add();
value.* = .{
.bb = .{
.unary = .{
.id = unary_id,
.value = right,
},
},
else => @trap(),
};
return value;
}
fn rule_after_dereference(noalias module: *Module, value_builder: Value.Builder) *Value {
_ = module;
_ = value_builder;
@trap();
const value = module.values.add();
value.* = .{
.bb = .{
.dereference = value_builder.left orelse unreachable,
},
};
return value;
}
fn rule_before_parenthesis(noalias module: *Module, value_builder: Value.Builder) *Value {
@ -2022,6 +2076,8 @@ pub const Module = struct {
.di_builder = di_builder,
.file = file,
.compile_unit = compile_unit,
.void_type = context.get_void_type(),
.pointer_type = context.get_pointer_type(0).to_type(),
};
}
@ -2049,50 +2105,6 @@ pub const Module = struct {
pub fn emit(module: *Module) void {
module.initialize_llvm();
const llvm_pointer_type = module.llvm.context.get_pointer_type(0).to_type();
const void_type = module.llvm.context.get_void_type();
for (module.types.get_slice()) |*ty| {
const llvm_type = switch (ty.bb) {
.void, .noreturn => void_type,
.integer => |integer| module.llvm.context.get_integer_type(integer.bit_count).to_type(),
// Consider function types later since we need to deal with ABI
.function => null,
.pointer => llvm_pointer_type,
else => @trap(),
};
ty.llvm.handle = llvm_type;
if (module.has_debug_info) {
const debug_type = switch (ty.bb) {
.void => module.llvm.di_builder.create_basic_type(ty.name, 0, .void, .{}),
.noreturn => module.llvm.di_builder.create_basic_type(ty.name, 0, .void, .{ .no_return = true }),
.integer => |integer| module.llvm.di_builder.create_basic_type(ty.name, integer.bit_count, switch (integer.signed) {
true => .signed,
false => .unsigned,
}, .{}),
.function => |function| blk: {
var debug_argument_type_buffer: [64]*llvm.DI.Type = undefined;
const semantic_debug_argument_types = debug_argument_type_buffer[0 .. function.semantic_argument_types.len + 1 + @intFromBool(function.is_var_args)];
semantic_debug_argument_types[0] = function.semantic_return_type.llvm.debug.?;
for (function.semantic_argument_types, semantic_debug_argument_types[1..][0..function.semantic_argument_types.len]) |semantic_type, *debug_argument_type| {
debug_argument_type.* = semantic_type.llvm.debug.?;
}
if (function.is_var_args) {
semantic_debug_argument_types[function.semantic_argument_types.len + 1] = module.void_type.llvm.debug.?;
}
const subroutine_type = module.llvm.di_builder.create_subroutine_type(module.llvm.file, semantic_debug_argument_types, .{});
break :blk subroutine_type.to_type();
},
.pointer => |pointer| module.llvm.di_builder.create_pointer_type(pointer.type.llvm.debug.?, 64, 64, 0, ty.name).to_type(),
else => @trap(),
};
ty.llvm.debug = debug_type;
}
}
for (module.globals.get_slice()) |*global| {
switch (global.variable.storage.?.bb) {
.function => {
@ -2158,7 +2170,7 @@ pub const Module = struct {
},
}
const llvm_abi_argument_types = llvm_abi_argument_type_buffer[0..abi_argument_type_count];
const llvm_function_type = llvm.Type.Function.get(function_type.abi_return_type.llvm.handle.?, llvm_abi_argument_types, function_type.is_var_args);
const llvm_function_type = llvm.Type.Function.get(function_type.abi_return_type.resolve(module).handle, llvm_abi_argument_types, function_type.is_var_args);
const subroutine_type_flags = llvm.DI.Flags{};
const subroutine_type = if (module.has_debug_info) blk: {
@ -2619,7 +2631,12 @@ pub const Module = struct {
module.emit_value(function, unary.value);
break :b unary.value.llvm orelse unreachable;
};
break :blk negate_llvm_value(unary_value, unary.value.is_constant());
break :blk module.negate_llvm_value(unary_value, unary.value.is_constant());
},
.@"&" => blk: {
assert(value_type == unary.value.type);
module.emit_value(function, unary.value);
break :blk unary.value.llvm orelse unreachable;
},
else => @trap(),
},
@ -2684,7 +2701,12 @@ pub const Module = struct {
}),
.aggregate => @trap(),
.complex => @trap(),
} else @trap(),
} else if (variable.storage.?.type == value_type) blk: {
assert(value.kind == .left);
break :blk variable.storage.?.llvm.?;
} else {
@trap();
},
.intrinsic => |intrinsic| switch (intrinsic) {
.extend => |extended_value| blk: {
if (extended_value.llvm == null) {
@ -2700,6 +2722,18 @@ pub const Module = struct {
},
else => @trap(),
},
.dereference => |dereferenceable_value| blk: {
module.emit_value(function, dereferenceable_value);
const result = switch (value.kind) {
.left => @trap(),
.right => module.create_load(.{
.type = dereferenceable_value.type.?.bb.pointer.type,
.value = dereferenceable_value.llvm.?,
.alignment = dereferenceable_value.type.?.bb.pointer.alignment,
}),
};
break :blk result;
},
else => @trap(),
};
@ -2779,6 +2813,16 @@ pub const Module = struct {
},
else => @trap(),
},
.dereference => |dereferenceable_value| {
module.analyze_value_type(function, dereferenceable_value, .{});
if (dereferenceable_value.type.?.bb != .pointer) {
module.report_error();
}
if (dereferenceable_value.type.?.bb.pointer.type != expected_type) {
module.report_error();
}
},
else => @trap(),
},
else => @trap(),
@ -2786,6 +2830,10 @@ pub const Module = struct {
// Resolve the value type. If a result type does not exist, compute it
const value_type = if (analysis.type) |expected_type| expected_type else switch (value.bb) {
.unary => |unary| blk: {
module.analyze_value_type(function, unary.value, .{});
break :blk unary.value.type.?;
},
.binary => |binary| blk: {
if (binary.left.bb == .constant_integer and binary.right.bb == .constant_integer) {
module.report_error();
@ -2811,8 +2859,9 @@ pub const Module = struct {
assert(binary.left.type == binary.right.type);
break :blk binary.left.type.?;
},
.variable_reference => |variable| blk: {
break :blk variable.type;
.variable_reference => |variable| switch (value.kind) {
.right => variable.type,
else => variable.storage.?.type.?,
},
else => @trap(),
};
@ -3072,7 +3121,7 @@ pub const Module = struct {
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);
const v = module.llvm.builder.create_alloca(abi_type.resolve(module).handle, options.name);
v.set_alignment(alignment);
return v;
}
@ -3119,6 +3168,13 @@ pub const Module = struct {
};
return result;
}
fn negate_llvm_value(module: *Module, value: *llvm.Value, is_constant: bool) *llvm.Value {
return switch (is_constant) {
true => value.to_constant().negate().to_value(),
false => module.llvm.builder.create_neg(value),
};
}
};
pub const Options = struct {

View File

@ -78,6 +78,7 @@ pub extern fn LLVMBuildXor(builder: *llvm.Builder, left: *llvm.Value, right: *ll
pub extern fn LLVMBuildICmp(builder: *llvm.Builder, predicate: llvm.IntPredicate, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value;
pub extern fn LLVMBuildBr(builder: *llvm.Builder, block: *llvm.BasicBlock) *llvm.Value;
pub extern fn LLVMBuildCondBr(builder: *llvm.Builder, condition: *llvm.Value, taken: *llvm.BasicBlock, not_taken: *llvm.BasicBlock) *llvm.Value;
pub extern fn LLVMBuildNeg(builder: *llvm.Builder, value: *llvm.Value, name: [*:0]const u8) *llvm.Value;
pub extern fn llvm_builder_create_alloca(builder: *llvm.Builder, ty: *llvm.Type, address_space: c_uint, name: llvm.String) *llvm.Value;
pub extern fn LLVMBuildStore(builder: *llvm.Builder, value: *llvm.Value, pointer: *llvm.Value) *llvm.Value;

View File

@ -187,5 +187,7 @@ const names = &[_][]const u8{
"minimal_stack_arithmetic2",
"minimal_stack_arithmetic3",
"extend",
"stack_negation",
"basic_pointer",
// "pointer",
};

6
tests/basic_pointer.bbb Normal file
View File

@ -0,0 +1,6 @@
[export] main = fn [cc(c)] () s32
{
>a: s32 = 0;
>pointer = &a;
return pointer.&;
}

5
tests/stack_negation.bbb Normal file
View File

@ -0,0 +1,5 @@
[export] main = fn [cc(c)] () s32
{
>v: s32 = 0;
return -v;
}