| author | |
| committer | |
| log | 530e67cb868fbd24900d48eee891efa1aa135096 |
| tree | eb6c40f94b148d83143992cc3868af100c22af9c |
| parent | 96e593145dcdb53ca02f2a365abc68415c4302b6 |
| parent | afbcb6209dbe6812679324aab564884085b8cf44 |
| signature |
Initial bringup for Linux/Thumb214 files changed, 233 insertions(+), 16 deletions(-)
lib/std/os/bits/linux.zig+1-1| ... | ... | @@ -18,7 +18,7 @@ pub usingnamespace switch (builtin.arch) { |
| 18 | 18 | .i386 => @import("linux/i386.zig"), |
| 19 | 19 | .x86_64 => @import("linux/x86_64.zig"), |
| 20 | 20 | .aarch64 => @import("linux/arm64.zig"), |
| 21 | .arm => @import("linux/arm-eabi.zig"), | |
| 21 | .arm, .thumb => @import("linux/arm-eabi.zig"), | |
| 22 | 22 | .riscv64 => @import("linux/riscv64.zig"), |
| 23 | 23 | .sparcv9 => @import("linux/sparc64.zig"), |
| 24 | 24 | .mips, .mipsel => @import("linux/mips.zig"), |
lib/std/os/linux.zig+1| ... | ... | @@ -23,6 +23,7 @@ pub usingnamespace switch (builtin.arch) { |
| 23 | 23 | .x86_64 => @import("linux/x86_64.zig"), |
| 24 | 24 | .aarch64 => @import("linux/arm64.zig"), |
| 25 | 25 | .arm => @import("linux/arm-eabi.zig"), |
| 26 | .thumb => @import("linux/thumb.zig"), | |
| 26 | 27 | .riscv64 => @import("linux/riscv64.zig"), |
| 27 | 28 | .sparcv9 => @import("linux/sparc64.zig"), |
| 28 | 29 | .mips, .mipsel => @import("linux/mips.zig"), |
lib/std/os/linux/thumb.zig created+168| ... | ... | @@ -0,0 +1,168 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2021 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | usingnamespace @import("../bits.zig"); | |
| 7 | ||
| 8 | // The syscall interface is identical to the ARM one but we're facing an extra | |
| 9 | // challenge: r7, the register where the syscall number is stored, may be | |
| 10 | // reserved for the frame pointer. | |
| 11 | // Save and restore r7 around the syscall without touching the stack pointer not | |
| 12 | // to break the frame chain. | |
| 13 | ||
| 14 | pub fn syscall0(number: SYS) usize { | |
| 15 | @setRuntimeSafety(false); | |
| 16 | ||
| 17 | var buf: [2]usize = .{ @enumToInt(number), undefined }; | |
| 18 | return asm volatile ( | |
| 19 | \\ str r7, [%[tmp], #4] | |
| 20 | \\ ldr r7, [%[tmp]] | |
| 21 | \\ svc #0 | |
| 22 | \\ ldr r7, [%[tmp], #4] | |
| 23 | : [ret] "={r0}" (-> usize) | |
| 24 | : [tmp] "{r1}" (buf) | |
| 25 | : "memory" | |
| 26 | ); | |
| 27 | } | |
| 28 | ||
| 29 | pub fn syscall1(number: SYS, arg1: usize) usize { | |
| 30 | @setRuntimeSafety(false); | |
| 31 | ||
| 32 | var buf: [2]usize = .{ @enumToInt(number), undefined }; | |
| 33 | return asm volatile ( | |
| 34 | \\ str r7, [%[tmp], #4] | |
| 35 | \\ ldr r7, [%[tmp]] | |
| 36 | \\ svc #0 | |
| 37 | \\ ldr r7, [%[tmp], #4] | |
| 38 | : [ret] "={r0}" (-> usize) | |
| 39 | : [tmp] "{r1}" (buf), | |
| 40 | [arg1] "{r0}" (arg1) | |
| 41 | : "memory" | |
| 42 | ); | |
| 43 | } | |
| 44 | ||
| 45 | pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { | |
| 46 | @setRuntimeSafety(false); | |
| 47 | ||
| 48 | var buf: [2]usize = .{ @enumToInt(number), undefined }; | |
| 49 | return asm volatile ( | |
| 50 | \\ str r7, [%[tmp], #4] | |
| 51 | \\ ldr r7, [%[tmp]] | |
| 52 | \\ svc #0 | |
| 53 | \\ ldr r7, [%[tmp], #4] | |
| 54 | : [ret] "={r0}" (-> usize) | |
| 55 | : [tmp] "{r2}" (buf), | |
| 56 | [arg1] "{r0}" (arg1), | |
| 57 | [arg2] "{r1}" (arg2) | |
| 58 | : "memory" | |
| 59 | ); | |
| 60 | } | |
| 61 | ||
| 62 | pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { | |
| 63 | @setRuntimeSafety(false); | |
| 64 | ||
| 65 | var buf: [2]usize = .{ @enumToInt(number), undefined }; | |
| 66 | return asm volatile ( | |
| 67 | \\ str r7, [%[tmp], #4] | |
| 68 | \\ ldr r7, [%[tmp]] | |
| 69 | \\ svc #0 | |
| 70 | \\ ldr r7, [%[tmp], #4] | |
| 71 | : [ret] "={r0}" (-> usize) | |
| 72 | : [tmp] "{r3}" (buf), | |
| 73 | [arg1] "{r0}" (arg1), | |
| 74 | [arg2] "{r1}" (arg2), | |
| 75 | [arg3] "{r2}" (arg3) | |
| 76 | : "memory" | |
| 77 | ); | |
| 78 | } | |
| 79 | ||
| 80 | pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { | |
| 81 | @setRuntimeSafety(false); | |
| 82 | ||
| 83 | var buf: [2]usize = .{ @enumToInt(number), undefined }; | |
| 84 | return asm volatile ( | |
| 85 | \\ str r7, [%[tmp], #4] | |
| 86 | \\ ldr r7, [%[tmp]] | |
| 87 | \\ svc #0 | |
| 88 | \\ ldr r7, [%[tmp], #4] | |
| 89 | : [ret] "={r0}" (-> usize) | |
| 90 | : [tmp] "{r4}" (buf), | |
| 91 | [arg1] "{r0}" (arg1), | |
| 92 | [arg2] "{r1}" (arg2), | |
| 93 | [arg3] "{r2}" (arg3), | |
| 94 | [arg4] "{r3}" (arg4) | |
| 95 | : "memory" | |
| 96 | ); | |
| 97 | } | |
| 98 | ||
| 99 | pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { | |
| 100 | @setRuntimeSafety(false); | |
| 101 | ||
| 102 | var buf: [2]usize = .{ @enumToInt(number), undefined }; | |
| 103 | return asm volatile ( | |
| 104 | \\ str r7, [%[tmp], #4] | |
| 105 | \\ ldr r7, [%[tmp]] | |
| 106 | \\ svc #0 | |
| 107 | \\ ldr r7, [%[tmp], #4] | |
| 108 | : [ret] "={r0}" (-> usize) | |
| 109 | : [tmp] "{r5}" (buf), | |
| 110 | [arg1] "{r0}" (arg1), | |
| 111 | [arg2] "{r1}" (arg2), | |
| 112 | [arg3] "{r2}" (arg3), | |
| 113 | [arg4] "{r3}" (arg4), | |
| 114 | [arg5] "{r4}" (arg5) | |
| 115 | : "memory" | |
| 116 | ); | |
| 117 | } | |
| 118 | ||
| 119 | pub fn syscall6( | |
| 120 | number: SYS, | |
| 121 | arg1: usize, | |
| 122 | arg2: usize, | |
| 123 | arg3: usize, | |
| 124 | arg4: usize, | |
| 125 | arg5: usize, | |
| 126 | arg6: usize, | |
| 127 | ) usize { | |
| 128 | @setRuntimeSafety(false); | |
| 129 | ||
| 130 | var buf: [2]usize = .{ @enumToInt(number), undefined }; | |
| 131 | return asm volatile ( | |
| 132 | \\ str r7, [%[tmp], #4] | |
| 133 | \\ ldr r7, [%[tmp]] | |
| 134 | \\ svc #0 | |
| 135 | \\ ldr r7, [%[tmp], #4] | |
| 136 | : [ret] "={r0}" (-> usize) | |
| 137 | : [tmp] "{r6}" (buf), | |
| 138 | [arg1] "{r0}" (arg1), | |
| 139 | [arg2] "{r1}" (arg2), | |
| 140 | [arg3] "{r2}" (arg3), | |
| 141 | [arg4] "{r3}" (arg4), | |
| 142 | [arg5] "{r4}" (arg5), | |
| 143 | [arg6] "{r5}" (arg6) | |
| 144 | : "memory" | |
| 145 | ); | |
| 146 | } | |
| 147 | ||
| 148 | /// This matches the libc clone function. | |
| 149 | pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; | |
| 150 | ||
| 151 | pub fn restore() callconv(.Naked) void { | |
| 152 | return asm volatile ( | |
| 153 | \\ mov r7, %[number] | |
| 154 | \\ svc #0 | |
| 155 | : | |
| 156 | : [number] "I" (@enumToInt(SYS.sigreturn)) | |
| 157 | ); | |
| 158 | } | |
| 159 | ||
| 160 | pub fn restore_rt() callconv(.Naked) void { | |
| 161 | return asm volatile ( | |
| 162 | \\ mov r7, %[number] | |
| 163 | \\ svc #0 | |
| 164 | : | |
| 165 | : [number] "I" (@enumToInt(SYS.rt_sigreturn)) | |
| 166 | : "memory" | |
| 167 | ); | |
| 168 | } |
lib/std/os/linux/tls.zig+3-3| ... | ... | @@ -53,7 +53,7 @@ const TLSVariant = enum { |
| 53 | 53 | }; |
| 54 | 54 | |
| 55 | 55 | const tls_variant = switch (builtin.arch) { |
| 56 | .arm, .armeb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI, | |
| 56 | .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI, | |
| 57 | 57 | .x86_64, .i386, .sparcv9 => TLSVariant.VariantII, |
| 58 | 58 | else => @compileError("undefined tls_variant for this architecture"), |
| 59 | 59 | }; |
| ... | ... | @@ -62,7 +62,7 @@ const tls_variant = switch (builtin.arch) { |
| 62 | 62 | const tls_tcb_size = switch (builtin.arch) { |
| 63 | 63 | // ARM EABI mandates enough space for two pointers: the first one points to |
| 64 | 64 | // the DTV while the second one is unspecified but reserved |
| 65 | .arm, .armeb, .aarch64, .aarch64_be => 2 * @sizeOf(usize), | |
| 65 | .arm, .armeb, .thumb, .aarch64, .aarch64_be => 2 * @sizeOf(usize), | |
| 66 | 66 | // One pointer-sized word that points either to the DTV or the TCB itself |
| 67 | 67 | else => @sizeOf(usize), |
| 68 | 68 | }; |
| ... | ... | @@ -150,7 +150,7 @@ pub fn setThreadPointer(addr: usize) void { |
| 150 | 150 | : [addr] "r" (addr) |
| 151 | 151 | ); |
| 152 | 152 | }, |
| 153 | .arm => { | |
| 153 | .arm, .thumb => { | |
| 154 | 154 | const rc = std.os.linux.syscall1(.set_tls, addr); |
| 155 | 155 | assert(rc == 0); |
| 156 | 156 | }, |
lib/std/special/c.zig+1-1| ... | ... | @@ -385,7 +385,7 @@ fn clone() callconv(.Naked) void { |
| 385 | 385 | \\ svc #0 |
| 386 | 386 | ); |
| 387 | 387 | }, |
| 388 | .arm => { | |
| 388 | .arm, .thumb => { | |
| 389 | 389 | // __clone(func, stack, flags, arg, ptid, tls, ctid) |
| 390 | 390 | // r0, r1, r2, r3, +0, +4, +8 |
| 391 | 391 |
lib/std/special/compiler_rt/clzsi2.zig+20-7| ... | ... | @@ -26,6 +26,8 @@ fn __clzsi2_generic(a: i32) callconv(.C) i32 { |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | 28 | fn __clzsi2_thumb1() callconv(.Naked) void { |
| 29 | @setRuntimeSafety(false); | |
| 30 | ||
| 29 | 31 | // Similar to the generic version with the last two rounds replaced by a LUT |
| 30 | 32 | asm volatile ( |
| 31 | 33 | \\ movs r1, #32 |
| ... | ... | @@ -58,6 +60,8 @@ fn __clzsi2_thumb1() callconv(.Naked) void { |
| 58 | 60 | } |
| 59 | 61 | |
| 60 | 62 | fn __clzsi2_arm32() callconv(.Naked) void { |
| 63 | @setRuntimeSafety(false); | |
| 64 | ||
| 61 | 65 | asm volatile ( |
| 62 | 66 | \\ // Assumption: n != 0 |
| 63 | 67 | \\ // r0: n |
| ... | ... | @@ -104,13 +108,22 @@ fn __clzsi2_arm32() callconv(.Naked) void { |
| 104 | 108 | unreachable; |
| 105 | 109 | } |
| 106 | 110 | |
| 107 | pub const __clzsi2 = switch (std.Target.current.cpu.arch) { | |
| 108 | .arm, .armeb => if (std.Target.arm.featureSetHas(std.Target.current.cpu.features, .noarm)) | |
| 109 | __clzsi2_thumb1 | |
| 110 | else | |
| 111 | __clzsi2_arm32, | |
| 112 | .thumb, .thumbeb => __clzsi2_thumb1, | |
| 113 | else => __clzsi2_generic, | |
| 111 | pub const __clzsi2 = impl: { | |
| 112 | switch (std.Target.current.cpu.arch) { | |
| 113 | .arm, .armeb, .thumb, .thumbeb => { | |
| 114 | const use_thumb1 = | |
| 115 | (std.Target.current.cpu.arch.isThumb() or | |
| 116 | std.Target.arm.featureSetHas(std.Target.current.cpu.features, .noarm)) and | |
| 117 | !std.Target.arm.featureSetHas(std.Target.current.cpu.features, .thumb2); | |
| 118 | ||
| 119 | if (use_thumb1) break :impl __clzsi2_thumb1 | |
| 120 | // From here on we're either targeting Thumb2 or ARM. | |
| 121 | else if (!std.Target.current.cpu.arch.isThumb()) break :impl __clzsi2_arm32 | |
| 122 | // Use the generic implementation otherwise. | |
| 123 | else break :impl __clzsi2_generic; | |
| 124 | }, | |
| 125 | else => break :impl __clzsi2_generic, | |
| 126 | } | |
| 114 | 127 | }; |
| 115 | 128 | |
| 116 | 129 | test "test clzsi2" { |
lib/std/special/compiler_rt/clzsi2_test.zig+2| ... | ... | @@ -7,6 +7,8 @@ const clzsi2 = @import("clzsi2.zig"); |
| 7 | 7 | const testing = @import("std").testing; |
| 8 | 8 | |
| 9 | 9 | fn test__clzsi2(a: u32, expected: i32) void { |
| 10 | // XXX At high optimization levels this test may be horribly miscompiled if | |
| 11 | // one of the naked implementations is selected. | |
| 10 | 12 | var nakedClzsi2 = clzsi2.__clzsi2; |
| 11 | 13 | var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2); |
| 12 | 14 | var x = @bitCast(i32, a); |
lib/std/start.zig+1-1| ... | ... | @@ -176,7 +176,7 @@ fn _start() callconv(.Naked) noreturn { |
| 176 | 176 | : [argc] "={esp}" (-> [*]usize) |
| 177 | 177 | ); |
| 178 | 178 | }, |
| 179 | .aarch64, .aarch64_be, .arm, .armeb => { | |
| 179 | .aarch64, .aarch64_be, .arm, .armeb, .thumb => { | |
| 180 | 180 | argc_argv_ptr = asm volatile ( |
| 181 | 181 | \\ mov fp, #0 |
| 182 | 182 | \\ mov lr, #0 |
lib/std/zig/system.zig+9| ... | ... | @@ -349,6 +349,15 @@ pub const NativeTargetInfo = struct { |
| 349 | 349 | } |
| 350 | 350 | } |
| 351 | 351 | }, |
| 352 | .arm, .armeb => { | |
| 353 | // XXX What do we do if the target has the noarm feature? | |
| 354 | // What do we do if the user specifies +thumb_mode? | |
| 355 | }, | |
| 356 | .thumb, .thumbeb => { | |
| 357 | result.target.cpu.features.addFeature( | |
| 358 | @enumToInt(std.Target.arm.Feature.thumb_mode), | |
| 359 | ); | |
| 360 | }, | |
| 352 | 361 | else => {}, |
| 353 | 362 | } |
| 354 | 363 | cross_target.updateCpuFeatures(&result.target.cpu.features); |
src/stage1/codegen.cpp+3-1| ... | ... | @@ -4880,6 +4880,9 @@ static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutableGen *executable, I |
| 4880 | 4880 | type_ref = get_llvm_type(g, wider_type); |
| 4881 | 4881 | value_ref = gen_widen_or_shorten(g, false, type, wider_type, value_ref); |
| 4882 | 4882 | } |
| 4883 | } else if (handle_is_ptr(g, type)) { | |
| 4884 | ZigType *gen_type = get_pointer_to_type(g, type, true); | |
| 4885 | type_ref = get_llvm_type(g, gen_type); | |
| 4883 | 4886 | } |
| 4884 | 4887 | |
| 4885 | 4888 | param_types[param_index] = type_ref; |
| ... | ... | @@ -9296,7 +9299,6 @@ static void init(CodeGen *g) { |
| 9296 | 9299 | char *layout_str = LLVMCopyStringRepOfTargetData(g->target_data_ref); |
| 9297 | 9300 | LLVMSetDataLayout(g->module, layout_str); |
| 9298 | 9301 | |
| 9299 | ||
| 9300 | 9302 | assert(g->pointer_size_bytes == LLVMPointerSize(g->target_data_ref)); |
| 9301 | 9303 | g->is_big_endian = (LLVMByteOrder(g->target_data_ref) == LLVMBigEndian); |
| 9302 | 9304 |
test/stage1/behavior/align.zig+3| ... | ... | @@ -141,6 +141,7 @@ fn alignedBig() align(16) i32 { |
| 141 | 141 | test "@alignCast functions" { |
| 142 | 142 | // function alignment is a compile error on wasm32/wasm64 |
| 143 | 143 | if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; |
| 144 | if (builtin.arch == .thumb) return error.SkipZigTest; | |
| 144 | 145 | |
| 145 | 146 | expect(fnExpectsOnly1(simple4) == 0x19); |
| 146 | 147 | } |
| ... | ... | @@ -157,6 +158,7 @@ fn simple4() align(4) i32 { |
| 157 | 158 | test "generic function with align param" { |
| 158 | 159 | // function alignment is a compile error on wasm32/wasm64 |
| 159 | 160 | if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; |
| 161 | if (builtin.arch == .thumb) return error.SkipZigTest; | |
| 160 | 162 | |
| 161 | 163 | expect(whyWouldYouEverDoThis(1) == 0x1); |
| 162 | 164 | expect(whyWouldYouEverDoThis(4) == 0x1); |
| ... | ... | @@ -338,6 +340,7 @@ test "align(@alignOf(T)) T does not force resolution of T" { |
| 338 | 340 | test "align(N) on functions" { |
| 339 | 341 | // function alignment is a compile error on wasm32/wasm64 |
| 340 | 342 | if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; |
| 343 | if (builtin.arch == .thumb) return error.SkipZigTest; | |
| 341 | 344 | |
| 342 | 345 | expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0); |
| 343 | 346 | } |
test/stage1/behavior/asm.zig+15| ... | ... | @@ -87,6 +87,21 @@ test "sized integer/float in asm input" { |
| 87 | 87 | ); |
| 88 | 88 | } |
| 89 | 89 | |
| 90 | test "struct/array/union types as input values" { | |
| 91 | asm volatile ("" | |
| 92 | : | |
| 93 | : [_] "m" (@as([1]u32, undefined)) | |
| 94 | ); // fails | |
| 95 | asm volatile ("" | |
| 96 | : | |
| 97 | : [_] "m" (@as(struct { x: u32, y: u8 }, undefined)) | |
| 98 | ); // fails | |
| 99 | asm volatile ("" | |
| 100 | : | |
| 101 | : [_] "m" (@as(union { x: u32, y: u8 }, undefined)) | |
| 102 | ); // fails | |
| 103 | } | |
| 104 | ||
| 90 | 105 | extern fn this_is_my_alias() i32; |
| 91 | 106 | |
| 92 | 107 | export fn derp() i32 { |
test/stage1/behavior/async_fn.zig+3| ... | ... | @@ -110,6 +110,9 @@ test "calling an inferred async function" { |
| 110 | 110 | } |
| 111 | 111 | |
| 112 | 112 | test "@frameSize" { |
| 113 | if (builtin.arch == .thumb or builtin.arch == .thumbeb) | |
| 114 | return error.SkipZigTest; | |
| 115 | ||
| 113 | 116 | const S = struct { |
| 114 | 117 | fn doTheTest() void { |
| 115 | 118 | { |
test/stage1/behavior/atomics.zig+3-2| ... | ... | @@ -149,9 +149,10 @@ fn testAtomicStore() void { |
| 149 | 149 | } |
| 150 | 150 | |
| 151 | 151 | test "atomicrmw with floats" { |
| 152 | if (builtin.arch == .aarch64 or builtin.arch == .arm or builtin.arch == .riscv64) { | |
| 152 | switch (builtin.arch) { | |
| 153 | 153 | // https://github.com/ziglang/zig/issues/4457 |
| 154 | return error.SkipZigTest; | |
| 154 | .aarch64, .arm, .thumb, .riscv64 => return error.SkipZigTest, | |
| 155 | else => {}, | |
| 155 | 156 | } |
| 156 | 157 | testAtomicRmwFloat(); |
| 157 | 158 | comptime testAtomicRmwFloat(); |