More C ABI coverage

This commit is contained in:
David Gonzalez Martin 2025-02-27 13:08:28 -06:00
parent 95c1bf6702
commit 3c7fea2526
6 changed files with 187 additions and 91 deletions

View File

@ -488,6 +488,12 @@ const targets = [@typeInfo(Architecture).@"enum".fields.len]type{
api.get_initializer(.X86), api.get_initializer(.X86),
}; };
pub const Intrinsic = enum {
pub const Id = enum(c_uint) {
_,
};
};
pub const Context = opaque { pub const Context = opaque {
pub const create = api.LLVMContextCreate; pub const create = api.LLVMContextCreate;
@ -518,6 +524,10 @@ pub const Context = opaque {
pub const get_void_type = api.LLVMVoidTypeInContext; pub const get_void_type = api.LLVMVoidTypeInContext;
pub const get_integer_type = api.LLVMIntTypeInContext; pub const get_integer_type = api.LLVMIntTypeInContext;
pub const get_pointer_type = api.LLVMPointerTypeInContext; pub const get_pointer_type = api.LLVMPointerTypeInContext;
pub fn get_intrinsic_type(context: *Context, intrinsic_id: Intrinsic.Id, parameter_types: []const *Type) *Type.Function {
return api.LLVMIntrinsicGetType(context, intrinsic_id, parameter_types.ptr, parameter_types.len);
}
}; };
pub const BasicBlock = opaque { pub const BasicBlock = opaque {
@ -568,6 +578,10 @@ pub const Module = opaque {
result.error_message = string.to_slice(); result.error_message = string.to_slice();
return result; return result;
} }
pub fn get_intrinsic_declaration(module: *Module, intrinsic_id: Intrinsic.Id, parameter_types: []const *Type) *Value {
return api.LLVMGetIntrinsicDeclaration(module, intrinsic_id, parameter_types.ptr, parameter_types.len);
}
}; };
pub const VerifyResult = struct { pub const VerifyResult = struct {
@ -1218,6 +1232,10 @@ pub const Dwarf = struct {
}; };
}; };
pub fn lookup_intrinsic_id(name: []const u8) Intrinsic.Id {
return api.LLVMLookupIntrinsicID(name.ptr, name.len);
}
pub const IntPredicate = enum(c_int) { pub const IntPredicate = enum(c_int) {
eq = 32, eq = 32,
ne, ne,

View File

@ -115,6 +115,9 @@ const Module = struct {
global_scope: *llvm.DI.Scope, global_scope: *llvm.DI.Scope,
file: *llvm.DI.File, file: *llvm.DI.File,
pointer_type: *llvm.Type, pointer_type: *llvm.Type,
// intrinsics: struct {
// trap:
// },
}; };
pub fn get_infer_or_ignore_value(module: *Module) *Value { pub fn get_infer_or_ignore_value(module: *Module) *Value {
@ -272,6 +275,31 @@ const Module = struct {
return module; return module;
} }
pub fn get_pointer_type(module: *Module, element_type: *Type) *Type {
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;
}
}; };
pub const Function = struct { pub const Function = struct {
@ -612,28 +640,7 @@ const Converter = struct {
const element_type = converter.parse_type(module); const element_type = converter.parse_type(module);
const all_types = module.types.get(); return module.get_pointer_type(element_type);
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(), else => @trap(),
} }
@ -925,6 +932,12 @@ const Converter = struct {
} else { } else {
converter.report_error(); converter.report_error();
} }
} else if (statement_start_ch == '#') {
const intrinsic = converter.parse_intrinsic(module, null);
switch (intrinsic.type.bb) {
.void, .noreturn => {},
else => @trap(),
}
} else if (is_identifier_start_ch(statement_start_ch)) { } else if (is_identifier_start_ch(statement_start_ch)) {
const statement_start_identifier = converter.parse_identifier(); const statement_start_identifier = converter.parse_identifier();
@ -970,16 +983,22 @@ const Converter = struct {
} }
var is_second_block_terminated = false; var is_second_block_terminated = false;
module.llvm.builder.position_at_end(not_taken_block);
if (is_else) { if (is_else) {
module.llvm.builder.position_at_end(not_taken_block);
converter.parse_block(module); converter.parse_block(module);
is_second_block_terminated = current_function.current_basic_block.get_terminator() != null; is_second_block_terminated = current_function.current_basic_block.get_terminator() != null;
} else {
@trap();
} }
if (!(is_first_block_terminated and is_second_block_terminated)) { if (!(is_first_block_terminated and is_second_block_terminated)) {
@trap(); if (!is_first_block_terminated) {
@trap();
}
if (!is_second_block_terminated) {
if (is_else) {
@trap();
} else {}
}
} }
require_semicolon = false; require_semicolon = false;
@ -1252,11 +1271,12 @@ const Converter = struct {
const Prefix = enum { const Prefix = enum {
none, none,
negative, negative,
not_zero,
}; };
const Intrinsic = enum { const Intrinsic = enum {
extend, extend,
foo, trap,
}; };
fn parse_intrinsic(noalias converter: *Converter, noalias module: *Module, expected_type: ?*Type) *Value { fn parse_intrinsic(noalias converter: *Converter, noalias module: *Module, expected_type: ?*Type) *Value {
@ -1294,7 +1314,27 @@ const Converter = struct {
return value; return value;
}, },
else => unreachable, .trap => {
converter.expect_character(right_parenthesis);
// TODO: lookup in advance
const intrinsic_id = llvm.lookup_intrinsic_id("llvm.trap");
const parameter_types: []const *llvm.Type = &.{};
const parameter_values: []const *llvm.Value = &.{};
const intrinsic_function = module.llvm.handle.get_intrinsic_declaration(intrinsic_id, parameter_types);
const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, parameter_types);
const llvm_call = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, parameter_values);
_ = module.llvm.builder.create_unreachable();
const value = module.values.add();
value.* = .{
.llvm = llvm_call,
.type = module.noreturn_type,
.bb = .instruction,
};
return value;
},
} }
} }
@ -1509,12 +1549,19 @@ const Converter = struct {
converter.offset += 1; converter.offset += 1;
return converter.parse_value(module, expected_type, .pointer); return converter.parse_value(module, expected_type, .pointer);
}, },
'!' => blk: {
converter.offset += 1;
// TODO: should we skip space here?
converter.skip_space();
break :blk .not_zero;
},
else => os.abort(), else => os.abort(),
}; };
const value_offset = converter.offset; const value_offset = converter.offset;
const value_start_ch = converter.content[value_offset]; const value_start_ch = converter.content[value_offset];
const value = switch (value_start_ch) { var value = switch (value_start_ch) {
'a'...'z', 'A'...'Z', '_' => b: { 'a'...'z', 'A'...'Z', '_' => b: {
if (module.current_function) |current_function| { if (module.current_function) |current_function| {
const identifier = converter.parse_identifier(); const identifier = converter.parse_identifier();
@ -1668,6 +1715,21 @@ const Converter = struct {
'0'...'9' => converter.parse_integer(module, expected_type.?, prefix == .negative), '0'...'9' => converter.parse_integer(module, expected_type.?, prefix == .negative),
else => os.abort(), else => os.abort(),
}; };
_ = &value;
switch (prefix) {
.none,
.negative, // Already done in 'parse_integer' // TODO:
=> {},
.not_zero => {
const llvm_value = module.llvm.builder.create_compare(.eq, value.llvm, value.type.llvm.handle.to_integer().get_constant(0, 0).to_value());
value.* = .{
.llvm = llvm_value,
.bb = .instruction,
.type = module.integer_type(1, false),
};
},
}
return value; return value;
} }
@ -1924,12 +1986,10 @@ pub const Abi = struct {
} }
} }
fn get_int_type_at_offset(ty: *Type, offset: u32, source_type: *Type, source_offset: u32) *Type { fn get_int_type_at_offset(module: *Module, ty: *Type, offset: u32, source_type: *Type, source_offset: u32) *Type {
switch (ty.bb) { switch (ty.bb) {
.bits => { .bits => |bits| {
@trap(); return get_int_type_at_offset(module, bits.backing_type, offset, if (source_type == ty) bits.backing_type else source_type, source_offset);
// const bitfield = ty.get_payload(.bitfield);
// return get_int_type_at_offset(bitfield.backing_type, offset, if (source_type == ty) bitfield.backing_type else source_type, source_offset);
}, },
.integer => |integer_type| { .integer => |integer_type| {
switch (integer_type.bit_count) { switch (integer_type.bit_count) {
@ -1948,29 +2008,25 @@ pub const Abi = struct {
// .typed_pointer => return if (offset == 0) ty else unreachable, // .typed_pointer => return if (offset == 0) ty else unreachable,
.@"struct" => { .@"struct" => {
if (get_member_at_offset(ty, offset)) |field| { if (get_member_at_offset(ty, offset)) |field| {
return get_int_type_at_offset(field.type, @intCast(offset - field.byte_offset), source_type, source_offset); return get_int_type_at_offset(module, field.type, @intCast(offset - field.byte_offset), source_type, source_offset);
} }
unreachable; unreachable;
}, },
.array => { .array => |array_type| {
@trap(); const element_type = array_type.element_type;
// const array_type = ty.get_payload(.array); const element_size = element_type.get_byte_size();
// const element_type = array_type.descriptor.element_type; const element_offset = (offset / element_size) * element_size;
// const element_size = element_type.size; return get_int_type_at_offset(module, element_type, @intCast(offset - element_offset), source_type, source_offset);
// const element_offset = (offset / element_size) * element_size;
// return get_int_type_at_offset(element_type, @intCast(offset - element_offset), source_type, source_offset);
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
if (source_type.get_byte_size() - source_offset > 8) { if (source_type.get_byte_size() - source_offset > 8) {
@trap(); return module.integer_type(64, false);
// return &instance.threads[ty.sema.thread].integers[63].type;
} else { } else {
// const byte_count = source_type.size - source_offset; const byte_count = source_type.get_byte_size() - source_offset;
// const bit_count = byte_count * 8; const bit_count = byte_count * 8;
// return &instance.threads[ty.sema.thread].integers[bit_count - 1].type; return module.integer_type(@intCast(bit_count), false);
@trap();
} }
unreachable; unreachable;
@ -2090,13 +2146,13 @@ pub const Abi = struct {
unreachable; unreachable;
} }
fn indirect_return(ty: *Type) Function.Abi.Information { fn indirect_return(ty: *Type) Abi.Information {
if (ty.is_aggregate()) { if (ty.is_aggregate()) {
return .{ return .{
.kind = .{ .kind = .{
.indirect = .{ .indirect = .{
.type = ty, .type = ty,
.alignment = ty.alignment, .alignment = @intCast(ty.get_byte_alignment()),
}, },
}, },
}; };
@ -2324,30 +2380,28 @@ pub noinline fn convert(options: ConvertOptions) void {
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}, },
.integer => b: { .integer => b: {
const result_type = Abi.SystemV.get_int_type_at_offset(semantic_return_type, 0, semantic_return_type, 0); const result_type = Abi.SystemV.get_int_type_at_offset(module, semantic_return_type, 0, semantic_return_type, 0);
if (type_classes[1] == .none and semantic_return_type.get_bit_size() < 32) { if (type_classes[1] == .none and semantic_return_type.get_bit_size() < 32) {
// const signed = switch (semantic_return_type.sema.id) { const signed = switch (semantic_return_type.bb) {
// .integer => @intFromEnum(semantic_return_type.get_payload(.integer).signedness) != 0, .integer => |integer_type| integer_type.signed,
// .bitfield => false, .bits => false,
// else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
// }; };
// _ = signed; // _ = signed;
break :ret_ty_abi .{
@trap(); .kind = .{
// break :rta .{ .direct_coerce = semantic_return_type,
// .kind = .{ },
// .direct_coerce = semantic_return_type, .attributes = .{
// }, .sign_extend = signed,
// .attributes = .{ .zero_extend = !signed,
// .sign_extend = signed, },
// .zero_extend = !signed, };
// },
// };
} }
break :b result_type; break :b result_type;
}, },
.memory => @trap(), // break :rta Abi.SystemV.indirect_return(semantic_return_type), .memory => break :ret_ty_abi Abi.SystemV.indirect_return(semantic_return_type),
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
@ -2355,7 +2409,7 @@ pub noinline fn convert(options: ConvertOptions) void {
.none, .memory => null, .none, .memory => null,
.integer => b: { .integer => b: {
assert(type_classes[0] != .none); assert(type_classes[0] != .none);
const high_part = Abi.SystemV.get_int_type_at_offset(semantic_return_type, 8, semantic_return_type, 8); const high_part = Abi.SystemV.get_int_type_at_offset(module, semantic_return_type, 8, semantic_return_type, 8);
break :b high_part; break :b high_part;
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
@ -2372,12 +2426,11 @@ pub noinline fn convert(options: ConvertOptions) void {
.kind = .direct, .kind = .direct,
}; };
} else { } else {
@trap(); break :ret_ty_abi Abi.Information{
// break :rta Function.Abi.Information{ .kind = .{
// .kind = .{ .direct_coerce = result_type,
// .direct_coerce = result_type, },
// }, };
// };
} }
} else { } else {
unreachable; unreachable;
@ -2415,7 +2468,7 @@ pub noinline fn convert(options: ConvertOptions) void {
const result_type = switch (type_classes[0]) { const result_type = switch (type_classes[0]) {
.integer => b: { .integer => b: {
needed_registers.gpr += 1; needed_registers.gpr += 1;
const result_type = Abi.SystemV.get_int_type_at_offset(semantic_argument_type, 0, semantic_argument_type, 0); const result_type = Abi.SystemV.get_int_type_at_offset(module, semantic_argument_type, 0, semantic_argument_type, 0);
if (type_classes[1] == .none and semantic_argument_type.get_bit_size() < 32) { if (type_classes[1] == .none and semantic_argument_type.get_bit_size() < 32) {
const signed = switch (semantic_argument_type.bb) { const signed = switch (semantic_argument_type.bb) {
.integer => |integer_type| integer_type.signed, .integer => |integer_type| integer_type.signed,
@ -2443,7 +2496,7 @@ pub noinline fn convert(options: ConvertOptions) void {
.integer => b: { .integer => b: {
assert(type_classes[0] != .none); assert(type_classes[0] != .none);
needed_registers.gpr += 1; needed_registers.gpr += 1;
const high_part = Abi.SystemV.get_int_type_at_offset(semantic_argument_type, 8, semantic_argument_type, 8); const high_part = Abi.SystemV.get_int_type_at_offset(module, semantic_argument_type, 8, semantic_argument_type, 8);
break :b high_part; break :b high_part;
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
@ -2497,16 +2550,20 @@ pub noinline fn convert(options: ConvertOptions) void {
const abi_return_type = switch (return_type_abi.kind) { const abi_return_type = switch (return_type_abi.kind) {
.ignore, .direct => semantic_return_type, .ignore, .direct => semantic_return_type,
.direct_coerce => |coerced_type| coerced_type, .direct_coerce => |coerced_type| coerced_type,
// .indirect => |indirect| b: { .indirect => |indirect| b: {
// _ = abi_argument_types.append(get_typed_pointer(thread, .{ const indirect_pointer_type = module.get_pointer_type(indirect.type);
// .pointee = indirect.type, abi_argument_type_buffer[abi_argument_type_count] = indirect_pointer_type;
// })); llvm_abi_argument_type_buffer[abi_argument_type_count] = indirect_pointer_type.llvm.handle;
// break :b &thread.void; abi_argument_type_count += 1;
// }, break :b module.void_type;
},
.direct_pair => |pair| b: { .direct_pair => |pair| b: {
for (module.anonymous_pair_type_buffer[0..module.anonymous_pair_type_count]) |*anonymous_type| { for (module.anonymous_pair_type_buffer[0..module.anonymous_pair_type_count]) |anonymous_type_index| {
_ = anonymous_type; const anonymous_type = &module.types.get()[anonymous_type_index];
@trap(); const fields = anonymous_type.bb.@"struct".fields;
if (fields.len == 2 and pair[0] == fields[0].type and pair[1] == fields[1].type) {
break :b anonymous_type;
}
} else { } else {
const llvm_pair_members = &.{ pair[0].llvm.handle, pair[1].llvm.handle }; 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 llvm_pair = module.llvm.context.get_struct_type(llvm_pair_members);
@ -2572,9 +2629,12 @@ pub noinline fn convert(options: ConvertOptions) void {
llvm_abi_argument_type_buffer[abi_argument_type_count] = pair[1].llvm.handle; llvm_abi_argument_type_buffer[abi_argument_type_count] = pair[1].llvm.handle;
abi_argument_type_count += 1; abi_argument_type_count += 1;
}, },
// .indirect => |indirect| _ = abi_argument_types.append(get_typed_pointer(thread, .{ .indirect => |indirect| {
// .pointee = indirect.type, const indirect_pointer_type = module.get_pointer_type(indirect.type);
// })), abi_argument_type_buffer[abi_argument_type_count] = indirect_pointer_type;
llvm_abi_argument_type_buffer[abi_argument_type_count] = indirect_pointer_type.llvm.handle;
abi_argument_type_count += 1;
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }

View File

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

View File

@ -65,6 +65,11 @@ pub extern fn LLVMTypeOf(value: *llvm.Value) *llvm.Type;
pub extern fn LLVMGlobalGetValueType(value: *llvm.GlobalValue) *llvm.Type; pub extern fn LLVMGlobalGetValueType(value: *llvm.GlobalValue) *llvm.Type;
pub extern fn llvm_value_is_instruction(value: *llvm.Value) bool; pub extern fn llvm_value_is_instruction(value: *llvm.Value) bool;
// Intrinsics
pub extern fn LLVMLookupIntrinsicID(name_pointer: [*]const u8, name_length: usize) llvm.Intrinsic.Id;
pub extern fn LLVMGetIntrinsicDeclaration(module: *llvm.Module, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Value;
pub extern fn LLVMIntrinsicGetType(context: *llvm.Context, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Type.Function;
// TYPES // TYPES
// Types: integers // Types: integers
pub extern fn LLVMVoidTypeInContext(context: *llvm.Context) *llvm.Type; pub extern fn LLVMVoidTypeInContext(context: *llvm.Context) *llvm.Type;

View File

@ -138,9 +138,9 @@ ByVal = struct
[extern] c_ret_struct_with_array = fn [cc(c)] () StructWithArray; [extern] c_ret_struct_with_array = fn [cc(c)] () StructWithArray;
[extern] c_modify_by_ref_param = fn [cc(c)] (x: ByRef) ByRef; [extern] c_modify_by_ref_param = fn [cc(c)] (x: ByRef) ByRef;
[extern] c_func_ptr_byval fn [cc(c)] (a: u64, b: u64, c: ByVal, d: u64, e: u64, f: u64) void; [extern] c_func_ptr_byval = fn [cc(c)] (a: u64, b: u64, c: ByVal, d: u64, e: u64, f: u64) void;
[export] require = fn [cc(c)] (ok: u8) [export] require = fn [cc(c)] (ok: u8) void
{ {
if (!ok) if (!ok)
{ {

9
tests/if_no_else.bbb Normal file
View File

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