lib/sys/src/tinyrt.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const builtin = @import("builtin");
2 const std = @import("std");
3 const capabilities = @import("capabilities.zig");
4
5 pub const required_capabilities = capabilities.noLibc(&.{ .descriptors, .libc_interop });
6
7 pub const stdout_fd: c_int = 1;
8
9 pub fn writeRaw(fd: c_int, bytes: [*]const u8, len: usize) usize {
10 @setRuntimeSafety(false);
11 return switch (builtin.os.tag) {
12 .linux => std.os.linux.write(fd, bytes, len),
13 .macos => @bitCast(darwin.write(fd, bytes, len)),
14 else => @bitCast(@as(isize, -1)),
15 };
16 }
17
18 const darwin = struct {
19 extern "c" fn write(fd: c_int, bytes: [*]const u8, len: usize) isize;
20 };
21
22 pub export fn memmove(dest: ?[*]u8, src: ?[*]const u8, len: usize) callconv(.c) ?[*]u8 {
23 @setRuntimeSafety(false);
24 const dest_ptr = dest orelse return dest;
25 const src_ptr = src orelse return dest;
26 if (@intFromPtr(dest_ptr) <= @intFromPtr(src_ptr)) {
27 for (0..len) |index| dest_ptr[index] = src_ptr[index];
28 } else {
29 for (0..len) |index| dest_ptr[len - 1 - index] = src_ptr[len - 1 - index];
30 }
31 return dest;
32 }
33
34 pub export fn __divti3(a: i128, b: i128) callconv(.c) i128 {
35 @setRuntimeSafety(false);
36 const sign_a = a >> 127;
37 const sign_b = b >> 127;
38 const magnitude_a = (a ^ sign_a) -% sign_a;
39 const magnitude_b = (b ^ sign_b) -% sign_b;
40 const quotient = udivmod128(@bitCast(magnitude_a), @bitCast(magnitude_b), null);
41 const sign = sign_a ^ sign_b;
42 return (@as(i128, @bitCast(quotient)) ^ sign) -% sign;
43 }
44
45 pub export fn __modti3(a: i128, b: i128) callconv(.c) i128 {
46 @setRuntimeSafety(false);
47 const sign_a = a >> 127;
48 const sign_b = b >> 127;
49 const magnitude_a = (a ^ sign_a) -% sign_a;
50 const magnitude_b = (b ^ sign_b) -% sign_b;
51 var remainder: u128 = undefined;
52 _ = udivmod128(@bitCast(magnitude_a), @bitCast(magnitude_b), &remainder);
53 return (@as(i128, @bitCast(remainder)) ^ sign_a) -% sign_a;
54 }
55
56 pub export fn __udivti3(a: u128, b: u128) callconv(.c) u128 {
57 @setRuntimeSafety(false);
58 return udivmod128(a, b, null);
59 }
60
61 pub export fn __umodti3(a: u128, b: u128) callconv(.c) u128 {
62 @setRuntimeSafety(false);
63 var remainder: u128 = undefined;
64 _ = udivmod128(a, b, &remainder);
65 return remainder;
66 }
67
68 pub export fn __ashlti3(value: i128, shift: i32) callconv(.c) i128 {
69 @setRuntimeSafety(false);
70 if (shift <= 0) return value;
71 if (shift >= 128) return 0;
72
73 const bits: u128 = @bitCast(value);
74 const low: u64 = @truncate(bits);
75 const high: u64 = @truncate(bits >> 64);
76 const shifted = if (shift >= 64) blk: {
77 const amount: std.math.Log2Int(u64) = @intCast(shift - 64);
78 break :blk joinU128(low << amount, 0);
79 } else blk: {
80 const amount: std.math.Log2Int(u64) = @intCast(shift);
81 break :blk joinU128((high << amount) | (low >> @intCast(64 - shift)), low << amount);
82 };
83 return @bitCast(shifted);
84 }
85
86 pub export fn __lshrti3(value: i128, shift: i32) callconv(.c) i128 {
87 @setRuntimeSafety(false);
88 if (shift <= 0) return value;
89 if (shift >= 128) return 0;
90
91 const bits: u128 = @bitCast(value);
92 const low: u64 = @truncate(bits);
93 const high: u64 = @truncate(bits >> 64);
94 const shifted = if (shift >= 64) blk: {
95 const amount: std.math.Log2Int(u64) = @intCast(shift - 64);
96 break :blk joinU128(0, high >> amount);
97 } else blk: {
98 const amount: std.math.Log2Int(u64) = @intCast(shift);
99 break :blk joinU128(high >> amount, (low >> amount) | (high << @intCast(64 - shift)));
100 };
101 return @bitCast(shifted);
102 }
103
104 fn joinU128(high: u64, low: u64) u128 {
105 @setRuntimeSafety(false);
106 return (@as(u128, high) << 64) | low;
107 }
108
109 fn udivmod128(numerator: u128, denominator: u128, remainder_out: ?*u128) u128 {
110 @setRuntimeSafety(false);
111 if (denominator == 0) @trap();
112
113 var quotient: u128 = 0;
114 var remainder: u128 = 0;
115 var bit_index: usize = 128;
116 while (bit_index != 0) {
117 bit_index -= 1;
118 const shift: std.math.Log2Int(u128) = @intCast(bit_index);
119 remainder = (remainder << 1) | ((numerator >> shift) & 1);
120 if (remainder >= denominator) {
121 remainder -= denominator;
122 quotient |= @as(u128, 1) << shift;
123 }
124 }
125
126 if (remainder_out) |out| out.* = remainder;
127 return quotient;
128 }
129
130 pub export fn __zig_probe_stack() callconv(.c) void {}
131
132 test "tinyrt writeRaw reports invalid descriptor failure" {
133 const bytes: []const u8 = "";
134 const written = writeRaw(-1, bytes.ptr, bytes.len);
135 const signed: isize = @bitCast(written);
136 try std.testing.expect(signed < 0);
137 }
138
139 test "tinyrt compiler support helpers cover 128-bit arithmetic" {
140 try std.testing.expectEqual(@divTrunc(@as(i128, -7), @as(i128, 3)), __divti3(-7, 3));
141 try std.testing.expectEqual(@rem(@as(i128, -7), @as(i128, 3)), __modti3(-7, 3));
142 try std.testing.expectEqual(@as(u128, 1) << 95, __udivti3(@as(u128, 1) << 100, 32));
143 try std.testing.expectEqual(@as(u128, 17), __umodti3((@as(u128, 1) << 100) + 17, 32));
144 try std.testing.expectEqual(@as(i128, 1) << 70, __ashlti3(1, 70));
145
146 const high_bit: i128 = @bitCast(@as(u128, 1) << 127);
147 const shifted_high_bit: i128 = @bitCast((@as(u128, 1) << 127) >> 65);
148 try std.testing.expectEqual(shifted_high_bit, __lshrti3(high_bit, 65));
149 }