common.zig (10957B) - Raw
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 const native_endian = builtin.cpu.arch.endian(); 4 const ofmt_c = builtin.object_format == .c; 5 6 /// For now, we prefer weak linkage because some of the routines we implement here may also be 7 /// provided by system/dynamic libc. Eventually we should be more disciplined about this on a 8 /// per-symbol, per-target basis: https://github.com/ziglang/zig/issues/11883 9 pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) 10 .internal 11 else if (ofmt_c) 12 .strong 13 else 14 .weak; 15 16 /// Determines the symbol's visibility to other objects. 17 /// For WebAssembly this allows the symbol to be resolved to other modules, but will not 18 /// export it to the host runtime. 19 pub const visibility: std.builtin.SymbolVisibility = if (linkage == .internal or builtin.link_mode == .dynamic) 20 .default 21 else 22 .hidden; 23 24 pub const PreferredLoadStoreElement = element: { 25 if (std.simd.suggestVectorLength(u8)) |vec_size| { 26 const Vec = @Vector(vec_size, u8); 27 28 if (@sizeOf(Vec) == vec_size and std.math.isPowerOfTwo(vec_size)) { 29 break :element Vec; 30 } 31 } 32 break :element usize; 33 }; 34 35 pub const want_aeabi = switch (builtin.abi) { 36 .eabi, 37 .eabihf, 38 .musleabi, 39 .musleabihf, 40 .gnueabi, 41 .gnueabihf, 42 .android, 43 .androideabi, 44 => switch (builtin.cpu.arch) { 45 .arm, .armeb, .thumb, .thumbeb => true, 46 else => false, 47 }, 48 else => false, 49 }; 50 51 /// These functions are provided by libc when targeting MSVC, but not MinGW. 52 // Temporarily used for thumb-uefi until https://github.com/ziglang/zig/issues/21630 is addressed. 53 pub const want_windows_arm_abi = builtin.cpu.arch.isArm() and (builtin.os.tag == .windows or builtin.os.tag == .uefi) and (builtin.abi.isGnu() or !builtin.link_libc); 54 55 pub const want_windows_msvc_or_itanium_abi = switch (builtin.abi) { 56 .none, .msvc, .itanium => builtin.os.tag == .windows, 57 else => false, 58 }; 59 60 pub const want_ppc_abi = builtin.cpu.arch.isPowerPC(); 61 62 pub const want_float_exceptions = !builtin.cpu.arch.isWasm(); 63 64 // Libcalls that involve u128 on Windows x86-64 are expected by LLVM to use the 65 // calling convention of @Vector(2, u64), rather than what's standard. 66 pub const want_windows_v2u64_abi = builtin.os.tag == .windows and builtin.cpu.arch == .x86_64 and !ofmt_c; 67 68 /// This governs whether to use these symbol names for f16/f32 conversions 69 /// rather than the standard names: 70 /// * __gnu_f2h_ieee 71 /// * __gnu_h2f_ieee 72 /// Known correct configurations: 73 /// x86_64-freestanding-none => true 74 /// x86_64-linux-none => true 75 /// x86_64-linux-gnu => true 76 /// x86_64-linux-musl => true 77 /// x86_64-linux-eabi => true 78 /// arm-linux-musleabihf => true 79 /// arm-linux-gnueabihf => true 80 /// arm-linux-eabihf => false 81 /// wasm32-wasi-musl => false 82 /// wasm32-freestanding-none => false 83 /// x86_64-windows-gnu => true 84 /// x86_64-windows-msvc => true 85 /// any-macos-any => false 86 pub const gnu_f16_abi = switch (builtin.cpu.arch) { 87 .wasm32, 88 .wasm64, 89 .riscv64, 90 .riscv32, 91 => false, 92 93 .x86, .x86_64 => true, 94 95 .arm, .armeb, .thumb, .thumbeb => switch (builtin.abi) { 96 .eabi, .eabihf => false, 97 else => true, 98 }, 99 100 else => !builtin.os.tag.isDarwin(), 101 }; 102 103 pub const want_sparc_abi = builtin.cpu.arch.isSPARC(); 104 105 pub const test_safety = switch (builtin.zig_backend) { 106 .stage2_aarch64 => false, 107 else => builtin.is_test, 108 }; 109 110 // Avoid dragging in the runtime safety mechanisms into this .o file, unless 111 // we're trying to test compiler-rt. 112 pub const panic = if (test_safety) std.debug.FullPanic(std.debug.defaultPanic) else std.debug.no_panic; 113 114 /// This seems to mostly correspond to `clang::TargetInfo::HasFloat16`. 115 pub fn F16T(comptime OtherType: type) type { 116 return switch (builtin.cpu.arch) { 117 .amdgcn, 118 .arm, 119 .armeb, 120 .thumb, 121 .thumbeb, 122 .aarch64, 123 .aarch64_be, 124 .nvptx, 125 .nvptx64, 126 .riscv32, 127 .riscv64, 128 .spirv32, 129 .spirv64, 130 => f16, 131 .hexagon => if (builtin.target.cpu.has(.hexagon, .v68)) f16 else u16, 132 .x86, .x86_64 => if (builtin.target.os.tag.isDarwin()) switch (OtherType) { 133 // Starting with LLVM 16, Darwin uses different abi for f16 134 // depending on the type of the other return/argument..??? 135 f32, f64 => u16, 136 f80, f128 => f16, 137 else => unreachable, 138 } else f16, 139 else => u16, 140 }; 141 } 142 143 pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { 144 switch (Z) { 145 u16 => { 146 // 16x16 --> 32 bit multiply 147 const product = @as(u32, a) * @as(u32, b); 148 hi.* = @intCast(product >> 16); 149 lo.* = @truncate(product); 150 }, 151 u32 => { 152 // 32x32 --> 64 bit multiply 153 const product = @as(u64, a) * @as(u64, b); 154 hi.* = @truncate(product >> 32); 155 lo.* = @truncate(product); 156 }, 157 u64 => { 158 const S = struct { 159 fn loWord(x: u64) u64 { 160 return @as(u32, @truncate(x)); 161 } 162 fn hiWord(x: u64) u64 { 163 return @as(u32, @truncate(x >> 32)); 164 } 165 }; 166 // 64x64 -> 128 wide multiply for platforms that don't have such an operation; 167 // many 64-bit platforms have this operation, but they tend to have hardware 168 // floating-point, so we don't bother with a special case for them here. 169 // Each of the component 32x32 -> 64 products 170 const plolo: u64 = S.loWord(a) * S.loWord(b); 171 const plohi: u64 = S.loWord(a) * S.hiWord(b); 172 const philo: u64 = S.hiWord(a) * S.loWord(b); 173 const phihi: u64 = S.hiWord(a) * S.hiWord(b); 174 // Sum terms that contribute to lo in a way that allows us to get the carry 175 const r0: u64 = S.loWord(plolo); 176 const r1: u64 = S.hiWord(plolo) +% S.loWord(plohi) +% S.loWord(philo); 177 lo.* = r0 +% (r1 << 32); 178 // Sum terms contributing to hi with the carry from lo 179 hi.* = S.hiWord(plohi) +% S.hiWord(philo) +% S.hiWord(r1) +% phihi; 180 }, 181 u128 => { 182 const Word_LoMask: u64 = 0x00000000ffffffff; 183 const Word_HiMask: u64 = 0xffffffff00000000; 184 const Word_FullMask: u64 = 0xffffffffffffffff; 185 const S = struct { 186 fn Word_1(x: u128) u64 { 187 return @as(u32, @truncate(x >> 96)); 188 } 189 fn Word_2(x: u128) u64 { 190 return @as(u32, @truncate(x >> 64)); 191 } 192 fn Word_3(x: u128) u64 { 193 return @as(u32, @truncate(x >> 32)); 194 } 195 fn Word_4(x: u128) u64 { 196 return @as(u32, @truncate(x)); 197 } 198 }; 199 // 128x128 -> 256 wide multiply for platforms that don't have such an operation; 200 // many 64-bit platforms have this operation, but they tend to have hardware 201 // floating-point, so we don't bother with a special case for them here. 202 203 const product11: u64 = S.Word_1(a) * S.Word_1(b); 204 const product12: u64 = S.Word_1(a) * S.Word_2(b); 205 const product13: u64 = S.Word_1(a) * S.Word_3(b); 206 const product14: u64 = S.Word_1(a) * S.Word_4(b); 207 const product21: u64 = S.Word_2(a) * S.Word_1(b); 208 const product22: u64 = S.Word_2(a) * S.Word_2(b); 209 const product23: u64 = S.Word_2(a) * S.Word_3(b); 210 const product24: u64 = S.Word_2(a) * S.Word_4(b); 211 const product31: u64 = S.Word_3(a) * S.Word_1(b); 212 const product32: u64 = S.Word_3(a) * S.Word_2(b); 213 const product33: u64 = S.Word_3(a) * S.Word_3(b); 214 const product34: u64 = S.Word_3(a) * S.Word_4(b); 215 const product41: u64 = S.Word_4(a) * S.Word_1(b); 216 const product42: u64 = S.Word_4(a) * S.Word_2(b); 217 const product43: u64 = S.Word_4(a) * S.Word_3(b); 218 const product44: u64 = S.Word_4(a) * S.Word_4(b); 219 220 const sum0: u128 = @as(u128, product44); 221 const sum1: u128 = @as(u128, product34) +% 222 @as(u128, product43); 223 const sum2: u128 = @as(u128, product24) +% 224 @as(u128, product33) +% 225 @as(u128, product42); 226 const sum3: u128 = @as(u128, product14) +% 227 @as(u128, product23) +% 228 @as(u128, product32) +% 229 @as(u128, product41); 230 const sum4: u128 = @as(u128, product13) +% 231 @as(u128, product22) +% 232 @as(u128, product31); 233 const sum5: u128 = @as(u128, product12) +% 234 @as(u128, product21); 235 const sum6: u128 = @as(u128, product11); 236 237 const r0: u128 = (sum0 & Word_FullMask) +% 238 ((sum1 & Word_LoMask) << 32); 239 const r1: u128 = (sum0 >> 64) +% 240 ((sum1 >> 32) & Word_FullMask) +% 241 (sum2 & Word_FullMask) +% 242 ((sum3 << 32) & Word_HiMask); 243 244 lo.* = r0 +% (r1 << 64); 245 hi.* = (r1 >> 64) +% 246 (sum1 >> 96) +% 247 (sum2 >> 64) +% 248 (sum3 >> 32) +% 249 sum4 +% 250 (sum5 << 32) +% 251 (sum6 << 64); 252 }, 253 else => @compileError("unsupported"), 254 } 255 } 256 257 pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).float.bits)) i32 { 258 const Z = std.meta.Int(.unsigned, @typeInfo(T).float.bits); 259 const integerBit = @as(Z, 1) << std.math.floatFractionalBits(T); 260 261 const shift = @clz(significand.*) - @clz(integerBit); 262 significand.* <<= @as(std.math.Log2Int(Z), @intCast(shift)); 263 return @as(i32, 1) - shift; 264 } 265 266 pub inline fn fneg(a: anytype) @TypeOf(a) { 267 const F = @TypeOf(a); 268 const bits = @typeInfo(F).float.bits; 269 const U = @Type(.{ .int = .{ 270 .signedness = .unsigned, 271 .bits = bits, 272 } }); 273 const sign_bit_mask = @as(U, 1) << (bits - 1); 274 const negated = @as(U, @bitCast(a)) ^ sign_bit_mask; 275 return @bitCast(negated); 276 } 277 278 /// Allows to access underlying bits as two equally sized lower and higher 279 /// signed or unsigned integers. 280 pub fn HalveInt(comptime T: type, comptime signed_half: bool) type { 281 return extern union { 282 pub const bits = @divExact(@typeInfo(T).int.bits, 2); 283 pub const HalfTU = std.meta.Int(.unsigned, bits); 284 pub const HalfTS = std.meta.Int(.signed, bits); 285 pub const HalfT = if (signed_half) HalfTS else HalfTU; 286 287 all: T, 288 s: if (native_endian == .little) 289 extern struct { low: HalfT, high: HalfT } 290 else 291 extern struct { high: HalfT, low: HalfT }, 292 }; 293 }