diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index f0680a8..a278077 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -648,12 +648,22 @@ pub fn compileCSourceFile(context: *const Context, arguments: []const []const u8 "/usr/lib/clang/17/include", "/usr/include", "/usr/include/x86_64-linux-gnu", - } else &.{ - "/usr/include/c++/13.2.1", - "/usr/include/c++/13.2.1/x86_64-pc-linux-gnu", - "/usr/lib/clang/17/include", - "/usr/include", - "/usr/include/linux", + } else switch (@import("builtin").cpu.arch) { + .x86_64 => &.{ + "/usr/include/c++/13.2.1", + "/usr/include/c++/13.2.1/x86_64-pc-linux-gnu", + "/usr/lib/clang/17/include", + "/usr/include", + "/usr/include/linux", + }, + .aarch64 => &.{ + "/usr/include/c++/13", + "/usr/include/c++/13/aarch64-redhat-linux", + "/usr/lib/clang/17/include", + "/usr/include", + "/usr/include/linux", + }, + else => unreachable, }, else => unreachable, //@compileError("ABI not supported"), }, @@ -2771,28 +2781,25 @@ const Abi = enum { pub fn buildExecutable(context: *const Context, arguments: []const []const u8, options: ExecutableOptions) !void { var maybe_executable_path: ?[]const u8 = null; var maybe_main_package_path: ?[]const u8 = null; - var arch: Arch = undefined; - var os: Os = undefined; - var abi: Abi = undefined; - switch (@import("builtin").os.tag) { - .linux => { - arch = .x86_64; - os = .linux; - abi = .gnu; - }, - .macos => { - arch = .aarch64; - os = .macos; - abi = .none; - }, - .windows => { - arch = .x86_64; - os = .windows; - abi = .gnu; - }, + // TODO: make these mutable + const arch: Arch = switch (@import("builtin").cpu.arch) { + .aarch64 => .aarch64, + .x86_64 => .x86_64, else => unreachable, - } + }; + const os: Os = switch (@import("builtin").os.tag) { + .linux => .linux, + .macos => .macos, + .windows => .windows, + else => unreachable, + }; + const abi: Abi = switch (@import("builtin").os.tag) { + .linux => .gnu, + .macos => .none, + .windows => .gnu, + else => unreachable, + }; var maybe_only_parse: ?bool = null; var link_libc = false; @@ -2982,7 +2989,7 @@ pub fn buildExecutable(context: *const Context, arguments: []const []const u8, o try unit.compile(context); } -fn createUnit(context: *const Context, arguments: struct{ +fn createUnit(context: *const Context, arguments: struct { main_package_path: []const u8, executable_path: []const u8, object_path: []const u8, @@ -2996,7 +3003,7 @@ fn createUnit(context: *const Context, arguments: struct{ name: []const u8, is_test: bool, c_source_files: []const []const u8, -}) !*Unit{ +}) !*Unit { const unit = try context.allocator.create(Unit); unit.* = .{ .descriptor = .{ @@ -4687,86 +4694,89 @@ pub const Builder = struct { }; }, .@"asm" => { - const architecture = InlineAssembly.x86_64; + switch (unit.descriptor.arch) { + inline else => |arch| { + const architecture = @field(InlineAssembly, @tagName(arch)); + assert(argument_node_list.len == 1); + const assembly_block_node = unit.getNode(argument_node_list[0]); + const instruction_node_list = unit.getNodeList(assembly_block_node.left); + var instructions = try context.arena.new_array(InlineAssembly.Instruction.Index, instruction_node_list.len); + instructions.len = 0; - assert(argument_node_list.len == 1); - const assembly_block_node = unit.getNode(argument_node_list[0]); - const instruction_node_list = unit.getNodeList(assembly_block_node.left); - var instructions = try context.arena.new_array(InlineAssembly.Instruction.Index, instruction_node_list.len); - instructions.len = 0; + for (instruction_node_list) |assembly_statement_node_index| { + const assembly_instruction_node = unit.getNode(assembly_statement_node_index); + const assembly_instruction_name_node = unit.getNode(assembly_instruction_node.left); + const instruction_name = unit.getExpectedTokenBytes(assembly_instruction_name_node.token, .identifier); + const instruction = inline for (@typeInfo(architecture.Instruction).Enum.fields) |instruction_enum_field| { + if (byte_equal(instruction_name, instruction_enum_field.name)) { + break @field(architecture.Instruction, instruction_enum_field.name); + } + } else unreachable; + const operand_nodes = unit.getNodeList(assembly_instruction_node.right); - for (instruction_node_list) |assembly_statement_node_index| { - const assembly_instruction_node = unit.getNode(assembly_statement_node_index); - const assembly_instruction_name_node = unit.getNode(assembly_instruction_node.left); - const instruction_name = unit.getExpectedTokenBytes(assembly_instruction_name_node.token, .identifier); - const instruction = inline for (@typeInfo(architecture.Instruction).Enum.fields) |instruction_enum_field| { - if (byte_equal(instruction_name, instruction_enum_field.name)) { - break @field(architecture.Instruction, instruction_enum_field.name); - } - } else unreachable; - const operand_nodes = unit.getNodeList(assembly_instruction_node.right); + var operands = try context.arena.new_array(InlineAssembly.Operand, operand_nodes.len); + operands.len = 0; - var operands = try context.arena.new_array(InlineAssembly.Operand, operand_nodes.len); - operands.len = 0; + for (operand_nodes) |operand_node_index| { + const operand_node = unit.getNode(operand_node_index); + const operand: InlineAssembly.Operand = switch (operand_node.id) { + .assembly_register => blk: { + const register_name = unit.getExpectedTokenBytes(operand_node.token, .identifier); - for (operand_nodes) |operand_node_index| { - const operand_node = unit.getNode(operand_node_index); - const operand: InlineAssembly.Operand = switch (operand_node.id) { - .assembly_register => blk: { - const register_name = unit.getExpectedTokenBytes(operand_node.token, .identifier); - - const register = inline for (@typeInfo(architecture.Register).Enum.fields) |register_enum_field| { - if (byte_equal(register_name, register_enum_field.name)) { - break @field(architecture.Register, register_enum_field.name); - } - } else unreachable; - break :blk .{ - .register = @intFromEnum(register), + const register = inline for (@typeInfo(architecture.Register).Enum.fields) |register_enum_field| { + if (byte_equal(register_name, register_enum_field.name)) { + break @field(architecture.Register, register_enum_field.name); + } + } else unreachable; + break :blk .{ + .register = @intFromEnum(register), + }; + }, + .number_literal => switch (std.zig.parseNumberLiteral(unit.getExpectedTokenBytes(operand_node.token, .number_literal))) { + .int => |integer| .{ + .number_literal = integer, + }, + else => |t| @panic(@tagName(t)), + }, + .assembly_code_expression => .{ + .value = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, operand_node.left, .left), + }, + else => |t| @panic(@tagName(t)), }; + + const index = operands.len; + operands.len += 1; + operands[index] = operand; + } + + const instruction_index = unit.assembly_instructions.append_index(.{ + .id = @intFromEnum(instruction), + .operands = operands, + }); + + const index = instructions.len; + instructions.len += 1; + instructions[index] = instruction_index; + } + + const inline_assembly = unit.inline_assembly.append_index(.{ + .instructions = instructions, + }); + + const inline_asm = unit.instructions.append_index(.{ + .inline_assembly = inline_assembly, + }); + try builder.appendInstruction(unit, inline_asm); + + return .{ + .value = .{ + .runtime = inline_asm, }, - .number_literal => switch (std.zig.parseNumberLiteral(unit.getExpectedTokenBytes(operand_node.token, .number_literal))) { - .int => |integer| .{ - .number_literal = integer, - }, - else => |t| @panic(@tagName(t)), - }, - .assembly_code_expression => .{ - .value = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, operand_node.left, .left), - }, - else => |t| @panic(@tagName(t)), + // TODO: WARN fix + .type = .noreturn, }; - - const index = operands.len; - operands.len += 1; - operands[index] = operand; - } - - const instruction_index = unit.assembly_instructions.append_index(.{ - .id = @intFromEnum(instruction), - .operands = operands, - }); - - const index = instructions.len; - instructions.len += 1; - instructions[index] = instruction_index; - } - - const inline_assembly = unit.inline_assembly.append_index(.{ - .instructions = instructions, - }); - - const inline_asm = unit.instructions.append_index(.{ - .inline_assembly = inline_assembly, - }); - try builder.appendInstruction(unit, inline_asm); - - return .{ - .value = .{ - .runtime = inline_asm, }, - // TODO: WARN fix - .type = .noreturn, - }; + } }, .cast => { assert(argument_node_list.len == 1); @@ -6289,7 +6299,7 @@ pub const Builder = struct { const pointer_to_global = try unit.getPointerType(.{ .type = global.declaration.type, .termination = switch (type_expect) { - .none => .none, + .none, .cast => .none, .type => |type_index| switch (unit.types.get(type_index).*) { .pointer => |pointer| pointer.termination, else => .none, @@ -6297,7 +6307,7 @@ pub const Builder = struct { else => unreachable, }, .mutability = switch (type_expect) { - .none => .@"var", + .none, .cast => .@"var", .type => |type_index| switch (unit.types.get(type_index).*) { .pointer => |pointer| pointer.mutability, else => .@"var", @@ -10034,6 +10044,73 @@ pub const Builder = struct { } }, .character_literal => return try unit.resolve_character_literal(node_index), + .negation => { + assert(node.left != .null); + assert(node.right == .null); + + const value = try builder.resolveComptimeValue(unit, context, type_expect, .{}, node.left, null, .right, &.{}, null, &.{}); + switch (value) { + .constant_int => |constant_int| switch (type_expect) { + .type => |type_index| { + assert(type_index == value.type); + const expected_type = unit.types.get(type_index); + switch (expected_type.*) { + .integer => |integer| switch (integer.kind) { + .materialized_int => { + assert(integer.signedness == .signed); + var v: i64 = @intCast(constant_int.value); + v = 0 - v; + + return .{ + .constant_int = .{ + .value = @bitCast(v), + }, + }; + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + } + }, + else => |t| @panic(@tagName(t)), + }, + .comptime_int => |ct_int| switch (type_expect) { + .type => |type_index| switch (unit.types.get(type_index).*) { + .integer => |integer| switch (integer.kind) { + .materialized_int => { + assert(integer.signedness == .signed); + var v = switch (ct_int.signedness) { + .signed => 0 - @as(i64, @intCast(ct_int.value)), + .unsigned => @as(i64, @intCast(ct_int.value)), + }; + v = 0 - v; + + return .{ + .constant_int = .{ + .value = @bitCast(v), + }, + }; + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + .none => { + return .{ + .comptime_int = .{ + .value = ct_int.value, + .signedness = switch (ct_int.signedness) { + .unsigned => .signed, + .signed => .unsigned, + }, + }, + }; + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + } + }, else => |t| @panic(@tagName(t)), } } @@ -12299,6 +12376,7 @@ pub const Builder = struct { .negation => block: { assert(node.left != .null); assert(node.right == .null); + const value = try builder.resolveRuntimeValue(unit, context, type_expect, node.left, .right); switch (value.value) { @@ -18095,6 +18173,51 @@ pub const InlineAssembly = struct { rdi, }; }; + + pub const aarch64 = struct { + pub const Instruction = enum { + b, + mov, + }; + + pub const Register = enum { + fp, + lr, + sp, + x0, + x1, + x2, + x3, + x4, + x5, + x6, + x7, + x8, + x9, + x10, + x11, + x12, + x13, + x14, + x15, + x16, + x17, + x18, + x19, + x20, + x21, + x22, + x23, + x24, + x25, + x26, + x27, + x28, + x29, + x30, + x31, + }; + }; }; const LogKind = enum { diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index b768e43..fbed358 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -2674,12 +2674,72 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo _ = assembly_statements.pop(); } - assembly_statements.appendSliceAssumeCapacity("\n\t"); + assembly_statements.appendSliceAssumeCapacity("\n"); } + _ = assembly_statements.pop(); + // try constraints.append_slice(context.allocator, ",~{dirflag},~{fpsr},~{flags}"); }, - else => |t| @panic(@tagName(t)), + .aarch64 => { + for (assembly_block.instructions) |assembly_instruction_index| { + const instruction = unit.assembly_instructions.get(assembly_instruction_index); + const instruction_id: Compilation.InlineAssembly.aarch64.Instruction = @enumFromInt(instruction.id); + assembly_statements.appendSliceAssumeCapacity(@tagName(instruction_id)); + assembly_statements.appendAssumeCapacity(' '); + + if (instruction.operands.len > 0) { + for (instruction.operands) |operand| { + switch (operand) { + .register => |register_value| { + const register: Compilation.InlineAssembly.aarch64.Register = @enumFromInt(register_value); + assembly_statements.appendSliceAssumeCapacity(@tagName(register)); + }, + .number_literal => |literal| { + var buffer: [65]u8 = undefined; + const number_literal = format_int(&buffer, literal, 10, false); + const slice_ptr = number_literal.ptr - 1; + const literal_slice = slice_ptr[0 .. number_literal.len + 1]; + literal_slice[0] = '#'; + assembly_statements.appendSliceAssumeCapacity(try context.arena.duplicate_bytes(literal_slice)); + }, + .value => |sema_value| { + if (llvm.llvm_value_map.get(sema_value)) |v| { + _ = v; // autofix + unreachable; + } else { + const value = try llvm.emitLeftValue(unit, context, sema_value); + var buffer: [65]u8 = undefined; + const operand_number = format_int(&buffer, operand_values.len, 16, false); + const slice_ptr = operand_number.ptr - 2; + const operand_slice = slice_ptr[0 .. operand_number.len + 2]; + operand_slice[0] = '$'; + operand_slice[1] = '{'; + var new_buffer: [65]u8 = undefined; + @memcpy(new_buffer[0..operand_slice.len], operand_slice); + new_buffer[operand_slice.len] = '}'; + const new_slice = try context.arena.duplicate_bytes(new_buffer[0 .. operand_slice.len + 1]); + assembly_statements.appendSliceAssumeCapacity(new_slice); + operand_values.appendAssumeCapacity(value); + const value_type = value.getType(); + operand_types.appendAssumeCapacity(value_type); + constraints.appendAssumeCapacity('X'); + } + }, + } + + assembly_statements.appendSliceAssumeCapacity(", "); + } + + _ = assembly_statements.pop(); + _ = assembly_statements.pop(); + } + + assembly_statements.appendSliceAssumeCapacity("\n"); + } + + _ = assembly_statements.pop(); + }, } const is_var_args = false; @@ -2908,31 +2968,41 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const function_type = LLVM.Context.getFunctionType(return_type, syscall_argument_types.ptr, syscall_argument_types.len, is_var_args) orelse unreachable; var constraints = BoundedArray(u8, 4096){}; - const inline_asm = switch (unit.descriptor.arch) { - .x86_64 => blk: { - constraints.appendSliceAssumeCapacity("={rax}"); - - const syscall_registers = [7][]const u8{ "rax", "rdi", "rsi", "rdx", "r10", "r8", "r9" }; - for (syscall_registers[0..syscall_argument_count]) |syscall_register| { - constraints.appendAssumeCapacity(','); - constraints.appendAssumeCapacity('{'); - constraints.appendSliceAssumeCapacity(syscall_register); - constraints.appendAssumeCapacity('}'); - } - - constraints.appendSliceAssumeCapacity(",~{rcx},~{r11},~{memory}"); - - const assembly = "syscall"; - const has_side_effects = true; - const is_align_stack = true; - const can_throw = false; - const inline_assembly = LLVM.Value.InlineAssembly.get(function_type, assembly, assembly.len, &constraints.buffer, constraints.len, has_side_effects, is_align_stack, LLVM.Value.InlineAssembly.Dialect.@"at&t", can_throw) orelse return LLVM.Value.Error.inline_assembly; - break :blk inline_assembly; - }, - else => |t| @panic(@tagName(t)), + const syscall_registers: [7][]const u8 = switch (unit.descriptor.arch) { + .x86_64 => .{ "rax", "rdi", "rsi", "rdx", "r10", "r8", "r9" }, + .aarch64 => .{ "x8", "x0", "x1", "x2", "x3", "x4", "x5" }, + }; + const syscall_instruction: []const u8 = switch (unit.descriptor.arch) { + .x86_64 => "syscall", + .aarch64 => "svc #0", + }; + const return_constraints: []const u8 = switch (unit.descriptor.arch) { + .x86_64 => "={rax}", + .aarch64 => "={x0}", }; - const call_to_asm = llvm.builder.createCall(function_type, inline_asm.toValue(), syscall_arguments.ptr, syscall_arguments.len, "syscall", "syscall".len, null) orelse return LLVM.Value.Instruction.Error.call; + const clobber_constraints: []const u8 = switch (unit.descriptor.arch) { + .x86_64 => ",~{rcx},~{r11},~{memory}", + .aarch64 => ",~{memory},~{cc}", + }; + + constraints.appendSliceAssumeCapacity(return_constraints); + + for (syscall_registers[0..syscall_argument_count]) |syscall_register| { + constraints.appendAssumeCapacity(','); + constraints.appendAssumeCapacity('{'); + constraints.appendSliceAssumeCapacity(syscall_register); + constraints.appendAssumeCapacity('}'); + } + constraints.appendSliceAssumeCapacity(clobber_constraints); + + const has_side_effects = true; + const is_align_stack = true; + const can_throw = false; + const inline_assembly = LLVM.Value.InlineAssembly.get(function_type, syscall_instruction.ptr, syscall_instruction.len, &constraints.buffer, constraints.len, has_side_effects, is_align_stack, LLVM.Value.InlineAssembly.Dialect.@"at&t", can_throw) orelse return LLVM.Value.Error.inline_assembly; + + const call_to_asm = llvm.builder.createCall(function_type, inline_assembly.toValue(), syscall_arguments.ptr, syscall_arguments.len, "syscall", "syscall".len, null) orelse return LLVM.Value.Instruction.Error.call; + try llvm.llvm_instruction_map.put_no_clobber(instruction_index, call_to_asm.toValue()); }, .@"unreachable" => { @@ -3298,7 +3368,10 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo // TODO: proper target selection const target_triple = switch (unit.descriptor.os) { - .linux => "x86_64-linux-none", + .linux => switch (unit.descriptor.arch) { + .aarch64 => "aarch64-linux-none", + .x86_64 => "x86_64-linux-none", + }, .macos => "aarch64-apple-macosx-none", .windows => "x86_64-windows-gnu", }; diff --git a/bootstrap/frontend/lexer.zig b/bootstrap/frontend/lexer.zig index be805d2..8e6f54e 100644 --- a/bootstrap/frontend/lexer.zig +++ b/bootstrap/frontend/lexer.zig @@ -6,6 +6,7 @@ const log = std.log; const library = @import("../library.zig"); const byte_equal = library.byte_equal; const enumFromString = library.enumFromString; +const exit_with_error = library.exit_with_error; const MyAllocator = library.MyAllocator; const PinnedArray = library.PinnedArray; @@ -505,7 +506,7 @@ pub fn analyze(text: []const u8, token_buffer: *Token.Buffer) !Result { 'A'...'Z', 'a'...'z' => { while (true) { switch (text[index]) { - 'A'...'Z', 'a'...'z' => index += 1, + 'A'...'Z', 'a'...'z', '0'...'9' => index += 1, else => break, } } @@ -559,12 +560,13 @@ pub fn analyze(text: []const u8, token_buffer: *Token.Buffer) !Result { hex, bin, octal, + decimal, }; const representation: Representation = switch (text[index]) { 'x' => .hex, - else => unreachable, + else => .decimal, }; - index += 1; + index += @intFromBool(representation != .decimal); switch (representation) { .hex => { while (true) { @@ -574,6 +576,20 @@ pub fn analyze(text: []const u8, token_buffer: *Token.Buffer) !Result { } } + _ = token_buffer.tokens.append(.{ + .id = .number_literal, + .line = line_index, + .offset = start_i, + .length = index - start_i, + }); + }, + .decimal => { + switch (text[index]) { + '0'...'9' => unreachable, + else => {}, + } + + _ = token_buffer.tokens.append(.{ .id = .number_literal, .line = line_index, @@ -590,9 +606,16 @@ pub fn analyze(text: []const u8, token_buffer: *Token.Buffer) !Result { const ch_fmt = library.format_int(&ch_array, start_ch, 16, false); try Compilation.write(.panic, ch_fmt); try Compilation.write(.panic, "\n"); - std.posix.exit(0); + exit_with_error(); }, } + + const debug_asm_tokens = false; + if (debug_asm_tokens) { + try Compilation.write(.panic, "ASM token: '"); + try Compilation.write(.panic, text[start_i..index]); + try Compilation.write(.panic, "'\n"); + } } _ = token_buffer.tokens.append(.{ diff --git a/bootstrap/library.zig b/bootstrap/library.zig index f3b79fb..449c12f 100644 --- a/bootstrap/library.zig +++ b/bootstrap/library.zig @@ -239,7 +239,7 @@ pub fn PinnedArrayAdvanced(comptime T: type, comptime MaybeIndex: ?type) type { array.ensure_capacity(1); const src = array.slice()[index..]; array.length += 1; - const dst = array.slice()[index + 1..]; + const dst = array.slice()[index + 1 ..]; copy_backwards(T, dst, src); array.slice()[index] = item; } @@ -707,3 +707,8 @@ pub fn align_forward(value: u64, alignment: u64) u64 { const mask = alignment - 1; return (value + mask) & ~mask; } + +pub fn exit_with_error() noreturn { + @breakpoint(); + std.posix.exit(1); +} diff --git a/bootstrap/linker/lld.zig b/bootstrap/linker/lld.zig index 3bcfd9b..4354bdf 100644 --- a/bootstrap/linker/lld.zig +++ b/bootstrap/linker/lld.zig @@ -20,6 +20,17 @@ pub fn link(context: *const Compilation.Context, options: linker.Options) !void _ = argv.append(driver_program); _ = argv.append("--error-limit=0"); + switch (@import("builtin").cpu.arch) { + .aarch64 => switch (@import("builtin").os.tag) { + .linux => { + _ = argv.append("-znow"); + _ = argv.append_slice(&.{ "-m", "aarch64linux"}); + }, + else => {}, + }, + else => {}, + } + // const output_path = out_path orelse "a.out"; _ = argv.append("-o"); _ = argv.append(options.output_file_path); @@ -78,14 +89,30 @@ pub fn link(context: *const Compilation.Context, options: linker.Options) !void } else { if (options.link_libcpp) { assert(options.link_libc); - _ = argv.append("/usr/lib/libstdc++.so"); + switch (@import("builtin").cpu.arch) { + .x86_64 => _ = argv.append("/usr/lib/libstdc++.so"), + .aarch64 => _ = argv.append("/usr/lib64/libstdc++.so.6"), + else => unreachable, + } } if (options.link_libc) { - _ = argv.append("/usr/lib/crt1.o"); - _ = argv.append("/usr/lib/crti.o"); - argv.append_slice(&.{ "-L", "/usr/lib" }); - argv.append_slice(&.{ "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2" }); + switch (@import("builtin").cpu.arch) { + .x86_64 => { + _ = argv.append("/usr/lib/crt1.o"); + _ = argv.append("/usr/lib/crti.o"); + argv.append_slice(&.{ "-L", "/usr/lib" }); + argv.append_slice(&.{ "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2" }); + }, + .aarch64 => { + _ = argv.append("/usr/lib64/crt1.o"); + _ = argv.append("/usr/lib64/crti.o"); + argv.append_slice(&.{ "-L", "/usr/lib64" }); + argv.append_slice(&.{ "-dynamic-linker", "/lib/ld-linux-aarch64.so.1" }); + }, + else => unreachable, + } + _ = argv.append("--as-needed"); _ = argv.append("-lm"); _ = argv.append("-lpthread"); @@ -93,7 +120,16 @@ pub fn link(context: *const Compilation.Context, options: linker.Options) !void _ = argv.append("-ldl"); _ = argv.append("-lrt"); _ = argv.append("-lutil"); - _ = argv.append("/usr/lib/crtn.o"); + + switch (@import("builtin").cpu.arch) { + .x86_64 => { + _ = argv.append("/usr/lib/crtn.o"); + }, + .aarch64 => { + _ = argv.append("/usr/lib64/crtn.o"); + }, + else => unreachable, + } } } }, diff --git a/build.zig b/build.zig index bbf3dc3..b3ce438 100644 --- a/build.zig +++ b/build.zig @@ -446,7 +446,7 @@ pub fn build(b: *std.Build) !void { const result = try std.ChildProcess.run(.{ .allocator = b.allocator, .argv = &.{ - "cc", "--version", + "c++", "--version", }, .max_output_bytes = 0xffffffffffffff, }); @@ -470,11 +470,27 @@ pub fn build(b: *std.Build) !void { unreachable; }; - const cxx_include_base = try std.mem.concat(b.allocator, u8, &.{ "/usr/include/c++/", cxx_version }); - compiler.addObjectFile(.{ .cwd_relative = "/usr/lib/libstdc++.so" }); + const cxx_include_base = switch (@import("builtin").cpu.arch) { + .x86_64 => try std.mem.concat(b.allocator, u8, &.{ "/usr/include/c++/", cxx_version }), + .aarch64 => "/usr/include/c++/13", + else => @compileError("Arch not supported"), + }; + compiler.addObjectFile(.{ + .cwd_relative = switch (@import("builtin").cpu.arch) { + .aarch64 => "/lib64/libstdc++.so.6", + .x86_64 => "/usr/lib/libstdc++.so", + else => @compileError("Arch not supported"), + }, + }); compiler.addIncludePath(.{ .cwd_relative = "/usr/include" }); compiler.addIncludePath(.{ .cwd_relative = cxx_include_base }); - compiler.addIncludePath(.{ .cwd_relative = try std.mem.concat(b.allocator, u8, &.{ cxx_include_base, "/x86_64-pc-linux-gnu" }) }); + compiler.addIncludePath(.{ + .cwd_relative = try std.mem.concat(b.allocator, u8, &.{ cxx_include_base, switch (@import("builtin").cpu.arch) { + .x86_64 => "/x86_64-pc-linux-gnu", + .aarch64 => "/aarch64-redhat-linux", + else => @compileError("Arch not supported"), + } }), + }); compiler.addLibraryPath(.{ .cwd_relative = "/usr/lib" }); } }, diff --git a/build/test_runner.zig b/build/test_runner.zig index 3825a8e..6a52ddb 100644 --- a/build/test_runner.zig +++ b/build/test_runner.zig @@ -131,7 +131,7 @@ fn runBuildTests(allocator: Allocator, args: struct { const test_dir_path = "test/build"; 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, args.compiler_path); + try std.posix.chdir(test_dir_realpath); const total_compilation_count = test_names.len; @@ -142,16 +142,23 @@ fn runBuildTests(allocator: Allocator, args: struct { var failed_test_count: usize = 0; const total_test_count = test_names.len; + errdefer { + std.posix.chdir(previous_cwd) catch unreachable; + } + for (test_names) |test_name| { std.debug.print("{s}... ", .{test_name}); try std.posix.chdir(test_name); - const compile_run = try std.ChildProcess.run(.{ + const compile_run = std.ChildProcess.run(.{ .allocator = allocator, - // TODO: delete -main_source_file? - .argv = &.{ compiler_realpath, "build" }, + .argv = &.{ args.compiler_path, "build" }, .max_output_bytes = std.math.maxInt(u64), - }); + }) catch |err| { + const compilation_success = false; + std.debug.print("[COMPILATION {s}] ", .{if (compilation_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"}); + return err; + }; ran_compilation_count += 1; @@ -177,12 +184,20 @@ fn runBuildTests(allocator: Allocator, args: struct { if (compilation_success and !args.self_hosted) { const test_path = try std.mem.concat(allocator, u8, &.{ "nat/", test_name }); - const test_run = try std.ChildProcess.run(.{ + const test_run = std.ChildProcess.run(.{ .allocator = allocator, // TODO: delete -main_source_file? .argv = &.{test_path}, .max_output_bytes = std.math.maxInt(u64), - }); + }) catch |err| { + const test_success = false; + std.debug.print("[TEST {s}]\n", .{if (test_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"}); + std.debug.print("{}\n", .{err}); + if (@errorReturnTrace()) |error_trace| { + std.debug.dumpStackTrace(error_trace.*); + } + return err; + }; ran_test_count += 1; const test_result: TestError!bool = switch (test_run.term) { .Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code, @@ -213,6 +228,9 @@ fn runBuildTests(allocator: Allocator, args: struct { std.debug.print("TOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{ total_test_count, ran_test_count, failed_test_count }); try std.posix.chdir(previous_cwd); + const current_cwd = try std.fs.cwd().realpathAlloc(allocator, "."); + std.debug.assert(std.mem.eql(u8, current_cwd, previous_cwd)); + std.debug.print("Hello \n", .{}); if (failed_compilation_count > 0 or failed_test_count > 0) { return error.fail; @@ -226,11 +244,21 @@ fn runStdTests(allocator: Allocator, args: struct { var errors = false; std.debug.print("std... ", .{}); - const result = try std.ChildProcess.run(.{ + std.debug.print("CWD: {s}\n", .{std.fs.cwd().realpathAlloc(allocator, ".") catch unreachable}); + + const argv = &.{ args.compiler_path, "test", "-main_source_file", "lib/std/std.nat", "-name", "std" }; + + const result = std.ChildProcess.run(.{ .allocator = allocator, - .argv = &.{ args.compiler_path, "test", "-main_source_file", "lib/std/std.nat", "-name", "std" }, + .argv = argv, .max_output_bytes = std.math.maxInt(u64), - }); + }) catch |err| { + std.debug.print("Error: {}", .{err}); + if (@errorReturnTrace()) |error_trace| { + std.debug.dumpStackTrace(error_trace.*); + } + return err; + }; const compilation_result: TestError!bool = switch (result.term) { .Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code, .Signal => error.signaled, @@ -290,7 +318,6 @@ fn runCmakeTests(allocator: Allocator, args: struct { const cc_dir = try std.fs.cwd().openDir(args.dir_path, .{ .iterate = true, }); - const compiler_realpath = try std.fs.cwd().realpathAlloc(allocator, args.compiler_path); const cc_dir_path = try cc_dir.realpathAlloc(allocator, "."); try std.posix.chdir(cc_dir_path); @@ -316,9 +343,9 @@ fn runCmakeTests(allocator: Allocator, args: struct { "-G", "Ninja", // "-DCMAKE_VERBOSE_MAKEFILE=On", - try std.mem.concat(allocator, u8, &.{ "-DCMAKE_C_COMPILER=", compiler_realpath, ";cc" }), - try std.mem.concat(allocator, u8, &.{ "-DCMAKE_CXX_COMPILER=", compiler_realpath, ";c++" }), - try std.mem.concat(allocator, u8, &.{ "-DCMAKE_ASM_COMPILER=", compiler_realpath, ";cc" }), + try std.mem.concat(allocator, u8, &.{ "-DCMAKE_C_COMPILER=", args.compiler_path, ";cc" }), + try std.mem.concat(allocator, u8, &.{ "-DCMAKE_CXX_COMPILER=", args.compiler_path, ";c++" }), + try std.mem.concat(allocator, u8, &.{ "-DCMAKE_ASM_COMPILER=", args.compiler_path, ";cc" }), }, .max_output_bytes = std.math.maxInt(u64), }); @@ -474,19 +501,21 @@ fn run_test_suite(allocator: Allocator, args: struct { std.debug.print("TESTING {s} COMPILER: {s}...\n=================\n", .{ if (self_hosted) "SELF-HOSTED" else "BOOTSTRAP", args.compiler_path }); var errors = false; + const compiler_path = std.fs.cwd().realpathAlloc(allocator, args.compiler_path) catch unreachable; + runStandalone(allocator, .{ .directory_path = "test/standalone", .group_name = "STANDALONE", .is_test = false, .self_hosted = self_hosted, - .compiler_path = args.compiler_path, + .compiler_path = compiler_path, }) catch { errors = true; }; runBuildTests(allocator, .{ .self_hosted = self_hosted, - .compiler_path = args.compiler_path, + .compiler_path = compiler_path, }) catch { errors = true; }; @@ -496,14 +525,14 @@ fn run_test_suite(allocator: Allocator, args: struct { .group_name = "TEST EXECUTABLE", .is_test = true, .self_hosted = self_hosted, - .compiler_path = args.compiler_path, + .compiler_path = compiler_path, }) catch { errors = true; }; runStdTests(allocator, .{ .self_hosted = self_hosted, - .compiler_path = args.compiler_path, + .compiler_path = compiler_path, }) catch { errors = true; }; @@ -511,37 +540,41 @@ fn run_test_suite(allocator: Allocator, args: struct { if (!self_hosted) { runCmakeTests(allocator, .{ .dir_path = "test/cc", - .compiler_path = args.compiler_path, + .compiler_path = compiler_path, }) catch { errors = true; }; runCmakeTests(allocator, .{ .dir_path = "test/c++", - .compiler_path = args.compiler_path, + .compiler_path = compiler_path, }) catch { errors = true; }; - switch (@import("builtin").os.tag) { - .macos => runCmakeTests(allocator, .{ - .dir_path = "test/cc_macos", - .compiler_path = args.compiler_path, - }) catch { - errors = true; - }, - .windows => {}, - .linux => switch (@import("builtin").abi) { - .gnu => runCmakeTests(allocator, .{ - .dir_path = "test/cc_linux", - .compiler_path = args.compiler_path, + switch (@import("builtin").cpu.arch) { + .aarch64 => switch (@import("builtin").os.tag) { + .linux => runCmakeTests(allocator, .{ + .dir_path = "test/cc_aarch64_linux", + .compiler_path = compiler_path, }) catch { errors = true; }, - .musl => {}, - else => @compileError("ABI not supported"), + .macos => runCmakeTests(allocator, .{ + .dir_path = "test/cc_aarch64_macos", + .compiler_path = compiler_path, + }) catch { + errors = true; + }, + else => unreachable, }, - else => @compileError("OS not supported"), + .x86_64 => runCmakeTests(allocator, .{ + .dir_path = "test/cc_x86_64", + .compiler_path = compiler_path, + }) catch { + errors = true; + }, + else => @compileError("Arch not supported"), } } diff --git a/lib/std/os.nat b/lib/std/os.nat index 723a92a..953f665 100644 --- a/lib/std/os.nat +++ b/lib/std/os.nat @@ -24,8 +24,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 => system.exit(exit_code), + .linux, .macos => system.exit(exit_code), .windows => windows.ExitProcess(#cast(exit_code)), } } @@ -323,6 +322,7 @@ const duplicate_process = fn () DuplicateProcessError!Process.Id { const ExecveError = error{ execve_failed, }; + 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 => { diff --git a/lib/std/os/linux.nat b/lib/std/os/linux.nat index 9dcd826..9f2ad2a 100644 --- a/lib/std/os/linux.nat +++ b/lib/std/os/linux.nat @@ -3,14 +3,333 @@ const std = #import("std"); const stdin: FileDescriptor = 0; const stdout: FileDescriptor = 1; const stderr: FileDescriptor = 2; +const cpu = #import("builtin").cpu; -const Syscall = switch (#import("builtin").cpu) { +const Syscall = switch (cpu) { .x86_64 => Syscall_x86_64, + .aarch64 => Syscall_aarch64, else => #error("Architecture not supported"), }; +const current_working_directory_file_descriptor: ssize = -100; + const ProcessId = s32; +const Syscall_aarch64 = enum(usize) { + io_setup = 0, + io_destroy = 1, + io_submit = 2, + io_cancel = 3, + io_getevents = 4, + setxattr = 5, + lsetxattr = 6, + fsetxattr = 7, + getxattr = 8, + lgetxattr = 9, + fgetxattr = 10, + listxattr = 11, + llistxattr = 12, + flistxattr = 13, + removexattr = 14, + lremovexattr = 15, + fremovexattr = 16, + getcwd = 17, + lookup_dcookie = 18, + eventfd2 = 19, + epoll_create1 = 20, + epoll_ctl = 21, + epoll_pwait = 22, + dup = 23, + dup3 = 24, + fcntl = 25, + inotify_init1 = 26, + inotify_add_watch = 27, + inotify_rm_watch = 28, + ioctl = 29, + ioprio_set = 30, + ioprio_get = 31, + flock = 32, + mknodat = 33, + mkdirat = 34, + unlinkat = 35, + symlinkat = 36, + linkat = 37, + renameat = 38, + umount2 = 39, + mount = 40, + pivot_root = 41, + nfsservctl = 42, + statfs = 43, + fstatfs = 44, + truncate = 45, + ftruncate = 46, + fallocate = 47, + faccessat = 48, + chdir = 49, + fchdir = 50, + chroot = 51, + fchmod = 52, + fchmodat = 53, + fchownat = 54, + fchown = 55, + openat = 56, + close = 57, + vhangup = 58, + pipe2 = 59, + quotactl = 60, + getdents64 = 61, + lseek = 62, + read = 63, + write = 64, + readv = 65, + writev = 66, + pread64 = 67, + pwrite64 = 68, + preadv = 69, + pwritev = 70, + sendfile = 71, + pselect6 = 72, + ppoll = 73, + signalfd4 = 74, + vmsplice = 75, + splice = 76, + tee = 77, + readlinkat = 78, + fstatat = 79, + fstat = 80, + sync = 81, + fsync = 82, + fdatasync = 83, + sync_file_range = 84, + timerfd_create = 85, + timerfd_settime = 86, + timerfd_gettime = 87, + utimensat = 88, + acct = 89, + capget = 90, + capset = 91, + personality = 92, + exit = 93, + exit_group = 94, + waitid = 95, + set_tid_address = 96, + unshare = 97, + futex = 98, + set_robust_list = 99, + get_robust_list = 100, + nanosleep = 101, + getitimer = 102, + setitimer = 103, + kexec_load = 104, + init_module = 105, + delete_module = 106, + timer_create = 107, + timer_gettime = 108, + timer_getoverrun = 109, + timer_settime = 110, + timer_delete = 111, + clock_settime = 112, + clock_gettime = 113, + clock_getres = 114, + clock_nanosleep = 115, + syslog = 116, + ptrace = 117, + sched_setparam = 118, + sched_setscheduler = 119, + sched_getscheduler = 120, + sched_getparam = 121, + sched_setaffinity = 122, + sched_getaffinity = 123, + sched_yield = 124, + sched_get_priority_max = 125, + sched_get_priority_min = 126, + sched_rr_get_interval = 127, + restart_syscall = 128, + kill = 129, + tkill = 130, + tgkill = 131, + sigaltstack = 132, + rt_sigsuspend = 133, + rt_sigaction = 134, + rt_sigprocmask = 135, + rt_sigpending = 136, + rt_sigtimedwait = 137, + rt_sigqueueinfo = 138, + rt_sigreturn = 139, + setpriority = 140, + getpriority = 141, + reboot = 142, + setregid = 143, + setgid = 144, + setreuid = 145, + setuid = 146, + setresuid = 147, + getresuid = 148, + setresgid = 149, + getresgid = 150, + setfsuid = 151, + setfsgid = 152, + times = 153, + setpgid = 154, + getpgid = 155, + getsid = 156, + setsid = 157, + getgroups = 158, + setgroups = 159, + uname = 160, + sethostname = 161, + setdomainname = 162, + getrlimit = 163, + setrlimit = 164, + getrusage = 165, + umask = 166, + prctl = 167, + getcpu = 168, + gettimeofday = 169, + settimeofday = 170, + adjtimex = 171, + getpid = 172, + getppid = 173, + getuid = 174, + geteuid = 175, + getgid = 176, + getegid = 177, + gettid = 178, + sysinfo = 179, + mq_open = 180, + mq_unlink = 181, + mq_timedsend = 182, + mq_timedreceive = 183, + mq_notify = 184, + mq_getsetattr = 185, + msgget = 186, + msgctl = 187, + msgrcv = 188, + msgsnd = 189, + semget = 190, + semctl = 191, + semtimedop = 192, + semop = 193, + shmget = 194, + shmctl = 195, + shmat = 196, + shmdt = 197, + socket = 198, + socketpair = 199, + bind = 200, + listen = 201, + accept = 202, + connect = 203, + getsockname = 204, + getpeername = 205, + sendto = 206, + recvfrom = 207, + setsockopt = 208, + getsockopt = 209, + shutdown = 210, + sendmsg = 211, + recvmsg = 212, + readahead = 213, + brk = 214, + munmap = 215, + mremap = 216, + add_key = 217, + request_key = 218, + keyctl = 219, + clone = 220, + execve = 221, + mmap = 222, + fadvise64 = 223, + swapon = 224, + swapoff = 225, + mprotect = 226, + msync = 227, + mlock = 228, + munlock = 229, + mlockall = 230, + munlockall = 231, + mincore = 232, + madvise = 233, + remap_file_pages = 234, + mbind = 235, + get_mempolicy = 236, + set_mempolicy = 237, + migrate_pages = 238, + move_pages = 239, + rt_tgsigqueueinfo = 240, + perf_event_open = 241, + accept4 = 242, + recvmmsg = 243, + wait4 = 260, + prlimit64 = 261, + fanotify_init = 262, + fanotify_mark = 263, + name_to_handle_at = 264, + open_by_handle_at = 265, + clock_adjtime = 266, + syncfs = 267, + setns = 268, + sendmmsg = 269, + process_vm_readv = 270, + process_vm_writev = 271, + kcmp = 272, + finit_module = 273, + sched_setattr = 274, + sched_getattr = 275, + renameat2 = 276, + seccomp = 277, + getrandom = 278, + memfd_create = 279, + bpf = 280, + execveat = 281, + userfaultfd = 282, + membarrier = 283, + mlock2 = 284, + copy_file_range = 285, + preadv2 = 286, + pwritev2 = 287, + pkey_mprotect = 288, + pkey_alloc = 289, + pkey_free = 290, + statx = 291, + io_pgetevents = 292, + rseq = 293, + kexec_file_load = 294, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + memfd_secret = 447, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, + cachestat = 451, + fchmodat2 = 452, + map_shadow_stack = 453, + futex_wake = 454, + futex_wait = 455, + futex_requeue = 456, +}; + const Syscall_x86_64 = enum(usize) { read = 0, write = 1, @@ -865,6 +1184,11 @@ const get_map_flags = fn(flags: std.os.MapFlags) MapFlags{ }; } +const exit = fn (exit_code: s32) noreturn { + _ = #syscall(#cast(Syscall.exit), #cast(exit_code)); + unreachable; +} + const mmap = fn(address: ?[&]u8, length: usize, protection_flags: ProtectionFlags, map_flags: MapFlags, fd: FileDescriptor, offset: u64) usize { const flat_protection_flags: u32 = #cast(protection_flags); const flat_map_flags: u32 = #cast(map_flags); @@ -884,13 +1208,70 @@ const munmap = fn(bytes: []const u8) usize { } const readlink = fn(file_path: [&:0]const u8, bytes: []u8) usize { - const result = #syscall(#cast(Syscall.readlink), #cast(file_path), #cast(bytes.pointer), bytes.length); - return result; + switch (cpu) { + .x86_64 => { + const result = #syscall(#cast(Syscall.readlink), #cast(file_path), #cast(bytes.pointer), bytes.length); + return result; + }, + .aarch64 => { + const result = #syscall(#cast(Syscall.readlinkat), #cast(current_working_directory_file_descriptor), #cast(file_path), #cast(bytes.pointer), bytes.length); + return result; + }, + } } +const SIG = struct { + const BLOCK = 0; + const UNBLOCK = 1; + const SETMASK = 2; + + const HUP = 1; + const INT = 2; + const QUIT = 3; + const ILL = 4; + const TRAP = 5; + const ABRT = 6; + const IOT = ABRT; + const BUS = 7; + const FPE = 8; + const KILL = 9; + const USR1 = 10; + const SEGV = 11; + const USR2 = 12; + const PIPE = 13; + const ALRM = 14; + const TERM = 15; + const STKFLT = 16; + const CHLD = 17; + const CONT = 18; + const STOP = 19; + const TSTP = 20; + const TTIN = 21; + const TTOU = 22; + const URG = 23; + const XCPU = 24; + const XFSZ = 25; + const VTALRM = 26; + const PROF = 27; + const WINCH = 28; + const IO = 29; + const POLL = 29; + const PWR = 30; + const SYS = 31; +}; + + const fork = fn() usize { - const result = #syscall(#cast(Syscall.fork)); - return result; + switch (cpu) { + .x86_64 => { + const result = #syscall(#cast(Syscall.fork)); + return result; + }, + .aarch64 => { + const result = #syscall(#cast(Syscall.clone), SIG.CHLD, 0); + return result; + }, + } } const execve = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: [&:null]const ?[&:null]const u8) usize { @@ -910,8 +1291,16 @@ const dup2 = fn(old: FileDescriptor, new: FileDescriptor) usize { const open = fn(path: [&:0]const u8, flags: OpenFlags, permissions: u32) usize { const flattened_flags: u32 = #cast(flags); - const result = #syscall(#cast(Syscall.open), #cast(path), flattened_flags, permissions); - return result; + switch (cpu) { + .x86_64 => { + const result = #syscall(#cast(Syscall.open), #cast(path), flattened_flags, permissions); + return result; + }, + .aarch64 => { + const result = #syscall(#cast(Syscall.openat), #cast(current_working_directory_file_descriptor), #cast(path), flattened_flags, permissions); + return result; + }, + } } const openat = fn(directory_file_descriptor: FileDescriptor, path: [&:0]const u8, flags: u32, permissions: u32) usize { diff --git a/lib/std/start.nat b/lib/std/start.nat index 300c845..6dc9455 100644 --- a/lib/std/start.nat +++ b/lib/std/start.nat @@ -9,12 +9,21 @@ comptime { } const _start = fn naked cc(.c) () noreturn { - #asm(` - xor ebp, ebp; - mov rdi, rsp; - and rsp, 0xfffffffffffffff0; - call {start}; - `); + switch (builtin.cpu) { + .x86_64 => #asm(` + xor ebp, ebp; + mov rdi, rsp; + and rsp, 0xfffffffffffffff0; + call {start}; + `), + .aarch64 => #asm(` + mov fp, 0; + mov lr, 0; + mov x0, sp; + b {start}; + `), + else => #error("Architecture not supported"), + } } var argument_count: usize = 0; diff --git a/test/cc_linux/c_asm/.gitignore b/test/cc_aarch64_linux/c_asm/.gitignore similarity index 100% rename from test/cc_linux/c_asm/.gitignore rename to test/cc_aarch64_linux/c_asm/.gitignore diff --git a/test/cc_linux/c_asm/CMakeLists.txt b/test/cc_aarch64_linux/c_asm/CMakeLists.txt similarity index 100% rename from test/cc_linux/c_asm/CMakeLists.txt rename to test/cc_aarch64_linux/c_asm/CMakeLists.txt diff --git a/test/cc_aarch64_linux/c_asm/assembly.S b/test/cc_aarch64_linux/c_asm/assembly.S new file mode 100644 index 0000000..f6febae --- /dev/null +++ b/test/cc_aarch64_linux/c_asm/assembly.S @@ -0,0 +1,4 @@ +.global foo +foo: + mov w0, #42 + ret diff --git a/test/cc_linux/c_asm/main.c b/test/cc_aarch64_linux/c_asm/main.c similarity index 100% rename from test/cc_linux/c_asm/main.c rename to test/cc_aarch64_linux/c_asm/main.c diff --git a/test/cc_macos/c_asm/.gitignore b/test/cc_aarch64_macos/c_asm/.gitignore similarity index 100% rename from test/cc_macos/c_asm/.gitignore rename to test/cc_aarch64_macos/c_asm/.gitignore diff --git a/test/cc_macos/c_asm/CMakeLists.txt b/test/cc_aarch64_macos/c_asm/CMakeLists.txt similarity index 100% rename from test/cc_macos/c_asm/CMakeLists.txt rename to test/cc_aarch64_macos/c_asm/CMakeLists.txt diff --git a/test/cc_macos/c_asm/assembly.S b/test/cc_aarch64_macos/c_asm/assembly.S similarity index 100% rename from test/cc_macos/c_asm/assembly.S rename to test/cc_aarch64_macos/c_asm/assembly.S diff --git a/test/cc_macos/c_asm/main.c b/test/cc_aarch64_macos/c_asm/main.c similarity index 100% rename from test/cc_macos/c_asm/main.c rename to test/cc_aarch64_macos/c_asm/main.c diff --git a/test/cc_x86_64/c_asm/.gitignore b/test/cc_x86_64/c_asm/.gitignore new file mode 100644 index 0000000..8326e08 --- /dev/null +++ b/test/cc_x86_64/c_asm/.gitignore @@ -0,0 +1,2 @@ +*.o +build/ diff --git a/test/cc_x86_64/c_asm/CMakeLists.txt b/test/cc_x86_64/c_asm/CMakeLists.txt new file mode 100644 index 0000000..5294827 --- /dev/null +++ b/test/cc_x86_64/c_asm/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.15) +project(c_asm C ASM) +add_executable(c_asm main.c assembly.S) diff --git a/test/cc_linux/c_asm/assembly.S b/test/cc_x86_64/c_asm/assembly.S similarity index 100% rename from test/cc_linux/c_asm/assembly.S rename to test/cc_x86_64/c_asm/assembly.S diff --git a/test/cc_x86_64/c_asm/main.c b/test/cc_x86_64/c_asm/main.c new file mode 100644 index 0000000..cfa19e8 --- /dev/null +++ b/test/cc_x86_64/c_asm/main.c @@ -0,0 +1,7 @@ +extern int foo(); +#include +int main() +{ + assert(foo() == 42); + return 0; +}