commit e84cda0ebf8886346d42db78e8f3eb8d0bf515bd (tree)
parent 8f5c333ebe6631629be3b88d797a03b1d677d439
Author: Andrew Kelley <andrew@ziglang.org>
Date: Mon, 31 Jul 2023 15:34:32 -0700
Merge pull request #16622 from jacobly0/cbe-asm-compat
CBE: fix regressions and get new targets passing behavior tests
Diffstat:
43 files changed, 769 insertions(+), 515 deletions(-)
diff --git a/lib/c.zig b/lib/c.zig
@@ -200,72 +200,67 @@ fn clone() callconv(.Naked) void {
// syscall(SYS_clone, flags, stack, ptid, tls, ctid)
// eax, ebx, ecx, edx, esi, edi
asm volatile (
- \\ push %%ebp
- \\ mov %%esp,%%ebp
- \\ push %%ebx
- \\ push %%esi
- \\ push %%edi
+ \\ pushl %%ebp
+ \\ movl %%esp,%%ebp
+ \\ pushl %%ebx
+ \\ pushl %%esi
+ \\ pushl %%edi
\\ // Setup the arguments
- \\ mov 16(%%ebp),%%ebx
- \\ mov 12(%%ebp),%%ecx
- \\ and $-16,%%ecx
- \\ sub $20,%%ecx
- \\ mov 20(%%ebp),%%eax
- \\ mov %%eax,4(%%ecx)
- \\ mov 8(%%ebp),%%eax
- \\ mov %%eax,0(%%ecx)
- \\ mov 24(%%ebp),%%edx
- \\ mov 28(%%ebp),%%esi
- \\ mov 32(%%ebp),%%edi
- \\ mov $120,%%eax
+ \\ movl 16(%%ebp),%%ebx
+ \\ movl 12(%%ebp),%%ecx
+ \\ andl $-16,%%ecx
+ \\ subl $20,%%ecx
+ \\ movl 20(%%ebp),%%eax
+ \\ movl %%eax,4(%%ecx)
+ \\ movl 8(%%ebp),%%eax
+ \\ movl %%eax,0(%%ecx)
+ \\ movl 24(%%ebp),%%edx
+ \\ movl 28(%%ebp),%%esi
+ \\ movl 32(%%ebp),%%edi
+ \\ movl $120,%%eax
\\ int $128
- \\ test %%eax,%%eax
+ \\ testl %%eax,%%eax
\\ jnz 1f
- \\ pop %%eax
- \\ xor %%ebp,%%ebp
- \\ call *%%eax
- \\ mov %%eax,%%ebx
- \\ xor %%eax,%%eax
- \\ inc %%eax
+ \\ popl %%eax
+ \\ xorl %%ebp,%%ebp
+ \\ calll *%%eax
+ \\ movl %%eax,%%ebx
+ \\ movl $1,%%eax
\\ int $128
- \\ hlt
\\1:
- \\ pop %%edi
- \\ pop %%esi
- \\ pop %%ebx
- \\ pop %%ebp
- \\ ret
+ \\ popl %%edi
+ \\ popl %%esi
+ \\ popl %%ebx
+ \\ popl %%ebp
+ \\ retl
);
},
.x86_64 => {
asm volatile (
- \\ xor %%eax,%%eax
- \\ mov $56,%%al // SYS_clone
- \\ mov %%rdi,%%r11
- \\ mov %%rdx,%%rdi
- \\ mov %%r8,%%rdx
- \\ mov %%r9,%%r8
- \\ mov 8(%%rsp),%%r10
- \\ mov %%r11,%%r9
- \\ and $-16,%%rsi
- \\ sub $8,%%rsi
- \\ mov %%rcx,(%%rsi)
+ \\ movl $56,%%eax // SYS_clone
+ \\ movq %%rdi,%%r11
+ \\ movq %%rdx,%%rdi
+ \\ movq %%r8,%%rdx
+ \\ movq %%r9,%%r8
+ \\ movq 8(%%rsp),%%r10
+ \\ movq %%r11,%%r9
+ \\ andq $-16,%%rsi
+ \\ subq $8,%%rsi
+ \\ movq %%rcx,(%%rsi)
\\ syscall
- \\ test %%eax,%%eax
+ \\ testq %%rax,%%rax
\\ jnz 1f
- \\ xor %%ebp,%%ebp
- \\ pop %%rdi
- \\ call *%%r9
- \\ mov %%eax,%%edi
- \\ xor %%eax,%%eax
- \\ mov $60,%%al // SYS_exit
+ \\ xorl %%ebp,%%ebp
+ \\ popq %%rdi
+ \\ callq *%%r9
+ \\ movl %%eax,%%edi
+ \\ movl $60,%%eax // SYS_exit
\\ syscall
- \\ hlt
\\1: ret
\\
);
},
- .aarch64 => {
+ .aarch64, .aarch64_be => {
// __clone(func, stack, flags, arg, ptid, tls, ctid)
// x0, x1, w2, x3, x4, x5, x6
@@ -400,69 +395,69 @@ fn clone() callconv(.Naked) void {
\\ syscall
);
},
- .powerpc => {
+ .powerpc, .powerpcle => {
// __clone(func, stack, flags, arg, ptid, tls, ctid)
// 3, 4, 5, 6, 7, 8, 9
// syscall(SYS_clone, flags, stack, ptid, tls, ctid)
// 0 3, 4, 5, 6, 7
asm volatile (
- \\# store non-volatile regs r30, r31 on stack in order to put our
- \\# start func and its arg there
- \\stwu 30, -16(1)
- \\stw 31, 4(1)
+ \\ # store non-volatile regs r30, r31 on stack in order to put our
+ \\ # start func and its arg there
+ \\ stwu 30, -16(1)
+ \\ stw 31, 4(1)
\\
- \\# save r3 (func) into r30, and r6(arg) into r31
- \\mr 30, 3
- \\mr 31, 6
+ \\ # save r3 (func) into r30, and r6(arg) into r31
+ \\ mr 30, 3
+ \\ mr 31, 6
\\
- \\# create initial stack frame for new thread
- \\clrrwi 4, 4, 4
- \\li 0, 0
- \\stwu 0, -16(4)
+ \\ # create initial stack frame for new thread
+ \\ clrrwi 4, 4, 4
+ \\ li 0, 0
+ \\ stwu 0, -16(4)
\\
- \\#move c into first arg
- \\mr 3, 5
- \\#mr 4, 4
- \\mr 5, 7
- \\mr 6, 8
- \\mr 7, 9
+ \\ #move c into first arg
+ \\ mr 3, 5
+ \\ #mr 4, 4
+ \\ mr 5, 7
+ \\ mr 6, 8
+ \\ mr 7, 9
\\
- \\# move syscall number into r0
- \\li 0, 120
+ \\ # move syscall number into r0
+ \\ li 0, 120
\\
- \\sc
+ \\ sc
\\
- \\# check for syscall error
- \\bns+ 1f # jump to label 1 if no summary overflow.
- \\#else
- \\neg 3, 3 #negate the result (errno)
- \\1:
- \\# compare sc result with 0
- \\cmpwi cr7, 3, 0
+ \\ # check for syscall error
+ \\ bns+ 1f # jump to label 1 if no summary overflow.
+ \\ #else
+ \\ neg 3, 3 #negate the result (errno)
+ \\ 1:
+ \\ # compare sc result with 0
+ \\ cmpwi cr7, 3, 0
\\
- \\# if not 0, jump to end
- \\bne cr7, 2f
+ \\ # if not 0, jump to end
+ \\ bne cr7, 2f
\\
- \\#else: we're the child
- \\#call funcptr: move arg (d) into r3
- \\mr 3, 31
- \\#move r30 (funcptr) into CTR reg
- \\mtctr 30
- \\# call CTR reg
- \\bctrl
- \\# mov SYS_exit into r0 (the exit param is already in r3)
- \\li 0, 1
- \\sc
+ \\ #else: we're the child
+ \\ #call funcptr: move arg (d) into r3
+ \\ mr 3, 31
+ \\ #move r30 (funcptr) into CTR reg
+ \\ mtctr 30
+ \\ # call CTR reg
+ \\ bctrl
+ \\ # mov SYS_exit into r0 (the exit param is already in r3)
+ \\ li 0, 1
+ \\ sc
\\
- \\2:
+ \\ 2:
\\
- \\# restore stack
- \\lwz 30, 0(1)
- \\lwz 31, 4(1)
- \\addi 1, 1, 16
+ \\ # restore stack
+ \\ lwz 30, 0(1)
+ \\ lwz 31, 4(1)
+ \\ addi 1, 1, 16
\\
- \\blr
+ \\ blr
);
},
.powerpc64, .powerpc64le => {
diff --git a/lib/compiler_rt/arm.zig b/lib/compiler_rt/arm.zig
@@ -9,7 +9,7 @@ pub const panic = common.panic;
comptime {
if (!builtin.is_test) {
- if (arch.isARM() or arch.isThumb()) {
+ if (arch.isArmOrThumb()) {
@export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = common.linkage, .visibility = common.visibility });
@export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = common.linkage, .visibility = common.visibility });
@export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/clzsi2_test.zig b/lib/compiler_rt/clzsi2_test.zig
@@ -268,7 +268,7 @@ test "clzsi2" {
try test__clzsi2(0xFE000000, 0);
try test__clzsi2(0xFF000000, 0);
// arm and thumb1 assume input a != 0
- if (!builtin.cpu.arch.isARM() and !builtin.cpu.arch.isThumb())
+ if (!builtin.cpu.arch.isArmOrThumb())
try test__clzsi2(0x00000000, 32);
try test__clzsi2(0x00000001, 31);
try test__clzsi2(0x00000002, 30);
diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig
@@ -1275,12 +1275,14 @@ const LinuxThreadImpl = struct {
.entry_number = os.linux.tls.tls_image.gdt_entry_number,
.base_addr = tls_ptr,
.limit = 0xfffff,
- .seg_32bit = 1,
- .contents = 0, // Data
- .read_exec_only = 0,
- .limit_in_pages = 1,
- .seg_not_present = 0,
- .useable = 1,
+ .flags = .{
+ .seg_32bit = 1,
+ .contents = 0, // Data
+ .read_exec_only = 0,
+ .limit_in_pages = 1,
+ .seg_not_present = 0,
+ .useable = 1,
+ },
};
}
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
@@ -568,7 +568,7 @@ pub const VaListX86_64 = extern struct {
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const VaList = switch (builtin.cpu.arch) {
- .aarch64 => switch (builtin.os.tag) {
+ .aarch64, .aarch64_be => switch (builtin.os.tag) {
.windows => *u8,
.ios, .macos, .tvos, .watchos => *u8,
else => @compileError("disabled due to miscompilations"), // VaListAarch64,
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
@@ -35,13 +35,13 @@ const syscall_bits = switch (native_arch) {
const arch_bits = switch (native_arch) {
.x86 => @import("linux/x86.zig"),
.x86_64 => @import("linux/x86_64.zig"),
- .aarch64 => @import("linux/arm64.zig"),
+ .aarch64, .aarch64_be => @import("linux/arm64.zig"),
.arm, .thumb => @import("linux/arm-eabi.zig"),
.riscv64 => @import("linux/riscv64.zig"),
.sparc64 => @import("linux/sparc64.zig"),
.mips, .mipsel => @import("linux/mips.zig"),
.mips64, .mips64el => @import("linux/mips64.zig"),
- .powerpc => @import("linux/powerpc.zig"),
+ .powerpc, .powerpcle => @import("linux/powerpc.zig"),
.powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),
else => struct {},
};
@@ -98,13 +98,13 @@ pub const syscalls = @import("linux/syscalls.zig");
pub const SYS = switch (@import("builtin").cpu.arch) {
.x86 => syscalls.X86,
.x86_64 => syscalls.X64,
- .aarch64 => syscalls.Arm64,
+ .aarch64, .aarch64_be => syscalls.Arm64,
.arm, .thumb => syscalls.Arm,
.riscv64 => syscalls.RiscV64,
.sparc64 => syscalls.Sparc64,
.mips, .mipsel => syscalls.Mips,
.mips64, .mips64el => syscalls.Mips64,
- .powerpc => syscalls.PowerPC,
+ .powerpc, .powerpcle => syscalls.PowerPC,
.powerpc64, .powerpc64le => syscalls.PowerPC64,
else => @compileError("The Zig Standard Library is missing syscall definitions for the target CPU architecture"),
};
@@ -1176,14 +1176,12 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
const mask_size = @sizeOf(@TypeOf(ksa.mask));
if (act) |new| {
- const restore_rt_ptr = &restore_rt;
- const restore_ptr = &restore;
- const restorer_fn = if ((new.flags & SA.SIGINFO) != 0) restore_rt_ptr else restore_ptr;
+ const restorer_fn = if ((new.flags & SA.SIGINFO) != 0) &restore_rt else &restore;
ksa = k_sigaction{
.handler = new.handler.handler,
.flags = new.flags | SA.RESTORER,
.mask = undefined,
- .restorer = @as(k_sigaction_funcs.restorer, @ptrCast(restorer_fn)),
+ .restorer = @ptrCast(restorer_fn),
};
@memcpy(@as([*]u8, @ptrCast(&ksa.mask))[0..mask_size], @as([*]const u8, @ptrCast(&new.mask)));
}
diff --git a/lib/std/os/linux/arm-eabi.zig b/lib/std/os/linux/arm-eabi.zig
@@ -103,20 +103,40 @@ const CloneFn = *const fn (arg: usize) callconv(.C) u8;
/// This matches the libc clone function.
pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
-pub fn restore() callconv(.Naked) void {
- return asm volatile ("svc #0"
- :
- : [number] "{r7}" (@intFromEnum(SYS.sigreturn)),
- : "memory"
- );
+pub fn restore() callconv(.Naked) noreturn {
+ switch (@import("builtin").zig_backend) {
+ .stage2_c => asm volatile (
+ \\ mov r7, %[number]
+ \\ svc #0
+ :
+ : [number] "I" (@intFromEnum(SYS.sigreturn)),
+ : "memory"
+ ),
+ else => asm volatile (
+ \\ svc #0
+ :
+ : [number] "{r7}" (@intFromEnum(SYS.sigreturn)),
+ : "memory"
+ ),
+ }
}
-pub fn restore_rt() callconv(.Naked) void {
- return asm volatile ("svc #0"
- :
- : [number] "{r7}" (@intFromEnum(SYS.rt_sigreturn)),
- : "memory"
- );
+pub fn restore_rt() callconv(.Naked) noreturn {
+ switch (@import("builtin").zig_backend) {
+ .stage2_c => asm volatile (
+ \\ mov r7, %[number]
+ \\ svc #0
+ :
+ : [number] "I" (@intFromEnum(SYS.rt_sigreturn)),
+ : "memory"
+ ),
+ else => asm volatile (
+ \\ svc #0
+ :
+ : [number] "{r7}" (@intFromEnum(SYS.rt_sigreturn)),
+ : "memory"
+ ),
+ }
}
pub const MMAP2_UNIT = 4096;
diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig
@@ -105,16 +105,17 @@ pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *
pub const restore = restore_rt;
-pub fn restore_rt() callconv(.Naked) void {
+pub fn restore_rt() callconv(.Naked) noreturn {
switch (@import("builtin").zig_backend) {
- .stage2_c => return asm volatile (
+ .stage2_c => asm volatile (
\\ mov x8, %[number]
\\ svc #0
:
: [number] "i" (@intFromEnum(SYS.rt_sigreturn)),
: "memory", "cc"
),
- else => return asm volatile ("svc #0"
+ else => asm volatile (
+ \\ svc #0
:
: [number] "{x8}" (@intFromEnum(SYS.rt_sigreturn)),
: "memory", "cc"
diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig
@@ -195,16 +195,18 @@ const CloneFn = *const fn (arg: usize) callconv(.C) u8;
/// This matches the libc clone function.
pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
-pub fn restore() callconv(.Naked) void {
- return asm volatile ("syscall"
+pub fn restore() callconv(.Naked) noreturn {
+ asm volatile (
+ \\ syscall
:
: [number] "{$2}" (@intFromEnum(SYS.sigreturn)),
: "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
);
}
-pub fn restore_rt() callconv(.Naked) void {
- return asm volatile ("syscall"
+pub fn restore_rt() callconv(.Naked) noreturn {
+ asm volatile (
+ \\ syscall
:
: [number] "{$2}" (@intFromEnum(SYS.rt_sigreturn)),
: "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
diff --git a/lib/std/os/linux/mips64.zig b/lib/std/os/linux/mips64.zig
@@ -180,16 +180,18 @@ const CloneFn = *const fn (arg: usize) callconv(.C) u8;
/// This matches the libc clone function.
pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
-pub fn restore() callconv(.Naked) void {
- return asm volatile ("syscall"
+pub fn restore() callconv(.Naked) noreturn {
+ asm volatile (
+ \\ syscall
:
: [number] "{$2}" (@intFromEnum(SYS.rt_sigreturn)),
: "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
);
}
-pub fn restore_rt() callconv(.Naked) void {
- return asm volatile ("syscall"
+pub fn restore_rt() callconv(.Naked) noreturn {
+ asm volatile (
+ \\ syscall
:
: [number] "{$2}" (@intFromEnum(SYS.rt_sigreturn)),
: "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig
@@ -133,8 +133,9 @@ pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid:
pub const restore = restore_rt;
-pub fn restore_rt() callconv(.Naked) void {
- return asm volatile ("sc"
+pub fn restore_rt() callconv(.Naked) noreturn {
+ asm volatile (
+ \\ sc
:
: [number] "{r0}" (@intFromEnum(SYS.rt_sigreturn)),
: "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig
@@ -133,8 +133,9 @@ pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid:
pub const restore = restore_rt;
-pub fn restore_rt() callconv(.Naked) void {
- return asm volatile ("sc"
+pub fn restore_rt() callconv(.Naked) noreturn {
+ asm volatile (
+ \\ sc
:
: [number] "{r0}" (@intFromEnum(SYS.rt_sigreturn)),
: "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig
@@ -101,8 +101,9 @@ pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *
pub const restore = restore_rt;
-pub fn restore_rt() callconv(.Naked) void {
- return asm volatile ("ecall"
+pub fn restore_rt() callconv(.Naked) noreturn {
+ asm volatile (
+ \\ ecall
:
: [number] "{x17}" (@intFromEnum(SYS.rt_sigreturn)),
: "memory"
diff --git a/lib/std/os/linux/thumb.zig b/lib/std/os/linux/thumb.zig
@@ -141,8 +141,8 @@ pub fn syscall6(
);
}
-pub fn restore() callconv(.Naked) void {
- return asm volatile (
+pub fn restore() callconv(.Naked) noreturn {
+ asm volatile (
\\ mov r7, %[number]
\\ svc #0
:
@@ -150,8 +150,8 @@ pub fn restore() callconv(.Naked) void {
);
}
-pub fn restore_rt() callconv(.Naked) void {
- return asm volatile (
+pub fn restore_rt() callconv(.Naked) noreturn {
+ asm volatile (
\\ mov r7, %[number]
\\ svc #0
:
diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig
@@ -48,7 +48,7 @@ const TLSVariant = enum {
};
const tls_variant = switch (native_arch) {
- .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI,
+ .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpcle, .powerpc64, .powerpc64le => TLSVariant.VariantI,
.x86_64, .x86, .sparc64 => TLSVariant.VariantII,
else => @compileError("undefined tls_variant for this architecture"),
};
@@ -115,12 +115,14 @@ pub fn setThreadPointer(addr: usize) void {
.entry_number = tls_image.gdt_entry_number,
.base_addr = addr,
.limit = 0xfffff,
- .seg_32bit = 1,
- .contents = 0, // Data
- .read_exec_only = 0,
- .limit_in_pages = 1,
- .seg_not_present = 0,
- .useable = 1,
+ .flags = .{
+ .seg_32bit = 1,
+ .contents = 0, // Data
+ .read_exec_only = 0,
+ .limit_in_pages = 1,
+ .seg_not_present = 0,
+ .useable = 1,
+ },
};
const rc = std.os.linux.syscall1(.set_thread_area, @intFromPtr(&user_desc));
assert(rc == 0);
@@ -138,7 +140,7 @@ pub fn setThreadPointer(addr: usize) void {
const rc = std.os.linux.syscall2(.arch_prctl, std.os.linux.ARCH.SET_FS, addr);
assert(rc == 0);
},
- .aarch64 => {
+ .aarch64, .aarch64_be => {
asm volatile (
\\ msr tpidr_el0, %[addr]
:
@@ -160,7 +162,7 @@ pub fn setThreadPointer(addr: usize) void {
const rc = std.os.linux.syscall1(.set_thread_area, addr);
assert(rc == 0);
},
- .powerpc => {
+ .powerpc, .powerpcle => {
asm volatile (
\\ mr 2, %[addr]
:
diff --git a/lib/std/os/linux/x86.zig b/lib/std/os/linux/x86.zig
@@ -123,46 +123,40 @@ const CloneFn = *const fn (arg: usize) callconv(.C) u8;
/// This matches the libc clone function.
pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
-pub fn restore() callconv(.Naked) void {
+pub fn restore() callconv(.Naked) noreturn {
switch (@import("builtin").zig_backend) {
.stage2_c => asm volatile (
\\ movl %[number], %%eax
\\ int $0x80
- \\ ret
:
: [number] "i" (@intFromEnum(SYS.sigreturn)),
: "memory"
),
else => asm volatile (
\\ int $0x80
- \\ ret
:
: [number] "{eax}" (@intFromEnum(SYS.sigreturn)),
: "memory"
),
}
- unreachable;
}
-pub fn restore_rt() callconv(.Naked) void {
+pub fn restore_rt() callconv(.Naked) noreturn {
switch (@import("builtin").zig_backend) {
.stage2_c => asm volatile (
\\ movl %[number], %%eax
\\ int $0x80
- \\ ret
:
: [number] "i" (@intFromEnum(SYS.rt_sigreturn)),
: "memory"
),
else => asm volatile (
\\ int $0x80
- \\ ret
:
: [number] "{eax}" (@intFromEnum(SYS.rt_sigreturn)),
: "memory"
),
}
- unreachable;
}
pub const O = struct {
@@ -354,16 +348,19 @@ pub const ucontext_t = extern struct {
pub const Elf_Symndx = u32;
-pub const user_desc = packed struct {
+pub const user_desc = extern struct {
entry_number: u32,
base_addr: u32,
limit: u32,
- seg_32bit: u1,
- contents: u2,
- read_exec_only: u1,
- limit_in_pages: u1,
- seg_not_present: u1,
- useable: u1,
+ flags: packed struct(u32) {
+ seg_32bit: u1,
+ contents: u2,
+ read_exec_only: u1,
+ limit_in_pages: u1,
+ seg_not_present: u1,
+ useable: u1,
+ _: u25 = undefined,
+ },
};
/// socketcall() call numbers
@@ -398,77 +395,80 @@ noinline fn getContextReturnAddress() usize {
return @returnAddress();
}
-pub fn getContextInternal() callconv(.Naked) void {
+pub fn getContextInternal() callconv(.Naked) usize {
asm volatile (
- \\ movl $0, (%[flags_offset])(%%edx)
- \\ movl $0, (%[link_offset])(%%edx)
- \\ movl %%edi, (%[edi_offset])(%%edx)
- \\ movl %%esi, (%[esi_offset])(%%edx)
- \\ movl %%ebp, (%[ebp_offset])(%%edx)
- \\ movl %%ebx, (%[ebx_offset])(%%edx)
- \\ movl %%edx, (%[edx_offset])(%%edx)
- \\ movl %%ecx, (%[ecx_offset])(%%edx)
- \\ movl %%eax, (%[eax_offset])(%%edx)
+ \\ movl $0, %[flags_offset:c](%%edx)
+ \\ movl $0, %[link_offset:c](%%edx)
+ \\ movl %%edi, %[edi_offset:c](%%edx)
+ \\ movl %%esi, %[esi_offset:c](%%edx)
+ \\ movl %%ebp, %[ebp_offset:c](%%edx)
+ \\ movl %%ebx, %[ebx_offset:c](%%edx)
+ \\ movl %%edx, %[edx_offset:c](%%edx)
+ \\ movl %%ecx, %[ecx_offset:c](%%edx)
+ \\ movl %%eax, %[eax_offset:c](%%edx)
\\ movl (%%esp), %%ecx
- \\ movl %%ecx, (%[eip_offset])(%%edx)
+ \\ movl %%ecx, %[eip_offset:c](%%edx)
\\ leal 4(%%esp), %%ecx
- \\ movl %%ecx, (%[esp_offset])(%%edx)
+ \\ movl %%ecx, %[esp_offset:c](%%edx)
\\ xorl %%ecx, %%ecx
\\ movw %%fs, %%cx
- \\ movl %%ecx, (%[fs_offset])(%%edx)
- \\ leal (%[regspace_offset])(%%edx), %%ecx
- \\ movl %%ecx, (%[fpregs_offset])(%%edx)
+ \\ movl %%ecx, %[fs_offset:c](%%edx)
+ \\ leal %[regspace_offset:c](%%edx), %%ecx
+ \\ movl %%ecx, %[fpregs_offset:c](%%edx)
\\ fnstenv (%%ecx)
\\ fldenv (%%ecx)
\\ pushl %%ebx
\\ pushl %%esi
\\ xorl %%ebx, %%ebx
\\ movl %[sigaltstack], %%eax
- \\ leal (%[stack_offset])(%%edx), %%ecx
+ \\ leal %[stack_offset:c](%%edx), %%ecx
\\ int $0x80
- \\ cmpl $0, %%eax
- \\ jne return
+ \\ testl %%eax, %%eax
+ \\ jnz 0f
\\ movl %[sigprocmask], %%eax
\\ xorl %%ecx, %%ecx
- \\ leal (%[sigmask_offset])(%%edx), %%edx
+ \\ leal %[sigmask_offset:c](%%edx), %%edx
\\ movl %[sigset_size], %%esi
\\ int $0x80
- \\ return:
+ \\0:
\\ popl %%esi
\\ popl %%ebx
+ \\ retl
:
- : [flags_offset] "p" (@offsetOf(ucontext_t, "flags")),
- [link_offset] "p" (@offsetOf(ucontext_t, "link")),
- [edi_offset] "p" (comptime gpRegisterOffset(REG.EDI)),
- [esi_offset] "p" (comptime gpRegisterOffset(REG.ESI)),
- [ebp_offset] "p" (comptime gpRegisterOffset(REG.EBP)),
- [esp_offset] "p" (comptime gpRegisterOffset(REG.ESP)),
- [ebx_offset] "p" (comptime gpRegisterOffset(REG.EBX)),
- [edx_offset] "p" (comptime gpRegisterOffset(REG.EDX)),
- [ecx_offset] "p" (comptime gpRegisterOffset(REG.ECX)),
- [eax_offset] "p" (comptime gpRegisterOffset(REG.EAX)),
- [eip_offset] "p" (comptime gpRegisterOffset(REG.EIP)),
- [fs_offset] "p" (comptime gpRegisterOffset(REG.FS)),
- [fpregs_offset] "p" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")),
- [regspace_offset] "p" (@offsetOf(ucontext_t, "regspace")),
+ : [flags_offset] "i" (@offsetOf(ucontext_t, "flags")),
+ [link_offset] "i" (@offsetOf(ucontext_t, "link")),
+ [edi_offset] "i" (comptime gpRegisterOffset(REG.EDI)),
+ [esi_offset] "i" (comptime gpRegisterOffset(REG.ESI)),
+ [ebp_offset] "i" (comptime gpRegisterOffset(REG.EBP)),
+ [esp_offset] "i" (comptime gpRegisterOffset(REG.ESP)),
+ [ebx_offset] "i" (comptime gpRegisterOffset(REG.EBX)),
+ [edx_offset] "i" (comptime gpRegisterOffset(REG.EDX)),
+ [ecx_offset] "i" (comptime gpRegisterOffset(REG.ECX)),
+ [eax_offset] "i" (comptime gpRegisterOffset(REG.EAX)),
+ [eip_offset] "i" (comptime gpRegisterOffset(REG.EIP)),
+ [fs_offset] "i" (comptime gpRegisterOffset(REG.FS)),
+ [fpregs_offset] "i" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")),
+ [regspace_offset] "i" (@offsetOf(ucontext_t, "regspace")),
[sigaltstack] "i" (@intFromEnum(linux.SYS.sigaltstack)),
- [stack_offset] "p" (@offsetOf(ucontext_t, "stack")),
+ [stack_offset] "i" (@offsetOf(ucontext_t, "stack")),
[sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)),
- [sigmask_offset] "p" (@offsetOf(ucontext_t, "sigmask")),
+ [sigmask_offset] "i" (@offsetOf(ucontext_t, "sigmask")),
[sigset_size] "i" (linux.NSIG / 8),
- : "memory", "eax", "ecx", "edx"
+ : "cc", "memory", "eax", "ecx", "edx"
);
}
pub inline fn getcontext(context: *ucontext_t) usize {
// This method is used so that getContextInternal can control
// its prologue in order to read ESP from a constant offset.
- // The unused &getContextInternal input is required so the function is included in the binary.
+ // An aligned stack is not needed for getContextInternal.
+ var clobber_edx: usize = undefined;
return asm volatile (
- \\ call os.linux.x86.getContextInternal
- : [ret] "={eax}" (-> usize),
- : [context] "{edx}" (context),
+ \\ calll %[getContextInternal:P]
+ : [_] "={eax}" (-> usize),
+ [_] "={edx}" (clobber_edx),
+ : [_] "{edx}" (context),
[getContextInternal] "X" (&getContextInternal),
- : "memory", "ecx"
+ : "cc", "memory", "ecx"
);
}
diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig
@@ -107,25 +107,22 @@ pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid:
pub const restore = restore_rt;
-pub fn restore_rt() callconv(.Naked) void {
+pub fn restore_rt() callconv(.Naked) noreturn {
switch (@import("builtin").zig_backend) {
.stage2_c => asm volatile (
\\ movl %[number], %%eax
\\ syscall
- \\ retq
:
: [number] "i" (@intFromEnum(SYS.rt_sigreturn)),
: "rcx", "r11", "memory"
),
else => asm volatile (
\\ syscall
- \\ retq
:
: [number] "{rax}" (@intFromEnum(SYS.rt_sigreturn)),
: "rcx", "r11", "memory"
),
}
- unreachable;
}
pub const mode_t = usize;
@@ -400,92 +397,95 @@ fn gpRegisterOffset(comptime reg_index: comptime_int) usize {
return @offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "gregs") + @sizeOf(usize) * reg_index;
}
-fn getContextInternal() callconv(.Naked) void {
+fn getContextInternal() callconv(.Naked) usize {
// TODO: Read GS/FS registers?
asm volatile (
- \\ movq $0, (%[flags_offset])(%%rdi)
- \\ movq $0, (%[link_offset])(%%rdi)
- \\ movq %%r8, (%[r8_offset])(%%rdi)
- \\ movq %%r9, (%[r9_offset])(%%rdi)
- \\ movq %%r10, (%[r10_offset])(%%rdi)
- \\ movq %%r11, (%[r11_offset])(%%rdi)
- \\ movq %%r12, (%[r12_offset])(%%rdi)
- \\ movq %%r13, (%[r13_offset])(%%rdi)
- \\ movq %%r14, (%[r14_offset])(%%rdi)
- \\ movq %%r15, (%[r15_offset])(%%rdi)
- \\ movq %%rdi, (%[rdi_offset])(%%rdi)
- \\ movq %%rsi, (%[rsi_offset])(%%rdi)
- \\ movq %%rbp, (%[rbp_offset])(%%rdi)
- \\ movq %%rbx, (%[rbx_offset])(%%rdi)
- \\ movq %%rdx, (%[rdx_offset])(%%rdi)
- \\ movq %%rax, (%[rax_offset])(%%rdi)
- \\ movq %%rcx, (%[rcx_offset])(%%rdi)
+ \\ movq $0, %[flags_offset:c](%%rdi)
+ \\ movq $0, %[link_offset:c](%%rdi)
+ \\ movq %%r8, %[r8_offset:c](%%rdi)
+ \\ movq %%r9, %[r9_offset:c](%%rdi)
+ \\ movq %%r10, %[r10_offset:c](%%rdi)
+ \\ movq %%r11, %[r11_offset:c](%%rdi)
+ \\ movq %%r12, %[r12_offset:c](%%rdi)
+ \\ movq %%r13, %[r13_offset:c](%%rdi)
+ \\ movq %%r14, %[r14_offset:c](%%rdi)
+ \\ movq %%r15, %[r15_offset:c](%%rdi)
+ \\ movq %%rdi, %[rdi_offset:c](%%rdi)
+ \\ movq %%rsi, %[rsi_offset:c](%%rdi)
+ \\ movq %%rbp, %[rbp_offset:c](%%rdi)
+ \\ movq %%rbx, %[rbx_offset:c](%%rdi)
+ \\ movq %%rdx, %[rdx_offset:c](%%rdi)
+ \\ movq %%rax, %[rax_offset:c](%%rdi)
+ \\ movq %%rcx, %[rcx_offset:c](%%rdi)
\\ movq (%%rsp), %%rcx
- \\ movq %%rcx, (%[rip_offset])(%%rdi)
+ \\ movq %%rcx, %[rip_offset:c](%%rdi)
\\ leaq 8(%%rsp), %%rcx
- \\ movq %%rcx, (%[rsp_offset])(%%rdi)
+ \\ movq %%rcx, %[rsp_offset:c](%%rdi)
\\ pushfq
- \\ popq (%[efl_offset])(%%rdi)
- \\ leaq (%[fpmem_offset])(%%rdi), %%rcx
- \\ movq %%rcx, (%[fpstate_offset])(%%rdi)
+ \\ popq %[efl_offset:c](%%rdi)
+ \\ leaq %[fpmem_offset:c](%%rdi), %%rcx
+ \\ movq %%rcx, %[fpstate_offset:c](%%rdi)
\\ fnstenv (%%rcx)
\\ fldenv (%%rcx)
- \\ stmxcsr (%[mxcsr_offset])(%%rdi)
- \\ leaq (%[stack_offset])(%%rdi), %%rsi
+ \\ stmxcsr %[mxcsr_offset:c](%%rdi)
+ \\ leaq %[stack_offset:c](%%rdi), %%rsi
\\ movq %%rdi, %%r8
- \\ xorq %%rdi, %%rdi
+ \\ xorl %%edi, %%edi
\\ movq %[sigaltstack], %%rax
\\ syscall
- \\ cmpq $0, %%rax
- \\ jne return
+ \\ testq %%rax, %%rax
+ \\ jnz 0f
\\ movq %[sigprocmask], %%rax
- \\ xorq %%rsi, %%rsi
- \\ leaq (%[sigmask_offset])(%%r8), %%rdx
- \\ movq %[sigset_size], %%r10
+ \\ xorl %%esi, %%esi
+ \\ leaq %[sigmask_offset:c](%%r8), %%rdx
+ \\ movl %[sigset_size], %%r10d
\\ syscall
- \\ return:
+ \\0:
+ \\ retq
:
- : [flags_offset] "p" (@offsetOf(ucontext_t, "flags")),
- [link_offset] "p" (@offsetOf(ucontext_t, "link")),
- [r8_offset] "p" (comptime gpRegisterOffset(REG.R8)),
- [r9_offset] "p" (comptime gpRegisterOffset(REG.R9)),
- [r10_offset] "p" (comptime gpRegisterOffset(REG.R10)),
- [r11_offset] "p" (comptime gpRegisterOffset(REG.R11)),
- [r12_offset] "p" (comptime gpRegisterOffset(REG.R12)),
- [r13_offset] "p" (comptime gpRegisterOffset(REG.R13)),
- [r14_offset] "p" (comptime gpRegisterOffset(REG.R14)),
- [r15_offset] "p" (comptime gpRegisterOffset(REG.R15)),
- [rdi_offset] "p" (comptime gpRegisterOffset(REG.RDI)),
- [rsi_offset] "p" (comptime gpRegisterOffset(REG.RSI)),
- [rbp_offset] "p" (comptime gpRegisterOffset(REG.RBP)),
- [rbx_offset] "p" (comptime gpRegisterOffset(REG.RBX)),
- [rdx_offset] "p" (comptime gpRegisterOffset(REG.RDX)),
- [rax_offset] "p" (comptime gpRegisterOffset(REG.RAX)),
- [rcx_offset] "p" (comptime gpRegisterOffset(REG.RCX)),
- [rsp_offset] "p" (comptime gpRegisterOffset(REG.RSP)),
- [rip_offset] "p" (comptime gpRegisterOffset(REG.RIP)),
- [efl_offset] "p" (comptime gpRegisterOffset(REG.EFL)),
- [fpstate_offset] "p" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")),
- [fpmem_offset] "p" (@offsetOf(ucontext_t, "fpregs_mem")),
- [mxcsr_offset] "p" (@offsetOf(ucontext_t, "fpregs_mem") + @offsetOf(fpstate, "mxcsr")),
+ : [flags_offset] "i" (@offsetOf(ucontext_t, "flags")),
+ [link_offset] "i" (@offsetOf(ucontext_t, "link")),
+ [r8_offset] "i" (comptime gpRegisterOffset(REG.R8)),
+ [r9_offset] "i" (comptime gpRegisterOffset(REG.R9)),
+ [r10_offset] "i" (comptime gpRegisterOffset(REG.R10)),
+ [r11_offset] "i" (comptime gpRegisterOffset(REG.R11)),
+ [r12_offset] "i" (comptime gpRegisterOffset(REG.R12)),
+ [r13_offset] "i" (comptime gpRegisterOffset(REG.R13)),
+ [r14_offset] "i" (comptime gpRegisterOffset(REG.R14)),
+ [r15_offset] "i" (comptime gpRegisterOffset(REG.R15)),
+ [rdi_offset] "i" (comptime gpRegisterOffset(REG.RDI)),
+ [rsi_offset] "i" (comptime gpRegisterOffset(REG.RSI)),
+ [rbp_offset] "i" (comptime gpRegisterOffset(REG.RBP)),
+ [rbx_offset] "i" (comptime gpRegisterOffset(REG.RBX)),
+ [rdx_offset] "i" (comptime gpRegisterOffset(REG.RDX)),
+ [rax_offset] "i" (comptime gpRegisterOffset(REG.RAX)),
+ [rcx_offset] "i" (comptime gpRegisterOffset(REG.RCX)),
+ [rsp_offset] "i" (comptime gpRegisterOffset(REG.RSP)),
+ [rip_offset] "i" (comptime gpRegisterOffset(REG.RIP)),
+ [efl_offset] "i" (comptime gpRegisterOffset(REG.EFL)),
+ [fpstate_offset] "i" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")),
+ [fpmem_offset] "i" (@offsetOf(ucontext_t, "fpregs_mem")),
+ [mxcsr_offset] "i" (@offsetOf(ucontext_t, "fpregs_mem") + @offsetOf(fpstate, "mxcsr")),
[sigaltstack] "i" (@intFromEnum(linux.SYS.sigaltstack)),
- [stack_offset] "p" (@offsetOf(ucontext_t, "stack")),
+ [stack_offset] "i" (@offsetOf(ucontext_t, "stack")),
[sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)),
- [sigmask_offset] "p" (@offsetOf(ucontext_t, "sigmask")),
+ [sigmask_offset] "i" (@offsetOf(ucontext_t, "sigmask")),
[sigset_size] "i" (linux.NSIG / 8),
- : "memory", "rcx", "rdx", "rdi", "rsi", "r8", "r10", "r11"
+ : "cc", "memory", "rax", "rcx", "rdx", "rdi", "rsi", "r8", "r10", "r11"
);
}
pub inline fn getcontext(context: *ucontext_t) usize {
// This method is used so that getContextInternal can control
// its prologue in order to read RSP from a constant offset
- // The unused &getContextInternal input is required so the function is included in the binary.
+ // An aligned stack is not needed for getContextInternal.
+ var clobber_rdi: usize = undefined;
return asm volatile (
- \\ call os.linux.x86_64.getContextInternal
- : [ret] "={rax}" (-> usize),
- : [context] "{rdi}" (context),
+ \\ callq %[getContextInternal:P]
+ : [_] "={rax}" (-> usize),
+ [_] "={rdi}" (clobber_rdi),
+ : [_] "{rdi}" (context),
[getContextInternal] "X" (&getContextInternal),
- : "memory", "rcx", "rdx", "rdi", "rsi", "r8", "r10", "r11"
+ : "cc", "memory", "rcx", "rdx", "rsi", "r8", "r10", "r11"
);
}
diff --git a/lib/std/start.zig b/lib/std/start.zig
@@ -101,7 +101,7 @@ fn main2() callconv(.C) c_int {
return 0;
}
-fn _start2() callconv(.Naked) noreturn {
+fn _start2() noreturn {
callMain2();
}
@@ -254,116 +254,92 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
}
fn _start() callconv(.Naked) noreturn {
- switch (builtin.zig_backend) {
- .stage2_c => {
- @export(argc_argv_ptr, .{ .name = "argc_argv_ptr" });
- @export(posixCallMainAndExit, .{ .name = "_posixCallMainAndExit" });
- switch (native_arch) {
- .x86_64 => asm volatile (
- \\ xorl %%ebp, %%ebp
- \\ movq %%rsp, argc_argv_ptr
- \\ andq $-16, %%rsp
- \\ call _posixCallMainAndExit
- ),
- .x86 => asm volatile (
- \\ xorl %%ebp, %%ebp
- \\ movl %%esp, argc_argv_ptr
- \\ andl $-16, %%esp
- \\ jmp _posixCallMainAndExit
- ),
- .aarch64, .aarch64_be => asm volatile (
- \\ mov fp, #0
- \\ mov lr, #0
- \\ mov x0, sp
- \\ adrp x1, argc_argv_ptr
- \\ str x0, [x1, :lo12:argc_argv_ptr]
- \\ b _posixCallMainAndExit
- ),
- .arm, .armeb, .thumb => asm volatile (
- \\ mov fp, #0
- \\ mov lr, #0
- \\ str sp, argc_argv_ptr
- \\ and sp, #-16
- \\ b _posixCallMainAndExit
- ),
- else => @compileError("unsupported arch"),
- }
- unreachable;
- },
- else => switch (native_arch) {
- .x86_64 => {
- argc_argv_ptr = asm volatile (
- \\ xor %%ebp, %%ebp
- : [argc] "={rsp}" (-> [*]usize),
- );
- },
- .x86 => {
- argc_argv_ptr = asm volatile (
- \\ xor %%ebp, %%ebp
- : [argc] "={esp}" (-> [*]usize),
- );
- },
- .aarch64, .aarch64_be, .arm, .armeb, .thumb => {
- argc_argv_ptr = asm volatile (
- \\ mov fp, #0
- \\ mov lr, #0
- : [argc] "={sp}" (-> [*]usize),
- );
- },
- .riscv64 => {
- argc_argv_ptr = asm volatile (
- \\ li s0, 0
- \\ li ra, 0
- : [argc] "={sp}" (-> [*]usize),
- );
- },
- .mips, .mipsel, .mips64, .mips64el => {
- // The lr is already zeroed on entry, as specified by the ABI.
- argc_argv_ptr = asm volatile (
- \\ move $fp, $0
- : [argc] "={sp}" (-> [*]usize),
- );
- },
- .powerpc => {
- // Setup the initial stack frame and clear the back chain pointer.
- argc_argv_ptr = asm volatile (
- \\ mr 4, 1
- \\ li 0, 0
- \\ stwu 1,-16(1)
- \\ stw 0, 0(1)
- \\ mtlr 0
- : [argc] "={r4}" (-> [*]usize),
- :
- : "r0"
- );
- },
- .powerpc64le => {
- // Setup the initial stack frame and clear the back chain pointer.
- // TODO: Support powerpc64 (big endian) on ELFv2.
- argc_argv_ptr = asm volatile (
- \\ mr 4, 1
- \\ li 0, 0
- \\ stdu 0, -32(1)
- \\ mtlr 0
- : [argc] "={r4}" (-> [*]usize),
- :
- : "r0"
- );
- },
- .sparc64 => {
- // argc is stored after a register window (16 registers) plus stack bias
- argc_argv_ptr = asm (
- \\ mov %%g0, %%i6
- \\ add %%o6, 2175, %[argc]
- : [argc] "=r" (-> [*]usize),
- );
- },
+ asm volatile (switch (native_arch) {
+ .x86_64 =>
+ \\ xorl %%ebp, %%ebp
+ \\ movq %%rsp, %[argc_argv_ptr]
+ \\ andq $-16, %%rsp
+ \\ callq %[posixCallMainAndExit:P]
+ ,
+ .x86 =>
+ \\ xorl %%ebp, %%ebp
+ \\ movl %%esp, %[argc_argv_ptr]
+ \\ andl $-16, %%esp
+ \\ calll %[posixCallMainAndExit:P]
+ ,
+ .aarch64, .aarch64_be =>
+ \\ mov fp, #0
+ \\ mov lr, #0
+ \\ mov x0, sp
+ \\ str x0, %[argc_argv_ptr]
+ \\ b %[posixCallMainAndExit]
+ ,
+ .arm, .armeb, .thumb, .thumbeb =>
+ \\ mov fp, #0
+ \\ mov lr, #0
+ \\ str sp, %[argc_argv_ptr]
+ \\ and sp, #-16
+ \\ b %[posixCallMainAndExit]
+ ,
+ .riscv64 =>
+ \\ li s0, 0
+ \\ li ra, 0
+ \\ sd sp, %[argc_argv_ptr]
+ \\ andi sp, sp, -16
+ \\ tail %[posixCallMainAndExit]@plt
+ ,
+ .mips, .mipsel =>
+ // The lr is already zeroed on entry, as specified by the ABI.
+ \\ addiu $fp, $zero, 0
+ \\ sw $sp, %[argc_argv_ptr]
+ \\ .set push
+ \\ .set noat
+ \\ addiu $1, $zero, -16
+ \\ and $sp, $sp, $1
+ \\ .set pop
+ \\ j %[posixCallMainAndExit]
+ ,
+ .mips64, .mips64el =>
+ // The lr is already zeroed on entry, as specified by the ABI.
+ \\ addiu $fp, $zero, 0
+ \\ sd $sp, %[argc_argv_ptr]
+ \\ .set push
+ \\ .set noat
+ \\ daddiu $1, $zero, -16
+ \\ and $sp, $sp, $1
+ \\ .set pop
+ \\ j %[posixCallMainAndExit]
+ ,
+ .powerpc, .powerpcle =>
+ // Setup the initial stack frame and clear the back chain pointer.
+ \\ stw 1, %[argc_argv_ptr]
+ \\ li 0, 0
+ \\ stwu 1, -16(1)
+ \\ stw 0, 0(1)
+ \\ mtlr 0
+ \\ b %[posixCallMainAndExit]
+ ,
+ .powerpc64, .powerpc64le =>
+ // Setup the initial stack frame and clear the back chain pointer.
+ // TODO: Support powerpc64 (big endian) on ELFv2.
+ \\ std 1, %[argc_argv_ptr]
+ \\ li 0, 0
+ \\ stdu 0, -32(1)
+ \\ mtlr 0
+ \\ b %[posixCallMainAndExit]
+ ,
+ .sparc64 =>
+ // argc is stored after a register window (16 registers) plus stack bias
+ \\ mov %%g0, %%i6
+ \\ add %%o6, 2175, %%l0
+ \\ stx %%l0, %[argc_argv_ptr]
+ \\ ba %[posixCallMainAndExit]
+ ,
else => @compileError("unsupported arch"),
- },
- }
- // If LLVM inlines stack variables into _start, they will overwrite
- // the command line argument data.
- @call(.never_inline, posixCallMainAndExit, .{});
+ }
+ : [argc_argv_ptr] "=m" (argc_argv_ptr),
+ : [posixCallMainAndExit] "X" (&posixCallMainAndExit),
+ );
}
fn WinStartup() callconv(std.os.windows.WINAPI) noreturn {
@@ -390,8 +366,6 @@ fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn {
}
fn posixCallMainAndExit() callconv(.C) noreturn {
- @setAlignStack(16);
-
const argc = argc_argv_ptr[0];
const argv = @as([*][*:0]u8, @ptrCast(argc_argv_ptr + 1));
diff --git a/lib/std/target.zig b/lib/std/target.zig
@@ -899,6 +899,10 @@ pub const Target = struct {
};
}
+ pub fn isArmOrThumb(arch: Arch) bool {
+ return arch.isARM() or arch.isThumb();
+ }
+
pub fn isWasm(arch: Arch) bool {
return switch (arch) {
.wasm32, .wasm64 => true,
@@ -1960,6 +1964,7 @@ pub const Target = struct {
.thumbeb,
=> return if (target.os.tag.isDarwin() or target.os.tag == .windows) .signed else .unsigned,
.powerpc, .powerpc64 => return if (target.os.tag.isDarwin()) .signed else .unsigned,
+ .powerpcle,
.powerpc64le,
.s390x,
.xcore,
diff --git a/lib/zig.h b/lib/zig.h
@@ -1176,10 +1176,10 @@ typedef signed __int128 zig_i128;
#if zig_little_endian
typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128;
-typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;
+typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;
#else
typedef struct { zig_align(16) uint64_t hi; uint64_t lo; } zig_u128;
-typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;
+typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;
#endif
#define zig_make_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) })
@@ -3197,11 +3197,9 @@ zig_convert_builtin(zig_compiler_rt_f16, zig_f16, trunc, zig_f64,
zig_convert_builtin(zig_f16, zig_f16, trunc, zig_f80, zig_f80, 2)
zig_convert_builtin(zig_f16, zig_f16, trunc, zig_f128, zig_f128, 2)
zig_convert_builtin(zig_f32, zig_f32, extend, zig_compiler_rt_f16, zig_f16, 2)
-zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f64, zig_f64, 2)
zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f80, zig_f80, 2)
zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f128, zig_f128, 2)
zig_convert_builtin(zig_f64, zig_f64, extend, zig_compiler_rt_f16, zig_f16, 2)
-zig_convert_builtin(zig_f64, zig_f64, extend, zig_f32, zig_f32, 2)
zig_convert_builtin(zig_f64, zig_f64, trunc, zig_f80, zig_f80, 2)
zig_convert_builtin(zig_f64, zig_f64, trunc, zig_f128, zig_f128, 2)
zig_convert_builtin(zig_f80, zig_f80, extend, zig_f16, zig_f16, 2)
@@ -3213,6 +3211,21 @@ zig_convert_builtin(zig_f128, zig_f128, extend, zig_f32,
zig_convert_builtin(zig_f128, zig_f128, extend, zig_f64, zig_f64, 2)
zig_convert_builtin(zig_f128, zig_f128, extend, zig_f80, zig_f80, 2)
+#ifdef __ARM_EABI__
+
+zig_extern zig_callconv(pcs("aapcs")) zig_f32 __aeabi_d2f(zig_f64);
+static inline zig_f32 zig_truncdfsf(zig_f64 arg) { return __aeabi_d2f(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) zig_f64 __aeabi_f2d(zig_f32);
+static inline zig_f64 zig_extendsfdf(zig_f32 arg) { return __aeabi_f2d(arg); }
+
+#else /* __ARM_EABI__ */
+
+zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f64, zig_f64, 2)
+zig_convert_builtin(zig_f64, zig_f64, extend, zig_f32, zig_f32, 2)
+
+#endif /* __ARM_EABI__ */
+
#define zig_float_negate_builtin_0(w, c, sb) \
zig_expand_concat(zig_xor_, zig_repr_f##w)(arg, zig_make_f##w(-0x0.0p0, c sb))
#define zig_float_negate_builtin_1(w, c, sb) -arg
@@ -3255,17 +3268,11 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
return lhs operator rhs; \
}
-#define zig_float_builtins(w) \
- zig_convert_builtin( int32_t, int32_t, fix, zig_f##w, zig_f##w, ) \
- zig_convert_builtin(uint32_t, uint32_t, fixuns, zig_f##w, zig_f##w, ) \
+#define zig_common_float_builtins(w) \
zig_convert_builtin( int64_t, int64_t, fix, zig_f##w, zig_f##w, ) \
- zig_convert_builtin(uint64_t, uint64_t, fixuns, zig_f##w, zig_f##w, ) \
zig_convert_builtin(zig_i128, zig_i128, fix, zig_f##w, zig_f##w, ) \
zig_convert_builtin(zig_u128, zig_u128, fixuns, zig_f##w, zig_f##w, ) \
- zig_convert_builtin(zig_f##w, zig_f##w, float, int32_t, int32_t, ) \
- zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint32_t, uint32_t, ) \
zig_convert_builtin(zig_f##w, zig_f##w, float, int64_t, int64_t, ) \
- zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint64_t, uint64_t, ) \
zig_convert_builtin(zig_f##w, zig_f##w, float, zig_i128, zig_i128, ) \
zig_convert_builtin(zig_f##w, zig_f##w, floatun, zig_u128, zig_u128, ) \
zig_expand_concat(zig_float_less_builtin_, zig_has_f##w)(f##w, cmp) \
@@ -3309,12 +3316,68 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \
return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \
}
+zig_common_float_builtins(16)
+zig_common_float_builtins(32)
+zig_common_float_builtins(64)
+zig_common_float_builtins(80)
+zig_common_float_builtins(128)
+
+#define zig_float_builtins(w) \
+ zig_convert_builtin( int32_t, int32_t, fix, zig_f##w, zig_f##w, ) \
+ zig_convert_builtin(uint32_t, uint32_t, fixuns, zig_f##w, zig_f##w, ) \
+ zig_convert_builtin(uint64_t, uint64_t, fixuns, zig_f##w, zig_f##w, ) \
+ zig_convert_builtin(zig_f##w, zig_f##w, float, int32_t, int32_t, ) \
+ zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint32_t, uint32_t, ) \
+ zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint64_t, uint64_t, )
zig_float_builtins(16)
-zig_float_builtins(32)
-zig_float_builtins(64)
zig_float_builtins(80)
zig_float_builtins(128)
+#ifdef __ARM_EABI__
+
+zig_extern zig_callconv(pcs("aapcs")) int32_t __aeabi_f2iz(zig_f32);
+static inline int32_t zig_fixsfsi(zig_f32 arg) { return __aeabi_f2iz(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) uint32_t __aeabi_f2uiz(zig_f32);
+static inline uint32_t zig_fixunssfsi(zig_f32 arg) { return __aeabi_f2uiz(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) uint64_t __aeabi_f2ulz(zig_f32);
+static inline uint64_t zig_fixunssfdi(zig_f32 arg) { return __aeabi_f2ulz(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) zig_f32 __aeabi_i2f(int32_t);
+static inline zig_f32 zig_floatsisf(int32_t arg) { return __aeabi_i2f(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) zig_f32 __aeabi_ui2f(uint32_t);
+static inline zig_f32 zig_floatunsisf(uint32_t arg) { return __aeabi_ui2f(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) zig_f32 __aeabi_ul2f(uint64_t);
+static inline zig_f32 zig_floatundisf(uint64_t arg) { return __aeabi_ul2f(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) int32_t __aeabi_d2iz(zig_f64);
+static inline int32_t zig_fixdfsi(zig_f64 arg) { return __aeabi_d2iz(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) uint32_t __aeabi_d2uiz(zig_f64);
+static inline uint32_t zig_fixunsdfsi(zig_f64 arg) { return __aeabi_d2uiz(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) uint64_t __aeabi_d2ulz(zig_f64);
+static inline uint64_t zig_fixunsdfdi(zig_f64 arg) { return __aeabi_d2ulz(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) zig_f64 __aeabi_i2d(int32_t);
+static inline zig_f64 zig_floatsidf(int32_t arg) { return __aeabi_i2d(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) zig_f64 __aeabi_ui2d(uint32_t);
+static inline zig_f64 zig_floatunsidf(uint32_t arg) { return __aeabi_ui2d(arg); }
+
+zig_extern zig_callconv(pcs("aapcs")) zig_f64 __aeabi_ul2d(uint64_t);
+static inline zig_f64 zig_floatundidf(uint64_t arg) { return __aeabi_ul2d(arg); }
+
+#else /* __ARM_EABI__ */
+
+zig_float_builtins(32)
+zig_float_builtins(64)
+
+#endif /* __ARM_EABI__ */
+
/* ============================ Atomics Support ============================= */
/* Note that atomics should be implemented as macros because most
diff --git a/src/Module.zig b/src/Module.zig
@@ -4186,6 +4186,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
.owner_decl = new_decl,
.owner_decl_index = new_decl_index,
.func_index = .none,
+ .func_is_naked = false,
.fn_ret_ty = Type.void,
.fn_ret_ty_ies = null,
.owner_func_index = .none,
@@ -4268,6 +4269,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
.owner_decl = decl,
.owner_decl_index = decl_index,
.func_index = .none,
+ .func_is_naked = false,
.fn_ret_ty = Type.void,
.fn_ret_ty_ies = null,
.owner_func_index = .none,
@@ -5213,6 +5215,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
.owner_decl = decl,
.owner_decl_index = decl_index,
.func_index = func_index,
+ .func_is_naked = fn_ty_info.cc == .Naked,
.fn_ret_ty = fn_ty_info.return_type.toType(),
.fn_ret_ty_ies = null,
.owner_func_index = func_index,
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -30,6 +30,8 @@ owner_func_index: InternPool.Index,
/// an inline or comptime function call.
/// This could be `none`, a `func_decl`, or a `func_instance`.
func_index: InternPool.Index,
+/// Whether the type of func_index has a calling convention of `.Naked`.
+func_is_naked: bool,
/// Used to restore the error return trace when returning a non-error from a function.
error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none,
/// When semantic analysis needs to know the return type of the function whose body
@@ -6827,6 +6829,10 @@ fn analyzeCall(
var is_inline_call = is_comptime_call or modifier == .always_inline or
func_ty_info.cc == .Inline;
+ if (sema.func_is_naked and !is_inline_call and !is_comptime_call) {
+ return sema.fail(block, call_src, "runtime call not allowed in naked function", .{});
+ }
+
if (!is_inline_call and is_generic_call) {
if (sema.instantiateGenericCall(
block,
@@ -7509,6 +7515,9 @@ fn instantiateGenericCall(
.owner_decl = sema.owner_decl,
.owner_decl_index = sema.owner_decl_index,
.func_index = sema.owner_func_index,
+ // This may not be known yet, since the calling convention could be generic, but there
+ // should be no illegal instructions encountered while creating the function anyway.
+ .func_is_naked = false,
.fn_ret_ty = Type.void,
.fn_ret_ty_ies = null,
.owner_func_index = .none,
@@ -18193,10 +18202,20 @@ fn zirRetImplicit(
const tracy = trace(@src());
defer tracy.end();
+ if (block.inlining == null and sema.func_is_naked) {
+ assert(!block.is_comptime);
+ if (block.wantSafety()) {
+ // Calling a safety function from a naked function would not be legal.
+ _ = try block.addNoOp(.trap);
+ } else {
+ try block.addUnreachable(false);
+ }
+ return always_noreturn;
+ }
+
const mod = sema.mod;
const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
const operand = try sema.resolveInst(inst_data.operand);
-
const r_brace_src = inst_data.src();
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod);
@@ -18222,7 +18241,7 @@ fn zirRetImplicit(
return sema.failWithOwnedErrorMsg(msg);
}
- return sema.analyzeRet(block, operand, .unneeded);
+ return sema.analyzeRet(block, operand, r_brace_src);
}
fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
@@ -18244,7 +18263,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
const src = inst_data.src();
const ret_ptr = try sema.resolveInst(inst_data.operand);
- if (block.is_comptime or block.inlining != null) {
+ if (block.is_comptime or block.inlining != null or sema.func_is_naked) {
const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
return sema.analyzeRet(block, operand, src);
}
@@ -18450,6 +18469,8 @@ fn analyzeRet(
return always_noreturn;
} else if (block.is_comptime) {
return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
+ } else if (sema.func_is_naked) {
+ return sema.fail(block, src, "cannot return from naked function", .{});
}
try sema.resolveTypeLayout(sema.fn_ret_ty);
@@ -33571,6 +33592,7 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
.owner_decl = decl,
.owner_decl_index = decl_index,
.func_index = .none,
+ .func_is_naked = false,
.fn_ret_ty = Type.void,
.fn_ret_ty_ies = null,
.owner_func_index = .none,
@@ -33622,6 +33644,7 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
.owner_decl = decl,
.owner_decl_index = decl_index,
.func_index = .none,
+ .func_is_naked = false,
.fn_ret_ty = Type.void,
.fn_ret_ty_ies = null,
.owner_func_index = .none,
@@ -34405,6 +34428,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
.owner_decl = decl,
.owner_decl_index = decl_index,
.func_index = .none,
+ .func_is_naked = false,
.fn_ret_ty = Type.void,
.fn_ret_ty_ies = null,
.owner_func_index = .none,
@@ -34748,6 +34772,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
.owner_decl = decl,
.owner_decl_index = decl_index,
.func_index = .none,
+ .func_is_naked = false,
.fn_ret_ty = Type.void,
.fn_ret_ty_ies = null,
.owner_func_index = .none,
diff --git a/src/TypedValue.zig b/src/TypedValue.zig
@@ -196,6 +196,7 @@ pub fn print(
.undef => return writer.writeAll("undefined"),
.runtime_value => return writer.writeAll("(runtime value)"),
.simple_value => |simple_value| switch (simple_value) {
+ .void => return writer.writeAll("{}"),
.empty_struct => return printAggregate(ty, val, writer, level, mod),
.generic_poison => return writer.writeAll("(generic poison)"),
else => return writer.writeAll(@tagName(simple_value)),
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
@@ -177,7 +177,7 @@ pub const MCValue = union(enum) {
/// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register.
register_overflow: struct { reg: Register, eflags: Condition },
/// The value is in memory at a hard-coded address.
- /// If the type is a pointer, it means the pointer address is at this memory location.
+ /// If the type is a pointer, it means the pointer address is stored at this memory location.
memory: u64,
/// The value is in memory at a constant offset from the address in a register.
indirect: RegisterOffset,
@@ -300,7 +300,7 @@ pub const MCValue = union(enum) {
.load_tlv,
.load_frame,
.reserved_frame,
- => unreachable, // not a dereferenceable
+ => unreachable, // not dereferenceable
.immediate => |addr| .{ .memory = addr },
.register => |reg| .{ .indirect = .{ .reg = reg } },
.register_offset => |reg_off| .{ .indirect = reg_off },
@@ -3468,14 +3468,14 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
const mod = self.bin_file.options.module.?;
const abi_size: u32 = @intCast(ty.abiSize(mod));
const int_info = ty.intInfo(mod);
- const dividend: Register = switch (lhs) {
+ const dividend = switch (lhs) {
.register => |reg| reg,
else => try self.copyToTmpRegister(ty, lhs),
};
const dividend_lock = self.register_manager.lockReg(dividend);
defer if (dividend_lock) |lock| self.register_manager.unlockReg(lock);
- const divisor: Register = switch (rhs) {
+ const divisor = switch (rhs) {
.register => |reg| reg,
else => try self.copyToTmpRegister(ty, rhs),
};
@@ -9184,6 +9184,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
}
fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
+ const mod = self.bin_file.options.module.?;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.Asm, ty_pl.payload);
const clobbers_len: u31 = @truncate(extra.data.flags);
@@ -9196,23 +9197,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
var result: MCValue = .none;
var args = std.StringArrayHashMap(MCValue).init(self.gpa);
- try args.ensureTotalCapacity(outputs.len + inputs.len + clobbers_len);
+ try args.ensureTotalCapacity(outputs.len + inputs.len);
defer {
- for (args.values()) |arg| switch (arg) {
- .register => |reg| self.register_manager.unlockReg(.{ .register = reg }),
- else => {},
- };
+ for (args.values()) |arg| if (arg.getReg()) |reg|
+ self.register_manager.unlockReg(.{ .register = reg });
args.deinit();
}
- if (outputs.len > 1) {
- return self.fail("TODO implement codegen for asm with more than 1 output", .{});
- }
-
+ var outputs_extra_i = extra_i;
for (outputs) |output| {
- if (output != .none) {
- return self.fail("TODO implement codegen for non-expr asm", .{});
- }
const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
@@ -9220,21 +9213,48 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
// for the string, we still use the next u32 for the null terminator.
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
- const mcv: MCValue = if (mem.eql(u8, constraint, "=r"))
- .{ .register = self.register_manager.tryAllocReg(inst, gp) orelse
- return self.fail("ran out of registers lowering inline asm", .{}) }
+ const maybe_inst = switch (output) {
+ .none => inst,
+ else => null,
+ };
+ const ty = switch (output) {
+ .none => self.typeOfIndex(inst),
+ else => self.typeOf(output).childType(mod),
+ };
+ const arg_maybe_reg: ?Register = if (mem.eql(u8, constraint, "=r"))
+ self.register_manager.tryAllocReg(maybe_inst, regClassForType(ty, mod)) orelse
+ return self.fail("ran out of registers lowering inline asm", .{})
+ else if (mem.eql(u8, constraint, "=m"))
+ if (output != .none) null else return self.fail(
+ "memory constraint unsupported for asm result",
+ .{},
+ )
+ else if (mem.eql(u8, constraint, "=g"))
+ self.register_manager.tryAllocReg(maybe_inst, regClassForType(ty, mod)) orelse
+ if (output != .none) null else return self.fail(
+ "ran out of register lowering inline asm",
+ .{},
+ )
else if (mem.startsWith(u8, constraint, "={") and mem.endsWith(u8, constraint, "}"))
- .{ .register = parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse
- return self.fail("unrecognized register constraint: '{s}'", .{constraint}) }
+ parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse
+ return self.fail("invalid register constraint: '{s}'", .{constraint})
else
- return self.fail("unrecognized constraint: '{s}'", .{constraint});
- args.putAssumeCapacity(name, mcv);
- switch (mcv) {
- .register => |reg| _ = if (RegisterManager.indexOfRegIntoTracked(reg)) |_|
- self.register_manager.lockRegAssumeUnused(reg),
- else => {},
- }
- if (output == .none) result = mcv;
+ return self.fail("invalid constraint: '{s}'", .{constraint});
+ const arg_mcv: MCValue = if (arg_maybe_reg) |reg| .{ .register = reg } else arg: {
+ const ptr_mcv = try self.resolveInst(output);
+ switch (ptr_mcv) {
+ .immediate => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_|
+ break :arg ptr_mcv.deref(),
+ .register, .register_offset, .lea_frame => break :arg ptr_mcv.deref(),
+ else => {},
+ }
+ break :arg .{ .indirect = .{ .reg = try self.copyToTmpRegister(Type.usize, ptr_mcv) } };
+ };
+ if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| {
+ _ = self.register_manager.lockRegAssumeUnused(reg);
+ };
+ args.putAssumeCapacity(name, arg_mcv);
+ if (output == .none) result = arg_mcv;
}
for (inputs) |input| {
@@ -9245,16 +9265,53 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
// for the string, we still use the next u32 for the null terminator.
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
- if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
- return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
- }
- const reg_name = constraint[1 .. constraint.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail("unrecognized register: '{s}'", .{reg_name});
-
- const arg_mcv = try self.resolveInst(input);
- try self.register_manager.getReg(reg, null);
- try self.genSetReg(reg, self.typeOf(input), arg_mcv);
+ const ty = self.typeOf(input);
+ const input_mcv = try self.resolveInst(input);
+ const arg_mcv: MCValue = if (mem.eql(u8, constraint, "r")) switch (input_mcv) {
+ .register => input_mcv,
+ else => .{ .register = try self.copyToTmpRegister(ty, input_mcv) },
+ } else if (mem.eql(u8, constraint, "m")) arg: {
+ switch (input_mcv) {
+ .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_|
+ break :arg input_mcv,
+ .indirect, .load_frame => break :arg input_mcv,
+ .load_direct, .load_got, .load_tlv => {},
+ else => {
+ const temp_mcv = try self.allocTempRegOrMem(ty, false);
+ try self.genCopy(ty, temp_mcv, input_mcv);
+ break :arg temp_mcv;
+ },
+ }
+ const addr_reg = self.register_manager.tryAllocReg(null, gp) orelse {
+ const temp_mcv = try self.allocTempRegOrMem(ty, false);
+ try self.genCopy(ty, temp_mcv, input_mcv);
+ break :arg temp_mcv;
+ };
+ try self.genSetReg(addr_reg, Type.usize, input_mcv.address());
+ break :arg .{ .indirect = .{ .reg = addr_reg } };
+ } else if (mem.eql(u8, constraint, "g")) arg: {
+ switch (input_mcv) {
+ .register, .indirect, .load_frame => break :arg input_mcv,
+ .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_|
+ break :arg input_mcv,
+ else => {},
+ }
+ const temp_mcv = try self.allocTempRegOrMem(ty, true);
+ try self.genCopy(ty, temp_mcv, input_mcv);
+ break :arg temp_mcv;
+ } else if (mem.eql(u8, constraint, "X"))
+ input_mcv
+ else if (mem.startsWith(u8, constraint, "{") and mem.endsWith(u8, constraint, "}")) arg: {
+ const reg = parseRegName(constraint["{".len .. constraint.len - "}".len]) orelse
+ return self.fail("invalid register constraint: '{s}'", .{constraint});
+ try self.register_manager.getReg(reg, null);
+ try self.genSetReg(reg, ty, input_mcv);
+ break :arg .{ .register = reg };
+ } else return self.fail("invalid constraint: '{s}'", .{constraint});
+ if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| {
+ _ = self.register_manager.lockReg(reg);
+ };
+ args.putAssumeCapacity(name, arg_mcv);
}
{
@@ -9293,7 +9350,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
}
}
break :mnem std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse
- return self.fail("Invalid mnemonic: '{s}'", .{mnem_str});
+ return self.fail("invalid mnemonic: '{s}'", .{mnem_str});
} };
var op_it = mem.tokenizeScalar(u8, mnem_it.rest(), ',');
@@ -9304,43 +9361,73 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
if (mem.startsWith(u8, op_str, "%%")) {
const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':');
const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse
- return self.fail("Invalid register: '{s}'", .{op_str});
+ return self.fail("invalid register: '{s}'", .{op_str});
if (colon) |colon_pos| {
const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch
- return self.fail("Invalid displacement: '{s}'", .{op_str});
+ return self.fail("invalid displacement: '{s}'", .{op_str});
op.* = .{ .mem = Memory.sib(
- mnem_size orelse return self.fail("Unknown size: '{s}'", .{op_str}),
+ mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}),
.{ .base = .{ .reg = reg }, .disp = disp },
) };
} else {
if (mnem_size) |size| if (reg.bitSize() != size.bitSize())
- return self.fail("Invalid register size: '{s}'", .{op_str});
+ return self.fail("invalid register size: '{s}'", .{op_str});
op.* = .{ .reg = reg };
}
} else if (mem.startsWith(u8, op_str, "%[") and mem.endsWith(u8, op_str, "]")) {
- switch (args.get(op_str["%[".len .. op_str.len - "]".len]) orelse
- return self.fail("No matching constraint: '{s}'", .{op_str})) {
- .register => |reg| op.* = .{ .reg = reg },
- else => return self.fail("Invalid constraint: '{s}'", .{op_str}),
- }
+ const colon = mem.indexOfScalarPos(u8, op_str, "%[".len, ':');
+ const modifier = if (colon) |colon_pos|
+ op_str[colon_pos + 1 .. op_str.len - "]".len]
+ else
+ "";
+ op.* = switch (args.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse
+ return self.fail("no matching constraint: '{s}'", .{op_str})) {
+ .register => |reg| if (std.mem.eql(u8, modifier, ""))
+ .{ .reg = reg }
+ else
+ return self.fail("invalid modifier: '{s}'", .{modifier}),
+ .memory => |addr| if (std.mem.eql(u8, modifier, "") or
+ std.mem.eql(u8, modifier, "P"))
+ .{ .mem = Memory.sib(
+ mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}),
+ .{ .base = .{ .reg = .ds }, .disp = @intCast(@as(i64, @bitCast(addr))) },
+ ) }
+ else
+ return self.fail("invalid modifier: '{s}'", .{modifier}),
+ .indirect => |reg_off| if (std.mem.eql(u8, modifier, ""))
+ .{ .mem = Memory.sib(
+ mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}),
+ .{ .base = .{ .reg = reg_off.reg }, .disp = reg_off.off },
+ ) }
+ else
+ return self.fail("invalid modifier: '{s}'", .{modifier}),
+ .load_frame => |frame_addr| if (std.mem.eql(u8, modifier, ""))
+ .{ .mem = Memory.sib(
+ mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}),
+ .{ .base = .{ .frame = frame_addr.index }, .disp = frame_addr.off },
+ ) }
+ else
+ return self.fail("invalid modifier: '{s}'", .{modifier}),
+ else => return self.fail("invalid constraint: '{s}'", .{op_str}),
+ };
} else if (mem.startsWith(u8, op_str, "$")) {
if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| {
if (mnem_size) |size| {
const max = @as(u64, math.maxInt(u64)) >> @intCast(64 - (size.bitSize() - 1));
if ((if (s < 0) ~s else s) > max)
- return self.fail("Invalid immediate size: '{s}'", .{op_str});
+ return self.fail("invalid immediate size: '{s}'", .{op_str});
}
op.* = .{ .imm = Immediate.s(s) };
} else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| {
if (mnem_size) |size| {
const max = @as(u64, math.maxInt(u64)) >> @intCast(64 - size.bitSize());
if (u > max)
- return self.fail("Invalid immediate size: '{s}'", .{op_str});
+ return self.fail("invalid immediate size: '{s}'", .{op_str});
}
op.* = .{ .imm = Immediate.u(u) };
- } else |_| return self.fail("Invalid immediate: '{s}'", .{op_str});
- } else return self.fail("Invalid operand: '{s}'", .{op_str});
- } else if (op_it.next()) |op_str| return self.fail("Extra operand: '{s}'", .{op_str});
+ } else |_| return self.fail("invalid immediate: '{s}'", .{op_str});
+ } else return self.fail("invalid operand: '{s}'", .{op_str});
+ } else if (op_it.next()) |op_str| return self.fail("extra operand: '{s}'", .{op_str});
(switch (ops[0]) {
.none => self.asmOpOnly(mnem_tag),
@@ -9407,6 +9494,20 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
};
}
+ for (outputs, args.values()[0..outputs.len]) |output, mcv| {
+ const extra_bytes = std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]);
+ const constraint =
+ std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0);
+ const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
+ // This equation accounts for the fact that even if we have exactly 4 bytes
+ // for the string, we still use the next u32 for the null terminator.
+ outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4;
+
+ if (output == .none) continue;
+ if (mcv != .register) continue;
+ try self.store(self.typeOf(output), try self.resolveInst(output), mcv);
+ }
+
simple: {
var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
var buf_index: usize = 0;
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
@@ -30,7 +30,7 @@ pub const CValue = union(enum) {
/// Address of a local.
local_ref: LocalIndex,
/// A constant instruction, to be rendered inline.
- constant: Air.Inst.Ref,
+ constant: InternPool.Index,
/// Index into the parameters
arg: usize,
/// The array field of a parameter
@@ -302,7 +302,7 @@ pub const Function = struct {
try f.object.dg.renderValue(writer, ty, val, .StaticInitializer);
try writer.writeAll(";\n ");
break :result decl_c_value;
- } else .{ .constant = ref };
+ } else .{ .constant = val.toIntern() };
gop.value_ptr.* = result;
return result;
@@ -352,57 +352,63 @@ pub const Function = struct {
fn writeCValue(f: *Function, w: anytype, c_value: CValue, location: ValueRenderLocation) !void {
switch (c_value) {
- .constant => |inst| {
- const mod = f.object.dg.module;
- const ty = f.typeOf(inst);
- const val = (try f.air.value(inst, mod)).?;
- return f.object.dg.renderValue(w, ty, val, location);
- },
- .undef => |ty| return f.object.dg.renderValue(w, ty, Value.undef, location),
- else => return f.object.dg.writeCValue(w, c_value),
+ .constant => |val| try f.object.dg.renderValue(
+ w,
+ f.object.dg.module.intern_pool.typeOf(val).toType(),
+ val.toValue(),
+ location,
+ ),
+ .undef => |ty| try f.object.dg.renderValue(w, ty, Value.undef, location),
+ else => try f.object.dg.writeCValue(w, c_value),
}
}
fn writeCValueDeref(f: *Function, w: anytype, c_value: CValue) !void {
switch (c_value) {
- .constant => |inst| {
- const mod = f.object.dg.module;
- const ty = f.typeOf(inst);
- const val = (try f.air.value(inst, mod)).?;
+ .constant => |val| {
try w.writeAll("(*");
- try f.object.dg.renderValue(w, ty, val, .Other);
- return w.writeByte(')');
+ try f.object.dg.renderValue(
+ w,
+ f.object.dg.module.intern_pool.typeOf(val).toType(),
+ val.toValue(),
+ .Other,
+ );
+ try w.writeByte(')');
},
- else => return f.object.dg.writeCValueDeref(w, c_value),
+ else => try f.object.dg.writeCValueDeref(w, c_value),
}
}
fn writeCValueMember(f: *Function, w: anytype, c_value: CValue, member: CValue) !void {
switch (c_value) {
- .constant => |inst| {
- const mod = f.object.dg.module;
- const ty = f.typeOf(inst);
- const val = (try f.air.value(inst, mod)).?;
- try f.object.dg.renderValue(w, ty, val, .Other);
+ .constant => |val| {
+ try f.object.dg.renderValue(
+ w,
+ f.object.dg.module.intern_pool.typeOf(val).toType(),
+ val.toValue(),
+ .Other,
+ );
try w.writeByte('.');
- return f.writeCValue(w, member, .Other);
+ try f.writeCValue(w, member, .Other);
},
- else => return f.object.dg.writeCValueMember(w, c_value, member),
+ else => try f.object.dg.writeCValueMember(w, c_value, member),
}
}
fn writeCValueDerefMember(f: *Function, w: anytype, c_value: CValue, member: CValue) !void {
switch (c_value) {
- .constant => |inst| {
- const mod = f.object.dg.module;
- const ty = f.typeOf(inst);
- const val = (try f.air.value(inst, mod)).?;
+ .constant => |val| {
try w.writeByte('(');
- try f.object.dg.renderValue(w, ty, val, .Other);
+ try f.object.dg.renderValue(
+ w,
+ f.object.dg.module.intern_pool.typeOf(val).toType(),
+ val.toValue(),
+ .Other,
+ );
try w.writeAll(")->");
- return f.writeCValue(w, member, .Other);
+ try f.writeCValue(w, member, .Other);
},
- else => return f.object.dg.writeCValueDerefMember(w, c_value, member),
+ else => try f.object.dg.writeCValueDerefMember(w, c_value, member),
}
}
@@ -4763,11 +4769,22 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
return .none;
}
-fn asmInputNeedsLocal(constraint: []const u8, value: CValue) bool {
+fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool {
+ const target = f.object.dg.module.getTarget();
return switch (constraint[0]) {
'{' => true,
'i', 'r' => false,
- else => value == .constant,
+ 'I' => !target.cpu.arch.isArmOrThumb(),
+ else => switch (value) {
+ .constant => |val| switch (f.object.dg.module.intern_pool.indexToKey(val)) {
+ .ptr => |ptr| switch (ptr.addr) {
+ .decl => false,
+ else => true,
+ },
+ else => true,
+ },
+ else => false,
+ },
};
}
@@ -4848,7 +4865,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
const is_reg = constraint[0] == '{';
const input_val = try f.resolveInst(input);
- if (asmInputNeedsLocal(constraint, input_val)) {
+ if (asmInputNeedsLocal(f, constraint, input_val)) {
const input_ty = f.typeOf(input);
if (is_reg) try writer.writeAll("register ");
const alignment = 0;
@@ -4969,7 +4986,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
const is_reg = constraint[0] == '{';
const input_val = try f.resolveInst(input);
try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)});
- try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: {
+ try f.writeCValue(writer, if (asmInputNeedsLocal(f, constraint, input_val)) local: {
const input_local = .{ .local = locals_index };
locals_index += 1;
break :local input_local;
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
@@ -417,7 +417,7 @@ const DataLayoutBuilder = struct {
if (idx != size) try writer.print(":{d}", .{idx});
}
}
- if (self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb())
+ if (self.target.cpu.arch.isArmOrThumb())
try writer.writeAll("-Fi8"); // for thumb interwork
if (self.target.cpu.arch != .hexagon) {
if (self.target.cpu.arch == .s390x) try self.typeAlignment(.integer, 1, 8, 8, false, writer);
@@ -620,7 +620,7 @@ const DataLayoutBuilder = struct {
else => {},
}
},
- .vector => if (self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb()) {
+ .vector => if (self.target.cpu.arch.isArmOrThumb()) {
switch (size) {
128 => abi = 64,
else => {},
@@ -670,7 +670,7 @@ const DataLayoutBuilder = struct {
else => {},
},
.aggregate => if (self.target.os.tag == .uefi or self.target.os.tag == .windows or
- self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb())
+ self.target.cpu.arch.isArmOrThumb())
{
pref = @min(pref, self.target.ptrBitWidth());
} else if (self.target.cpu.arch == .hexagon) {
@@ -6809,8 +6809,6 @@ pub const FuncGen = struct {
}
llvm_constraints.appendAssumeCapacity('=');
- // Pass any non-return outputs indirectly, if the constraint accepts a memory location
- is_indirect.* = (output != .none) and constraintAllowsMemory(constraint);
if (output != .none) {
const output_inst = try self.resolveInst(output);
const output_ty = self.typeOf(output);
@@ -6825,6 +6823,8 @@ pub const FuncGen = struct {
}),
}
+ // Pass any non-return outputs indirectly, if the constraint accepts a memory location
+ is_indirect.* = constraintAllowsMemory(constraint);
if (is_indirect.*) {
// Pass the result by reference as an indirect output (e.g. "=*m")
llvm_constraints.appendAssumeCapacity('*');
@@ -6841,11 +6841,13 @@ pub const FuncGen = struct {
} else {
switch (constraint[0]) {
'=' => {},
- else => return self.todo("unsupported output constraint on result type '{c}'", .{
- constraint[0],
+ else => return self.todo("unsupported output constraint on result type '{s}'", .{
+ constraint,
}),
}
+ is_indirect.* = false;
+
const ret_ty = self.typeOfIndex(inst);
llvm_ret_types[llvm_ret_i] = try o.lowerType(ret_ty);
llvm_ret_i += 1;
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
@@ -1654,7 +1654,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
}
if (self.base.options.link_mode == .Static) {
- if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
+ if (target.cpu.arch.isArmOrThumb()) {
try argv.append("-Bstatic");
} else {
try argv.append("-static");
diff --git a/test/behavior/align.zig b/test/behavior/align.zig
@@ -198,11 +198,12 @@ test "alignment and size of structs with 128-bit fields" {
else => return error.SkipZigTest,
};
+ const min_struct_align = if (builtin.zig_backend == .stage2_c) 16 else 0;
comptime {
- assert(@alignOf(A) == expected.a_align);
+ assert(@alignOf(A) == @max(expected.a_align, min_struct_align));
assert(@sizeOf(A) == expected.a_size);
- assert(@alignOf(B) == expected.b_align);
+ assert(@alignOf(B) == @max(expected.b_align, min_struct_align));
assert(@sizeOf(B) == expected.b_size);
assert(@alignOf(u128) == expected.u128_align);
diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig
@@ -421,7 +421,7 @@ test "bitcast nan float does modify signaling bit" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
// TODO: https://github.com/ziglang/zig/issues/14366
- if (builtin.cpu.arch == .arm and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_llvm and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
// 16 bit
const snan_f16_const = math.nan_f16;
diff --git a/test/behavior/call_tail.zig b/test/behavior/call_tail.zig
@@ -32,7 +32,16 @@ noinline fn insertionSort(data: []u64) void {
test "arguments pointed to on stack into tailcall" {
switch (builtin.cpu.arch) {
- .wasm32, .mips, .mipsel, .powerpc, .powerpcle, .powerpc64le => return error.SkipZigTest,
+ .wasm32,
+ .mips,
+ .mipsel,
+ .mips64,
+ .mips64el,
+ .powerpc,
+ .powerpcle,
+ .powerpc64,
+ .powerpc64le,
+ => return error.SkipZigTest,
else => {},
}
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig
@@ -123,6 +123,7 @@ test "@floatFromInt(f80)" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
const S = struct {
fn doTheTest(comptime Int: type) !void {
@@ -1369,6 +1370,7 @@ test "cast f16 to wider types" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig
@@ -533,6 +533,7 @@ test "runtime 128 bit integer division" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
var a: u128 = 152313999999999991610955792383;
var b: u128 = 10000000000000000000;
diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig
@@ -554,6 +554,7 @@ test "another, possibly redundant, @fabs test" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try testFabsLegacy(f128, 12.0);
try comptime testFabsLegacy(f128, 12.0);
@@ -576,6 +577,7 @@ test "@fabs f80" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try testFabsLegacy(f80, 12.0);
try comptime testFabsLegacy(f80, 12.0);
@@ -593,6 +595,7 @@ test "a third @fabs test, surely there should not be three fabs tests" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
// normals
@@ -684,6 +687,7 @@ test "@floor f80" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
// https://github.com/ziglang/zig/issues/12602
@@ -700,6 +704,7 @@ test "@floor f128" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try testFloorLegacy(f128, 12.0);
try comptime testFloorLegacy(f128, 12.0);
@@ -780,6 +785,7 @@ test "@ceil f80" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
// https://github.com/ziglang/zig/issues/12602
@@ -796,6 +802,7 @@ test "@ceil f128" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try testCeilLegacy(f128, 12.0);
try comptime testCeilLegacy(f128, 12.0);
@@ -875,6 +882,7 @@ test "@trunc f80" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
// https://github.com/ziglang/zig/issues/12602
@@ -897,6 +905,7 @@ test "@trunc f128" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try testTruncLegacy(f128, 12.0);
try comptime testTruncLegacy(f128, 12.0);
diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig
@@ -151,6 +151,7 @@ fn fnWithUnreachable() noreturn {
test "extern struct with stdcallcc fn pointer" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and builtin.cpu.arch == .x86) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const S = extern struct {
diff --git a/test/behavior/math.zig b/test/behavior/math.zig
@@ -664,6 +664,7 @@ test "128-bit multiplication" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
{
var a: i128 = 3;
@@ -1311,6 +1312,7 @@ test "remainder division" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
// https://github.com/ziglang/zig/issues/12602
@@ -1455,6 +1457,7 @@ test "@round f80" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try testRound(f80, 12.0);
try comptime testRound(f80, 12.0);
@@ -1467,6 +1470,7 @@ test "@round f128" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try testRound(f128, 12.0);
try comptime testRound(f128, 12.0);
diff --git a/test/behavior/maximum_minimum.zig b/test/behavior/maximum_minimum.zig
@@ -110,6 +110,7 @@ test "@min/max for floats" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
const S = struct {
fn doTheTest(comptime T: type) !void {
diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig
@@ -56,6 +56,7 @@ test "@mulAdd f80" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try comptime testMulAdd80();
try testMulAdd80();
@@ -75,6 +76,7 @@ test "@mulAdd f128" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try comptime testMulAdd128();
try testMulAdd128();
@@ -177,6 +179,7 @@ test "vector f80" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try comptime vector80();
try vector80();
@@ -201,6 +204,7 @@ test "vector f128" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
try comptime vector128();
try vector128();
diff --git a/test/behavior/saturating_arithmetic.zig b/test/behavior/saturating_arithmetic.zig
@@ -157,6 +157,7 @@ test "saturating multiplication" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .wasm32) {
// https://github.com/ziglang/zig/issues/9660
diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig
@@ -428,7 +428,7 @@ test "packed struct 24bits" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.cpu.arch == .wasm32) return error.SkipZigTest; // TODO
- if (builtin.cpu.arch == .arm) return error.SkipZigTest; // TODO
+ if (comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
@@ -824,6 +824,7 @@ test "non-packed struct with u128 entry in union" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
const U = union(enum) {
Num: u128,
@@ -944,7 +945,7 @@ test "comptime struct field" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
- if (builtin.cpu.arch == .arm) return error.SkipZigTest; // TODO
+ if (comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; // TODO
const T = struct {
a: i32,
diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig
@@ -101,7 +101,7 @@ test "simple variadic function" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
- if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) {
+ if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
@@ -151,7 +151,7 @@ test "variadic functions" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
- if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) {
+ if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
@@ -195,7 +195,7 @@ test "copy VaList" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
- if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) {
+ if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
@@ -228,7 +228,7 @@ test "unused VaList arg" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
- if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) {
+ if (builtin.os.tag != .macos and comptime builtin.cpu.arch.isAARCH64()) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig
@@ -102,6 +102,7 @@ test "vector float operators" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
const S = struct {
@@ -494,6 +495,7 @@ test "vector division operators" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_llvm and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
@@ -705,6 +707,7 @@ test "vector reduce operation" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
const S = struct {
fn testReduce(comptime op: std.builtin.ReduceOp, x: anytype, expected: anytype) !void {
diff --git a/test/src/Cases.zig b/test/src/Cases.zig
@@ -1524,7 +1524,7 @@ fn runOneCase(
}
} else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) {
.native => {
- if (case.backend == .stage2 and case.target.getCpuArch() == .arm) {
+ if (case.backend == .stage2 and case.target.getCpuArch().isArmOrThumb()) {
// https://github.com/ziglang/zig/issues/13623
continue :update; // Pass test.
}
diff --git a/test/tests.zig b/test/tests.zig
@@ -1053,6 +1053,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
// TODO stop violating these pedantic errors. spotted on linux
"-Wno-address-of-packed-member",
"-Wno-gnu-folding-constant",
+ "-Wno-incompatible-function-pointer-types",
"-Wno-incompatible-pointer-types",
"-Wno-overlength-strings",
// TODO stop violating these pedantic errors. spotted on darwin