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) {
1818 .i386 => @import("linux/i386.zig"),
1919 .x86_64 => @import("linux/x86_64.zig"),
2020 .aarch64 => @import("linux/arm64.zig"),
21 .arm => @import("linux/arm-eabi.zig"),
21 .arm, .thumb => @import("linux/arm-eabi.zig"),
2222 .riscv64 => @import("linux/riscv64.zig"),
2323 .sparcv9 => @import("linux/sparc64.zig"),
2424 .mips, .mipsel => @import("linux/mips.zig"),
lib/std/os/linux.zig+1
......@@ -23,6 +23,7 @@ pub usingnamespace switch (builtin.arch) {
2323 .x86_64 => @import("linux/x86_64.zig"),
2424 .aarch64 => @import("linux/arm64.zig"),
2525 .arm => @import("linux/arm-eabi.zig"),
26 .thumb => @import("linux/thumb.zig"),
2627 .riscv64 => @import("linux/riscv64.zig"),
2728 .sparcv9 => @import("linux/sparc64.zig"),
2829 .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 {
5353};
5454
5555const 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,
5757 .x86_64, .i386, .sparcv9 => TLSVariant.VariantII,
5858 else => @compileError("undefined tls_variant for this architecture"),
5959};
......@@ -62,7 +62,7 @@ const tls_variant = switch (builtin.arch) {
6262const tls_tcb_size = switch (builtin.arch) {
6363 // ARM EABI mandates enough space for two pointers: the first one points to
6464 // 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),
6666 // One pointer-sized word that points either to the DTV or the TCB itself
6767 else => @sizeOf(usize),
6868};
......@@ -150,7 +150,7 @@ pub fn setThreadPointer(addr: usize) void {
150150 : [addr] "r" (addr)
151151 );
152152 },
153 .arm => {
153 .arm, .thumb => {
154154 const rc = std.os.linux.syscall1(.set_tls, addr);
155155 assert(rc == 0);
156156 },
lib/std/special/c.zig+1-1
......@@ -385,7 +385,7 @@ fn clone() callconv(.Naked) void {
385385 \\ svc #0
386386 );
387387 },
388 .arm => {
388 .arm, .thumb => {
389389 // __clone(func, stack, flags, arg, ptid, tls, ctid)
390390 // 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 {
2626}
2727
2828fn __clzsi2_thumb1() callconv(.Naked) void {
29 @setRuntimeSafety(false);
30
2931 // Similar to the generic version with the last two rounds replaced by a LUT
3032 asm volatile (
3133 \\ movs r1, #32
......@@ -58,6 +60,8 @@ fn __clzsi2_thumb1() callconv(.Naked) void {
5860}
5961
6062fn __clzsi2_arm32() callconv(.Naked) void {
63 @setRuntimeSafety(false);
64
6165 asm volatile (
6266 \\ // Assumption: n != 0
6367 \\ // r0: n
......@@ -104,13 +108,22 @@ fn __clzsi2_arm32() callconv(.Naked) void {
104108 unreachable;
105109}
106110
107pub 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,
111pub 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 }
114127};
115128
116129test "test clzsi2" {
lib/std/special/compiler_rt/clzsi2_test.zig+2
......@@ -7,6 +7,8 @@ const clzsi2 = @import("clzsi2.zig");
77const testing = @import("std").testing;
88
99fn 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.
1012 var nakedClzsi2 = clzsi2.__clzsi2;
1113 var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2);
1214 var x = @bitCast(i32, a);
lib/std/start.zig+1-1
......@@ -176,7 +176,7 @@ fn _start() callconv(.Naked) noreturn {
176176 : [argc] "={esp}" (-> [*]usize)
177177 );
178178 },
179 .aarch64, .aarch64_be, .arm, .armeb => {
179 .aarch64, .aarch64_be, .arm, .armeb, .thumb => {
180180 argc_argv_ptr = asm volatile (
181181 \\ mov fp, #0
182182 \\ mov lr, #0
lib/std/zig/system.zig+9
......@@ -349,6 +349,15 @@ pub const NativeTargetInfo = struct {
349349 }
350350 }
351351 },
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 },
352361 else => {},
353362 }
354363 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
48804880 type_ref = get_llvm_type(g, wider_type);
48814881 value_ref = gen_widen_or_shorten(g, false, type, wider_type, value_ref);
48824882 }
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);
48834886 }
48844887
48854888 param_types[param_index] = type_ref;
......@@ -9296,7 +9299,6 @@ static void init(CodeGen *g) {
92969299 char *layout_str = LLVMCopyStringRepOfTargetData(g->target_data_ref);
92979300 LLVMSetDataLayout(g->module, layout_str);
92989301
9299
93009302 assert(g->pointer_size_bytes == LLVMPointerSize(g->target_data_ref));
93019303 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 {
141141test "@alignCast functions" {
142142 // function alignment is a compile error on wasm32/wasm64
143143 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;
144 if (builtin.arch == .thumb) return error.SkipZigTest;
144145
145146 expect(fnExpectsOnly1(simple4) == 0x19);
146147}
......@@ -157,6 +158,7 @@ fn simple4() align(4) i32 {
157158test "generic function with align param" {
158159 // function alignment is a compile error on wasm32/wasm64
159160 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;
161 if (builtin.arch == .thumb) return error.SkipZigTest;
160162
161163 expect(whyWouldYouEverDoThis(1) == 0x1);
162164 expect(whyWouldYouEverDoThis(4) == 0x1);
......@@ -338,6 +340,7 @@ test "align(@alignOf(T)) T does not force resolution of T" {
338340test "align(N) on functions" {
339341 // function alignment is a compile error on wasm32/wasm64
340342 if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;
343 if (builtin.arch == .thumb) return error.SkipZigTest;
341344
342345 expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0);
343346}
test/stage1/behavior/asm.zig+15
......@@ -87,6 +87,21 @@ test "sized integer/float in asm input" {
8787 );
8888}
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
90105extern fn this_is_my_alias() i32;
91106
92107export fn derp() i32 {
test/stage1/behavior/async_fn.zig+3
......@@ -110,6 +110,9 @@ test "calling an inferred async function" {
110110}
111111
112112test "@frameSize" {
113 if (builtin.arch == .thumb or builtin.arch == .thumbeb)
114 return error.SkipZigTest;
115
113116 const S = struct {
114117 fn doTheTest() void {
115118 {
test/stage1/behavior/atomics.zig+3-2
......@@ -149,9 +149,10 @@ fn testAtomicStore() void {
149149}
150150
151151test "atomicrmw with floats" {
152 if (builtin.arch == .aarch64 or builtin.arch == .arm or builtin.arch == .riscv64) {
152 switch (builtin.arch) {
153153 // https://github.com/ziglang/zig/issues/4457
154 return error.SkipZigTest;
154 .aarch64, .arm, .thumb, .riscv64 => return error.SkipZigTest,
155 else => {},
155156 }
156157 testAtomicRmwFloat();
157158 comptime testAtomicRmwFloat();