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 }