Merge pull request #117 from birth-software/c-abi-tests

Implement basic C ABI support
This commit is contained in:
David 2024-03-24 09:00:31 -06:00 committed by GitHub
commit 03f4d6368e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 10487 additions and 1645 deletions

View File

@ -14,15 +14,17 @@ concurrency:
jobs:
self_hosted_linux:
runs-on: [ self-hosted, Linux, x64 ]
#runs-on: [ self-hosted, Linux, x64 ]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Test
run: |
zig build test -Dself_hosted_ci=true -Dstatic=true -Dllvm_path=../../../../../dev/llvm/llvm-static-release-zen4-17.0.6/out/x86_64-linux-musl-native
zig build test -Dself_hosted_ci=true -Dstatic=false
echo "TODO"
#zig build test -Dself_hosted_ci=true -Dstatic=true -Dllvm_path=../../../../../dev/llvm/llvm-static-release-zen4-17.0.6/out/x86_64-linux-musl-native
#zig build test -Dself_hosted_ci=true -Dstatic=false
# macos_m1:
# runs-on: macos-14
# timeout-minutes: 15

2
.vscode/launch.json vendored
View File

@ -12,7 +12,7 @@
"args": [
"exe",
"-main_source_file",
"test/standalone/hello_world/main.nat"
"test/build/c-abi/main.nat",
],
"cwd": "${workspaceFolder}",
},

View File

@ -1,3 +1,4 @@
{
"zig.initialSetupDone": true
"zig.initialSetupDone": true,
"cmake.configureOnOpen": false
}

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,7 @@ pub const LLVM = struct {
llvm_instruction_map: MyHashMap(Compilation.Instruction.Index, *LLVM.Value) = .{},
llvm_value_map: MyHashMap(Compilation.V, *LLVM.Value) = .{},
llvm_block_map: MyHashMap(Compilation.BasicBlock.Index, *LLVM.Value.BasicBlock) = .{},
llvm_external_functions: MyHashMap(*Compilation.Debug.Declaration.Global, *LLVM.Value.Constant.Function) = .{},
global_variable_map: MyHashMap(*Compilation.Debug.Declaration.Global, *LLVM.Value.Constant.GlobalVariable) = .{},
scope_map: MyHashMap(*Compilation.Debug.Scope, *LLVM.DebugInfo.Scope) = .{},
pointer_type: ?*LLVM.Type.Pointer = null,
@ -46,11 +47,20 @@ pub const LLVM = struct {
return_phi_node: ?*LLVM.Value.Instruction.PhiNode = null,
scope: *LLVM.DebugInfo.Scope = undefined,
file: *LLVM.DebugInfo.File = undefined,
attributes: Attributes,
// subprogram: *LLVM.DebugInfo.Subprogram = undefined,
arg_index: u32 = 0,
tag_count: c_uint = 0,
inside_branch: bool = false,
pub const Attributes = struct {
noreturn: *Attribute,
naked: *Attribute,
nounwind: *Attribute,
inreg: *Attribute,
@"noalias": *Attribute,
};
pub const Linkage = enum(c_uint) {
@"extern" = 0,
available_external = 1,
@ -80,6 +90,10 @@ pub const LLVM = struct {
const getPointerType = bindings.NativityLLVMGetPointerType;
const getStructType = bindings.NativityLLVMGetStructType;
const getIntrinsicType = bindings.NativityLLVMContextGetIntrinsicType;
const getAttributeFromEnum = bindings.NativityLLVMContextGetAttributeFromEnum;
const getAttributeFromString = bindings.NativityLLVMContextGetAttributeFromString;
const getAttributeFromType = bindings.NativityLLVMContextGetAttributeFromType;
const getAttributeSet = bindings.NativityLLVMContextGetAttributeSet;
};
pub const Module = opaque {
@ -130,6 +144,7 @@ pub const LLVM = struct {
const createGlobalString = bindings.NativityLLVMBuilderCreateGlobalString;
const createGlobalStringPointer = bindings.NativityLLVMBuilderCreateGlobalStringPointer;
const createPhi = bindings.NativityLLVMBuilderCreatePhi;
const createMemcpy = bindings.NativityLLVMBuilderCreateMemcpy;
const getInsertBlock = bindings.NativityLLVMBuilderGetInsertBlock;
const isCurrentBlockTerminated = bindings.NativityLLVMBuilderIsCurrentBlockTerminated;
@ -552,7 +567,9 @@ pub const LLVM = struct {
pub const Tuple = opaque {};
};
pub const Attribute = enum(u32) {
pub const Attribute = opaque {
pub const Set = opaque {};
pub const Id = enum(u32) {
AllocAlign = 1,
AllocatedPointer = 2,
AlwaysInline = 3,
@ -637,6 +654,7 @@ pub const LLVM = struct {
UWTable = 82,
VScaleRange = 83,
};
};
pub const Type = opaque {
const compare = bindings.NativityLLVMCompareTypes;
@ -647,6 +665,7 @@ pub const LLVM = struct {
const isPointer = bindings.NativityLLVMTypeIsPointer;
const isInteger = bindings.NativityLLVMTypeIsInteger;
const isVoid = bindings.NativityLLVMTypeIsVoid;
const assertEqual = bindings.NativityLLVMTypeAssertEqual;
pub const Array = opaque {
fn toType(integer: *@This()) *Type {
@ -748,6 +767,7 @@ pub const LLVM = struct {
pub const Call = opaque {
const setCallingConvention = bindings.NativityLLVMCallSetCallingConvention;
const setAttributes = bindings.NativityLLVMCallSetAttributes;
fn toValue(this: *@This()) *Value {
return @ptrCast(this);
}
@ -860,15 +880,17 @@ pub const LLVM = struct {
pub const Constant = opaque {
pub const Function = opaque {
const getArgument = bindings.NativityLLVMFunctionGetArgument;
const getArguments = bindings.NativityLLVMFunctionGetArguments;
const getType = bindings.NativityLLVMFunctionGetType;
const addAttributeKey = bindings.NativityLLVMFunctionAddAttributeKey;
// const addAttributeKey = bindings.NativityLLVMFunctionAddAttributeKey;
const verify = bindings.NativityLLVMVerifyFunction;
const toString = bindings.NativityLLVMFunctionToString;
const setCallingConvention = bindings.NativityLLVMFunctionSetCallingConvention;
const getCallingConvention = bindings.NativityLLVMFunctionGetCallingConvention;
const setSubprogram = bindings.NativityLLVMFunctionSetSubprogram;
const getSubprogram = bindings.NativityLLVMFunctionGetSubprogram;
const setAttributes = bindings.NativityLLVMFunctionSetAttributes;
fn toValue(this: *@This()) *Value {
return @ptrCast(this);
@ -1181,21 +1203,11 @@ pub const LLVM = struct {
const llvm_type: *LLVM.Type = switch (sema_type.*) {
.function => |function_prototype_index| blk: {
const sema_function_prototype = unit.function_prototypes.get(function_prototype_index);
const llvm_return_type = try llvm.getType(unit, context, sema_function_prototype.return_type);
var parameter_types = try UnpinnedArray(*LLVM.Type).initialize_with_capacity(context.my_allocator, @intCast(sema_function_prototype.argument_types.len));
const llvm_return_type = try llvm.getType(unit, context, sema_function_prototype.abi.return_type);
var parameter_types = try UnpinnedArray(*LLVM.Type).initialize_with_capacity(context.my_allocator, @intCast(sema_function_prototype.abi.parameter_types.len));
for (sema_function_prototype.argument_types) |argument_type_index| {
switch (unit.types.get(argument_type_index).*) {
// TODO: ABI
.integer,
.pointer,
// .@"enum",
.@"struct",
.slice,
// .bool,
=> parameter_types.append_with_capacity(try llvm.getType(unit, context, argument_type_index)),
else => |t| @panic(@tagName(t)),
}
for (sema_function_prototype.abi.parameter_types) |argument_type_index| {
parameter_types.append_with_capacity(try llvm.getType(unit, context, argument_type_index));
}
const is_var_args = false;
@ -1265,6 +1277,16 @@ pub const LLVM = struct {
break :blk struct_type.toType();
},
.two_struct => |pair| blk: {
const types = [2]*LLVM.Type{
try llvm.getType(unit, context, pair[0]),
try llvm.getType(unit, context, pair[1]),
};
const is_packed = false;
const struct_type = llvm.context.getStructType(&types, types.len, is_packed) orelse return Type.Error.@"struct";
break :blk struct_type.toType();
},
else => |t| @panic(@tagName(t)),
},
.array => |array| blk: {
@ -1289,11 +1311,12 @@ pub const LLVM = struct {
if (llvm.debug_info_file_map.get(sema_file_index)) |file| {
return file;
} else {
// if (@intFromEnum(sema_file_index) == 4) @breakpoint();
const sema_file = unit.files.get(sema_file_index);
const sub_path = std.fs.path.dirname(sema_file.relative_path) orelse "";
const file_path = std.fs.path.basename(sema_file.relative_path);
const directory_path = try Compilation.joinPath(context, sema_file.package.directory.path, sub_path);
const debug_file = llvm.debug_info_builder.createFile(file_path.ptr, file_path.len, directory_path.ptr, directory_path.len) orelse unreachable;
const full_path = try Compilation.joinPath(context, sema_file.package.directory.path, sema_file.relative_path);
const filename = std.fs.path.basename(full_path);
const directory = full_path[0 .. full_path.len - filename.len];
const debug_file = llvm.debug_info_builder.createFile(filename.ptr, filename.len, directory.ptr, directory.len) orelse unreachable;
try llvm.debug_info_file_map.put_no_clobber(context.my_allocator, sema_file_index, debug_file);
return debug_file;
}
@ -1868,6 +1891,7 @@ pub const LLVM = struct {
.global => |global| {
const constant = switch (global.initial_value) {
.function_definition => llvm.function_definition_map.get(global).?.toConstant(),
.function_declaration => llvm.llvm_external_functions.get(global).?.toConstant(),
else => llvm.global_variable_map.get(global).?.toConstant(),
};
return constant;
@ -2040,7 +2064,97 @@ pub const LLVM = struct {
return call.toValue();
}
fn emitParameterAttributes(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, abi: Compilation.Function.AbiInfo, is_return: bool) !*const LLVM.Attribute.Set {
var attributes = UnpinnedArray(*LLVM.Attribute){};
if (abi.attributes.by_reg) {
try attributes.append(context.my_allocator, llvm.attributes.inreg);
}
switch (abi.kind) {
.ignore => {
assert(is_return);
},
.direct, .direct_pair, .direct_coerce => {},
.indirect => |indirect| {
const indirect_type = try llvm.getType(unit, context, indirect.type);
if (is_return) {
const sret = llvm.context.getAttributeFromType(.StructRet, indirect_type);
try attributes.append(context.my_allocator, sret);
try attributes.append(context.my_allocator, llvm.attributes.@"noalias");
// TODO: alignment
} else {
if (abi.attributes.by_value) {
const byval = llvm.context.getAttributeFromType(.ByVal, indirect_type);
try attributes.append(context.my_allocator, byval);
}
//TODO: alignment
}
},
else => |t| @panic(@tagName(t)),
}
const attribute_set = llvm.context.getAttributeSet(attributes.pointer, attributes.length);
return attribute_set;
}
fn getFunctionAttributes(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, function_prototype: *Compilation.Function.Prototype) !*const LLVM.Attribute.Set {
var function_attributes = UnpinnedArray(*LLVM.Attribute){};
try function_attributes.append(context.my_allocator, llvm.attributes.nounwind);
switch (unit.types.get(function_prototype.return_type).*) {
.noreturn => {
try function_attributes.append(context.my_allocator, llvm.attributes.noreturn);
},
else => {},
}
if (function_prototype.attributes.naked) {
try function_attributes.append(context.my_allocator, llvm.attributes.naked);
}
const function_attribute_set = llvm.context.getAttributeSet(function_attributes.pointer, function_attributes.length);
return function_attribute_set;
}
const CallOrFunction = union(enum) {
call: *LLVM.Value.Instruction.Call,
function: *LLVM.Value.Constant.Function,
};
fn setCallOrFunctionAttributes(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, function_prototype: *Compilation.Function.Prototype, call_or_function: CallOrFunction) !void {
const function_attribute_set = try llvm.getFunctionAttributes(unit, context, function_prototype);
var parameter_attribute_sets = try UnpinnedArray(*const LLVM.Attribute.Set).initialize_with_capacity(context.my_allocator, @intCast(function_prototype.abi.parameter_types_abi.len + @intFromBool(function_prototype.abi.return_type_abi.kind == .indirect)));
const return_attribute_set = blk: {
const attribute_set = try llvm.emitParameterAttributes(unit, context, function_prototype.abi.return_type_abi, true);
break :blk switch (function_prototype.abi.return_type_abi.kind) {
.indirect => b: {
parameter_attribute_sets.append_with_capacity(attribute_set);
break :b llvm.context.getAttributeSet(null, 0);
},
else => attribute_set,
};
};
for (function_prototype.abi.parameter_types_abi) |parameter_type_abi| {
const parameter_attribute_set = try llvm.emitParameterAttributes(unit, context, parameter_type_abi, false);
parameter_attribute_sets.append_with_capacity(parameter_attribute_set);
}
const calling_convention = getCallingConvention(function_prototype.calling_convention);
switch (call_or_function) {
.call => |call| {
call.setAttributes(llvm.context, function_attribute_set, return_attribute_set, parameter_attribute_sets.pointer, parameter_attribute_sets.length);
call.setCallingConvention(calling_convention);
},
.function => |function| {
function.setAttributes(llvm.context, function_attribute_set, return_attribute_set, parameter_attribute_sets.pointer, parameter_attribute_sets.length);
function.setCallingConvention(calling_convention);
},
}
}
fn emitFunctionDeclaration(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, declaration: *Compilation.Debug.Declaration.Global) !void {
const name = unit.getIdentifier(declaration.declaration.name);
const function_type = try llvm.getType(unit, context, declaration.declaration.type);
const is_export = declaration.attributes.contains(.@"export");
const is_extern = declaration.attributes.contains(.@"extern");
@ -2053,23 +2167,35 @@ pub const LLVM = struct {
// TODO: Check name collision
const mangle_name = !export_or_extern;
_ = mangle_name; // autofix
const name = unit.getIdentifier(declaration.declaration.name);
const function = llvm.module.createFunction(function_type.toFunction() orelse unreachable, linkage, address_space, name.ptr, name.len) orelse return Error.function;
const function_prototype = unit.function_prototypes.get(unit.types.get(declaration.declaration.type).function);
switch (unit.types.get(function_prototype.return_type).*) {
.noreturn => {
function.addAttributeKey(.NoReturn);
},
else => {},
}
try llvm.setCallOrFunctionAttributes(unit, context, function_prototype, .{
.function = function,
});
// const calling_convention = getCallingConvention(function_prototype.calling_convention);
// function.setCallingConvention(calling_convention);
//
// const function_attribute_set = llvm.getFunctionAttributes(unit, context, function_prototype);
//
// var parameter_attribute_sets = try UnpinnedArray(*const LLVM.Attribute.Set).initialize_with_capacity(context.my_allocator, @intCast(function_prototype.abi.parameter_types_abi.len + @intFromBool(function_prototype.abi.return_type_abi.kind == .indirect)));
// const return_attribute_set = blk: {
// const attribute_set = try llvm.emitParameterAttributes(unit, context, function_prototype.abi.return_type_abi, true);
// break :blk switch (function_prototype.abi.return_type_abi.kind) {
// .indirect => b: {
// parameter_attribute_sets.append_with_capacity(attribute_set);
// break :b llvm.context.getAttributeSet(null, 0);
// },
// else => attribute_set,
// };
// };
//
// for (function_prototype.abi.parameter_types_abi) |parameter_type_abi| {
// const parameter_attribute_set = try llvm.emitParameterAttributes(unit, context, parameter_type_abi, false);
// parameter_attribute_sets.append_with_capacity(parameter_attribute_set);
// }
if (function_prototype.attributes.naked) {
function.addAttributeKey(.Naked);
}
const calling_convention = getCallingConvention(function_prototype.calling_convention);
function.setCallingConvention(calling_convention);
// function.setAttributes(llvm.context, function_attribute_set, return_attribute_set, parameter_attribute_sets.pointer, parameter_attribute_sets.length);
switch (declaration.initial_value) {
.function_declaration => try llvm.function_declaration_map.put_no_clobber(context.my_allocator, declaration, function),
@ -2078,6 +2204,7 @@ pub const LLVM = struct {
}
if (unit.descriptor.generate_debug_information) {
// if (data_structures.byte_equal(name, "nat_split_struct_ints")) @breakpoint();
const debug_file = try llvm.getDebugInfoFile(unit, context, declaration.declaration.scope.file);
var parameter_types = try UnpinnedArray(*LLVM.DebugInfo.Type).initialize_with_capacity(context.my_allocator, @intCast(function_prototype.argument_types.len));
for (function_prototype.argument_types) |argument_type_index| {
@ -2116,7 +2243,6 @@ pub const LLVM = struct {
};
const subroutine_type_calling_convention = LLVM.DebugInfo.CallingConvention.none;
const subroutine_type = llvm.debug_info_builder.createSubroutineType(parameter_types.pointer, parameter_types.length, subroutine_type_flags, subroutine_type_calling_convention) orelse unreachable;
const scope_line = 0;
const subprogram_flags = LLVM.DebugInfo.Subprogram.Flags{
.virtuality = .none,
.local_to_unit = !export_or_extern,
@ -2131,11 +2257,13 @@ pub const LLVM = struct {
};
const subprogram_declaration = null;
const function_name = unit.getIdentifier(declaration.declaration.name);
const subprogram = llvm.debug_info_builder.createFunction(debug_file.toScope(), function_name.ptr, function_name.len, function_name.ptr, function_name.len, debug_file, declaration.declaration.line + 1, subroutine_type, scope_line, subroutine_type_flags, subprogram_flags, subprogram_declaration) orelse unreachable;
const subprogram = llvm.debug_info_builder.createFunction(debug_file.toScope(), function_name.ptr, function_name.len, function_name.ptr, function_name.len, debug_file, declaration.declaration.line + 1, subroutine_type, declaration.declaration.line + 1, subroutine_type_flags, subprogram_flags, subprogram_declaration) orelse unreachable;
function.setSubprogram(subprogram);
switch (declaration.initial_value) {
.function_declaration => {},
.function_declaration => {
try llvm.llvm_external_functions.put_no_clobber(context.my_allocator, declaration, function);
},
.function_definition => |function_definition_index| {
const function_definition = unit.function_definitions.get(function_definition_index);
const scope = subprogram.toLocalScope().toScope();
@ -2185,12 +2313,20 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
.module = module,
.builder = builder,
.debug_info_builder = module.createDebugInfoBuilder() orelse return Error.debug_info_builder,
.attributes = .{
.naked = llvm_context.getAttributeFromEnum(.Naked, 0),
.noreturn = llvm_context.getAttributeFromEnum(.NoReturn, 0),
.nounwind = llvm_context.getAttributeFromEnum(.NoUnwind, 0),
.inreg = llvm_context.getAttributeFromEnum(.InReg, 0),
.@"noalias" = llvm_context.getAttributeFromEnum(.NoAlias, 0),
},
};
if (unit.descriptor.generate_debug_information) {
const filename = "main";
const directory = ".";
const debug_info_file = llvm.debug_info_builder.createFile(filename, filename.len, directory, directory.len) orelse unreachable;
const full_path = try std.fs.cwd().realpathAlloc(context.allocator, unit.descriptor.main_package_path);
const filename = std.fs.path.basename(full_path);
const directory = full_path[0 .. full_path.len - filename.len];
const debug_info_file = llvm.debug_info_builder.createFile(filename.ptr, filename.len, directory.ptr, directory.len) orelse unreachable;
const producer = "nativity";
const is_optimized = false;
const flags = "";
@ -2291,7 +2427,6 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
llvm.scope = subprogram.toLocalScope().toScope();
}
llvm.arg_index = 0;
var alloca_map = MyHashMap(Compilation.Instruction.Index, *LLVM.Value){};
var block_command_list = BasicBlockList{};
@ -2438,18 +2573,23 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, call.toValue());
},
.stack_slot => |stack_slot| {
// const stack_slot_type = unit.types.get(stack_slot.type);
// const stack_slot_alignment = stack_slot_type.getAbiAlignment(unit);
const declaration_type = try llvm.getType(unit, context, stack_slot.type);
const type_alignment = unit.types.get(stack_slot.type).getAbiAlignment(unit);
const alloca_array_size = null;
const declaration_alloca = llvm.builder.createAlloca(declaration_type, address_space, alloca_array_size, "", "".len) orelse return LLVM.Value.Instruction.Error.alloca;
const declaration_alloca = llvm.builder.createAlloca(declaration_type, address_space, alloca_array_size, "", "".len, type_alignment) orelse return LLVM.Value.Instruction.Error.alloca;
try alloca_map.put_no_clobber(context.my_allocator, instruction_index, declaration_alloca.toValue());
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, declaration_alloca.toValue());
},
.store => |store| {
const right = try llvm.emitRightValue(unit, context, store.source);
const source_type = unit.types.get(store.source.type);
const alignment = source_type.getAbiAlignment(unit);
const is_volatile = false;
const left = try llvm.emitLeftValue(unit, context, store.destination);
const store_instruction = llvm.builder.createStore(right, left, is_volatile) orelse return LLVM.Value.Instruction.Error.store;
const store_instruction = llvm.builder.createStore(right, left, is_volatile, alignment) orelse return LLVM.Value.Instruction.Error.store;
_ = store_instruction; // autofix
},
.cast => |cast| {
@ -2469,6 +2609,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
.slice_to_nullable,
.slice_to_not_null,
.slice_coerce_to_zero_termination,
.slice_zero_to_no_termination,
.pointer_to_nullable,
.pointer_const_to_var,
.pointer_to_array_to_pointer_to_many,
@ -2515,9 +2656,11 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
break :blk value;
};
const load_type = unit.types.get(load.type);
const alignment = if (load.alignment) |alignment| alignment else load_type.getAbiAlignment(unit);
const value_type = try llvm.getType(unit, context, load.type);
const is_volatile = false;
const load_i = llvm.builder.createLoad(value_type, value, is_volatile, "", "".len) orelse return LLVM.Value.Instruction.Error.load;
const load_i = llvm.builder.createLoad(value_type, value, is_volatile, "", "".len, alignment) orelse return LLVM.Value.Instruction.Error.load;
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, load_i.toValue());
},
.integer_binary_operation => |binary_operation| {
@ -2553,66 +2696,60 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
};
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, instruction);
},
.call => |call| {
.call => |sema_call| {
var argument_buffer: [32]*LLVM.Value = undefined;
const argument_count = call.arguments.len;
const argument_count = sema_call.arguments.len;
const arguments = argument_buffer[0..argument_count];
switch (call.callable.value) {
.@"comptime" => |ct| switch (ct) {
.global => |call_function_declaration| {
const call_function_type = call_function_declaration.declaration.type;
const call_function_prototype = unit.function_prototypes.get(unit.types.get(call_function_type).function);
assert(call_function_type == call.function_type);
const call_function_type = unit.types.get(sema_call.function_type);
const function_prototype = unit.function_prototypes.get(call_function_type.function);
const call_type = try llvm.getType(unit, context, sema_call.function_type);
const call = switch (sema_call.callable.value) {
.@"comptime" => |ct| switch (ct) {
.global => |call_function_declaration| b: {
const callee = switch (call_function_declaration.initial_value) {
.function_definition => llvm.function_definition_map.get(call_function_declaration).?,
.function_declaration => llvm.function_declaration_map.get(call_function_declaration).?,
else => |t| @panic(@tagName(t)),
};
for (call.arguments, arguments) |argument_value, *argument| {
for (sema_call.arguments, arguments) |argument_value, *argument| {
argument.* = try llvm.emitRightValue(unit, context, argument_value);
}
const llvm_calling_convention = callee.getCallingConvention();
const name = "";
const call_type = try llvm.getType(unit, context, call.function_type);
const function_name = unit.getIdentifier(call_function_declaration.declaration.name);
_ = function_name; // autofix
const function_type = call_type.toFunction() orelse unreachable;
for (call.arguments, arguments, call_function_prototype.argument_types, 0..) |sema_argument, argument, sema_argument_type, i| {
for (sema_call.arguments, arguments, function_prototype.abi.parameter_types, 0..) |sema_argument, argument, sema_argument_type, i| {
assert(sema_argument.type == sema_argument_type);
const argument_type = function_type.getArgumentType(@intCast(i));
assert(argument_type == argument.getType());
argument_type.assertEqual(argument.getType());
}
const call_instruction = llvm.builder.createCall(function_type, callee.toValue(), arguments.ptr, arguments.len, name.ptr, name.len, null) orelse return LLVM.Value.Instruction.Error.call;
call_instruction.setCallingConvention(llvm_calling_convention);
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, call_instruction.toValue());
break :b call_instruction;
},
else => |t| @panic(@tagName(t)),
},
.runtime => |ii| {
.runtime => |ii| b: {
const callee = llvm.llvm_instruction_map.get(ii).?;
const callable_type = unit.types.get(call.function_type);
const sema_calling_convention = switch (callable_type.*) {
.function => |function_prototype_index| unit.function_prototypes.get(function_prototype_index).calling_convention,
else => |t| @panic(@tagName(t)),
};
const calling_convention = getCallingConvention(sema_calling_convention);
for (call.arguments, arguments) |argument_value, *argument| {
for (sema_call.arguments, arguments) |argument_value, *argument| {
argument.* = try llvm.emitRightValue(unit, context, argument_value);
}
const name = "";
const call_type = try llvm.getType(unit, context, call.function_type);
const function_type = call_type.toFunction() orelse unreachable;
const call_instruction = llvm.builder.createCall(function_type, callee, arguments.ptr, arguments.len, name.ptr, name.len, null) orelse return LLVM.Value.Instruction.Error.call;
call_instruction.setCallingConvention(calling_convention);
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, call_instruction.toValue());
break :b call_instruction;
},
else => |t| @panic(@tagName(t)),
}
};
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, call.toValue());
try llvm.setCallOrFunctionAttributes(unit, context, function_prototype, .{
.call = call,
});
},
.ret => |return_value| {
const value = switch (return_value.type) {
@ -2670,25 +2807,16 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
.@"unreachable" => {
_ = llvm.builder.createUnreachable() orelse return LLVM.Value.Instruction.Error.@"unreachable";
},
.argument_declaration => |argument_declaration| {
var argument_buffer: [16]*LLVM.Value.Argument = undefined;
var argument_count: usize = argument_buffer.len;
llvm.function.getArguments(&argument_buffer, &argument_count);
const arguments = argument_buffer[0..argument_count];
const argument_index = llvm.arg_index;
llvm.arg_index += 1;
const argument = arguments[argument_index];
const name = unit.getIdentifier(argument_declaration.declaration.name);
argument.toValue().setName(name.ptr, name.len);
const argument_type_index = argument_declaration.declaration.type;
_ = argument_type_index; // autofix
const argument_type = argument.toValue().getType();
const alloca_array_size: ?*LLVM.Value = null;
const argument_value = argument.toValue();
const declaration_alloca = llvm.builder.createAlloca(argument_type, address_space, alloca_array_size, "", "".len) orelse return LLVM.Value.Instruction.Error.alloca;
.abi_argument => |argument_index| {
const argument = llvm.function.getArgument(argument_index) orelse unreachable;
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, argument.toValue());
},
.debug_declare_argument => |debug_declare| {
if (unit.descriptor.generate_debug_information) {
const debug_declaration_type = try llvm.getDebugType(unit, context, argument_declaration.declaration.type);
const argument_index: c_uint = debug_declare.argument.index;
const declaration = &debug_declare.argument.declaration;
const debug_declaration_type = try llvm.getDebugType(unit, context, declaration.type);
const declaration_alloca = llvm.llvm_instruction_map.get(debug_declare.stack).?;
const always_preserve = true;
const flags = LLVM.DebugInfo.Node.Flags{
.visibility = .none,
@ -2719,22 +2847,17 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
.little_endian = false,
.all_calls_described = false,
};
const declaration_name = unit.getIdentifier(argument_declaration.declaration.name);
const line = argument_declaration.declaration.line;
const column = argument_declaration.declaration.column;
const declaration_name = unit.getIdentifier(declaration.name);
const line = declaration.line;
const column = declaration.column;
const debug_parameter_variable = llvm.debug_info_builder.createParameterVariable(llvm.scope, declaration_name.ptr, declaration_name.len, argument_index + 1, llvm.file, line, debug_declaration_type, always_preserve, flags) orelse unreachable;
const insert_declare = llvm.debug_info_builder.insertDeclare(declaration_alloca.toValue(), debug_parameter_variable, llvm.context, line, column, (llvm.function.getSubprogram() orelse unreachable).toLocalScope().toScope(), llvm.builder.getInsertBlock() orelse unreachable);
const insert_declare = llvm.debug_info_builder.insertDeclare(declaration_alloca, debug_parameter_variable, llvm.context, line, column, (llvm.function.getSubprogram() orelse unreachable).toLocalScope().toScope(), llvm.builder.getInsertBlock() orelse unreachable);
_ = insert_declare;
}
const is_volatile = false;
const store = llvm.builder.createStore(argument_value, declaration_alloca.toValue(), is_volatile) orelse return LLVM.Value.Instruction.Error.store;
_ = store; // autofix
try llvm.argument_allocas.put_no_clobber(context.my_allocator, instruction_index, declaration_alloca.toValue());
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, declaration_alloca.toValue());
},
.debug_declare_local_variable => |declare_local_variable| {
if (unit.descriptor.generate_debug_information) {
const local_variable = declare_local_variable.variable;
const debug_declaration_type = try llvm.getDebugType(unit, context, local_variable.declaration.type);
const always_preserve = true;
@ -2778,6 +2901,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
const insert_declare = llvm.debug_info_builder.insertDeclare(local, debug_local_variable, llvm.context, line, column, (llvm.function.getSubprogram() orelse unreachable).toLocalScope().toScope(), llvm.builder.getInsertBlock() orelse unreachable);
_ = insert_declare;
}
},
.insert_value => |insert_value| {
const aggregate = try llvm.emitRightValue(unit, context, insert_value.expression);
@ -2916,6 +3040,21 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
const switch_instruction = llvm.builder.createSwitch(condition, else_block, condition_array.pointer, basic_block_array.pointer, condition_array.length, branch_weights, unpredictable);
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, switch_instruction.toValue());
},
.memcpy => |memcpy| {
const destination = try llvm.emitLeftValue(unit, context, memcpy.destination);
const source = try llvm.emitLeftValue(unit, context, memcpy.source);
const destination_alignment = if (memcpy.destination_alignment) |alignment| alignment else b: {
const ty = unit.types.get(memcpy.destination.type);
const alignment = ty.getAbiAlignment(unit);
break :b alignment;
};
const source_alignment = if (memcpy.source_alignment) |alignment| alignment else b: {
const ty = unit.types.get(memcpy.source.type);
const alignment = ty.getAbiAlignment(unit);
break :b alignment;
};
_ = llvm.builder.createMemcpy(destination, destination_alignment, source, source_alignment, memcpy.size, memcpy.is_volatile);
},
else => |t| @panic(@tagName(t)),
}
}
@ -2965,7 +3104,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
const module_dump = module_ptr[0..module_len];
_ = module_dump; // autofix
try write(.llvm, function_ir);
try write(.panic, function_ir);
const error_message = message_ptr[0..message_len];
try write(.panic, error_message);
// std.debug.print("\nLLVM verification for function inside module failed:\nFull module: {s}\n```\n{s}\n```\n{s}\n", .{ module_dump, function_ir, error_message });
@ -2982,6 +3121,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
// logln(.llvm, .print_module, "{s}", .{module_string});
const verify_module = true;
const print_module = true;
if (verify_module) {
var message_ptr: [*]const u8 = undefined;
var message_len: usize = 0;
@ -2995,6 +3135,12 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
}
}
if (print_module) {
try write(.llvm, "Module: \n");
try write(.llvm, module_string);
try write(.llvm, "\n");
}
// TODO: initialize only the target we are going to use
bindings.NativityLLVMInitializeCodeGeneration();
// TODO: proper target selection

View File

@ -50,21 +50,29 @@ pub extern fn NativityLLVMValueSetName(value: *LLVM.Value, name_ptr: [*]const u8
pub extern fn NativityLLVMValueGetType(value: *LLVM.Value) *LLVM.Type;
pub extern fn NativityLLVMArgumentGetIndex(argument: *LLVM.Value.Argument) c_uint;
pub extern fn NativityLLVMFunctionGetArguments(function: *LLVM.Value.Constant.Function, argument_ptr: [*]*LLVM.Value.Argument, argument_len: *usize) void;
pub extern fn NativityLLVMFunctionGetArgument(function: *LLVM.Value.Constant.Function, index: c_uint) ?*LLVM.Value.Argument;
pub extern fn NativityLLVMFunctionGetType(function: *LLVM.Value.Constant.Function) *LLVM.Type.Function;
pub extern fn NativityLLVMFunctionTypeGetReturnType(function_type: *LLVM.Type.Function) *LLVM.Type;
pub extern fn NativityLLVMTypeIsVoid(type: *LLVM.Type) bool;
pub extern fn NativityLLVMBuilderCreateAlloca(builder: *LLVM.Builder, type: *LLVM.Type, address_space: c_uint, array_size: ?*LLVM.Value, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value.Instruction.Alloca;
pub extern fn NativityLLVMBuilderCreateStore(builder: *LLVM.Builder, value: *LLVM.Value, pointer: *LLVM.Value, is_volatile: bool) ?*LLVM.Value.Instruction.Store;
pub extern fn NativityLLVMBuilderCreateAlloca(builder: *LLVM.Builder, type: *LLVM.Type, address_space: c_uint, array_size: ?*LLVM.Value, name_ptr: [*]const u8, name_len: usize, alignment: u32) ?*LLVM.Value.Instruction.Alloca;
pub extern fn NativityLLVMBuilderCreateStore(builder: *LLVM.Builder, value: *LLVM.Value, pointer: *LLVM.Value, is_volatile: bool, alignment: u32) ?*LLVM.Value.Instruction.Store;
pub extern fn NativityLLVMBuilderCreateMemcpy(builder: *LLVM.Builder, destination: *LLVM.Value, destination_alignment: u32, source: *LLVM.Value, source_alignment: u32, size: u64, is_volatile: bool) *LLVM.Value.Instruction.Call;
pub extern fn NativityLLVMContextGetConstantInt(context: *LLVM.Context, bit_count: c_uint, value: u64, is_signed: bool) ?*LLVM.Value.Constant.Int;
pub extern fn NativityLLVMContextGetConstantString(context: *LLVM.Context, name_ptr: [*]const u8, name_len: usize, null_terminate: bool) ?*LLVM.Value.Constant;
pub extern fn NativityLLVMGetConstantArray(array_type: *LLVM.Type.Array, value_ptr: [*]const *LLVM.Value.Constant, value_count: usize) ?*LLVM.Value.Constant;
pub extern fn NativityLLVMGetConstantStruct(struct_type: *LLVM.Type.Struct, constant_ptr: [*]const *LLVM.Value.Constant, constant_len: usize) ?*LLVM.Value.Constant;
pub extern fn NativityLLVMConstantToInt(constant: *LLVM.Value.Constant) ?*LLVM.Value.Constant.Int;
pub extern fn NativityLLVMBuilderCreateICmp(builder: *LLVM.Builder, integer_comparison: LLVM.Value.Instruction.ICmp.Kind, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value;
pub extern fn NativityLLVMBuilderCreateLoad(builder: *LLVM.Builder, type: *LLVM.Type, value: *LLVM.Value, is_volatile: bool, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value.Instruction.Load;
pub extern fn NativityLLVMBuilderCreateLoad(builder: *LLVM.Builder, type: *LLVM.Type, value: *LLVM.Value, is_volatile: bool, name_ptr: [*]const u8, name_len: usize, alignment: u32) ?*LLVM.Value.Instruction.Load;
pub extern fn NativityLLVMBuilderCreateRet(builder: *LLVM.Builder, value: ?*LLVM.Value) ?*LLVM.Value.Instruction.Ret;
pub extern fn NativityLLVMBuilderCreateCast(builder: *LLVM.Builder, cast_type: LLVM.Value.Instruction.Cast.Type, value: *LLVM.Value, type: *LLVM.Type, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value;
pub extern fn NativityLLVMFunctionAddAttributeKey(builder: *LLVM.Value.Constant.Function, attribute_key: LLVM.Attribute) void;
pub extern fn NativityLLVMContextGetAttributeFromEnum(context: *LLVM.Context, attribute_key: LLVM.Attribute.Id, attribute_value: u64) *LLVM.Attribute;
pub extern fn NativityLLVMContextGetAttributeFromString(context: *LLVM.Context, key_ptr: [*]const u8, key_len: usize, value_ptr: [*]const u8, value_len: usize) *LLVM.Attribute;
pub extern fn NativityLLVMContextGetAttributeFromType(context: *LLVM.Context, attribute_key: LLVM.Attribute.Id, type: *LLVM.Type) *LLVM.Attribute;
pub extern fn NativityLLVMContextGetAttributeSet(context: *LLVM.Context, attribute_ptr: ?[*]const *LLVM.Attribute, attribute_count: usize) *const LLVM.Attribute.Set;
pub extern fn NativityLLVMFunctionSetAttributes(function: *LLVM.Value.Constant.Function, context: *LLVM.Context, function_attributes: *const LLVM.Attribute.Set, return_attributes: *const LLVM.Attribute.Set, parameter_attribute_set_ptr: [*]const *const LLVM.Attribute.Set, parameter_attribute_set_count: usize) void;
pub extern fn NativityLLVMCallSetAttributes(call: *LLVM.Value.Instruction.Call, context: *LLVM.Context, function_attributes: *const LLVM.Attribute.Set, return_attributes: *const LLVM.Attribute.Set, parameter_attribute_set_ptr: [*]const *const LLVM.Attribute.Set, parameter_attribute_set_count: usize) void;
// pub extern fn NativityLLVMFunctionAddAttributeKey(builder: *LLVM.Value.Constant.Function, attribute_key: LLVM.Attribute) void;
pub extern fn NativityLLVMGetVoidType(context: *LLVM.Context) ?*LLVM.Type;
pub extern fn NativityLLVMGetInlineAssembly(function_type: *LLVM.Type.Function, assembly_ptr: [*]const u8, assembly_len: usize, constraints_ptr: [*]const u8, constrains_len: usize, has_side_effects: bool, is_align_stack: bool, dialect: LLVM.Value.InlineAssembly.Dialect, can_throw: bool) ?*LLVM.Value.InlineAssembly;
pub extern fn NativityLLVMBuilderCreateCall(builder: *LLVM.Builder, function_type: *LLVM.Type.Function, callee: *LLVM.Value, argument_ptr: [*]const *LLVM.Value, argument_count: usize, name_ptr: [*]const u8, name_len: usize, fp_math_tag: ?*LLVM.Metadata.Node) ?*LLVM.Value.Instruction.Call;
@ -143,6 +151,7 @@ pub extern fn NativityLLVMTargetCreateTargetMachine(target: *LLVM.Target, target
pub extern fn NativityLLVMModuleSetTargetMachineDataLayout(module: *LLVM.Module, target_machine: *LLVM.Target.Machine) void;
pub extern fn NativityLLVMModuleSetTargetTriple(module: *LLVM.Module, target_triple_ptr: [*]const u8, target_triple_len: usize) void;
pub extern fn NativityLLVMModuleAddPassesToEmitFile(module: *LLVM.Module, target_machine: *LLVM.Target.Machine, object_file_path_ptr: [*]const u8, object_file_path_len: usize, codegen_file_type: LLVM.CodeGenFileType, disable_verify: bool) bool;
pub extern fn NativityLLVMTypeAssertEqual(a: *LLVM.Type, b: *LLVM.Type) void;
pub extern fn NativityLLDLinkELF(argument_ptr: [*]const [*:0]const u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool;
pub extern fn NativityLLDLinkCOFF(argument_ptr: [*]const [*:0]const u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool;
pub extern fn NativityLLDLinkMachO(argument_ptr: [*]const [*:0]const u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool;

View File

@ -67,7 +67,7 @@ pub fn analyze(allocator: *MyAllocator, text: []const u8, token_buffer: *Token.B
var index: u32 = 0;
var line_index: u32 = lexer.line_offset;
try token_buffer.ensure_with_capacity(allocator, len / 4);
try token_buffer.ensure_with_capacity(allocator, len / 3);
// logln(.lexer, .end, "START LEXER - TOKEN OFFSET: {} - LINE OFFSET: {}", .{ Token.unwrap(lexer.offset), lexer.line_offset });

View File

@ -420,13 +420,23 @@ const Analyzer = struct {
if (data_structures.byte_equal(identifier_name, enum_field.name)) {
const attribute = @field(Compilation.Function.Attribute, enum_field.name);
const attribute_node = switch (attribute) {
.naked,
=> try analyzer.addNode(.{
.naked => try analyzer.addNode(.{
.id = @field(Node.Id, "function_attribute_" ++ @tagName(attribute)),
.token = identifier,
.left = .null,
.right = .null,
}),
.cc => try analyzer.addNode(.{
.id = .function_attribute_cc,
.token = identifier,
.left = b: {
_ = try analyzer.expectToken(.operator_left_parenthesis);
const cc = try analyzer.expression();
_ = try analyzer.expectToken(.operator_right_parenthesis);
break :b cc;
},
.right = .null,
}),
else => |t| @panic(@tagName(t)),
};
break attribute_node;
@ -462,7 +472,11 @@ const Analyzer = struct {
var list = UnpinnedArray(Node.Index){};
while (analyzer.peekToken() != end_token) {
const identifier = try analyzer.expectToken(.identifier);
const identifier_token = analyzer.token_i;
switch (analyzer.peekToken()) {
.identifier, .discard => analyzer.consumeToken(),
else => |t| @panic(@tagName(t)),
}
_ = try analyzer.expectToken(.operator_colon);
const type_expression = try analyzer.typeExpression();
@ -472,7 +486,7 @@ const Analyzer = struct {
try list.append(analyzer.my_allocator, try analyzer.addNode(.{
.id = .argument_declaration,
.token = identifier,
.token = identifier_token,
.left = type_expression,
.right = Node.Index.null,
}));

View File

@ -2,6 +2,7 @@ const std = @import("std");
const builtin = @import("builtin");
const os = builtin.os.tag;
const arch = builtin.cpu.arch;
const page_size = std.mem.page_size;
pub fn assert(ok: bool) void {
if (!ok) unreachable;
@ -257,7 +258,7 @@ pub fn MyHashMap(comptime K: type, comptime V: type) type {
.Slice => byte_equal(k, key),
else => k == key,
},
.Struct => equal(k, key),
.Struct, .Array => equal(k, key),
else => k == key,
};
@ -331,7 +332,6 @@ pub fn enumFromString(comptime E: type, string: []const u8) ?E {
} else null;
}
const page_size = std.mem.page_size;
extern fn pthread_jit_write_protect_np(enabled: bool) void;
pub fn allocate_virtual_memory(size: usize, flags: packed struct {
@ -353,10 +353,10 @@ pub fn allocate_virtual_memory(size: usize, flags: packed struct {
.linux => u32,
.macos => c_int,
else => @compileError("OS not supported"),
} = if (flags.executable) std.os.PROT.EXEC else 0;
const protection_flags: u32 = @intCast(std.os.PROT.READ | std.os.PROT.WRITE | execute_flag);
} = if (flags.executable) std.posix.PROT.EXEC else 0;
const protection_flags: u32 = @intCast(std.posix.PROT.READ | std.posix.PROT.WRITE | execute_flag);
const result = try std.os.mmap(null, size, protection_flags, .{
const result = try std.posix.mmap(null, size, protection_flags, .{
.TYPE = .PRIVATE,
.ANONYMOUS = true,
}, -1, 0);
@ -372,13 +372,13 @@ pub fn allocate_virtual_memory(size: usize, flags: packed struct {
};
}
pub fn free_virtual_memory(slice: []align(0x1000) const u8) void {
pub fn free_virtual_memory(slice: []align(page_size) const u8) void {
switch (os) {
.windows => {
std.os.windows.VirtualFree(slice.ptr, slice.len, std.os.windows.MEM_RELEASE);
},
else => {
std.os.munmap(slice);
std.posix.munmap(slice);
},
}
}
@ -736,3 +736,8 @@ pub fn last(bytes: []const u8, byte: u8) ?usize {
return null;
}
pub fn align_forward(value: u64, alignment: u64) u64 {
const mask = alignment - 1;
return (value + mask) & ~mask;
}

View File

@ -55,7 +55,7 @@ pub fn entry_point(arguments: [][*:0]u8) !void {
return error.InvalidInput;
}
if (std.process.can_execv and std.os.getenvZ(env_detecting_libc_paths) != null) {
if (std.process.can_execv and std.posix.getenvZ(env_detecting_libc_paths) != null) {
todo();
}

178
build.zig
View File

@ -1,14 +1,32 @@
const std = @import("std");
const assert = std.debug.assert;
const builtin = @import("builtin");
const arch = builtin.cpu.arch;
const os = builtin.os.tag;
fn discover_brew_prefix(b: *std.Build, library_name: []const u8) ![]const u8 {
assert(os == .macos);
const result = try std.ChildProcess.run(.{
.allocator = b.allocator,
.argv = &.{ "brew", "--prefix", library_name },
});
var i = result.stdout.len - 1;
while (result.stdout[i] == '\n' or result.stdout[i] == ' ') {
i -= 1;
}
const trimmed_path = result.stdout[0 .. i + 1];
return trimmed_path;
}
pub fn build(b: *std.Build) !void {
const self_hosted_ci = b.option(bool, "self_hosted_ci", "This option enables the self-hosted CI behavior") orelse false;
const third_party_ci = b.option(bool, "third_party_ci", "This option enables the third-party CI behavior") orelse false;
const is_ci = self_hosted_ci or third_party_ci;
const print_stack_trace = b.option(bool, "print_stack_trace", "This option enables printing stack traces inside the compiler") orelse is_ci or @import("builtin").os.tag == .macos;
const print_stack_trace = b.option(bool, "print_stack_trace", "This option enables printing stack traces inside the compiler") orelse is_ci or os == .macos;
const native_target = b.resolveTargetQuery(.{});
const optimization = b.standardOptimizeOption(.{});
const static = b.option(bool, "static", "This option enables the compiler to be built statically") orelse false;
const static = b.option(bool, "static", "This option enables the compiler to be built statically") orelse true;
const compiler_options = b.addOptions();
compiler_options.addOption(bool, "print_stack_trace", print_stack_trace);
@ -21,7 +39,7 @@ pub fn build(b: *std.Build) !void {
});
var target_query = b.standardTargetOptionsQueryOnly(.{});
const abi = b.option(std.Target.Abi, "abi", "This option modifies the ABI used for the compiler") orelse if (static) switch (native_target.result.os.tag) {
const abi = b.option(std.Target.Abi, "abi", "This option modifies the ABI used for the compiler") orelse if (static) switch (os) {
else => target_query.abi,
.linux => b: {
const os_release = try std.fs.cwd().readFileAlloc(b.allocator, "/etc/os-release", 0xffff);
@ -86,60 +104,9 @@ pub fn build(b: *std.Build) !void {
compiler.linkLibC();
if (static) {
compiler.linkage = .static;
compiler.linkLibCpp();
const prefix = "nat/cache";
const llvm_version = "17.0.6";
const llvm_path = b.option([]const u8, "llvm_path", "LLVM prefix path") orelse blk: {
assert(!self_hosted_ci);
if (third_party_ci or (!target.query.isNativeOs() or !target.query.isNativeCpu())) {
var llvm_directory = try std.ArrayListUnmanaged(u8).initCapacity(b.allocator, 128);
llvm_directory.appendSliceAssumeCapacity(prefix ++ "/");
llvm_directory.appendSliceAssumeCapacity("llvm-");
llvm_directory.appendSliceAssumeCapacity(llvm_version);
llvm_directory.appendSliceAssumeCapacity("-");
llvm_directory.appendSliceAssumeCapacity(@tagName(target.result.cpu.arch));
llvm_directory.appendSliceAssumeCapacity("-");
llvm_directory.appendSliceAssumeCapacity(@tagName(target.result.os.tag));
llvm_directory.appendSliceAssumeCapacity("-");
llvm_directory.appendSliceAssumeCapacity(@tagName(target.result.abi));
llvm_directory.appendSliceAssumeCapacity("-");
const cpu = if (std.mem.eql(u8, target.result.cpu.model.name, @tagName(target.result.cpu.arch))) "baseline" else target.result.cpu.model.name;
llvm_directory.appendSliceAssumeCapacity(cpu);
const url = try std.mem.concat(b.allocator, u8, &.{ "https://github.com/birth-software/fetch-llvm/releases/download/v", llvm_version, "/llvm-", llvm_version, "-", @tagName(target.result.cpu.arch), "-", @tagName(target.result.os.tag), "-", @tagName(target.result.abi), "-", cpu, ".tar.xz" });
var dir = std.fs.cwd().openDir(llvm_directory.items, .{}) catch {
const run = b.addRunArtifact(fetcher);
compiler.step.dependOn(&run.step);
run.addArg("-prefix");
run.addArg(prefix);
run.addArg("-url");
run.addArg(url);
break :blk llvm_directory.items;
};
dir.close();
break :blk llvm_directory.items;
} else {
const use_debug = b.option(bool, "use_debug", "This option enables the LLVM debug build in the development PC") orelse false;
break :blk switch (use_debug) {
true => "../llvm-17-static-debug",
false => "../llvm-17-static-release",
};
}
};
const llvm_include_dir = try std.mem.concat(b.allocator, u8, &.{ llvm_path, "/include" });
compiler.addIncludePath(std.Build.LazyPath.relative(llvm_include_dir));
const llvm_lib_dir = try std.mem.concat(b.allocator, u8, &.{ llvm_path, "/lib" });
const zstd = if (target.result.os.tag == .windows) "zstd.lib" else "libzstd.a";
const llvm_libraries = [_][]const u8{
const static_llvm_libraries = [_][]const u8{
"libLLVMAArch64AsmParser.a",
"libLLVMAArch64CodeGen.a",
"libLLVMAArch64Desc.a",
@ -378,16 +345,62 @@ pub fn build(b: *std.Build) !void {
"libclangTransformer.a",
};
for (llvm_libraries) |llvm_library| {
if (static) {
if (os == .linux) compiler.linkage = .static;
compiler.linkLibCpp();
const prefix = "nat/cache";
const llvm_version = "17.0.6";
const llvm_path = b.option([]const u8, "llvm_path", "LLVM prefix path") orelse blk: {
assert(!self_hosted_ci);
if (third_party_ci or (!target.query.isNativeOs() or !target.query.isNativeCpu())) {
var llvm_directory = try std.ArrayListUnmanaged(u8).initCapacity(b.allocator, 128);
llvm_directory.appendSliceAssumeCapacity(prefix ++ "/");
llvm_directory.appendSliceAssumeCapacity("llvm-");
llvm_directory.appendSliceAssumeCapacity(llvm_version);
llvm_directory.appendSliceAssumeCapacity("-");
llvm_directory.appendSliceAssumeCapacity(@tagName(target.result.cpu.arch));
llvm_directory.appendSliceAssumeCapacity("-");
llvm_directory.appendSliceAssumeCapacity(@tagName(target.result.os.tag));
llvm_directory.appendSliceAssumeCapacity("-");
llvm_directory.appendSliceAssumeCapacity(@tagName(target.result.abi));
llvm_directory.appendSliceAssumeCapacity("-");
const cpu = if (std.mem.eql(u8, target.result.cpu.model.name, @tagName(target.result.cpu.arch))) "baseline" else target.result.cpu.model.name;
llvm_directory.appendSliceAssumeCapacity(cpu);
const url = try std.mem.concat(b.allocator, u8, &.{ "https://github.com/birth-software/fetch-llvm/releases/download/v", llvm_version, "/llvm-", llvm_version, "-", @tagName(target.result.cpu.arch), "-", @tagName(target.result.os.tag), "-", @tagName(target.result.abi), "-", cpu, ".tar.xz" });
var dir = std.fs.cwd().openDir(llvm_directory.items, .{}) catch {
const run = b.addRunArtifact(fetcher);
compiler.step.dependOn(&run.step);
run.addArg("-prefix");
run.addArg(prefix);
run.addArg("-url");
run.addArg(url);
break :blk llvm_directory.items;
};
dir.close();
break :blk llvm_directory.items;
} else {
const use_debug = b.option(bool, "use_debug", "This option enables the LLVM debug build in the development PC") orelse false;
break :blk switch (use_debug) {
true => "../llvm-17-static-debug",
false => "../llvm-17-static-release",
};
}
};
const llvm_include_dir = try std.mem.concat(b.allocator, u8, &.{ llvm_path, "/include" });
compiler.addIncludePath(std.Build.LazyPath.relative(llvm_include_dir));
const llvm_lib_dir = try std.mem.concat(b.allocator, u8, &.{ llvm_path, "/lib" });
for (static_llvm_libraries) |llvm_library| {
compiler.addObjectFile(std.Build.LazyPath.relative(try std.mem.concat(b.allocator, u8, &.{ llvm_lib_dir, "/", llvm_library })));
}
} else {
compiler.addObjectFile(.{ .cwd_relative = "/usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../lib/libstdc++.so" });
compiler.addIncludePath(.{ .cwd_relative = "/usr/include" });
compiler.addIncludePath(.{ .cwd_relative = "/usr/include/c++/13.2.1" });
compiler.addIncludePath(.{ .cwd_relative = "/usr/include/c++/13.2.1/x86_64-pc-linux-gnu" });
compiler.addLibraryPath(.{ .cwd_relative = "/usr/lib" });
compiler.linkSystemLibrary("LLVM");
compiler.linkSystemLibrary("LLVM-17");
compiler.linkSystemLibrary("clang-cpp");
compiler.linkSystemLibrary("lldCommon");
compiler.linkSystemLibrary("lldCOFF");
@ -395,6 +408,45 @@ pub fn build(b: *std.Build) !void {
compiler.linkSystemLibrary("lldMachO");
compiler.linkSystemLibrary("lldWasm");
compiler.linkSystemLibrary("unwind");
compiler.linkSystemLibrary("zlib");
compiler.linkSystemLibrary("zstd");
switch (target.result.os.tag) {
.linux => {
compiler.addObjectFile(.{ .cwd_relative = "/usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../lib/libstdc++.so" });
compiler.addIncludePath(.{ .cwd_relative = "/usr/include" });
compiler.addIncludePath(.{ .cwd_relative = "/usr/include/c++/13.2.1" });
compiler.addIncludePath(.{ .cwd_relative = "/usr/include/c++/13.2.1/x86_64-pc-linux-gnu" });
compiler.addLibraryPath(.{ .cwd_relative = "/usr/lib" });
},
.macos => {
compiler.linkLibCpp();
if (discover_brew_prefix(b, "llvm")) |llvm_prefix| {
const llvm_include_path = try std.mem.concat(b.allocator, u8, &.{ llvm_prefix, "/include" });
const llvm_lib_path = try std.mem.concat(b.allocator, u8, &.{ llvm_prefix, "/lib" });
compiler.addIncludePath(.{ .cwd_relative = llvm_include_path });
compiler.addLibraryPath(.{ .cwd_relative = llvm_lib_path });
} else |err| {
return err;
}
if (discover_brew_prefix(b, "zstd")) |zstd_prefix| {
const zstd_lib_path = try std.mem.concat(b.allocator, u8, &.{ zstd_prefix, "/lib" });
compiler.addLibraryPath(.{ .cwd_relative = zstd_lib_path });
} else |err| {
return err;
}
if (discover_brew_prefix(b, "zlib")) |zlib_prefix| {
const zlib_lib_path = try std.mem.concat(b.allocator, u8, &.{ zlib_prefix, "/lib" });
compiler.addLibraryPath(.{ .cwd_relative = zlib_lib_path });
} else |err| {
return err;
}
},
else => |tag| @panic(@tagName(tag)),
}
}
const install_exe = b.addInstallArtifact(compiler, .{});
@ -409,7 +461,7 @@ pub fn build(b: *std.Build) !void {
const run_command = b.addSystemCommand(&.{compiler_exe_path});
run_command.step.dependOn(b.getInstallStep());
const debug_command = switch (@import("builtin").os.tag) {
const debug_command = switch (os) {
.linux => blk: {
const result = b.addSystemCommand(&.{"gf2"});
result.addArgs(&.{ "-ex", "set disassembly-flavor intel" });

View File

@ -8,7 +8,7 @@ pub fn main() !void {
var url_arg: ?[:0]const u8 = null;
var prefix_arg: [:0]const u8 = "nat";
const State = enum{
const State = enum {
none,
prefix,
url,
@ -16,7 +16,6 @@ pub fn main() !void {
var state = State.none;
for (arguments[1..]) |argument| {
switch (state) {
.none => {
@ -43,8 +42,8 @@ pub fn main() !void {
if (state != .none) return error.InvalidInput;
const dot_index = std.mem.lastIndexOfScalar(u8, url, '.') orelse return error.InvalidInput;
const extension_string = url[dot_index + 1..];
const Extension = enum{
const extension_string = url[dot_index + 1 ..];
const Extension = enum {
xz,
gz,
zip,
@ -61,7 +60,7 @@ pub fn main() !void {
};
defer http_client.deinit();
var buffer: [16*1024]u8 = undefined;
var buffer: [16 * 1024]u8 = undefined;
var request = try http_client.open(.GET, uri, .{
.server_header_buffer = &buffer,
});

View File

@ -10,7 +10,7 @@ const TestError = error{
fail,
};
fn collectDirectoryDirEntries(allocator: Allocator, path: []const u8) ![]const []const u8{
fn collectDirectoryDirEntries(allocator: Allocator, path: []const u8) ![]const []const u8 {
var dir = try std.fs.cwd().openDir(path, .{
.iterate = true,
});
@ -48,11 +48,11 @@ fn runStandalone(allocator: Allocator, args: struct {
for (test_names) |test_name| {
std.debug.print("{s}... ", .{test_name});
const source_file_path = try std.mem.concat(allocator, u8, &.{args.directory_path, "/", test_name, "/main.nat"});
const source_file_path = try std.mem.concat(allocator, u8, &.{ args.directory_path, "/", test_name, "/main.nat" });
const compile_run = try std.ChildProcess.run(.{
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{"zig-out/bin/nat", if (args.is_test) "test" else "exe", "-main_source_file", source_file_path},
.argv = &.{ "zig-out/bin/nat", if (args.is_test) "test" else "exe", "-main_source_file", source_file_path },
});
ran_compilation_count += 1;
@ -77,11 +77,11 @@ fn runStandalone(allocator: Allocator, args: struct {
}
if (compilation_success) {
const test_path = try std.mem.concat(allocator, u8, &.{"nat/", test_name});
const test_path = try std.mem.concat(allocator, u8, &.{ "nat/", test_name });
const test_run = try std.ChildProcess.run(.{
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{ test_path },
.argv = &.{test_path},
});
ran_test_count += 1;
const test_result: TestError!bool = switch (test_run.term) {
@ -107,8 +107,8 @@ fn runStandalone(allocator: Allocator, args: struct {
}
}
std.debug.print("\n{s} COMPILATIONS: {}. FAILED: {}\n", .{args.group_name, total_compilation_count, failed_compilation_count});
std.debug.print("{s} TESTS: {}. RAN: {}. FAILED: {}\n", .{args.group_name, total_test_count, ran_test_count, failed_test_count});
std.debug.print("\n{s} COMPILATIONS: {}. FAILED: {}\n", .{ args.group_name, total_compilation_count, failed_compilation_count });
std.debug.print("{s} TESTS: {}. RAN: {}. FAILED: {}\n", .{ args.group_name, total_test_count, ran_test_count, failed_test_count });
if (failed_compilation_count > 0 or failed_test_count > 0) {
return error.fail;
@ -129,11 +129,11 @@ fn runStandaloneTests(allocator: Allocator) !void {
for (standalone_test_names) |standalone_test_name| {
std.debug.print("{s}... ", .{standalone_test_name});
const source_file_path = try std.mem.concat(allocator, u8, &.{standalone_test_dir_path, "/", standalone_test_name, "/main.nat"});
const source_file_path = try std.mem.concat(allocator, u8, &.{ standalone_test_dir_path, "/", standalone_test_name, "/main.nat" });
const compile_run = try std.ChildProcess.run(.{
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{"zig-out/bin/nat", "exe", "-main_source_file", source_file_path},
.argv = &.{ "zig-out/bin/nat", "exe", "-main_source_file", source_file_path },
});
ran_compilation_count += 1;
@ -158,11 +158,11 @@ fn runStandaloneTests(allocator: Allocator) !void {
}
if (compilation_success) {
const test_path = try std.mem.concat(allocator, u8, &.{"nat/", standalone_test_name});
const test_path = try std.mem.concat(allocator, u8, &.{ "nat/", standalone_test_name });
const test_run = try std.ChildProcess.run(.{
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{ test_path },
.argv = &.{test_path},
});
ran_test_count += 1;
const test_result: TestError!bool = switch (test_run.term) {
@ -188,8 +188,8 @@ fn runStandaloneTests(allocator: Allocator) !void {
}
}
std.debug.print("\nTOTAL COMPILATIONS: {}. FAILED: {}\n", .{total_compilation_count, failed_compilation_count});
std.debug.print("TOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{total_test_count, ran_test_count, failed_test_count});
std.debug.print("\nTOTAL COMPILATIONS: {}. FAILED: {}\n", .{ total_compilation_count, failed_compilation_count });
std.debug.print("TOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{ total_test_count, ran_test_count, failed_test_count });
if (failed_compilation_count > 0 or failed_test_count > 0) {
return error.fail;
@ -203,7 +203,7 @@ fn runBuildTests(allocator: Allocator) !void {
const test_names = try collectDirectoryDirEntries(allocator, test_dir_path);
const test_dir_realpath = try std.fs.cwd().realpathAlloc(allocator, test_dir_path);
const compiler_realpath = try std.fs.cwd().realpathAlloc(allocator, "zig-out/bin/nat");
try std.os.chdir(test_dir_realpath);
try std.posix.chdir(test_dir_realpath);
const total_compilation_count = test_names.len;
var ran_compilation_count: usize = 0;
@ -215,12 +215,12 @@ fn runBuildTests(allocator: Allocator) !void {
for (test_names) |test_name| {
std.debug.print("{s}... ", .{test_name});
try std.os.chdir(test_name);
try std.posix.chdir(test_name);
const compile_run = try std.ChildProcess.run(.{
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{compiler_realpath, "build"},
.argv = &.{ compiler_realpath, "build" },
});
ran_compilation_count += 1;
@ -246,11 +246,11 @@ fn runBuildTests(allocator: Allocator) !void {
}
if (compilation_success) {
const test_path = try std.mem.concat(allocator, u8, &.{"nat/", test_name});
const test_path = try std.mem.concat(allocator, u8, &.{ "nat/", test_name });
const test_run = try std.ChildProcess.run(.{
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{ test_path },
.argv = &.{test_path},
});
ran_test_count += 1;
const test_result: TestError!bool = switch (test_run.term) {
@ -275,13 +275,13 @@ fn runBuildTests(allocator: Allocator) !void {
std.debug.print("\n", .{});
}
try std.os.chdir(test_dir_realpath);
try std.posix.chdir(test_dir_realpath);
}
std.debug.print("\nTOTAL COMPILATIONS: {}. FAILED: {}\n", .{total_compilation_count, failed_compilation_count});
std.debug.print("TOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{total_test_count, ran_test_count, failed_test_count});
std.debug.print("\nTOTAL COMPILATIONS: {}. FAILED: {}\n", .{ total_compilation_count, failed_compilation_count });
std.debug.print("TOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{ total_test_count, ran_test_count, failed_test_count });
try std.os.chdir(previous_cwd);
try std.posix.chdir(previous_cwd);
if (failed_compilation_count > 0 or failed_test_count > 0) {
return error.fail;
@ -289,19 +289,26 @@ fn runBuildTests(allocator: Allocator) !void {
}
pub fn main() !void {
var errors = false;
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
const allocator = arena.allocator();
try runStandalone(allocator, .{
runStandalone(allocator, .{
.directory_path = "test/standalone",
.group_name = "STANDALONE",
.is_test = false,
});
try runBuildTests(allocator);
try runStandalone(allocator, .{
}) catch {
errors = true;
};
runBuildTests(allocator) catch {
errors = true;
};
runStandalone(allocator, .{
.directory_path = "test/tests",
.group_name = "TEST EXECUTABLE",
.is_test = true,
});
}) catch {
errors = true;
};
std.debug.print("std... ", .{});
@ -317,6 +324,7 @@ pub fn main() !void {
};
const compilation_success = compilation_result catch b: {
errors = true;
break :b false;
};
@ -332,7 +340,7 @@ pub fn main() !void {
const test_run = try std.ChildProcess.run(.{
.allocator = allocator,
// TODO: delete -main_source_file?
.argv = &.{ "nat/std" },
.argv = &.{"nat/std"},
});
const test_result: TestError!bool = switch (test_run.term) {
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
@ -342,6 +350,7 @@ pub fn main() !void {
};
const test_success = test_result catch b: {
errors = true;
break :b false;
};
std.debug.print("[TEST {s}]\n", .{if (test_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"});
@ -352,4 +361,8 @@ pub fn main() !void {
std.debug.print("STDERR:\n\n{s}\n\n", .{test_run.stderr});
}
}
if (errors) {
return error.fail;
}
}

View File

@ -16,6 +16,7 @@ const Abi = enum{
};
const CallingConvention = enum{
c,
system_v,
};

View File

@ -0,0 +1,83 @@
const std = #import("std");
const linux = std.os.linux;
const macos = std.os.macos;
const builtin = #import("builtin");
const os = builtin.os;
const Error = switch (os) {
.linux => linux.Error,
.macos => macos.Error,
else => #error("OS not supported"),
};
const unwrap_syscall = fn(syscall_result: ssize) Error!usize {
if (syscall_result == -1) {
const absolute_error: u64 = #cast(-syscall_result);
const error_int: u32 = #cast(absolute_error);
const err: Error = #cast(error_int);
return err;
} else {
const result: usize = #cast(syscall_result);
return result;
}
}
const MapFlags = switch (os) {
.macos => bitfield(u32){
shared: bool,
private: bool,
reserved: u2 = 0,
fixed: bool,
reserved0: bool = 0,
noreserve: bool,
reserved1: u2 = 0,
has_semaphore: bool,
no_cache: bool,
reserved2: u1 = 0,
anonymous: bool,
reserved3: u19 = 0,
},
.linux => linux.MapFlags,
else => #error("OS not supported"),
};
const FileDescriptor = s32;
const ProcessId = s32;
const MAP_FAILED = 0xffffffffffffffff;
const ProtectionFlags = bitfield(u32) {
read: bool,
write: bool,
execute: bool,
};
const get_protection_flags = fn(flags: std.os.ProtectionFlags) ProtectionFlags {
return ProtectionFlags{
.read = flags.read,
.write = flags.write,
.execute = flags.execute,
};
}
const get_map_flags = fn(flags: std.os.MapFlags) MapFlags{
return MapFlags{
.shared = false,
.private = true,
.fixed = false,
.noreserve = false,
.has_semaphore = false,
.no_cache = false,
.anonymous = true,
};
}
const write :: extern = fn cc(.c) (file_descriptor: FileDescriptor, bytes_ptr: [&]const u8, bytes_len: usize) ssize;
const exit :: extern = fn cc(.c) (exit_code: s32) noreturn;
const fork :: extern = fn cc(.c) () ProcessId;
const mmap :: extern = fn cc(.c) (address: ?[&]const u8, length: usize, protection_flags: ProtectionFlags, map_flags: MapFlags, file_descriptor: FileDescriptor, offset: u64) usize;
const munmap :: extern = fn cc(.c) (address: [&]const u8, length: usize) s32;
const execve :: extern = fn cc(.c) (path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: [&:null]const ?[&:null]const u8) s32;
const realpath :: extern = fn cc(.c) (path: [&:0]const u8, resolved_path: [&:0]u8) ?[&:0]u8;
const waitpid :: extern = fn cc(.c) (pid: ProcessId, status: &s32, flags: s32) s32;
const _NSGetExecutablePath :: extern = fn cc(.c) (buffer: [&:0]u8, buffer_size: &u32) s32;

View File

@ -15,7 +15,7 @@ const system = switch (link_libc) {
true => c,
false => switch (current) {
.linux => linux,
.macos => macos,
.macos => c,
.windows => windows,
},
};
@ -25,7 +25,7 @@ const unwrap_syscall = system.unwrap_syscall;
const exit = fn(exit_code: s32) noreturn {
switch (current) {
.linux => _ = #syscall(#cast(linux.Syscall.exit_group), #cast(exit_code)),
.macos => macos.exit(exit_code),
.macos => system.exit(exit_code),
.windows => windows.ExitProcess(#cast(exit_code)),
}
}
@ -46,7 +46,15 @@ const FileDescriptor = struct{
const read = fn(file_descriptor: FileDescriptor, bytes: []u8) ReadError!usize {
if (bytes.len > 0) {
switch (current) {
.linux, .macos => {
.linux => {
const len: usize = #min(max_file_operation_byte_count, bytes.len);
const syscall_result = system.read(file_descriptor, bytes);
const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) {
else => unreachable,
};
return byte_count;
},
.macos => {
const len: usize = #min(max_file_operation_byte_count, bytes.len);
const syscall_result = system.read(file_descriptor, bytes);
const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) {
@ -67,7 +75,7 @@ const FileDescriptor = struct{
const write = fn (file_descriptor: FileDescriptor, bytes: []const u8) WriteError!usize {
switch (current) {
.linux, .macos => {
.linux => {
const len: usize = #min(max_file_operation_byte_count, bytes.len);
const syscall_result = system.write(file_descriptor.handle, bytes[0..len]);
const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) {
@ -75,6 +83,14 @@ const FileDescriptor = struct{
};
return byte_count;
},
.macos => {
const len: usize = #min(max_file_operation_byte_count, bytes.len);
const syscall_result = system.write(file_descriptor.handle, bytes.ptr, bytes.len);
const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) {
else => return WriteError.write_failed,
};
return byte_count;
},
else => #error("OS not supported"),
}
}
@ -122,8 +138,8 @@ const allocate_virtual_memory = fn(address: ?[&]u8, length: usize, general_prote
.linux, .macos => {
const syscall_result = system.mmap(address, length, protection_flags, map_flags, file_descriptor, offset);
if (link_libc) {
if (result != system.MAP_FAILED) {
const result_address: [&]u8 = #cast(result);
if (syscall_result != system.MAP_FAILED) {
const result_address: [&]u8 = #cast(syscall_result);
return result_address;
} else {
// TODO:
@ -147,12 +163,18 @@ const FreeError = error{
};
const free_virtual_memory = fn(bytes: []const u8) FreeError!void {
switch (current) {
.linux, .macos => {
.linux => {
const syscall_result = system.munmap(bytes);
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
else => unreachable,
};
},
.macos => {
const syscall_result = system.munmap(bytes.ptr, bytes.len);
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
else => unreachable,
};
},
else => #error("OS not supported"),
}
}
@ -194,32 +216,31 @@ const current_executable_path = fn(buffer: [:0]u8) CurrentExecutablePath![]u8 {
};
return bytes;
},
//.macos => {
// TODO:
//var symlink_path_buffer: [max_path_byte_count:0]u8 = undefined;
//var symlink_path_len: u32 = symlink_path_buffer.len + 1;
//const ns_result = macos._NSGetExecutablePath(symlink_path_buffer.&, symlink_path_len.&);
//if (ns_result == 0) {
// const symlink_path = symlink_path_buffer[0..symlink_path_len];
// const result = macos.realpath(symlink_path.ptr, buffer.ptr);
// if (result != null) {
// var i: usize = 0;
// while (i < buffer.len) {
// if (result[i] == 0) {
// break;
// }
// i += 1;
// }
// assert(i < buffer.len);
.macos => {
var symlink_path_buffer: [max_path_byte_count:0]u8 = undefined;
var symlink_path_len: u32 = symlink_path_buffer.len + 1;
const ns_result = c._NSGetExecutablePath(symlink_path_buffer.&, symlink_path_len.&);
if (ns_result == 0) {
const symlink_path = symlink_path_buffer[0..symlink_path_len];
if (c.realpath(symlink_path.ptr, buffer.ptr)) |result| {
var i: usize = 0;
while (i < buffer.len) {
if (result[i] == 0) {
break;
}
i += 1;
}
assert(i < buffer.len);
// return result[0..i];
// } else {
// return null;
// }
//} else {
// return null;
//}
//},
const r: []u8 = result[0..i];
return r;
} else {
return CurrentExecutablePath.failed;
}
} else {
return CurrentExecutablePath.failed;
}
},
else => #error("OS not supported"),
}
}
@ -256,13 +277,11 @@ const ExecveError = error{
const execute = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: [&:null]const ?[&:null]const u8) ExecveError!noreturn {
switch (current) {
.linux, .macos => {
const syscall_result = linux.execve(path, argv, env);
const signed_syscall_result: ssize = #cast(syscall_result);
if (signed_syscall_result == -1) {
return ExecveError.execve_failed;
} else {
const syscall_result = system.execve(path, argv, env);
_ = unwrap_syscall(syscall_result) catch |err| switch (err) {
else => return ExecveError.execve_failed,
};
unreachable;
}
},
else => #error("OS not supported"),
}
@ -410,7 +429,7 @@ const waitpid = fn(pid: Process.Id, flags: u32) WaitPidError!u32 {
},
.macos => {
var status: s32 = undefined;
if (macos.waitpid(pid, status.&, #cast(flags)) != -1) {
if (system.waitpid(pid, status.&, #cast(flags)) != -1) {
const status_u: u32 = #cast(status);
return status_u;
} else {

View File

@ -1,57 +1,337 @@
const std = #import("std");
const FileDescriptor = s32;
const ProcessId = s32;
const MAP_FAILED = 0xffffffffffffffff;
const Error = error{
/// No error occurred.
SUCCESS = 0,
const MapFlags = bitfield(u32){
shared: bool,
private: bool,
reserved: u2 = 0,
fixed: bool,
reserved0: bool = 0,
noreserve: bool,
reserved1: u2 = 0,
has_semaphore: bool,
no_cache: bool,
reserved2: u1 = 0,
anonymous: bool,
reserved3: u19 = 0,
/// Operation not permitted
PERM = 1,
/// No such file or directory
NOENT = 2,
/// No such process
SRCH = 3,
/// Interrupted system call
INTR = 4,
/// Input/output error
IO = 5,
/// Device not configured
NXIO = 6,
/// Argument list too long
TOO_BIG = 7,
/// Exec format error
NOEXEC = 8,
/// Bad file descriptor
BADF = 9,
/// No child processes
CHILD = 10,
/// Resource deadlock avoided
DEADLK = 11,
/// Cannot allocate memory
NOMEM = 12,
/// Permission denied
ACCES = 13,
/// Bad address
FAULT = 14,
/// Block device required
NOTBLK = 15,
/// Device / Resource busy
BUSY = 16,
/// File exists
EXIST = 17,
/// Cross-device link
XDEV = 18,
/// Operation not supported by device
NODEV = 19,
/// Not a directory
NOTDIR = 20,
/// Is a directory
ISDIR = 21,
/// Invalid argument
INVAL = 22,
/// Too many open files in system
NFILE = 23,
/// Too many open files
MFILE = 24,
/// Inappropriate ioctl for device
NOTTY = 25,
/// Text file busy
TXTBSY = 26,
/// File too large
FBIG = 27,
/// No space left on device
NOSPC = 28,
/// Illegal seek
SPIPE = 29,
/// Read-only file system
ROFS = 30,
/// Too many links
MLINK = 31,
/// Broken pipe
PIPE = 32,
// math software
/// Numerical argument out of domain
DOM = 33,
/// Result too large
RANGE = 34,
// non-blocking and interrupt i/o
/// Resource temporarily unavailable
/// This is the same code used for `WOULDBLOCK`.
AGAIN = 35,
/// Operation now in progress
INPROGRESS = 36,
/// Operation already in progress
ALREADY = 37,
// ipc/network software -- argument errors
/// Socket operation on non-socket
NOTSOCK = 38,
/// Destination address required
DESTADDRREQ = 39,
/// Message too long
MSGSIZE = 40,
/// Protocol wrong type for socket
PROTOTYPE = 41,
/// Protocol not available
NOPROTOOPT = 42,
/// Protocol not supported
PROTONOSUPPORT = 43,
/// Socket type not supported
SOCKTNOSUPPORT = 44,
/// Operation not supported
/// The same code is used for `NOTSUP`.
OPNOTSUPP = 45,
/// Protocol family not supported
PFNOSUPPORT = 46,
/// Address family not supported by protocol family
AFNOSUPPORT = 47,
/// Address already in use
ADDRINUSE = 48,
/// Can't assign requested address
// ipc/network software -- operational errors
ADDRNOTAVAIL = 49,
/// Network is down
NETDOWN = 50,
/// Network is unreachable
NETUNREACH = 51,
/// Network dropped connection on reset
NETRESET = 52,
/// Software caused connection abort
CONNABORTED = 53,
/// Connection reset by peer
CONNRESET = 54,
/// No buffer space available
NOBUFS = 55,
/// Socket is already connected
ISCONN = 56,
/// Socket is not connected
NOTCONN = 57,
/// Can't send after socket shutdown
SHUTDOWN = 58,
/// Too many references: can't splice
TOOMANYREFS = 59,
/// Operation timed out
TIMEDOUT = 60,
/// Connection refused
CONNREFUSED = 61,
/// Too many levels of symbolic links
LOOP = 62,
/// File name too long
NAMETOOLONG = 63,
/// Host is down
HOSTDOWN = 64,
/// No route to host
HOSTUNREACH = 65,
/// Directory not empty
// quotas & mush
NOTEMPTY = 66,
/// Too many processes
PROCLIM = 67,
/// Too many users
USERS = 68,
/// Disc quota exceeded
// Network File System
DQUOT = 69,
/// Stale NFS file handle
STALE = 70,
/// Too many levels of remote in path
REMOTE = 71,
/// RPC struct is bad
BADRPC = 72,
/// RPC version wrong
RPCMISMATCH = 73,
/// RPC prog. not avail
PROGUNAVAIL = 74,
/// Program version wrong
PROGMISMATCH = 75,
/// Bad procedure for program
PROCUNAVAIL = 76,
/// No locks available
NOLCK = 77,
/// Function not implemented
NOSYS = 78,
/// Inappropriate file type or format
FTYPE = 79,
/// Authentication error
AUTH = 80,
/// Need authenticator
NEEDAUTH = 81,
// Intelligent device errors
/// Device power is off
PWROFF = 82,
/// Device error, e.g. paper out
DEVERR = 83,
/// Value too large to be stored in data type
OVERFLOW = 84,
// Program loading errors
/// Bad executable
BADEXEC = 85,
/// Bad CPU type in executable
BADARCH = 86,
/// Shared library version mismatch
SHLIBVERS = 87,
/// Malformed Macho file
BADMACHO = 88,
/// Operation canceled
CANCELED = 89,
/// Identifier removed
IDRM = 90,
/// No message of desired type
NOMSG = 91,
/// Illegal byte sequence
ILSEQ = 92,
/// Attribute not found
NOATTR = 93,
/// Bad message
BADMSG = 94,
/// Reserved
MULTIHOP = 95,
/// No message available on STREAM
NODATA = 96,
/// Reserved
NOLINK = 97,
/// No STREAM resources
NOSR = 98,
/// Not a STREAM
NOSTR = 99,
/// Protocol error
PROTO = 100,
/// STREAM ioctl timeout
TIME = 101,
/// No such policy registered
NOPOLICY = 103,
/// State not recoverable
NOTRECOVERABLE = 104,
/// Previous owner died
OWNERDEAD = 105,
/// Interface output queue is full
QFULL = 106,
};
const ProtectionFlags = bitfield(u32) {
read: bool,
write: bool,
execute: bool,
};
const get_protection_flags = fn(flags: std.os.ProtectionFlags) ProtectionFlags {
return ProtectionFlags{
.read = flags.read,
.write = flags.write,
.execute = flags.execute,
};
}
const get_map_flags = fn(flags: std.os.MapFlags) MapFlags{
return MapFlags{
.shared = false,
.private = true,
.fixed = false,
.noreserve = false,
.has_semaphore = false,
.no_cache = false,
.anonymous = true,
};
}
const write :: extern = fn (file_descriptor: FileDescriptor, bytes_ptr: [&]const u8, bytes_len: usize) ssize;
const exit :: extern = fn (exit_code: s32) noreturn;
const fork :: extern = fn () ProcessId;
const mmap :: extern = fn (address: ?[&]const u8, length: usize, protection_flags: ProtectionFlags, map_flags: MapFlags, file_descriptor: FileDescriptor, offset: u64) usize;
const munmap :: extern = fn (address: [&]const u8, length: usize) s32;
const execve :: extern = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: [&:null]const ?[&:null]const u8) s32;
const realpath :: extern = fn(path: [&:0]const u8, resolved_path: [&:0]u8) [&:0]u8;
const waitpid :: extern = fn(pid: ProcessId, status: &s32, flags: s32) s32;
const _NSGetExecutablePath :: extern = fn (buffer: [&:0]u8, buffer_size: &u32) s32;

View File

@ -2,13 +2,13 @@ const std = #import("std");
const builtin = #import("builtin");
comptime {
if (builtin.link_libc) {
_ = main;
#export(main);
} else {
_ = _start;
#export(_start);
}
}
const _start :: export = fn naked() noreturn {
const _start = fn naked cc(.c) () noreturn {
#asm(`
xor ebp, ebp;
mov rdi, rsp;
@ -21,7 +21,7 @@ var argument_count: usize = 0;
var argument_values: [&]const [&:0]const u8 = undefined;
var environment_values: [&:null]const ?[&:null]const u8 = undefined;
const start :: export = fn (argc_argv_address: usize) noreturn {
const start = fn cc(.c) (argc_argv_address: usize) noreturn {
var argument_address_iterator = argc_argv_address;
const argument_count_ptr: &usize = #cast(argument_address_iterator);
argument_count = argument_count_ptr.@;
@ -35,7 +35,7 @@ const start :: export = fn (argc_argv_address: usize) noreturn {
std.os.exit(0);
}
const main :: export = fn (argc: s32, argv: [&]const [&:0]const u8, env: [&:null]const ?[&:null]const u8) s32 {
const main = fn cc(.c) (argc: s32, argv: [&]const [&:0]const u8, env: [&:null]const ?[&:null]const u8) s32 {
const argc_u: u32 = #cast(argc);
argument_count = argc_u;
argument_values = argv;

View File

@ -12,6 +12,7 @@ test {
const build = #import("build.nat");
const builtin = #import("builtin.nat");
const os = #import("os.nat");
const c = #import("c.nat");
const start = #import("start.nat");
const testing = #import("testing.nat");

View File

@ -288,6 +288,7 @@ extern "C" Function* NativityLLVMModuleGetFunction(Module& module, const char* n
extern "C" void NativityLLVMFunctionAddAttributeKey(Function& function, Attribute::AttrKind attribute)
{
static_assert(sizeof(Attribute) == sizeof(size_t));
function.addFnAttr(attribute);
}
@ -357,6 +358,12 @@ extern "C" void NativityLLVMFunctionGetArguments(Function& function, Argument**
}
}
extern "C" Argument* NativityLLVMFunctionGetArgument(Function& function, unsigned index)
{
auto* arg = function.getArg(index);
return arg;
}
extern "C" void NativityLLVMFunctionSetSubprogram(Function& function, DISubprogram* subprogram)
{
function.setSubprogram(subprogram);
@ -386,16 +393,21 @@ extern "C" ConstantInt* NativityLLVMConstantToInt(Constant* constant)
return constant_int;
}
extern "C" StoreInst* NativityLLVMBuilderCreateStore(IRBuilder<>& builder, Value* value, Value* pointer, bool is_volatile)
extern "C" StoreInst* NativityLLVMBuilderCreateStore(IRBuilder<>& builder, Value* value, Value* pointer, bool is_volatile, uint32_t alignment)
{
auto* store = builder.CreateStore(value, pointer, is_volatile);
auto align = Align{alignment};
auto* basic_block = builder.GetInsertBlock();
auto* store = new StoreInst(value, pointer, is_volatile, align,
AtomicOrdering::NotAtomic, SyncScope::System, basic_block);
return store;
}
extern "C" AllocaInst* NativityLLVMBuilderCreateAlloca(IRBuilder<>& builder, Type* type, unsigned address_space, Value* array_size, const char* name_ptr, size_t name_len)
extern "C" AllocaInst* NativityLLVMBuilderCreateAlloca(IRBuilder<>& builder, Type* type, unsigned address_space, Value* array_size, const char* name_ptr, size_t name_len, uint32_t alignment)
{
auto name = StringRef(name_ptr, name_len);
auto* alloca = builder.CreateAlloca(type, address_space, array_size, name);
auto align = Align{ alignment };
BasicBlock* insert_block = builder.GetInsertBlock();
AllocaInst* alloca = new AllocaInst(type, address_space, array_size, align, name, insert_block);
return alloca;
}
@ -412,10 +424,13 @@ extern "C" Value* NativityLLVMBuilderCreateICmp(IRBuilder<>& builder, CmpInst::P
return icmp;
}
extern "C" LoadInst* NativityLLVMBuilderCreateLoad(IRBuilder<>& builder, Type* type, Value* value, bool is_volatile, const char* name_ptr, size_t name_len)
extern "C" LoadInst* NativityLLVMBuilderCreateLoad(IRBuilder<>& builder, Type* type, Value* pointer, bool is_volatile, const char* name_ptr, size_t name_len, uint32_t alignment)
{
auto align = Align{alignment};
auto name = StringRef(name_ptr, name_len);
auto* load = builder.CreateLoad(type, value, is_volatile, name);
auto* basic_block = builder.GetInsertBlock();
auto* load = new LoadInst(type, pointer, name, is_volatile,
align, AtomicOrdering::NotAtomic, SyncScope::System, basic_block);
return load;
}
@ -940,15 +955,61 @@ extern "C" bool NativityLLVMModuleAddPassesToEmitFile(Module& module, TargetMach
return true;
}
extern "C" bool NativityLLVMCompareTypes(Type* a, Type* b)
extern "C" Attribute NativityLLVMContextGetAttributeFromEnum(LLVMContext& context, Attribute::AttrKind kind, uint64_t value)
{
if (auto* int_a = dyn_cast<IntegerType>(a)) {
auto* int_b = dyn_cast<IntegerType>(b);
assert(int_b);
auto a_bit_count = int_a->getBitWidth();
auto b_bit_count = int_b->getBitWidth();
assert(a_bit_count == b_bit_count);
}
return a == b;
static_assert(sizeof(Attribute) == sizeof(uintptr_t));
auto attribute = Attribute::get(context, kind, value);
return attribute;
}
extern "C" Attribute NativityLLVMContextGetAttributeFromType(LLVMContext& context, Attribute::AttrKind kind, Type* type)
{
static_assert(sizeof(Attribute) == sizeof(uintptr_t));
auto attribute = Attribute::get(context, kind, type);
return attribute;
}
extern "C" Attribute NativityLLVMContextGetAttributeFromString(LLVMContext& context, const char* kind_ptr, size_t kind_len, const char* value_ptr, size_t value_len)
{
static_assert(sizeof(Attribute) == sizeof(uintptr_t));
auto kind = StringRef(kind_ptr, kind_len);
auto value = StringRef(value_ptr, value_len);
auto attribute = Attribute::get(context, kind, value);
return attribute;
}
extern "C" AttributeSet NativityLLVMContextGetAttributeSet(LLVMContext& context, const Attribute* attribute_ptr, size_t attribute_count)
{
static_assert(sizeof(AttributeSet) == sizeof(uintptr_t));
auto attributes = ArrayRef<Attribute>(attribute_ptr, attribute_count);
auto attribute_set = AttributeSet::get(context, attributes);
return attribute_set;
}
extern "C" void NativityLLVMFunctionSetAttributes(Function& function, LLVMContext& context, AttributeSet function_attributes, AttributeSet return_attributes, const AttributeSet* parameter_attribute_set_ptr, size_t parameter_attribute_set_count)
{
auto parameter_attribute_sets = ArrayRef<AttributeSet>(parameter_attribute_set_ptr, parameter_attribute_set_count);
auto attribute_list = AttributeList::get(context, function_attributes, return_attributes, parameter_attribute_sets);
function.setAttributes(attribute_list);
}
extern "C" void NativityLLVMCallSetAttributes(CallInst& call, LLVMContext& context, AttributeSet function_attributes, AttributeSet return_attributes, const AttributeSet* parameter_attribute_set_ptr, size_t parameter_attribute_set_count)
{
auto parameter_attribute_sets = ArrayRef<AttributeSet>(parameter_attribute_set_ptr, parameter_attribute_set_count);
auto attribute_list = AttributeList::get(context, function_attributes, return_attributes, parameter_attribute_sets);
call.setAttributes(attribute_list);
}
extern "C" CallInst* NativityLLVMBuilderCreateMemcpy(IRBuilder<>& builder, Value* destination, uint32_t destination_alignment, Value* source, uint32_t source_alignment, uint64_t size, bool is_volatile)
{
auto dst_alignment = MaybeAlign(destination_alignment);
auto src_alignment = MaybeAlign(source_alignment);
auto memcpy = builder.CreateMemCpy(destination, dst_alignment, source, src_alignment, size, is_volatile);
return memcpy;
}
extern "C" void NativityLLVMTypeAssertEqual(Type* a, Type* b)
{
assert(a == b);
}

17
test/build/c-abi/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "cppdbg",
"request": "launch",
"name": "Debug",
"program": "${workspaceFolder}/nat/c-abi",
"args": [],
"MIMode": "lldb",
"cwd": "${workspaceFolder}"
}
]
}

View File

@ -10,7 +10,7 @@ const main = fn() *!void {
},
.main_source_path = "main.nat",
.name = "c-abi",
.c_source_files = .{ "foo.c" }.&,
.c_source_files = .{ "c.c" }.&,
};
try executable.compile();

5483
test/build/c-abi/c.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
typedef unsigned u32;
u32 foo(u32 arg)
{
return arg;
}

View File

@ -1,7 +1,490 @@
const std = #import("std");
const expect = std.testing.expect;
const foo :: extern = fn(arg: u32) u32;
const run_c_tests :: extern = fn cc(.c) () void;
const has_i128 = false;
const main = fn () *!void {
const arg = 0xabcdef;
try expect(foo(arg) == arg);
run_c_tests();
c_u8(0xff);
c_u16(0xfffe);
c_u32(0xfffffffd);
c_u64(0xfffffffffffffffc);
if (has_i128) {
c_struct_u128(.{ .value = 0xfffffffffffffffc, });
}
c_s8(-1);
c_s16(-2);
c_s32(-3);
c_s64(-4);
if (has_i128) {
c_struct_i128(.{ .value = -6, });
}
c_bool(true);
c_five_integers(12, 34, 56, 78, 90);
const s = c_ret_struct_u64_u64();
try expect(s.a == 21);
try expect(s.b == 22);
c_struct_u64_u64_0(.{ .a = 23, .b = 24, });
c_struct_u64_u64_1(0, .{ .a = 25, .b = 26, });
c_struct_u64_u64_2(0, 1, .{ .a = 27, .b = 28, });
c_struct_u64_u64_3(0, 1, 2, .{ .a = 29, .b = 30, });
c_struct_u64_u64_4(0, 1, 2, 3, .{ .a = 31, .b = 32, });
c_struct_u64_u64_5(0, 1, 2, 3, 4, .{ .a = 33, .b = 34, });
c_struct_u64_u64_6(0, 1, 2, 3, 4, 5, .{ .a = 35, .b = 36, });
c_struct_u64_u64_7(0, 1, 2, 3, 4, 5, 6, .{ .a = 37, .b = 38, });
c_struct_u64_u64_8(0, 1, 2, 3, 4, 5, 6, 7, .{ .a = 39, .b = 40, });
const big_struct = BigStruct{
.a = 1,
.b = 2,
.c = 3,
.d = 4,
.e = 5,
};
c_big_struct(big_struct);
const small = SmallStructInts{
.a = 1,
.b = 2,
.c = 3,
.d = 4,
};
c_small_struct_ints(small);
const small2 = c_ret_small_struct_ints();
try expect(small2.a == 1);
try expect(small2.b == 2);
try expect(small2.c == 3);
try expect(small2.d == 4);
const med = MedStructInts{
.x = 1,
.y = 2,
.z = 3,
};
c_med_struct_ints(med);
const med2 = c_ret_med_struct_ints();
try expect(med2.x == 1);
try expect(med2.y == 2);
try expect(med2.z == 3);
const p = SmallPackedStruct{ .a = 0, .b = 1, .c = 2, .d = 3, };
c_small_packed_struct(p);
const p2 = c_ret_small_packed_struct();
try expect(p2.a == 0);
try expect(p2.b == 1);
try expect(p2.c == 2);
try expect(p2.d == 3);
const split = SplitStructInt{
.a = 1234,
.b = 100,
.c = 1337,
};
c_split_struct_ints(split);
const big = BigStruct{
.a = 1,
.b = 2,
.c = 3,
.d = 4,
.e = 5,
};
const big2 = c_big_struct_both(big);
try expect(big2.a == 10);
try expect(big2.b == 11);
try expect(big2.c == 12);
try expect(big2.d == 13);
try expect(big2.e == 14);
const r1 = Rect{
.left = 1,
.right = 21,
.top = 16,
.bottom = 4,
};
const r2 = Rect{
.left = 178,
.right = 189,
.top = 21,
.bottom = 15,
};
c_multiple_struct_ints(r1, r2);
try expect(c_ret_bool() == true);
try expect(c_ret_u8() == 0xff);
try expect(c_ret_u16() == 0xffff);
try expect(c_ret_u32() == 0xffffffff);
try expect(c_ret_u64() == 0xffffffffffffffff);
try expect(c_ret_s8() == -1);
try expect(c_ret_s16() == -1);
try expect(c_ret_s32() == -1);
try expect(c_ret_s64() == -1);
c_struct_with_array(.{ .a = 1, .padding = undefined, .b = 2, });
const x = c_ret_struct_with_array();
try expect(x.a == 4);
try expect(x.b == 155);
const res = c_modify_by_ref_param(.{ .val = 1, .arr = undefined, });
try expect(res.val == 42);
var function_pointer = c_func_ptr_byval.&;
function_pointer(1, 2, .{ .origin = .{ .x = 9, .y = 10, .z = 11, }, .size = .{ .width = 12, .height = 13, .depth = 14, }, }, 3, 4, 5);
}
const ByRef = struct {
val: s32,
arr: [15]s32,
};
const ByVal = struct {
origin: ByValOrigin,
size: ByValSize,
};
const ByValOrigin = struct{
x: u64,
y: u64,
z: u64,
};
const ByValSize = struct{
width: u64,
height: u64,
depth: u64,
};
const c_u8 :: extern = fn cc(.c) (x: u8) void;
const c_u16 :: extern = fn cc(.c) (x: u16) void;
const c_u32 :: extern = fn cc(.c) (x: u32) void;
const c_u64 :: extern = fn cc(.c) (x: u64) void;
const c_s8 :: extern = fn cc(.c) (x: s8) void;
const c_s16 :: extern = fn cc(.c) (x: s16) void;
const c_s32 :: extern = fn cc(.c) (x: s32) void;
const c_s64 :: extern = fn cc(.c) (x: s64) void;
const c_bool :: extern = fn cc(.c) (x: bool) void;
const c_five_integers :: extern = fn cc(.c) (a: s32, b: s32, c: s32, d: s32, e: s32) void;
const c_ret_struct_u64_u64 :: extern = fn cc(.c) () Struct_u64_u64;
const c_struct_u64_u64_0 :: extern = fn cc(.c) (x: Struct_u64_u64) void;
const c_struct_u64_u64_1 :: extern = fn cc(.c) (a: usize, b: Struct_u64_u64) void;
const c_struct_u64_u64_2 :: extern = fn cc(.c) (a: usize, b: usize, c: Struct_u64_u64) void;
const c_struct_u64_u64_3 :: extern = fn cc(.c) (a: usize, b: usize, c: usize, d: Struct_u64_u64) void;
const c_struct_u64_u64_4 :: extern = fn cc(.c) (a: usize, b: usize, c: usize, d: usize, e: Struct_u64_u64) void;
const c_struct_u64_u64_5 :: extern = fn cc(.c) (a: usize, b: usize, c: usize, d: usize, e: usize, f: Struct_u64_u64) void;
const c_struct_u64_u64_6 :: extern = fn cc(.c) (a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: Struct_u64_u64) void;
const c_struct_u64_u64_7 :: extern = fn cc(.c) (a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: Struct_u64_u64) void;
const c_struct_u64_u64_8 :: extern = fn cc(.c) (a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: Struct_u64_u64) void;
const c_big_struct :: extern = fn cc(.c) (x: BigStruct) void;
const c_small_struct_ints :: extern = fn cc(.c) (x: SmallStructInts) void;
const c_ret_small_struct_ints :: extern = fn cc(.c) () SmallStructInts;
const c_med_struct_ints :: extern = fn cc(.c) (x: MedStructInts) void;
const c_ret_med_struct_ints :: extern = fn cc(.c) () MedStructInts;
const c_small_packed_struct :: extern = fn cc(.c) (x: SmallPackedStruct) void;
const c_ret_small_packed_struct :: extern = fn cc(.c) () SmallPackedStruct;
const c_split_struct_ints :: extern = fn cc(.c) (x: SplitStructInt) void;
const c_big_struct_both :: extern = fn cc(.c) (x: BigStruct) BigStruct;
const c_multiple_struct_ints :: extern = fn cc(.c) (r1: Rect, r2: Rect) void;
const c_ret_bool :: extern = fn cc(.c) () bool;
const c_ret_u8 :: extern = fn cc(.c) () u8;
const c_ret_u16 :: extern = fn cc(.c) () u16;
const c_ret_u32 :: extern = fn cc(.c) () u32;
const c_ret_u64 :: extern = fn cc(.c) () u64;
const c_ret_s8 :: extern = fn cc(.c) () s8;
const c_ret_s16 :: extern = fn cc(.c) () s16;
const c_ret_s32 :: extern = fn cc(.c) () s32;
const c_ret_s64 :: extern = fn cc(.c) () s64;
const StructWithArray = struct{
a: s32,
padding: [4]u8,
b: s64,
};
const c_struct_with_array :: extern = fn cc(.c) (x: StructWithArray) void;
const c_ret_struct_with_array :: extern = fn cc(.c) () StructWithArray;
const c_modify_by_ref_param :: extern = fn cc(.c) (x: ByRef) ByRef;
const c_func_ptr_byval :: extern = fn cc(.c) (a: usize, b: usize, c: ByVal, d: u64, e: u64, f: u64) void;
const nat_u8 :: export = fn cc(.c) (x: u8) void {
expect(x == 0xff) catch #trap();
}
const nat_u16 :: export = fn cc(.c) (x: u16) void {
expect(x == 0xfffe) catch #trap();
}
const nat_u32 :: export = fn cc(.c) (x: u32) void {
expect(x == 0xfffffffd) catch #trap();
}
const nat_u64 :: export = fn cc(.c) (x: u64) void {
expect(x == 0xfffffffffffffffc) catch #trap();
}
const nat_s8 :: export = fn cc(.c) (x: s8) void {
expect(x == -1) catch #trap();
}
const nat_s16 :: export = fn cc(.c) (x: s16) void {
expect(x == -2) catch #trap();
}
const nat_s32 :: export = fn cc(.c) (x: s32) void {
expect(x == -3) catch #trap();
}
const nat_s64 :: export = fn cc(.c) (x: s64) void {
expect(x == -4) catch #trap();
}
// TODO: transform into a real pointer
const nat_ptr :: export = fn cc(.c) (x: usize) void {
expect(x == 0xdeadbeef) catch #trap();
}
const nat_five_integers :: export = fn cc(.c) (a: s32, b: s32, c: s32, d: s32, e: s32) void {
expect(a == 12) catch #trap();
expect(b == 34) catch #trap();
expect(c == 56) catch #trap();
expect(d == 78) catch #trap();
expect(e == 90) catch #trap();
}
const nat_bool:: export = fn cc(.c) (x: bool) void {
expect(x) catch #trap();
}
const Struct_u64_u64 = struct{
a: u64,
b: u64,
};
const nat_ret_struct_u64_u64 :: export = fn cc(.c) () Struct_u64_u64 {
return .{ .a = 1, .b = 2, };
}
const nat_struct_u64_u64_0 :: export = fn cc(.c) (s: Struct_u64_u64) void {
expect(s.a == 3) catch #trap();
expect(s.b == 4) catch #trap();
}
const nat_struct_u64_u64_1 :: export = fn cc(.c) (_: usize, s: Struct_u64_u64) void {
expect(s.a == 5) catch #trap();
expect(s.b == 6) catch #trap();
}
const nat_struct_u64_u64_2 :: export = fn cc(.c) (_: usize, _: usize, s: Struct_u64_u64) void {
expect(s.a == 7) catch #trap();
expect(s.b == 8) catch #trap();
}
const nat_struct_u64_u64_3 :: export = fn cc(.c) (_: usize, _: usize, _: usize, s: Struct_u64_u64) void {
expect(s.a == 9) catch #trap();
expect(s.b == 10) catch #trap();
}
const nat_struct_u64_u64_4 :: export = fn cc(.c) (_: usize, _: usize, _: usize, _: usize, s: Struct_u64_u64) void {
expect(s.a == 11) catch #trap();
expect(s.b == 12) catch #trap();
}
const nat_struct_u64_u64_5 :: export = fn cc(.c) (_: usize, _: usize, _: usize, _: usize, _: usize, s: Struct_u64_u64) void {
expect(s.a == 13) catch #trap();
expect(s.b == 14) catch #trap();
}
const nat_struct_u64_u64_6 :: export = fn cc(.c) (_: usize, _: usize, _: usize, _: usize, _: usize, _: usize, s: Struct_u64_u64) void {
expect(s.a == 15) catch #trap();
expect(s.b == 16) catch #trap();
}
const nat_struct_u64_u64_7 :: export = fn cc(.c) (_: usize, _: usize, _: usize, _: usize, _: usize, _: usize, _: usize, s: Struct_u64_u64) void {
expect(s.a == 17) catch #trap();
expect(s.b == 18) catch #trap();
}
const nat_struct_u64_u64_8 :: export = fn cc(.c) (_: usize, _: usize, _: usize, _: usize, _: usize, _: usize, _: usize, _: usize, s: Struct_u64_u64) void {
expect(s.a == 19) catch #trap();
expect(s.b == 20) catch #trap();
}
const BigStruct = struct {
a: u64,
b: u64,
c: u64,
d: u64,
e: u8,
};
const nat_big_struct:: export = fn cc(.c) (x: BigStruct) void {
expect(x.a == 1) catch #trap();
expect(x.b == 2) catch #trap();
expect(x.c == 3) catch #trap();
expect(x.d == 4) catch #trap();
expect(x.e == 5) catch #trap();
}
const SmallStructInts = struct {
a: u8,
b: u8,
c: u8,
d: u8,
};
const nat_small_struct_ints :: export = fn cc(.c) (x: SmallStructInts) void {
expect(x.a == 1) catch #trap();
expect(x.b == 2) catch #trap();
expect(x.c == 3) catch #trap();
expect(x.d == 4) catch #trap();
}
const MedStructInts = struct {
x: s32,
y: s32,
z: s32,
};
const nat_med_struct_ints :: export = fn cc(.c) (s: MedStructInts) void {
expect(s.x == 1) catch #trap();
expect(s.y == 2) catch #trap();
expect(s.z == 3) catch #trap();
}
const SmallPackedStruct = bitfield(u8) {
a: u2,
b: u2,
c: u2,
d: u2,
};
const nat_small_packed_struct :: export = fn cc(.c) (x: SmallPackedStruct) void {
expect(x.a == 0) catch #trap();
expect(x.b == 1) catch #trap();
expect(x.c == 2) catch #trap();
expect(x.d == 3) catch #trap();
}
const SplitStructInt = struct {
a: u64,
b: u8,
c: u32,
};
const nat_split_struct_ints :: export = fn cc(.c) (x: SplitStructInt) void {
expect(x.a == 1234) catch #trap();
expect(x.b == 100) catch #trap();
expect(x.c == 1337) catch #trap();
}
const nat_big_struct_both :: export = fn cc(.c) (x: BigStruct) BigStruct {
expect(x.a == 30) catch #trap();
expect(x.b == 31) catch #trap();
expect(x.c == 32) catch #trap();
expect(x.d == 33) catch #trap();
expect(x.e == 34) catch #trap();
const s = BigStruct{
.a = 20,
.b = 21,
.c = 22,
.d = 23,
.e = 24,
};
return s;
}
const Rect = struct {
left: u32,
right: u32,
top: u32,
bottom: u32,
};
const nat_multiple_struct_ints :: export = fn cc(.c) (x: Rect, y: Rect) void {
expect(x.left == 1) catch #trap();
expect(x.right == 21) catch #trap();
expect(x.top == 16) catch #trap();
expect(x.bottom == 4) catch #trap();
expect(y.left == 178) catch #trap();
expect(y.right == 189) catch #trap();
expect(y.top == 21) catch #trap();
expect(y.bottom == 15) catch #trap();
}
const nat_ret_bool :: export = fn cc(.c) () bool {
return true;
}
const nat_ret_u8 :: export = fn cc(.c) () u8 {
return 0xff;
}
const nat_ret_u16 :: export = fn cc(.c) () u16 {
return 0xffff;
}
const nat_ret_u32 :: export = fn cc(.c) () u32 {
return 0xffffffff;
}
const nat_ret_u64 :: export = fn cc(.c) () u64 {
return 0xffffffffffffffff;
}
const nat_ret_s8 :: export = fn cc(.c) () s8 {
return -1;
}
const nat_ret_s16 :: export = fn cc(.c) () s16 {
return -1;
}
const nat_ret_s32 :: export = fn cc(.c) () s32 {
return -1;
}
const nat_ret_s64 :: export = fn cc(.c) () s64 {
return -1;
}
const nat_ret_small_struct_ints :: export = fn cc(.c) () SmallStructInts {
return .{
.a = 1,
.b = 2,
.c = 3,
.d = 4,
};
}
const nat_ret_med_struct_ints :: export = fn cc(.c) () MedStructInts {
return .{
.x = 1,
.y = 2,
.z = 3,
};
}