authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-06 12:50:34-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-05-06 12:50:34-04:00
log530e67cb868fbd24900d48eee891efa1aa135096
treeeb6c40f94b148d83143992cc3868af100c22af9c
parent96e593145dcdb53ca02f2a365abc68415c4302b6
parentafbcb6209dbe6812679324aab564884085b8cf44
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8683 from LemonBoy/thumblinux

Initial bringup for Linux/Thumb2

14 files changed, 233 insertions(+), 16 deletions(-)

lib/std/os/bits/linux.zig+1-1
...@@ -18,7 +18,7 @@ pub usingnamespace switch (builtin.arch) {...@@ -18,7 +18,7 @@ pub usingnamespace switch (builtin.arch) {
18 .i386 => @import("linux/i386.zig"),18 .i386 => @import("linux/i386.zig"),
19 .x86_64 => @import("linux/x86_64.zig"),19 .x86_64 => @import("linux/x86_64.zig"),
20 .aarch64 => @import("linux/arm64.zig"),20 .aarch64 => @import("linux/arm64.zig"),
21 .arm => @import("linux/arm-eabi.zig"),21 .arm, .thumb => @import("linux/arm-eabi.zig"),
22 .riscv64 => @import("linux/riscv64.zig"),22 .riscv64 => @import("linux/riscv64.zig"),
23 .sparcv9 => @import("linux/sparc64.zig"),23 .sparcv9 => @import("linux/sparc64.zig"),
24 .mips, .mipsel => @import("linux/mips.zig"),24 .mips, .mipsel => @import("linux/mips.zig"),
lib/std/os/linux.zig+1
...@@ -23,6 +23,7 @@ pub usingnamespace switch (builtin.arch) {...@@ -23,6 +23,7 @@ pub usingnamespace switch (builtin.arch) {
23 .x86_64 => @import("linux/x86_64.zig"),23 .x86_64 => @import("linux/x86_64.zig"),
24 .aarch64 => @import("linux/arm64.zig"),24 .aarch64 => @import("linux/arm64.zig"),
25 .arm => @import("linux/arm-eabi.zig"),25 .arm => @import("linux/arm-eabi.zig"),
26 .thumb => @import("linux/thumb.zig"),
26 .riscv64 => @import("linux/riscv64.zig"),27 .riscv64 => @import("linux/riscv64.zig"),
27 .sparcv9 => @import("linux/sparc64.zig"),28 .sparcv9 => @import("linux/sparc64.zig"),
28 .mips, .mipsel => @import("linux/mips.zig"),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.
6usingnamespace @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
14pub 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
29pub 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
45pub 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
62pub 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
80pub 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
99pub 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
119pub 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.
149pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
150
151pub 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
160pub 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,7 +53,7 @@ const TLSVariant = enum {
53};53};
5454
55const tls_variant = switch (builtin.arch) {55const 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 .x86_64, .i386, .sparcv9 => TLSVariant.VariantII,57 .x86_64, .i386, .sparcv9 => TLSVariant.VariantII,
58 else => @compileError("undefined tls_variant for this architecture"),58 else => @compileError("undefined tls_variant for this architecture"),
59};59};
...@@ -62,7 +62,7 @@ const tls_variant = switch (builtin.arch) {...@@ -62,7 +62,7 @@ const tls_variant = switch (builtin.arch) {
62const tls_tcb_size = switch (builtin.arch) {62const tls_tcb_size = switch (builtin.arch) {
63 // ARM EABI mandates enough space for two pointers: the first one points to63 // ARM EABI mandates enough space for two pointers: the first one points to
64 // the DTV while the second one is unspecified but reserved64 // 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 // One pointer-sized word that points either to the DTV or the TCB itself66 // One pointer-sized word that points either to the DTV or the TCB itself
67 else => @sizeOf(usize),67 else => @sizeOf(usize),
68};68};
...@@ -150,7 +150,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -150,7 +150,7 @@ pub fn setThreadPointer(addr: usize) void {
150 : [addr] "r" (addr)150 : [addr] "r" (addr)
151 );151 );
152 },152 },
153 .arm => {153 .arm, .thumb => {
154 const rc = std.os.linux.syscall1(.set_tls, addr);154 const rc = std.os.linux.syscall1(.set_tls, addr);
155 assert(rc == 0);155 assert(rc == 0);
156 },156 },
lib/std/special/c.zig+1-1
...@@ -385,7 +385,7 @@ fn clone() callconv(.Naked) void {...@@ -385,7 +385,7 @@ fn clone() callconv(.Naked) void {
385 \\ svc #0385 \\ svc #0
386 );386 );
387 },387 },
388 .arm => {388 .arm, .thumb => {
389 // __clone(func, stack, flags, arg, ptid, tls, ctid)389 // __clone(func, stack, flags, arg, ptid, tls, ctid)
390 // r0, r1, r2, r3, +0, +4, +8390 // r0, r1, r2, r3, +0, +4, +8
391391
lib/std/special/compiler_rt/clzsi2.zig+20-7
...@@ -26,6 +26,8 @@ fn __clzsi2_generic(a: i32) callconv(.C) i32 {...@@ -26,6 +26,8 @@ fn __clzsi2_generic(a: i32) callconv(.C) i32 {
26}26}
2727
28fn __clzsi2_thumb1() callconv(.Naked) void {28fn __clzsi2_thumb1() callconv(.Naked) void {
29 @setRuntimeSafety(false);
30
29 // Similar to the generic version with the last two rounds replaced by a LUT31 // Similar to the generic version with the last two rounds replaced by a LUT
30 asm volatile (32 asm volatile (
31 \\ movs r1, #3233 \\ movs r1, #32
...@@ -58,6 +60,8 @@ fn __clzsi2_thumb1() callconv(.Naked) void {...@@ -58,6 +60,8 @@ fn __clzsi2_thumb1() callconv(.Naked) void {
58}60}
5961
60fn __clzsi2_arm32() callconv(.Naked) void {62fn __clzsi2_arm32() callconv(.Naked) void {
63 @setRuntimeSafety(false);
64
61 asm volatile (65 asm volatile (
62 \\ // Assumption: n != 066 \\ // Assumption: n != 0
63 \\ // r0: n67 \\ // r0: n
...@@ -104,13 +108,22 @@ fn __clzsi2_arm32() callconv(.Naked) void {...@@ -104,13 +108,22 @@ fn __clzsi2_arm32() callconv(.Naked) void {
104 unreachable;108 unreachable;
105}109}
106110
107pub const __clzsi2 = switch (std.Target.current.cpu.arch) {111pub const __clzsi2 = impl: {
108 .arm, .armeb => if (std.Target.arm.featureSetHas(std.Target.current.cpu.features, .noarm))112 switch (std.Target.current.cpu.arch) {
109 __clzsi2_thumb1113 .arm, .armeb, .thumb, .thumbeb => {
110 else114 const use_thumb1 =
111 __clzsi2_arm32,115 (std.Target.current.cpu.arch.isThumb() or
112 .thumb, .thumbeb => __clzsi2_thumb1,116 std.Target.arm.featureSetHas(std.Target.current.cpu.features, .noarm)) and
113 else => __clzsi2_generic,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};
115128
116test "test clzsi2" {129test "test clzsi2" {
lib/std/special/compiler_rt/clzsi2_test.zig+2
...@@ -7,6 +7,8 @@ const clzsi2 = @import("clzsi2.zig");...@@ -7,6 +7,8 @@ const clzsi2 = @import("clzsi2.zig");
7const testing = @import("std").testing;7const testing = @import("std").testing;
88
9fn test__clzsi2(a: u32, expected: i32) void {9fn 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 var nakedClzsi2 = clzsi2.__clzsi2;12 var nakedClzsi2 = clzsi2.__clzsi2;
11 var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2);13 var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2);
12 var x = @bitCast(i32, a);14 var x = @bitCast(i32, a);
lib/std/start.zig+1-1
...@@ -176,7 +176,7 @@ fn _start() callconv(.Naked) noreturn {...@@ -176,7 +176,7 @@ fn _start() callconv(.Naked) noreturn {
176 : [argc] "={esp}" (-> [*]usize)176 : [argc] "={esp}" (-> [*]usize)
177 );177 );
178 },178 },
179 .aarch64, .aarch64_be, .arm, .armeb => {179 .aarch64, .aarch64_be, .arm, .armeb, .thumb => {
180 argc_argv_ptr = asm volatile (180 argc_argv_ptr = asm volatile (
181 \\ mov fp, #0181 \\ mov fp, #0
182 \\ mov lr, #0182 \\ mov lr, #0
lib/std/zig/system.zig+9
...@@ -349,6 +349,15 @@ pub const NativeTargetInfo = struct {...@@ -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 else => {},361 else => {},
353 }362 }
354 cross_target.updateCpuFeatures(&result.target.cpu.features);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,6 +4880,9 @@ static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutableGen *executable, I
4880 type_ref = get_llvm_type(g, wider_type);4880 type_ref = get_llvm_type(g, wider_type);
4881 value_ref = gen_widen_or_shorten(g, false, type, wider_type, value_ref);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 }
48844887
4885 param_types[param_index] = type_ref;4888 param_types[param_index] = type_ref;
...@@ -9296,7 +9299,6 @@ static void init(CodeGen *g) {...@@ -9296,7 +9299,6 @@ static void init(CodeGen *g) {
9296 char *layout_str = LLVMCopyStringRepOfTargetData(g->target_data_ref);9299 char *layout_str = LLVMCopyStringRepOfTargetData(g->target_data_ref);
9297 LLVMSetDataLayout(g->module, layout_str);9300 LLVMSetDataLayout(g->module, layout_str);
92989301
9299
9300 assert(g->pointer_size_bytes == LLVMPointerSize(g->target_data_ref));9302 assert(g->pointer_size_bytes == LLVMPointerSize(g->target_data_ref));
9301 g->is_big_endian = (LLVMByteOrder(g->target_data_ref) == LLVMBigEndian);9303 g->is_big_endian = (LLVMByteOrder(g->target_data_ref) == LLVMBigEndian);
93029304
test/stage1/behavior/align.zig+3
...@@ -141,6 +141,7 @@ fn alignedBig() align(16) i32 {...@@ -141,6 +141,7 @@ fn alignedBig() align(16) i32 {
141test "@alignCast functions" {141test "@alignCast functions" {
142 // function alignment is a compile error on wasm32/wasm64142 // function alignment is a compile error on wasm32/wasm64
143 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;143 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;
144 if (builtin.arch == .thumb) return error.SkipZigTest;
144145
145 expect(fnExpectsOnly1(simple4) == 0x19);146 expect(fnExpectsOnly1(simple4) == 0x19);
146}147}
...@@ -157,6 +158,7 @@ fn simple4() align(4) i32 {...@@ -157,6 +158,7 @@ fn simple4() align(4) i32 {
157test "generic function with align param" {158test "generic function with align param" {
158 // function alignment is a compile error on wasm32/wasm64159 // function alignment is a compile error on wasm32/wasm64
159 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;160 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;
161 if (builtin.arch == .thumb) return error.SkipZigTest;
160162
161 expect(whyWouldYouEverDoThis(1) == 0x1);163 expect(whyWouldYouEverDoThis(1) == 0x1);
162 expect(whyWouldYouEverDoThis(4) == 0x1);164 expect(whyWouldYouEverDoThis(4) == 0x1);
...@@ -338,6 +340,7 @@ test "align(@alignOf(T)) T does not force resolution of T" {...@@ -338,6 +340,7 @@ test "align(@alignOf(T)) T does not force resolution of T" {
338test "align(N) on functions" {340test "align(N) on functions" {
339 // function alignment is a compile error on wasm32/wasm64341 // function alignment is a compile error on wasm32/wasm64
340 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;342 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;
343 if (builtin.arch == .thumb) return error.SkipZigTest;
341344
342 expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0);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,6 +87,21 @@ test "sized integer/float in asm input" {
87 );87 );
88}88}
8989
90test "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
90extern fn this_is_my_alias() i32;105extern fn this_is_my_alias() i32;
91106
92export fn derp() i32 {107export fn derp() i32 {
test/stage1/behavior/async_fn.zig+3
...@@ -110,6 +110,9 @@ test "calling an inferred async function" {...@@ -110,6 +110,9 @@ test "calling an inferred async function" {
110}110}
111111
112test "@frameSize" {112test "@frameSize" {
113 if (builtin.arch == .thumb or builtin.arch == .thumbeb)
114 return error.SkipZigTest;
115
113 const S = struct {116 const S = struct {
114 fn doTheTest() void {117 fn doTheTest() void {
115 {118 {
test/stage1/behavior/atomics.zig+3-2
...@@ -149,9 +149,10 @@ fn testAtomicStore() void {...@@ -149,9 +149,10 @@ fn testAtomicStore() void {
149}149}
150150
151test "atomicrmw with floats" {151test "atomicrmw with floats" {
152 if (builtin.arch == .aarch64 or builtin.arch == .arm or builtin.arch == .riscv64) {152 switch (builtin.arch) {
153 // https://github.com/ziglang/zig/issues/4457153 // https://github.com/ziglang/zig/issues/4457
154 return error.SkipZigTest;154 .aarch64, .arm, .thumb, .riscv64 => return error.SkipZigTest,
155 else => {},
155 }156 }
156 testAtomicRmwFloat();157 testAtomicRmwFloat();
157 comptime testAtomicRmwFloat();158 comptime testAtomicRmwFloat();