Implement basic C ABI support

This commit is contained in:
David Gonzalez Martin 2024-03-14 17:22:02 -06:00
parent 953e0b7013
commit ac3923f4d3
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,90 +567,93 @@ pub const LLVM = struct {
pub const Tuple = opaque {};
};
pub const Attribute = enum(u32) {
AllocAlign = 1,
AllocatedPointer = 2,
AlwaysInline = 3,
Builtin = 4,
Cold = 5,
Convergent = 6,
DisableSanitizerInstrumentation = 7,
FnRetThunkExtern = 8,
Hot = 9,
ImmArg = 10,
InReg = 11,
InlineHint = 12,
JumpTable = 13,
MinSize = 14,
MustProgress = 15,
Naked = 16,
Nest = 17,
NoAlias = 18,
NoBuiltin = 19,
NoCallback = 20,
NoCapture = 21,
NoCfCheck = 22,
NoDuplicate = 23,
NoFree = 24,
NoImplicitFloat = 25,
NoInline = 26,
NoMerge = 27,
NoProfile = 28,
NoRecurse = 29,
NoRedZone = 30,
NoReturn = 31,
NoSanitizeBounds = 32,
NoSanitizeCoverage = 33,
NoSync = 34,
NoUndef = 35,
NoUnwind = 36,
NonLazyBind = 37,
NonNull = 38,
NullPointerIsValid = 39,
OptForFuzzing = 40,
OptimizeForSize = 41,
OptimizeNone = 42,
PresplitCoroutine = 43,
ReadNone = 44,
ReadOnly = 45,
Returned = 46,
ReturnsTwice = 47,
SExt = 48,
SafeStack = 49,
SanitizeAddress = 50,
SanitizeHWAddress = 51,
SanitizeMemTag = 52,
SanitizeMemory = 53,
SanitizeThread = 54,
ShadowCallStack = 55,
SkipProfile = 56,
Speculatable = 57,
SpeculativeLoadHardening = 58,
StackProtect = 59,
StackProtectReq = 60,
StackProtectStrong = 61,
StrictFP = 62,
SwiftAsync = 63,
SwiftError = 64,
SwiftSelf = 65,
WillReturn = 66,
WriteOnly = 67,
ZExt = 68,
ByRef = 69,
ByVal = 70,
ElementType = 71,
InAlloca = 72,
Preallocated = 73,
StructRet = 74,
Alignment = 75,
AllocKind = 76,
AllocSize = 77,
Dereferenceable = 78,
DereferenceableOrNull = 79,
Memory = 80,
StackAlignment = 81,
UWTable = 82,
VScaleRange = 83,
pub const Attribute = opaque {
pub const Set = opaque {};
pub const Id = enum(u32) {
AllocAlign = 1,
AllocatedPointer = 2,
AlwaysInline = 3,
Builtin = 4,
Cold = 5,
Convergent = 6,
DisableSanitizerInstrumentation = 7,
FnRetThunkExtern = 8,
Hot = 9,
ImmArg = 10,
InReg = 11,
InlineHint = 12,
JumpTable = 13,
MinSize = 14,
MustProgress = 15,
Naked = 16,
Nest = 17,
NoAlias = 18,
NoBuiltin = 19,
NoCallback = 20,
NoCapture = 21,
NoCfCheck = 22,
NoDuplicate = 23,
NoFree = 24,
NoImplicitFloat = 25,
NoInline = 26,
NoMerge = 27,
NoProfile = 28,
NoRecurse = 29,
NoRedZone = 30,
NoReturn = 31,
NoSanitizeBounds = 32,
NoSanitizeCoverage = 33,
NoSync = 34,
NoUndef = 35,
NoUnwind = 36,
NonLazyBind = 37,
NonNull = 38,
NullPointerIsValid = 39,
OptForFuzzing = 40,
OptimizeForSize = 41,
OptimizeNone = 42,
PresplitCoroutine = 43,
ReadNone = 44,
ReadOnly = 45,
Returned = 46,
ReturnsTwice = 47,
SExt = 48,
SafeStack = 49,
SanitizeAddress = 50,
SanitizeHWAddress = 51,
SanitizeMemTag = 52,
SanitizeMemory = 53,
SanitizeThread = 54,
ShadowCallStack = 55,
SkipProfile = 56,
Speculatable = 57,
SpeculativeLoadHardening = 58,
StackProtect = 59,
StackProtectReq = 60,
StackProtectStrong = 61,
StrictFP = 62,
SwiftAsync = 63,
SwiftError = 64,
SwiftSelf = 65,
WillReturn = 66,
WriteOnly = 67,
ZExt = 68,
ByRef = 69,
ByVal = 70,
ElementType = 71,
InAlloca = 72,
Preallocated = 73,
StructRet = 74,
Alignment = 75,
AllocKind = 76,
AllocSize = 77,
Dereferenceable = 78,
DereferenceableOrNull = 79,
Memory = 80,
StackAlignment = 81,
UWTable = 82,
VScaleRange = 83,
};
};
pub const Type = opaque {
@ -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,65 +2847,61 @@ 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| {
const local_variable = declare_local_variable.variable;
const debug_declaration_type = try llvm.getDebugType(unit, context, local_variable.declaration.type);
const always_preserve = true;
const flags = LLVM.DebugInfo.Node.Flags{
.visibility = .none,
.forward_declaration = false,
.apple_block = false,
.block_by_ref_struct = false,
.virtual = false,
.artificial = false,
.explicit = false,
.prototyped = false,
.objective_c_class_complete = false,
.object_pointer = false,
.vector = false,
.static_member = false,
.lvalue_reference = false,
.rvalue_reference = false,
.reserved = false,
.inheritance = .none,
.introduced_virtual = false,
.bit_field = false,
.no_return = false,
.type_pass_by_value = false,
.type_pass_by_reference = false,
.enum_class = false,
.thunk = false,
.non_trivial = false,
.big_endian = false,
.little_endian = false,
.all_calls_described = false,
};
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;
const flags = LLVM.DebugInfo.Node.Flags{
.visibility = .none,
.forward_declaration = false,
.apple_block = false,
.block_by_ref_struct = false,
.virtual = false,
.artificial = false,
.explicit = false,
.prototyped = false,
.objective_c_class_complete = false,
.object_pointer = false,
.vector = false,
.static_member = false,
.lvalue_reference = false,
.rvalue_reference = false,
.reserved = false,
.inheritance = .none,
.introduced_virtual = false,
.bit_field = false,
.no_return = false,
.type_pass_by_value = false,
.type_pass_by_reference = false,
.enum_class = false,
.thunk = false,
.non_trivial = false,
.big_endian = false,
.little_endian = false,
.all_calls_described = false,
};
const alignment = 0;
const declaration_name = unit.getIdentifier(local_variable.declaration.name);
const line = local_variable.declaration.line;
const column = local_variable.declaration.column;
const debug_local_variable = llvm.debug_info_builder.createAutoVariable(llvm.scope, declaration_name.ptr, declaration_name.len, llvm.file, line, debug_declaration_type, always_preserve, flags, alignment) orelse unreachable;
const alignment = 0;
const declaration_name = unit.getIdentifier(local_variable.declaration.name);
const line = local_variable.declaration.line;
const column = local_variable.declaration.column;
const debug_local_variable = llvm.debug_info_builder.createAutoVariable(llvm.scope, declaration_name.ptr, declaration_name.len, llvm.file, line, debug_declaration_type, always_preserve, flags, alignment) orelse unreachable;
const local = alloca_map.get(declare_local_variable.stack).?;
const local = alloca_map.get(declare_local_variable.stack).?;
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;
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);
},
}
}
@ -726,7 +726,7 @@ pub fn span(ptr: [*:0]const u8) [:0]const u8 {
pub fn last(bytes: []const u8, byte: u8) ?usize {
var i = bytes.len;
while (i > 0) {
while (i > 0) {
i -= 1;
if (bytes[i] == byte) {
@ -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();
}

558
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,8 +104,249 @@ pub fn build(b: *std.Build) !void {
compiler.linkLibC();
const zstd = if (target.result.os.tag == .windows) "zstd.lib" else "libzstd.a";
const static_llvm_libraries = [_][]const u8{
"libLLVMAArch64AsmParser.a",
"libLLVMAArch64CodeGen.a",
"libLLVMAArch64Desc.a",
"libLLVMAArch64Disassembler.a",
"libLLVMAArch64Info.a",
"libLLVMAArch64Utils.a",
"libLLVMAggressiveInstCombine.a",
"libLLVMAMDGPUAsmParser.a",
"libLLVMAMDGPUCodeGen.a",
"libLLVMAMDGPUDesc.a",
"libLLVMAMDGPUDisassembler.a",
"libLLVMAMDGPUInfo.a",
"libLLVMAMDGPUTargetMCA.a",
"libLLVMAMDGPUUtils.a",
"libLLVMAnalysis.a",
"libLLVMARMAsmParser.a",
"libLLVMARMCodeGen.a",
"libLLVMARMDesc.a",
"libLLVMARMDisassembler.a",
"libLLVMARMInfo.a",
"libLLVMARMUtils.a",
"libLLVMAsmParser.a",
"libLLVMAsmPrinter.a",
"libLLVMAVRAsmParser.a",
"libLLVMAVRCodeGen.a",
"libLLVMAVRDesc.a",
"libLLVMAVRDisassembler.a",
"libLLVMAVRInfo.a",
"libLLVMBinaryFormat.a",
"libLLVMBitReader.a",
"libLLVMBitstreamReader.a",
"libLLVMBitWriter.a",
"libLLVMBPFAsmParser.a",
"libLLVMBPFCodeGen.a",
"libLLVMBPFDesc.a",
"libLLVMBPFDisassembler.a",
"libLLVMBPFInfo.a",
"libLLVMCFGuard.a",
"libLLVMCFIVerify.a",
"libLLVMCodeGen.a",
"libLLVMCodeGenTypes.a",
"libLLVMCore.a",
"libLLVMCoroutines.a",
"libLLVMCoverage.a",
"libLLVMDebugInfoBTF.a",
"libLLVMDebugInfoCodeView.a",
"libLLVMDebuginfod.a",
"libLLVMDebugInfoDWARF.a",
"libLLVMDebugInfoGSYM.a",
"libLLVMDebugInfoLogicalView.a",
"libLLVMDebugInfoMSF.a",
"libLLVMDebugInfoPDB.a",
"libLLVMDemangle.a",
"libLLVMDiff.a",
"libLLVMDlltoolDriver.a",
"libLLVMDWARFLinker.a",
"libLLVMDWARFLinkerParallel.a",
"libLLVMDWP.a",
"libLLVMExecutionEngine.a",
"libLLVMExtensions.a",
"libLLVMFileCheck.a",
"libLLVMFrontendHLSL.a",
"libLLVMFrontendOpenACC.a",
"libLLVMFrontendOpenMP.a",
"libLLVMFuzzerCLI.a",
"libLLVMFuzzMutate.a",
"libLLVMGlobalISel.a",
"libLLVMHexagonAsmParser.a",
"libLLVMHexagonCodeGen.a",
"libLLVMHexagonDesc.a",
"libLLVMHexagonDisassembler.a",
"libLLVMHexagonInfo.a",
"libLLVMInstCombine.a",
"libLLVMInstrumentation.a",
"libLLVMInterfaceStub.a",
"libLLVMInterpreter.a",
"libLLVMipo.a",
"libLLVMIRPrinter.a",
"libLLVMIRReader.a",
"libLLVMJITLink.a",
"libLLVMLanaiAsmParser.a",
"libLLVMLanaiCodeGen.a",
"libLLVMLanaiDesc.a",
"libLLVMLanaiDisassembler.a",
"libLLVMLanaiInfo.a",
"libLLVMLibDriver.a",
"libLLVMLineEditor.a",
"libLLVMLinker.a",
"libLLVMLoongArchAsmParser.a",
"libLLVMLoongArchCodeGen.a",
"libLLVMLoongArchDesc.a",
"libLLVMLoongArchDisassembler.a",
"libLLVMLoongArchInfo.a",
"libLLVMLTO.a",
"libLLVMMC.a",
"libLLVMMCA.a",
"libLLVMMCDisassembler.a",
"libLLVMMCJIT.a",
"libLLVMMCParser.a",
"libLLVMMipsAsmParser.a",
"libLLVMMipsCodeGen.a",
"libLLVMMipsDesc.a",
"libLLVMMipsDisassembler.a",
"libLLVMMipsInfo.a",
"libLLVMMIRParser.a",
"libLLVMMSP430AsmParser.a",
"libLLVMMSP430CodeGen.a",
"libLLVMMSP430Desc.a",
"libLLVMMSP430Disassembler.a",
"libLLVMMSP430Info.a",
"libLLVMNVPTXCodeGen.a",
"libLLVMNVPTXDesc.a",
"libLLVMNVPTXInfo.a",
"libLLVMObjCARCOpts.a",
"libLLVMObjCopy.a",
"libLLVMObject.a",
"libLLVMObjectYAML.a",
"libLLVMOption.a",
"libLLVMOrcJIT.a",
"libLLVMOrcShared.a",
"libLLVMOrcTargetProcess.a",
"libLLVMPasses.a",
"libLLVMPowerPCAsmParser.a",
"libLLVMPowerPCCodeGen.a",
"libLLVMPowerPCDesc.a",
"libLLVMPowerPCDisassembler.a",
"libLLVMPowerPCInfo.a",
"libLLVMProfileData.a",
"libLLVMRemarks.a",
"libLLVMRISCVAsmParser.a",
"libLLVMRISCVCodeGen.a",
"libLLVMRISCVDesc.a",
"libLLVMRISCVDisassembler.a",
"libLLVMRISCVInfo.a",
"libLLVMRISCVTargetMCA.a",
"libLLVMRuntimeDyld.a",
"libLLVMScalarOpts.a",
"libLLVMSelectionDAG.a",
"libLLVMSparcAsmParser.a",
"libLLVMSparcCodeGen.a",
"libLLVMSparcDesc.a",
"libLLVMSparcDisassembler.a",
"libLLVMSparcInfo.a",
"libLLVMSupport.a",
"libLLVMSymbolize.a",
"libLLVMSystemZAsmParser.a",
"libLLVMSystemZCodeGen.a",
"libLLVMSystemZDesc.a",
"libLLVMSystemZDisassembler.a",
"libLLVMSystemZInfo.a",
"libLLVMTableGen.a",
"libLLVMTableGenCommon.a",
"libLLVMTableGenGlobalISel.a",
"libLLVMTarget.a",
"libLLVMTargetParser.a",
"libLLVMTextAPI.a",
"libLLVMTransformUtils.a",
"libLLVMVEAsmParser.a",
"libLLVMVECodeGen.a",
"libLLVMVectorize.a",
"libLLVMVEDesc.a",
"libLLVMVEDisassembler.a",
"libLLVMVEInfo.a",
"libLLVMWebAssemblyAsmParser.a",
"libLLVMWebAssemblyCodeGen.a",
"libLLVMWebAssemblyDesc.a",
"libLLVMWebAssemblyDisassembler.a",
"libLLVMWebAssemblyInfo.a",
"libLLVMWebAssemblyUtils.a",
"libLLVMWindowsDriver.a",
"libLLVMWindowsManifest.a",
"libLLVMX86AsmParser.a",
"libLLVMX86CodeGen.a",
"libLLVMX86Desc.a",
"libLLVMX86Disassembler.a",
"libLLVMX86Info.a",
"libLLVMX86TargetMCA.a",
"libLLVMXCoreCodeGen.a",
"libLLVMXCoreDesc.a",
"libLLVMXCoreDisassembler.a",
"libLLVMXCoreInfo.a",
"libLLVMXRay.a",
//LLD
"liblldCOFF.a",
"liblldCommon.a",
"liblldELF.a",
"liblldMachO.a",
"liblldMinGW.a",
"liblldWasm.a",
// Zlib
"libz.a",
// Zstd
zstd,
// Clang
"libclangAnalysis.a",
"libclangAnalysisFlowSensitive.a",
"libclangAnalysisFlowSensitiveModels.a",
"libclangAPINotes.a",
"libclangARCMigrate.a",
"libclangAST.a",
"libclangASTMatchers.a",
"libclangBasic.a",
"libclangCodeGen.a",
"libclangCrossTU.a",
"libclangDependencyScanning.a",
"libclangDirectoryWatcher.a",
"libclangDriver.a",
"libclangDynamicASTMatchers.a",
"libclangEdit.a",
"libclangExtractAPI.a",
"libclangFormat.a",
"libclangFrontend.a",
"libclangFrontendTool.a",
"libclangHandleCXX.a",
"libclangHandleLLVM.a",
"libclangIndex.a",
"libclangIndexSerialization.a",
"libclangInterpreter.a",
"libclangLex.a",
"libclangParse.a",
"libclangRewrite.a",
"libclangRewriteFrontend.a",
"libclangSema.a",
"libclangSerialization.a",
"libclangStaticAnalyzerCheckers.a",
"libclangStaticAnalyzerCore.a",
"libclangStaticAnalyzerFrontend.a",
"libclangSupport.a",
"libclangTooling.a",
"libclangToolingASTDiff.a",
"libclangToolingCore.a",
"libclangToolingInclusions.a",
"libclangToolingInclusionsStdlib.a",
"libclangToolingRefactoring.a",
"libclangToolingSyntax.a",
"libclangTransformer.a",
};
if (static) {
compiler.linkage = .static;
if (os == .linux) compiler.linkage = .static;
compiler.linkLibCpp();
const prefix = "nat/cache";
@ -137,257 +396,11 @@ pub fn build(b: *std.Build) !void {
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{
"libLLVMAArch64AsmParser.a",
"libLLVMAArch64CodeGen.a",
"libLLVMAArch64Desc.a",
"libLLVMAArch64Disassembler.a",
"libLLVMAArch64Info.a",
"libLLVMAArch64Utils.a",
"libLLVMAggressiveInstCombine.a",
"libLLVMAMDGPUAsmParser.a",
"libLLVMAMDGPUCodeGen.a",
"libLLVMAMDGPUDesc.a",
"libLLVMAMDGPUDisassembler.a",
"libLLVMAMDGPUInfo.a",
"libLLVMAMDGPUTargetMCA.a",
"libLLVMAMDGPUUtils.a",
"libLLVMAnalysis.a",
"libLLVMARMAsmParser.a",
"libLLVMARMCodeGen.a",
"libLLVMARMDesc.a",
"libLLVMARMDisassembler.a",
"libLLVMARMInfo.a",
"libLLVMARMUtils.a",
"libLLVMAsmParser.a",
"libLLVMAsmPrinter.a",
"libLLVMAVRAsmParser.a",
"libLLVMAVRCodeGen.a",
"libLLVMAVRDesc.a",
"libLLVMAVRDisassembler.a",
"libLLVMAVRInfo.a",
"libLLVMBinaryFormat.a",
"libLLVMBitReader.a",
"libLLVMBitstreamReader.a",
"libLLVMBitWriter.a",
"libLLVMBPFAsmParser.a",
"libLLVMBPFCodeGen.a",
"libLLVMBPFDesc.a",
"libLLVMBPFDisassembler.a",
"libLLVMBPFInfo.a",
"libLLVMCFGuard.a",
"libLLVMCFIVerify.a",
"libLLVMCodeGen.a",
"libLLVMCodeGenTypes.a",
"libLLVMCore.a",
"libLLVMCoroutines.a",
"libLLVMCoverage.a",
"libLLVMDebugInfoBTF.a",
"libLLVMDebugInfoCodeView.a",
"libLLVMDebuginfod.a",
"libLLVMDebugInfoDWARF.a",
"libLLVMDebugInfoGSYM.a",
"libLLVMDebugInfoLogicalView.a",
"libLLVMDebugInfoMSF.a",
"libLLVMDebugInfoPDB.a",
"libLLVMDemangle.a",
"libLLVMDiff.a",
"libLLVMDlltoolDriver.a",
"libLLVMDWARFLinker.a",
"libLLVMDWARFLinkerParallel.a",
"libLLVMDWP.a",
"libLLVMExecutionEngine.a",
"libLLVMExtensions.a",
"libLLVMFileCheck.a",
"libLLVMFrontendHLSL.a",
"libLLVMFrontendOpenACC.a",
"libLLVMFrontendOpenMP.a",
"libLLVMFuzzerCLI.a",
"libLLVMFuzzMutate.a",
"libLLVMGlobalISel.a",
"libLLVMHexagonAsmParser.a",
"libLLVMHexagonCodeGen.a",
"libLLVMHexagonDesc.a",
"libLLVMHexagonDisassembler.a",
"libLLVMHexagonInfo.a",
"libLLVMInstCombine.a",
"libLLVMInstrumentation.a",
"libLLVMInterfaceStub.a",
"libLLVMInterpreter.a",
"libLLVMipo.a",
"libLLVMIRPrinter.a",
"libLLVMIRReader.a",
"libLLVMJITLink.a",
"libLLVMLanaiAsmParser.a",
"libLLVMLanaiCodeGen.a",
"libLLVMLanaiDesc.a",
"libLLVMLanaiDisassembler.a",
"libLLVMLanaiInfo.a",
"libLLVMLibDriver.a",
"libLLVMLineEditor.a",
"libLLVMLinker.a",
"libLLVMLoongArchAsmParser.a",
"libLLVMLoongArchCodeGen.a",
"libLLVMLoongArchDesc.a",
"libLLVMLoongArchDisassembler.a",
"libLLVMLoongArchInfo.a",
"libLLVMLTO.a",
"libLLVMMC.a",
"libLLVMMCA.a",
"libLLVMMCDisassembler.a",
"libLLVMMCJIT.a",
"libLLVMMCParser.a",
"libLLVMMipsAsmParser.a",
"libLLVMMipsCodeGen.a",
"libLLVMMipsDesc.a",
"libLLVMMipsDisassembler.a",
"libLLVMMipsInfo.a",
"libLLVMMIRParser.a",
"libLLVMMSP430AsmParser.a",
"libLLVMMSP430CodeGen.a",
"libLLVMMSP430Desc.a",
"libLLVMMSP430Disassembler.a",
"libLLVMMSP430Info.a",
"libLLVMNVPTXCodeGen.a",
"libLLVMNVPTXDesc.a",
"libLLVMNVPTXInfo.a",
"libLLVMObjCARCOpts.a",
"libLLVMObjCopy.a",
"libLLVMObject.a",
"libLLVMObjectYAML.a",
"libLLVMOption.a",
"libLLVMOrcJIT.a",
"libLLVMOrcShared.a",
"libLLVMOrcTargetProcess.a",
"libLLVMPasses.a",
"libLLVMPowerPCAsmParser.a",
"libLLVMPowerPCCodeGen.a",
"libLLVMPowerPCDesc.a",
"libLLVMPowerPCDisassembler.a",
"libLLVMPowerPCInfo.a",
"libLLVMProfileData.a",
"libLLVMRemarks.a",
"libLLVMRISCVAsmParser.a",
"libLLVMRISCVCodeGen.a",
"libLLVMRISCVDesc.a",
"libLLVMRISCVDisassembler.a",
"libLLVMRISCVInfo.a",
"libLLVMRISCVTargetMCA.a",
"libLLVMRuntimeDyld.a",
"libLLVMScalarOpts.a",
"libLLVMSelectionDAG.a",
"libLLVMSparcAsmParser.a",
"libLLVMSparcCodeGen.a",
"libLLVMSparcDesc.a",
"libLLVMSparcDisassembler.a",
"libLLVMSparcInfo.a",
"libLLVMSupport.a",
"libLLVMSymbolize.a",
"libLLVMSystemZAsmParser.a",
"libLLVMSystemZCodeGen.a",
"libLLVMSystemZDesc.a",
"libLLVMSystemZDisassembler.a",
"libLLVMSystemZInfo.a",
"libLLVMTableGen.a",
"libLLVMTableGenCommon.a",
"libLLVMTableGenGlobalISel.a",
"libLLVMTarget.a",
"libLLVMTargetParser.a",
"libLLVMTextAPI.a",
"libLLVMTransformUtils.a",
"libLLVMVEAsmParser.a",
"libLLVMVECodeGen.a",
"libLLVMVectorize.a",
"libLLVMVEDesc.a",
"libLLVMVEDisassembler.a",
"libLLVMVEInfo.a",
"libLLVMWebAssemblyAsmParser.a",
"libLLVMWebAssemblyCodeGen.a",
"libLLVMWebAssemblyDesc.a",
"libLLVMWebAssemblyDisassembler.a",
"libLLVMWebAssemblyInfo.a",
"libLLVMWebAssemblyUtils.a",
"libLLVMWindowsDriver.a",
"libLLVMWindowsManifest.a",
"libLLVMX86AsmParser.a",
"libLLVMX86CodeGen.a",
"libLLVMX86Desc.a",
"libLLVMX86Disassembler.a",
"libLLVMX86Info.a",
"libLLVMX86TargetMCA.a",
"libLLVMXCoreCodeGen.a",
"libLLVMXCoreDesc.a",
"libLLVMXCoreDisassembler.a",
"libLLVMXCoreInfo.a",
"libLLVMXRay.a",
//LLD
"liblldCOFF.a",
"liblldCommon.a",
"liblldELF.a",
"liblldMachO.a",
"liblldMinGW.a",
"liblldWasm.a",
// Zlib
"libz.a",
// Zstd
zstd,
// Clang
"libclangAnalysis.a",
"libclangAnalysisFlowSensitive.a",
"libclangAnalysisFlowSensitiveModels.a",
"libclangAPINotes.a",
"libclangARCMigrate.a",
"libclangAST.a",
"libclangASTMatchers.a",
"libclangBasic.a",
"libclangCodeGen.a",
"libclangCrossTU.a",
"libclangDependencyScanning.a",
"libclangDirectoryWatcher.a",
"libclangDriver.a",
"libclangDynamicASTMatchers.a",
"libclangEdit.a",
"libclangExtractAPI.a",
"libclangFormat.a",
"libclangFrontend.a",
"libclangFrontendTool.a",
"libclangHandleCXX.a",
"libclangHandleLLVM.a",
"libclangIndex.a",
"libclangIndexSerialization.a",
"libclangInterpreter.a",
"libclangLex.a",
"libclangParse.a",
"libclangRewrite.a",
"libclangRewriteFrontend.a",
"libclangSema.a",
"libclangSerialization.a",
"libclangStaticAnalyzerCheckers.a",
"libclangStaticAnalyzerCore.a",
"libclangStaticAnalyzerFrontend.a",
"libclangSupport.a",
"libclangTooling.a",
"libclangToolingASTDiff.a",
"libclangToolingCore.a",
"libclangToolingInclusions.a",
"libclangToolingInclusionsStdlib.a",
"libclangToolingRefactoring.a",
"libclangToolingSyntax.a",
"libclangTransformer.a",
};
for (llvm_libraries) |llvm_library| {
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 {
unreachable;
}
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,
};
}