From 7a89eebfc64fd81a504700e105bdc2609bb2a442 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 20 Sep 2022 22:46:42 -0400 Subject: [PATCH 1/6] std.math: add support to cast for a comptime_int argument This allows converting a comptime_int to an optional integer type, which either behaves the same as an implicit cast or produces null if the argument is outside the range of the destination type. --- lib/std/math.zig | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 2f16ecb5d8..8abc4fae66 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -1062,10 +1062,11 @@ test "negateCast" { /// return null. pub fn cast(comptime T: type, x: anytype) ?T { comptime assert(@typeInfo(T) == .Int); // must pass an integer - comptime assert(@typeInfo(@TypeOf(x)) == .Int); // must pass an integer - if (maxInt(@TypeOf(x)) > maxInt(T) and x > maxInt(T)) { + const is_comptime = @TypeOf(x) == comptime_int; + comptime assert(is_comptime or @typeInfo(@TypeOf(x)) == .Int); // must pass an integer + if ((is_comptime or maxInt(@TypeOf(x)) > maxInt(T)) and x > maxInt(T)) { return null; - } else if (minInt(@TypeOf(x)) < minInt(T) and x < minInt(T)) { + } else if ((is_comptime or minInt(@TypeOf(x)) < minInt(T)) and x < minInt(T)) { return null; } else { return @intCast(T, x); @@ -1073,12 +1074,18 @@ pub fn cast(comptime T: type, x: anytype) ?T { } test "cast" { + try testing.expect(cast(u8, 300) == null); try testing.expect(cast(u8, @as(u32, 300)) == null); + try testing.expect(cast(i8, -200) == null); try testing.expect(cast(i8, @as(i32, -200)) == null); + try testing.expect(cast(u8, -1) == null); try testing.expect(cast(u8, @as(i8, -1)) == null); + try testing.expect(cast(u64, -1) == null); try testing.expect(cast(u64, @as(i8, -1)) == null); + try testing.expect(cast(u8, 255).? == @as(u8, 255)); try testing.expect(cast(u8, @as(u32, 255)).? == @as(u8, 255)); + try testing.expect(@TypeOf(cast(u8, 255).?) == u8); try testing.expect(@TypeOf(cast(u8, @as(u32, 255)).?) == u8); } From 5c49341f097dc666eb4ad8c26c6b00fd2189a396 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 22 Sep 2022 02:44:22 -0400 Subject: [PATCH 2/6] big.int: add support for non-comptime scalars --- lib/std/math/big/int.zig | 22 ++++++++++++++++++++-- lib/std/math/big/int_test.zig | 13 ++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index e7c8ed72a2..010eb7e5f2 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -391,7 +391,16 @@ pub const Mutable = struct { /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`. pub fn addScalar(r: *Mutable, a: Const, scalar: anytype) void { - var limbs: [calcLimbLen(scalar)]Limb = undefined; + const Scalar = @TypeOf(scalar); + const magnitude = switch (@typeInfo(Scalar)) { + .ComptimeInt => scalar, + .Int => |int| switch (int.signedness) { + .signed => minInt(Scalar), + .unsigned => maxInt(Scalar), + }, + else => @compileError("expected scalar to be an int"), + }; + var limbs: [calcLimbLen(magnitude)]Limb = undefined; const operand = init(&limbs, scalar).toConst(); return add(r, a, operand); } @@ -2303,7 +2312,16 @@ pub const Const = struct { /// Same as `order` but the right-hand operand is a primitive integer. pub fn orderAgainstScalar(lhs: Const, scalar: anytype) math.Order { - var limbs: [calcLimbLen(scalar)]Limb = undefined; + const Scalar = @TypeOf(scalar); + const magnitude = switch (@typeInfo(Scalar)) { + .ComptimeInt => scalar, + .Int => |int| switch (int.signedness) { + .signed => minInt(Scalar), + .unsigned => maxInt(Scalar), + }, + else => @compileError("expected scalar to be an int"), + }; + var limbs: [calcLimbLen(magnitude)]Limb = undefined; const rhs = Mutable.init(&limbs, scalar); return order(lhs, rhs.toConst()); } diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 5b51106ca4..5685a38d41 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -573,7 +573,7 @@ test "big.int add sign" { try testing.expect((try a.to(i32)) == -3); } -test "big.int add scalar" { +test "big.int add comptime scalar" { var a = try Managed.initSet(testing.allocator, 50); defer a.deinit(); @@ -584,6 +584,17 @@ test "big.int add scalar" { try testing.expect((try b.to(u32)) == 55); } +test "big.int add scalar" { + var a = try Managed.initSet(testing.allocator, 123); + defer a.deinit(); + + var b = try Managed.init(testing.allocator); + defer b.deinit(); + try b.addScalar(&a, @as(u32, 31)); + + try testing.expect((try b.to(u32)) == 154); +} + test "big.int addWrap single-single, unsigned" { var a = try Managed.initSet(testing.allocator, maxInt(u17)); defer a.deinit(); From 2fe5bdb9ed4e17c94055a9539092bdf35a55f752 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 11 Oct 2022 19:57:13 -0400 Subject: [PATCH 3/6] big.int: rewrite confusing code in an equivalent but less confusing way --- lib/std/math/big/int.zig | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 010eb7e5f2..ba582ff843 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -391,16 +391,12 @@ pub const Mutable = struct { /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`. pub fn addScalar(r: *Mutable, a: Const, scalar: anytype) void { - const Scalar = @TypeOf(scalar); - const magnitude = switch (@typeInfo(Scalar)) { - .ComptimeInt => scalar, - .Int => |int| switch (int.signedness) { - .signed => minInt(Scalar), - .unsigned => maxInt(Scalar), - }, + const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) { + .ComptimeInt => calcLimbLen(scalar), + .Int => |info| calcTwosCompLimbCount(info.bits), else => @compileError("expected scalar to be an int"), }; - var limbs: [calcLimbLen(magnitude)]Limb = undefined; + var limbs: [limb_len]Limb = undefined; const operand = init(&limbs, scalar).toConst(); return add(r, a, operand); } @@ -2312,16 +2308,12 @@ pub const Const = struct { /// Same as `order` but the right-hand operand is a primitive integer. pub fn orderAgainstScalar(lhs: Const, scalar: anytype) math.Order { - const Scalar = @TypeOf(scalar); - const magnitude = switch (@typeInfo(Scalar)) { - .ComptimeInt => scalar, - .Int => |int| switch (int.signedness) { - .signed => minInt(Scalar), - .unsigned => maxInt(Scalar), - }, + const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) { + .ComptimeInt => calcLimbLen(scalar), + .Int => |info| calcTwosCompLimbCount(info.bits), else => @compileError("expected scalar to be an int"), }; - var limbs: [calcLimbLen(magnitude)]Limb = undefined; + var limbs: [limb_len]Limb = undefined; const rhs = Mutable.init(&limbs, scalar); return order(lhs, rhs.toConst()); } From 38ee512a25776976caf787dff53126eed33ff835 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 12 Oct 2022 08:18:47 -0400 Subject: [PATCH 4/6] math.big.int: add `calcLimbLen` doc comment note When trying to allocate memory for functions like `Managed.init` and `Managed.set` on the stack, a comptime-known allocation size is desired. The doc comments for these functions indicate that `calcLimbLen` can be used to determine how many limbs to allocate, but if `value` is not comptime-known, then neither is `calcLimbLen(value)`. However, an upper bound on the allocation size is still computable at comptime in this case, so this note documents an expression that can be used, rather than trying to add it to every doc comment that mentions `calcLimbLen`. --- lib/std/math/big/int.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index ba582ff843..1c5ac8194d 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -21,6 +21,9 @@ const debug_safety = false; /// Returns the number of limbs needed to store `scalar`, which must be a /// primitive integer value. +/// Note: A comptime-known upper bound of this value that may be used +/// instead if `scalar` is not already comptime-known is +/// `calcTwosCompLimbCount(@typeInfo(@TypeOf(scalar)).Int.bits)` pub fn calcLimbLen(scalar: anytype) usize { if (scalar == 0) { return 1; From e78d7704a4fcde4e1851582212908b6762720216 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 12 Oct 2022 08:56:13 -0400 Subject: [PATCH 5/6] math.big.int: document the purpose of limb_len in scalar methods Ideally this duplicated code could be factored out into a function, but there doesn't seem to be any way in the Zig type system to represent an argument to a function called at comptime that is only needed if it is comptime-known. Instead, we document what is going on in an adjacent comment in case it gets copy-pasted into new methods in the future. --- lib/std/math/big/int.zig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 1c5ac8194d..4d80d710fe 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -394,6 +394,12 @@ pub const Mutable = struct { /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`. pub fn addScalar(r: *Mutable, a: Const, scalar: anytype) void { + // Normally we could just determine the number of limbs needed with calcLimbLen, + // but that is not comptime-known when scalar is not a comptime_int. Instead, we + // use calcTwosCompLimbCount for a non-comptime_int scalar, which can be pessimistic + // in the case that scalar happens to be small in magnitude within its type, but it + // is well worth being able to use the stack and not needing an allocator passed in. + // Note that Mutable.init still sets operand.len to calcLimbLen(scalar) in any case. const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) { .ComptimeInt => calcLimbLen(scalar), .Int => |info| calcTwosCompLimbCount(info.bits), @@ -2311,6 +2317,12 @@ pub const Const = struct { /// Same as `order` but the right-hand operand is a primitive integer. pub fn orderAgainstScalar(lhs: Const, scalar: anytype) math.Order { + // Normally we could just determine the number of limbs needed with calcLimbLen, + // but that is not comptime-known when scalar is not a comptime_int. Instead, we + // use calcTwosCompLimbCount for a non-comptime_int scalar, which can be pessimistic + // in the case that scalar happens to be small in magnitude within its type, but it + // is well worth being able to use the stack and not needing an allocator passed in. + // Note that Mutable.init still sets rhs.len to calcLimbLen(scalar) in any case. const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) { .ComptimeInt => calcLimbLen(scalar), .Int => |info| calcTwosCompLimbCount(info.bits), From 02d7292a8c1f478b77ef23dbe875b5443bba951c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 12 Oct 2022 18:11:46 -0400 Subject: [PATCH 6/6] build.zig: Forward LLVM lib/include dirs from CMake Previously, you might obtain `-lLLVM-15` from the CMake configuration, but we might not be able to locate the library if it's not in your system library path. --- build.zig | 14 ++++++++++++++ lib/std/math/big/int.zig | 4 ++-- src/stage1/config.h.in | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/build.zig b/build.zig index 3c106bd314..5fa7a23a1a 100644 --- a/build.zig +++ b/build.zig @@ -554,6 +554,8 @@ fn addCmakeCfgOptionsToExe( }) catch unreachable); assert(cfg.lld_include_dir.len != 0); exe.addIncludePath(cfg.lld_include_dir); + exe.addIncludePath(cfg.llvm_include_dir); + exe.addLibraryPath(cfg.llvm_lib_dir); addCMakeLibraryList(exe, cfg.clang_libraries); addCMakeLibraryList(exe, cfg.lld_libraries); addCMakeLibraryList(exe, cfg.llvm_libraries); @@ -684,6 +686,8 @@ const CMakeConfig = struct { lld_include_dir: []const u8, lld_libraries: []const u8, clang_libraries: []const u8, + llvm_lib_dir: []const u8, + llvm_include_dir: []const u8, llvm_libraries: []const u8, dia_guids_lib: []const u8, }; @@ -745,6 +749,8 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig { .lld_include_dir = undefined, .lld_libraries = undefined, .clang_libraries = undefined, + .llvm_lib_dir = undefined, + .llvm_include_dir = undefined, .llvm_libraries = undefined, .dia_guids_lib = undefined, }; @@ -782,6 +788,14 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig { .prefix = "#define ZIG_DIA_GUIDS_LIB ", .field = "dia_guids_lib", }, + .{ + .prefix = "#define ZIG_LLVM_INCLUDE_PATH ", + .field = "llvm_include_dir", + }, + .{ + .prefix = "#define ZIG_LLVM_LIB_PATH ", + .field = "llvm_lib_dir", + }, // .prefix = ZIG_LLVM_LINK_MODE parsed manually below }; diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 4d80d710fe..1040226993 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -399,7 +399,7 @@ pub const Mutable = struct { // use calcTwosCompLimbCount for a non-comptime_int scalar, which can be pessimistic // in the case that scalar happens to be small in magnitude within its type, but it // is well worth being able to use the stack and not needing an allocator passed in. - // Note that Mutable.init still sets operand.len to calcLimbLen(scalar) in any case. + // Note that Mutable.init still sets len to calcLimbLen(scalar) in any case. const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) { .ComptimeInt => calcLimbLen(scalar), .Int => |info| calcTwosCompLimbCount(info.bits), @@ -2322,7 +2322,7 @@ pub const Const = struct { // use calcTwosCompLimbCount for a non-comptime_int scalar, which can be pessimistic // in the case that scalar happens to be small in magnitude within its type, but it // is well worth being able to use the stack and not needing an allocator passed in. - // Note that Mutable.init still sets rhs.len to calcLimbLen(scalar) in any case. + // Note that Mutable.init still sets len to calcLimbLen(scalar) in any case. const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) { .ComptimeInt => calcLimbLen(scalar), .Int => |info| calcTwosCompLimbCount(info.bits), diff --git a/src/stage1/config.h.in b/src/stage1/config.h.in index 2be0839996..8d1e688cbe 100644 --- a/src/stage1/config.h.in +++ b/src/stage1/config.h.in @@ -22,6 +22,8 @@ #define ZIG_LLD_INCLUDE_PATH "@LLD_INCLUDE_DIRS@" #define ZIG_LLD_LIBRARIES "@LLD_LIBRARIES@" #define ZIG_CLANG_LIBRARIES "@CLANG_LIBRARIES@" +#define ZIG_LLVM_INCLUDE_PATH "@LLVM_INCLUDE_DIRS@" +#define ZIG_LLVM_LIB_PATH "@LLVM_LIBDIRS@" #define ZIG_LLVM_LIBRARIES "@LLVM_LIBRARIES@" #define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@"