Date: Thu, 21 Jun 2018 14:43:55 -0400
Subject: [PATCH 25/82] add casting docs, __extenddftf2, and __extendsftf2
---
CMakeLists.txt | 1 +
doc/langref.html.in | 171 +++++++++++++++++--
src/ir.cpp | 6 +-
std/special/compiler_rt/extendXfYf2.zig | 87 ++++++++++
std/special/compiler_rt/extendXfYf2_test.zig | 108 ++++++++++++
std/special/compiler_rt/index.zig | 2 +
test/behavior.zig | 1 +
test/cases/widening.zig | 26 +++
8 files changed, 384 insertions(+), 18 deletions(-)
create mode 100644 std/special/compiler_rt/extendXfYf2.zig
create mode 100644 std/special/compiler_rt/extendXfYf2_test.zig
create mode 100644 test/cases/widening.zig
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 030398d71c0eba43a018034dce0ebd23b25fa45b..99de2328d2458039b394b79ddd6d49f1cda62511 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -558,6 +558,7 @@ set(ZIG_STD_FILES
"special/compiler_rt/aullrem.zig"
"special/compiler_rt/comparetf2.zig"
"special/compiler_rt/divti3.zig"
+ "special/compiler_rt/extendXfYf2.zig"
"special/compiler_rt/fixuint.zig"
"special/compiler_rt/fixunsdfdi.zig"
"special/compiler_rt/fixunsdfsi.zig"
diff --git a/doc/langref.html.in b/doc/langref.html.in
index bdc33cb808dbb7be9b2eea5e940344035da470eb..b76fc693850bcb7913395e8fdfa2dedd042ba37f 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -3573,14 +3573,161 @@ const optional_value: ?i32 = null;
{#header_close#}
{#header_close#}
{#header_open|Casting#}
- TODO: explain implicit vs explicit casting
- TODO: resolve peer types builtin
- TODO: truncate builtin
- TODO: bitcast builtin
- TODO: int to ptr builtin
- TODO: ptr to int builtin
- TODO: ptrcast builtin
- TODO: explain number literals vs concrete types
+
+ A type cast converts a value of one type to another.
+ Zig has {#link|Implicit Casts#} for conversions that are known to be completely safe and unambiguous,
+ and {#link|Explicit Casts#} for conversions that one would not want to happen on accident.
+ There is also a third kind of type conversion called {#link|Peer Type Resolution#} for
+ the case when a result type must be decided given multiple operand types.
+
+ {#header_open|Implicit Casts#}
+
+ An implicit cast occurs when one type is expected, but different type is provided:
+
+ {#code_begin|test#}
+test "implicit cast - variable declaration" {
+ var a: u8 = 1;
+ var b: u16 = a;
+}
+
+test "implicit cast - function call" {
+ var a: u8 = 1;
+ foo(a);
+}
+
+fn foo(b: u16) void {}
+
+test "implicit cast - invoke a type as a function" {
+ var a: u8 = 1;
+ var b = u16(a);
+}
+ {#code_end#}
+ {#header_open|Implicit Cast: Stricter Qualification#}
+
+ Values which have the same representation at runtime can be cast to increase the strictness
+ of the qualifiers, no matter how nested the qualifiers are:
+
+
+ const - non-const to const is allowed
+ volatile - non-volatile to volatile is allowed
+ align - bigger to smaller alignment is allowed
+ - {#link|error sets|Error Set Type#} to supersets is allowed
+
+
+ These casts are no-ops at runtime since the value representation does not change.
+
+ {#code_begin|test#}
+test "implicit cast - const qualification" {
+ var a: i32 = 1;
+ var b: *i32 = &a;
+ foo(b);
+}
+
+fn foo(a: *const i32) void {}
+ {#code_end#}
+
+ In addition, pointers implicitly cast to const optional pointers:
+
+ {#code_begin|test#}
+const std = @import("std");
+const assert = std.debug.assert;
+const mem = std.mem;
+
+test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
+ const window_name = [1][*]const u8{c"window name"};
+ const x: [*]const ?[*]const u8 = &window_name;
+ assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name"));
+}
+ {#code_end#}
+ {#header_close#}
+ {#header_open|Implicit Cast: Integer and Float Widening#}
+
+ {#link|Integers#} implicitly cast to integer types which can represent every value of the old type, and likewise
+ {#link|Floats#} implicitly cast to float types which can represent every value of the old type.
+
+ {#code_begin|test#}
+const std = @import("std");
+const assert = std.debug.assert;
+const mem = std.mem;
+
+test "integer widening" {
+ var a: u8 = 250;
+ var b: u16 = a;
+ var c: u32 = b;
+ var d: u64 = c;
+ var e: u64 = d;
+ var f: u128 = e;
+ assert(f == a);
+}
+
+test "implicit unsigned integer to signed integer" {
+ var a: u8 = 250;
+ var b: i16 = a;
+ assert(b == 250);
+}
+
+test "float widening" {
+ var a: f32 = 12.34;
+ var b: f64 = a;
+ var c: f128 = b;
+ assert(c == a);
+}
+ {#code_end#}
+ {#header_close#}
+ {#header_open|Implicit Cast: Arrays#}
+ TODO: [N]T to []const T
+ TODO: *const [N]T to []const T
+ TODO: [N]T to *const []const T
+ TODO: [N]T to ?[]const T
+ TODO: *[N]T to []T
+ TODO: *[N]T to [*]T
+ TODO: *T to *[1]T
+ TODO: [N]T to E![]const T
+ {#header_close#}
+ {#header_open|Implicit Cast: Optionals#}
+ TODO: T to ?T
+ TODO: T to E!?T
+ TODO: null to ?T
+ {#header_close#}
+ {#header_open|Implicit Cast: T to E!T#}
+ TODO
+ {#header_close#}
+ {#header_open|Implicit Cast: E to E!T#}
+ TODO
+ {#header_close#}
+ {#header_open|Implicit Cast: comptime_int to *const integer#}
+ TODO
+ {#header_close#}
+ {#header_open|Implicit Cast: comptime_float to *const float#}
+ TODO
+ {#header_close#}
+ {#header_open|Implicit Cast: compile-time known numbers#}
+ TODO
+ {#header_close#}
+ {#header_open|Implicit Cast: union to enum#}
+ TODO
+ {#header_close#}
+ {#header_open|Implicit Cast: enum to union#}
+ TODO
+ {#header_close#}
+ {#header_open|Implicit Cast: T to *T when @sizeOf(T) == 0#}
+ TODO
+ {#header_close#}
+ {#header_open|Implicit Cast: undefined#}
+ TODO
+ {#header_close#}
+ {#header_open|Implicit Cast: T to *const T#}
+ TODO
+ {#header_close#}
+ {#header_close#}
+
+ {#header_open|Explicit Casts#}
+ TODO
+ {#header_close#}
+
+ {#header_open|Peer Type Resolution#}
+ TODO
+ {#header_close#}
{#header_close#}
{#header_open|void#}
@@ -5522,12 +5669,6 @@ pub const FloatMode = enum {
{#see_also|Compile Variables#}
{#header_close#}
- {#header_open|@setGlobalSection#}
- @setGlobalSection(global_variable_name, comptime section_name: []const u8) bool
-
- Puts the global variable in the specified section.
-
- {#header_close#}
{#header_open|@shlExact#}
@shlExact(value: T, shift_amt: Log2T) T
@@ -6928,7 +7069,7 @@ hljs.registerLanguage("zig", function(t) {
a = t.IR + "\\s*\\(",
c = {
keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse",
- built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum",
+ built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum",
literal: "true false null undefined"
},
n = [e, t.CLCM, t.CBCM, s, r];
diff --git a/src/ir.cpp b/src/ir.cpp
index 950d0514929e3df1ac6c3e8b636c3acbd714ebd7..c6078e755de8289c89fec25edaa87cb95e0f5a1f 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -10092,7 +10092,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
- // cast from &const [N]T to []const T
+ // cast from *const [N]T to []const T
if (is_slice(wanted_type) &&
actual_type->id == TypeTableEntryIdPointer &&
actual_type->data.pointer.is_const &&
@@ -10111,7 +10111,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
- // cast from [N]T to &const []const T
+ // cast from [N]T to *const []const T
if (wanted_type->id == TypeTableEntryIdPointer &&
wanted_type->data.pointer.is_const &&
is_slice(wanted_type->data.pointer.child_type) &&
@@ -10136,7 +10136,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
- // cast from [N]T to ?[]const N
+ // cast from [N]T to ?[]const T
if (wanted_type->id == TypeTableEntryIdOptional &&
is_slice(wanted_type->data.maybe.child_type) &&
actual_type->id == TypeTableEntryIdArray)
diff --git a/std/special/compiler_rt/extendXfYf2.zig b/std/special/compiler_rt/extendXfYf2.zig
new file mode 100644
index 0000000000000000000000000000000000000000..6fa8cf4654a77d850781fab52c349ab614d9b256
--- /dev/null
+++ b/std/special/compiler_rt/extendXfYf2.zig
@@ -0,0 +1,87 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+pub extern fn __extenddftf2(a: f64) f128 {
+ return extendXfYf2(f128, f64, a);
+}
+
+pub extern fn __extendsftf2(a: f32) f128 {
+ return extendXfYf2(f128, f32, a);
+}
+
+const CHAR_BIT = 8;
+
+pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
+ const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits);
+ const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits);
+ const srcSigBits = std.math.floatMantissaBits(src_t);
+ const dstSigBits = std.math.floatMantissaBits(dst_t);
+ const SrcShift = std.math.Log2Int(src_rep_t);
+ const DstShift = std.math.Log2Int(dst_rep_t);
+
+ // Various constants whose values follow from the type parameters.
+ // Any reasonable optimizer will fold and propagate all of these.
+ const srcBits: i32 = @sizeOf(src_t) * CHAR_BIT;
+ const srcExpBits: i32 = srcBits - srcSigBits - 1;
+ const srcInfExp: i32 = (1 << srcExpBits) - 1;
+ const srcExpBias: i32 = srcInfExp >> 1;
+
+ const srcMinNormal: src_rep_t = src_rep_t(1) << srcSigBits;
+ const srcInfinity: src_rep_t = src_rep_t(@bitCast(u32, srcInfExp)) << srcSigBits;
+ const srcSignMask: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits +% srcExpBits);
+ const srcAbsMask: src_rep_t = srcSignMask -% 1;
+ const srcQNaN: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits -% 1);
+ const srcNaNCode: src_rep_t = srcQNaN -% 1;
+
+ const dstBits: i32 = @sizeOf(dst_t) * CHAR_BIT;
+ const dstExpBits: i32 = dstBits - dstSigBits - 1;
+ const dstInfExp: i32 = (1 << dstExpBits) - 1;
+ const dstExpBias: i32 = dstInfExp >> 1;
+
+ const dstMinNormal: dst_rep_t = dst_rep_t(1) << dstSigBits;
+
+ // Break a into a sign and representation of the absolute value
+ const aRep: src_rep_t = @bitCast(src_rep_t, a);
+ const aAbs: src_rep_t = aRep & srcAbsMask;
+ const sign: src_rep_t = aRep & srcSignMask;
+ var absResult: dst_rep_t = undefined;
+
+ // If @sizeOf(src_rep_t) < @sizeOf(int), the subtraction result is promoted
+ // to (signed) int. To avoid that, explicitly cast to src_rep_t.
+ if ((src_rep_t)(aAbs -% srcMinNormal) < srcInfinity -% srcMinNormal) {
+ // a is a normal number.
+ // Extend to the destination type by shifting the significand and
+ // exponent into the proper position and rebiasing the exponent.
+ absResult = dst_rep_t(aAbs) << (dstSigBits -% srcSigBits);
+ absResult += dst_rep_t(@bitCast(u32, dstExpBias -% srcExpBias)) << dstSigBits;
+ } else if (aAbs >= srcInfinity) {
+ // a is NaN or infinity.
+ // Conjure the result by beginning with infinity, then setting the qNaN
+ // bit (if needed) and right-aligning the rest of the trailing NaN
+ // payload field.
+ absResult = dst_rep_t(@bitCast(u32, dstInfExp)) << dstSigBits;
+ absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits);
+ absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits);
+ } else if (aAbs != 0) {
+ // a is denormal.
+ // renormalize the significand and clear the leading bit, then insert
+ // the correct adjusted exponent in the destination type.
+ const scale: i32 = @clz(aAbs) - @clz(srcMinNormal);
+ absResult = dst_rep_t(aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale);
+ absResult ^= dstMinNormal;
+ const resultExponent: i32 = dstExpBias - srcExpBias - scale + 1;
+ absResult |= dst_rep_t(@bitCast(u32, resultExponent)) << @intCast(DstShift, dstSigBits);
+ } else {
+ // a is zero.
+ absResult = 0;
+ }
+
+ // Apply the signbit to (dst_t)abs(a).
+ const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << @intCast(DstShift, dstBits - srcBits);
+ return @bitCast(dst_t, result);
+}
+
+test "import extendXfYf2" {
+ _ = @import("extendXfYf2_test.zig");
+}
diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..84fb410fbb1405a0c7b1f8c1d80c3e6e4d106977
--- /dev/null
+++ b/std/special/compiler_rt/extendXfYf2_test.zig
@@ -0,0 +1,108 @@
+const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2;
+const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2;
+const assert = @import("std").debug.assert;
+
+fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void {
+ const x = __extenddftf2(a);
+
+ const rep = @bitCast(u128, x);
+ const hi = @intCast(u64, rep >> 64);
+ const lo = @truncate(u64, rep);
+
+ if (hi == expectedHi and lo == expectedLo)
+ return;
+
+ // test other possible NaN representation(signal NaN)
+ if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) {
+ if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and
+ ((hi & 0xffffffffffff) > 0 or lo > 0))
+ {
+ return;
+ }
+ }
+
+ @panic("__extenddftf2 test failure");
+}
+
+fn test__extendsftf2(a: f32, expectedHi: u64, expectedLo: u64) void {
+ const x = __extendsftf2(a);
+
+ const rep = @bitCast(u128, x);
+ const hi = @intCast(u64, rep >> 64);
+ const lo = @truncate(u64, rep);
+
+ if (hi == expectedHi and lo == expectedLo)
+ return;
+
+ // test other possible NaN representation(signal NaN)
+ if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) {
+ if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and
+ ((hi & 0xffffffffffff) > 0 or lo > 0))
+ {
+ return;
+ }
+ }
+
+ @panic("__extendsftf2 test failure");
+}
+
+test "extenddftf2" {
+ // qNaN
+ test__extenddftf2(makeQNaN64(), 0x7fff800000000000, 0x0);
+
+ // NaN
+ test__extenddftf2(makeNaN64(0x7100000000000), 0x7fff710000000000, 0x0);
+
+ // inf
+ test__extenddftf2(makeInf64(), 0x7fff000000000000, 0x0);
+
+ // zero
+ test__extenddftf2(0.0, 0x0, 0x0);
+
+ test__extenddftf2(0x1.23456789abcdefp+5, 0x400423456789abcd, 0xf000000000000000);
+
+ test__extenddftf2(0x1.edcba987654321fp-9, 0x3ff6edcba9876543, 0x2000000000000000);
+
+ test__extenddftf2(0x1.23456789abcdefp+45, 0x402c23456789abcd, 0xf000000000000000);
+
+ test__extenddftf2(0x1.edcba987654321fp-45, 0x3fd2edcba9876543, 0x2000000000000000);
+}
+
+test "extendsftf2" {
+ // qNaN
+ test__extendsftf2(makeQNaN32(), 0x7fff800000000000, 0x0);
+ // NaN
+ test__extendsftf2(makeNaN32(0x410000), 0x7fff820000000000, 0x0);
+ // inf
+ test__extendsftf2(makeInf32(), 0x7fff000000000000, 0x0);
+ // zero
+ test__extendsftf2(0.0, 0x0, 0x0);
+ test__extendsftf2(0x1.23456p+5, 0x4004234560000000, 0x0);
+ test__extendsftf2(0x1.edcbap-9, 0x3ff6edcba0000000, 0x0);
+ test__extendsftf2(0x1.23456p+45, 0x402c234560000000, 0x0);
+ test__extendsftf2(0x1.edcbap-45, 0x3fd2edcba0000000, 0x0);
+}
+
+fn makeQNaN64() f64 {
+ return @bitCast(f64, u64(0x7ff8000000000000));
+}
+
+fn makeInf64() f64 {
+ return @bitCast(f64, u64(0x7ff0000000000000));
+}
+
+fn makeNaN64(rand: u64) f64 {
+ return @bitCast(f64, 0x7ff0000000000000 | (rand & 0xfffffffffffff));
+}
+
+fn makeQNaN32() f32 {
+ return @bitCast(f32, u32(0x7fc00000));
+}
+
+fn makeNaN32(rand: u32) f32 {
+ return @bitCast(f32, 0x7f800000 | (rand & 0x7fffff));
+}
+
+fn makeInf32() f32 {
+ return @bitCast(f32, u32(0x7f800000));
+}
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index 6ad7768cb20e2e1526a583a400c2b3823fe617d7..c96e1587f88761192184f35eb8b87fa6707b1859 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -20,6 +20,8 @@ comptime {
@export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage);
@export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage);
+ @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage);
+ @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage);
@export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage);
@export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage);
diff --git a/test/behavior.zig b/test/behavior.zig
index b494c623e25748d5e32581865b8fb1e760a969f9..3a2f706ad4428ad504b6276779b23bd659a6907d 100644
--- a/test/behavior.zig
+++ b/test/behavior.zig
@@ -59,4 +59,5 @@ comptime {
_ = @import("cases/var_args.zig");
_ = @import("cases/void.zig");
_ = @import("cases/while.zig");
+ _ = @import("cases/widening.zig");
}
diff --git a/test/cases/widening.zig b/test/cases/widening.zig
new file mode 100644
index 0000000000000000000000000000000000000000..18c12806d34c5168f4f1f9f41e5cd60b4437e495
--- /dev/null
+++ b/test/cases/widening.zig
@@ -0,0 +1,26 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const mem = std.mem;
+
+test "integer widening" {
+ var a: u8 = 250;
+ var b: u16 = a;
+ var c: u32 = b;
+ var d: u64 = c;
+ var e: u64 = d;
+ var f: u128 = e;
+ assert(f == a);
+}
+
+test "implicit unsigned integer to signed integer" {
+ var a: u8 = 250;
+ var b: i16 = a;
+ assert(b == 250);
+}
+
+test "float widening" {
+ var a: f32 = 12.34;
+ var b: f64 = a;
+ var c: f128 = b;
+ assert(c == a);
+}
--
2.54.0
From 459d72f8736ebd8372b9050c17e5f3bc00092573 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 21 Jun 2018 17:41:49 -0400
Subject: [PATCH 26/82] fix compiler crash for invalid enum
closes #1079
closes #1147
---
src/analyze.cpp | 5 +++--
test/compile_errors.zig | 13 +++++++++++++
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 479abef16a071e42c69bb7812cf964dd04fee486..5160a19e8198bbd221fa545bc6b9133e59151b55 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2318,8 +2318,9 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
return;
if (enum_type->data.enumeration.zero_bits_loop_flag) {
- enum_type->data.enumeration.zero_bits_known = true;
- enum_type->data.enumeration.zero_bits_loop_flag = false;
+ add_node_error(g, enum_type->data.enumeration.decl_node,
+ buf_sprintf("'%s' depends on itself", buf_ptr(&enum_type->name)));
+ enum_type->data.enumeration.is_invalid = true;
return;
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 17896f9ab9da332b19f2f853817782ff27564862..2247f0af966cc4058b2d33c23bb072e50cfe1edc 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,19 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.add(
+ "enum field value references enum",
+ \\pub const Foo = extern enum {
+ \\ A = Foo.B,
+ \\ C = D,
+ \\};
+ \\export fn entry() void {
+ \\ var s: Foo = Foo.E;
+ \\}
+ ,
+ ".tmp_source.zig:1:17: error: 'Foo' depends on itself",
+ );
+
cases.add(
"@floatToInt comptime safety",
\\comptime {
--
2.54.0
From 8866bef92c8b674c2a444c94326c57984597ab05 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 22 Jun 2018 01:49:32 -0400
Subject: [PATCH 27/82] clean up self hosted main. delete unsupported commands
---
src-self-hosted/arg.zig | 4 +-
src-self-hosted/main.zig | 546 ++++++++-----------------------------
src-self-hosted/module.zig | 24 --
test/cases/bugs/1111.zig | 6 +-
4 files changed, 121 insertions(+), 459 deletions(-)
diff --git a/src-self-hosted/arg.zig b/src-self-hosted/arg.zig
index dc89483213e7e3691b3d52c5332cbbc9ba911031..2ab44e5fdfe573692f3a8cafac937220d154a5f1 100644
--- a/src-self-hosted/arg.zig
+++ b/src-self-hosted/arg.zig
@@ -168,7 +168,7 @@ pub const Args = struct {
}
// e.g. --names value1 value2 value3
- pub fn many(self: *Args, name: []const u8) ?[]const []const u8 {
+ pub fn many(self: *Args, name: []const u8) []const []const u8 {
if (self.flags.get(name)) |entry| {
switch (entry.value) {
FlagArg.Many => |inner| {
@@ -177,7 +177,7 @@ pub const Args = struct {
else => @panic("attempted to retrieve flag with wrong type"),
}
} else {
- return null;
+ return []const []const u8{};
}
}
};
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index 45e6bb742a2a1e0218db7c53ce97c80cad4c0103..6dabddaefb7f64940bcbf6de9a2cf73e5f2f65a4 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -26,15 +26,11 @@ const usage =
\\
\\Commands:
\\
- \\ build Build project from build.zig
\\ build-exe [source] Create executable from source or object files
\\ build-lib [source] Create library from source or object files
\\ build-obj [source] Create object from source or assembly
\\ fmt [source] Parse file and render in canonical zig format
- \\ run [source] Create executable and run immediately
\\ targets List available compilation targets
- \\ test [source] Create and run a test build
- \\ translate-c [source] Convert c code to zig code
\\ version Print version number and exit
\\ zen Print zen of zig and exit
\\
@@ -47,7 +43,7 @@ const Command = struct {
};
pub fn main() !void {
- var allocator = std.heap.c_allocator;
+ const allocator = std.heap.c_allocator;
var stdout_file = try std.io.getStdOut();
var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
@@ -58,18 +54,16 @@ pub fn main() !void {
stderr = &stderr_out_stream.stream;
const args = try os.argsAlloc(allocator);
- defer os.argsFree(allocator, args);
+ // TODO I'm getting unreachable code here, which shouldn't happen
+ //defer os.argsFree(allocator, args);
if (args.len <= 1) {
+ try stderr.write("expected command argument\n\n");
try stderr.write(usage);
os.exit(1);
}
const commands = []Command{
- Command{
- .name = "build",
- .exec = cmdBuild,
- },
Command{
.name = "build-exe",
.exec = cmdBuildExe,
@@ -86,22 +80,10 @@ pub fn main() !void {
.name = "fmt",
.exec = cmdFmt,
},
- Command{
- .name = "run",
- .exec = cmdRun,
- },
Command{
.name = "targets",
.exec = cmdTargets,
},
- Command{
- .name = "test",
- .exec = cmdTest,
- },
- Command{
- .name = "translate-c",
- .exec = cmdTranslateC,
- },
Command{
.name = "version",
.exec = cmdVersion,
@@ -124,177 +106,15 @@ pub fn main() !void {
for (commands) |command| {
if (mem.eql(u8, command.name, args[1])) {
- try command.exec(allocator, args[2..]);
- return;
+ return command.exec(allocator, args[2..]);
}
}
try stderr.print("unknown command: {}\n\n", args[1]);
try stderr.write(usage);
+ os.exit(1);
}
-// cmd:build ///////////////////////////////////////////////////////////////////////////////////////
-
-const usage_build =
- \\usage: zig build
- \\
- \\General Options:
- \\ --help Print this help and exit
- \\ --init Generate a build.zig template
- \\ --build-file [file] Override path to build.zig
- \\ --cache-dir [path] Override path to cache directory
- \\ --verbose Print commands before executing them
- \\ --prefix [path] Override default install prefix
- \\
- \\Project-Specific Options:
- \\
- \\ Project-specific options become available when the build file is found.
- \\
- \\Advanced Options:
- \\ --build-file [file] Override path to build.zig
- \\ --cache-dir [path] Override path to cache directory
- \\ --verbose-tokenize Enable compiler debug output for tokenization
- \\ --verbose-ast Enable compiler debug output for parsing into an AST
- \\ --verbose-link Enable compiler debug output for linking
- \\ --verbose-ir Enable compiler debug output for Zig IR
- \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
- \\ --verbose-cimport Enable compiler debug output for C imports
- \\
- \\
-;
-
-const args_build_spec = []Flag{
- Flag.Bool("--help"),
- Flag.Bool("--init"),
- Flag.Arg1("--build-file"),
- Flag.Arg1("--cache-dir"),
- Flag.Bool("--verbose"),
- Flag.Arg1("--prefix"),
-
- Flag.Arg1("--build-file"),
- Flag.Arg1("--cache-dir"),
- Flag.Bool("--verbose-tokenize"),
- Flag.Bool("--verbose-ast"),
- Flag.Bool("--verbose-link"),
- Flag.Bool("--verbose-ir"),
- Flag.Bool("--verbose-llvm-ir"),
- Flag.Bool("--verbose-cimport"),
-};
-
-const missing_build_file =
- \\No 'build.zig' file found.
- \\
- \\Initialize a 'build.zig' template file with `zig build --init`,
- \\or build an executable directly with `zig build-exe $FILENAME.zig`.
- \\
- \\See: `zig build --help` or `zig help` for more options.
- \\
-;
-
-fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void {
- var flags = try Args.parse(allocator, args_build_spec, args);
- defer flags.deinit();
-
- if (flags.present("help")) {
- try stderr.write(usage_build);
- os.exit(0);
- }
-
- const zig_lib_dir = try introspect.resolveZigLibDir(allocator);
- defer allocator.free(zig_lib_dir);
-
- const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");
- defer allocator.free(zig_std_dir);
-
- const special_dir = try os.path.join(allocator, zig_std_dir, "special");
- defer allocator.free(special_dir);
-
- const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
- defer allocator.free(build_runner_path);
-
- const build_file = flags.single("build-file") orelse "build.zig";
- const build_file_abs = try os.path.resolve(allocator, ".", build_file);
- defer allocator.free(build_file_abs);
-
- const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false;
-
- if (flags.present("init")) {
- if (build_file_exists) {
- try stderr.print("build.zig already exists\n");
- os.exit(1);
- }
-
- // need a new scope for proper defer scope finalization on exit
- {
- const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");
- defer allocator.free(build_template_path);
-
- try os.copyFile(allocator, build_template_path, build_file_abs);
- try stderr.print("wrote build.zig template\n");
- }
-
- os.exit(0);
- }
-
- if (!build_file_exists) {
- try stderr.write(missing_build_file);
- os.exit(1);
- }
-
- // TODO: Invoke build.zig entrypoint directly?
- var zig_exe_path = try os.selfExePath(allocator);
- defer allocator.free(zig_exe_path);
-
- var build_args = ArrayList([]const u8).init(allocator);
- defer build_args.deinit();
-
- const build_file_basename = os.path.basename(build_file_abs);
- const build_file_dirname = os.path.dirname(build_file_abs) orelse ".";
-
- var full_cache_dir: []u8 = undefined;
- if (flags.single("cache-dir")) |cache_dir| {
- full_cache_dir = try os.path.resolve(allocator, ".", cache_dir, full_cache_dir);
- } else {
- full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");
- }
- defer allocator.free(full_cache_dir);
-
- const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");
- defer allocator.free(path_to_build_exe);
-
- try build_args.append(path_to_build_exe);
- try build_args.append(zig_exe_path);
- try build_args.append(build_file_dirname);
- try build_args.append(full_cache_dir);
-
- var proc = try os.ChildProcess.init(build_args.toSliceConst(), allocator);
- defer proc.deinit();
-
- var term = try proc.spawnAndWait();
- switch (term) {
- os.ChildProcess.Term.Exited => |status| {
- if (status != 0) {
- try stderr.print("{} exited with status {}\n", build_args.at(0), status);
- os.exit(1);
- }
- },
- os.ChildProcess.Term.Signal => |signal| {
- try stderr.print("{} killed by signal {}\n", build_args.at(0), signal);
- os.exit(1);
- },
- os.ChildProcess.Term.Stopped => |signal| {
- try stderr.print("{} stopped by signal {}\n", build_args.at(0), signal);
- os.exit(1);
- },
- os.ChildProcess.Term.Unknown => |status| {
- try stderr.print("{} encountered unknown failure {}\n", build_args.at(0), status);
- os.exit(1);
- },
- }
-}
-
-// cmd:build-exe ///////////////////////////////////////////////////////////////////////////////////
-
const usage_build_generic =
\\usage: zig build-exe [file]
\\ zig build-lib [file]
@@ -315,8 +135,11 @@ const usage_build_generic =
\\ --output-h [file] Override generated header file path
\\ --pkg-begin [name] [path] Make package available to import and push current pkg
\\ --pkg-end Pop current pkg
- \\ --release-fast Build with optimizations on and safety off
- \\ --release-safe Build with optimizations on and safety on
+ \\ --mode [mode] Set the build mode
+ \\ debug (default) optimizations off, safety on
+ \\ release-fast optimizations on, safety off
+ \\ release-safe optimizations on, safety on
+ \\ release-small optimize for small binary, safety off
\\ --static Output will be statically linked
\\ --strip Exclude debug symbols
\\ --target-arch [name] Specify target architecture
@@ -367,6 +190,12 @@ const args_build_generic = []Flag{
"off",
"on",
}),
+ Flag.Option("--mode", []const []const u8{
+ "debug",
+ "release-fast",
+ "release-safe",
+ "release-small",
+ }),
Flag.ArgMergeN("--assembly", 1),
Flag.Arg1("--cache-dir"),
@@ -383,8 +212,6 @@ const args_build_generic = []Flag{
// NOTE: Parsed manually after initial check
Flag.ArgN("--pkg-begin", 2),
Flag.Bool("--pkg-end"),
- Flag.Bool("--release-fast"),
- Flag.Bool("--release-safe"),
Flag.Bool("--static"),
Flag.Bool("--strip"),
Flag.Arg1("--target-arch"),
@@ -431,16 +258,25 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
defer flags.deinit();
if (flags.present("help")) {
- try stderr.write(usage_build_generic);
+ try stdout.write(usage_build_generic);
os.exit(0);
}
- var build_mode = builtin.Mode.Debug;
- if (flags.present("release-fast")) {
- build_mode = builtin.Mode.ReleaseFast;
- } else if (flags.present("release-safe")) {
- build_mode = builtin.Mode.ReleaseSafe;
- }
+ const build_mode = blk: {
+ if (flags.single("mode")) |mode_flag| {
+ if (mem.eql(u8, mode_flag, "debug")) {
+ break :blk builtin.Mode.Debug;
+ } else if (mem.eql(u8, mode_flag, "release-fast")) {
+ break :blk builtin.Mode.ReleaseFast;
+ } else if (mem.eql(u8, mode_flag, "release-safe")) {
+ break :blk builtin.Mode.ReleaseSafe;
+ } else if (mem.eql(u8, mode_flag, "release-small")) {
+ break :blk builtin.Mode.ReleaseSmall;
+ } else unreachable;
+ } else {
+ break :blk builtin.Mode.Debug;
+ }
+ };
const color = blk: {
if (flags.single("color")) |color_flag| {
@@ -456,20 +292,21 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
}
};
- var emit_type = Module.Emit.Binary;
- if (flags.single("emit")) |emit_flag| {
- if (mem.eql(u8, emit_flag, "asm")) {
- emit_type = Module.Emit.Assembly;
- } else if (mem.eql(u8, emit_flag, "bin")) {
- emit_type = Module.Emit.Binary;
- } else if (mem.eql(u8, emit_flag, "llvm-ir")) {
- emit_type = Module.Emit.LlvmIr;
+ const emit_type = blk: {
+ if (flags.single("emit")) |emit_flag| {
+ if (mem.eql(u8, emit_flag, "asm")) {
+ break :blk Module.Emit.Assembly;
+ } else if (mem.eql(u8, emit_flag, "bin")) {
+ break :blk Module.Emit.Binary;
+ } else if (mem.eql(u8, emit_flag, "llvm-ir")) {
+ break :blk Module.Emit.LlvmIr;
+ } else unreachable;
} else {
- unreachable;
+ break :blk Module.Emit.Binary;
}
- }
+ };
- var cur_pkg = try Module.CliPkg.init(allocator, "", "", null); // TODO: Need a path, name?
+ var cur_pkg = try CliPkg.init(allocator, "", "", null);
defer cur_pkg.deinit();
var i: usize = 0;
@@ -482,15 +319,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
i += 1;
const new_pkg_path = args[i];
- var new_cur_pkg = try Module.CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
+ var new_cur_pkg = try CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
try cur_pkg.children.append(new_cur_pkg);
cur_pkg = new_cur_pkg;
} else if (mem.eql(u8, "--pkg-end", arg_name)) {
- if (cur_pkg.parent == null) {
+ if (cur_pkg.parent) |parent| {
+ cur_pkg = parent;
+ } else {
try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");
os.exit(1);
}
- cur_pkg = cur_pkg.parent.?;
}
}
@@ -499,43 +337,42 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
os.exit(1);
}
- var in_file: ?[]const u8 = undefined;
- switch (flags.positionals.len) {
- 0 => {
- try stderr.write("--name [name] not provided and unable to infer\n");
- os.exit(1);
- },
- 1 => {
- in_file = flags.positionals.at(0);
- },
+ const provided_name = flags.single("name");
+ const root_source_file = switch (flags.positionals.len) {
+ 0 => null,
+ 1 => flags.positionals.at(0),
else => {
- try stderr.write("only one zig input file is accepted during build\n");
+ try stderr.print("unexpected extra parameter: {}\n", flags.positionals.at(1));
os.exit(1);
},
- }
+ };
- const basename = os.path.basename(in_file.?);
- var it = mem.split(basename, ".");
- const root_name = it.next() orelse {
- try stderr.write("file name cannot be empty\n");
- os.exit(1);
+ const root_name = if (provided_name) |n| n else blk: {
+ if (root_source_file) |file| {
+ const basename = os.path.basename(file);
+ var it = mem.split(basename, ".");
+ break :blk it.next() orelse basename;
+ } else {
+ try stderr.write("--name [name] not provided and unable to infer\n");
+ os.exit(1);
+ }
};
- const asm_a = flags.many("assembly");
- const obj_a = flags.many("object");
- if (in_file == null and (obj_a == null or obj_a.?.len == 0) and (asm_a == null or asm_a.?.len == 0)) {
+ const assembly_files = flags.many("assembly");
+ const link_objects = flags.many("object");
+ if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) {
try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
os.exit(1);
}
- if (out_type == Module.Kind.Obj and (obj_a != null and obj_a.?.len != 0)) {
+ if (out_type == Module.Kind.Obj and link_objects.len != 0) {
try stderr.write("When building an object file, --object arguments are invalid\n");
os.exit(1);
}
- const zig_root_source_file = in_file;
-
- const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") orelse "zig-cache"[0..]) catch {
+ const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..];
+ const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch {
+ try stderr.print("invalid cache dir: {}\n", rel_cache_dir);
os.exit(1);
};
defer allocator.free(full_cache_dir);
@@ -546,7 +383,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
var module = try Module.create(
allocator,
root_name,
- zig_root_source_file,
+ root_source_file,
Target.Native,
out_type,
build_mode,
@@ -561,24 +398,21 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
module.is_test = false;
- if (flags.single("linker-script")) |linker_script| {
- module.linker_script = linker_script;
- }
-
+ module.linker_script = flags.single("linker-script");
module.each_lib_rpath = flags.present("each-lib-rpath");
var clang_argv_buf = ArrayList([]const u8).init(allocator);
defer clang_argv_buf.deinit();
- if (flags.many("mllvm")) |mllvm_flags| {
- for (mllvm_flags) |mllvm| {
- try clang_argv_buf.append("-mllvm");
- try clang_argv_buf.append(mllvm);
- }
- module.llvm_argv = mllvm_flags;
- module.clang_argv = clang_argv_buf.toSliceConst();
+ const mllvm_flags = flags.many("mllvm");
+ for (mllvm_flags) |mllvm| {
+ try clang_argv_buf.append("-mllvm");
+ try clang_argv_buf.append(mllvm);
}
+ module.llvm_argv = mllvm_flags;
+ module.clang_argv = clang_argv_buf.toSliceConst();
+
module.strip = flags.present("strip");
module.is_static = flags.present("static");
@@ -610,18 +444,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
module.verbose_cimport = flags.present("verbose-cimport");
module.err_color = color;
-
- if (flags.many("library-path")) |lib_dirs| {
- module.lib_dirs = lib_dirs;
- }
-
- if (flags.many("framework")) |frameworks| {
- module.darwin_frameworks = frameworks;
- }
-
- if (flags.many("rpath")) |rpath_list| {
- module.rpath_list = rpath_list;
- }
+ module.lib_dirs = flags.many("library-path");
+ module.darwin_frameworks = flags.many("framework");
+ module.rpath_list = flags.many("rpath");
if (flags.single("output-h")) |output_h| {
module.out_h_path = output_h;
@@ -644,41 +469,25 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
}
module.emit_file_type = emit_type;
- if (flags.many("object")) |objects| {
- module.link_objects = objects;
- }
- if (flags.many("assembly")) |assembly_files| {
- module.assembly_files = assembly_files;
- }
+ module.link_objects = link_objects;
+ module.assembly_files = assembly_files;
try module.build();
- try module.link(flags.single("out-file") orelse null);
-
- if (flags.present("print-timing-info")) {
- // codegen_print_timing_info(g, stderr);
- }
-
- try stderr.print("building {}: {}\n", @tagName(out_type), in_file);
+ try module.link(flags.single("out-file"));
}
fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void {
- try buildOutputType(allocator, args, Module.Kind.Exe);
+ return buildOutputType(allocator, args, Module.Kind.Exe);
}
-// cmd:build-lib ///////////////////////////////////////////////////////////////////////////////////
-
fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void {
- try buildOutputType(allocator, args, Module.Kind.Lib);
+ return buildOutputType(allocator, args, Module.Kind.Lib);
}
-// cmd:build-obj ///////////////////////////////////////////////////////////////////////////////////
-
fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void {
- try buildOutputType(allocator, args, Module.Kind.Obj);
+ return buildOutputType(allocator, args, Module.Kind.Obj);
}
-// cmd:fmt /////////////////////////////////////////////////////////////////////////////////////////
-
const usage_fmt =
\\usage: zig fmt [file]...
\\
@@ -735,7 +544,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
defer flags.deinit();
if (flags.present("help")) {
- try stderr.write(usage_fmt);
+ try stdout.write(usage_fmt);
os.exit(0);
}
@@ -863,162 +672,16 @@ fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void {
}
}
-// cmd:version /////////////////////////////////////////////////////////////////////////////////////
-
fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void {
try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
}
-// cmd:test ////////////////////////////////////////////////////////////////////////////////////////
-
-const usage_test =
- \\usage: zig test [file]...
- \\
- \\Options:
- \\ --help Print this help and exit
- \\
- \\
-;
-
const args_test_spec = []Flag{Flag.Bool("--help")};
-fn cmdTest(allocator: *Allocator, args: []const []const u8) !void {
- var flags = try Args.parse(allocator, args_build_spec, args);
- defer flags.deinit();
-
- if (flags.present("help")) {
- try stderr.write(usage_test);
- os.exit(0);
- }
-
- if (flags.positionals.len != 1) {
- try stderr.write("expected exactly one zig source file\n");
- os.exit(1);
- }
-
- // compile the test program into the cache and run
-
- // NOTE: May be overlap with buildOutput, take the shared part out.
- try stderr.print("testing file {}\n", flags.positionals.at(0));
-}
-
-// cmd:run /////////////////////////////////////////////////////////////////////////////////////////
-
-// Run should be simple and not expose the full set of arguments provided by build-exe. If specific
-// build requirements are need, the user should `build-exe` then `run` manually.
-const usage_run =
- \\usage: zig run [file] --
- \\
- \\Options:
- \\ --help Print this help and exit
- \\
- \\
-;
-
-const args_run_spec = []Flag{Flag.Bool("--help")};
-
-fn cmdRun(allocator: *Allocator, args: []const []const u8) !void {
- var compile_args = args;
- var runtime_args: []const []const u8 = []const []const u8{};
-
- for (args) |argv, i| {
- if (mem.eql(u8, argv, "--")) {
- compile_args = args[0..i];
- runtime_args = args[i + 1 ..];
- break;
- }
- }
- var flags = try Args.parse(allocator, args_run_spec, compile_args);
- defer flags.deinit();
-
- if (flags.present("help")) {
- try stderr.write(usage_run);
- os.exit(0);
- }
-
- if (flags.positionals.len != 1) {
- try stderr.write("expected exactly one zig source file\n");
- os.exit(1);
- }
-
- try stderr.print("runtime args:\n");
- for (runtime_args) |cargs| {
- try stderr.print("{}\n", cargs);
- }
-}
-
-// cmd:translate-c /////////////////////////////////////////////////////////////////////////////////
-
-const usage_translate_c =
- \\usage: zig translate-c [file]
- \\
- \\Options:
- \\ --help Print this help and exit
- \\ --enable-timing-info Print timing diagnostics
- \\ --output [path] Output file to write generated zig file (default: stdout)
- \\
- \\
-;
-
-const args_translate_c_spec = []Flag{
- Flag.Bool("--help"),
- Flag.Bool("--enable-timing-info"),
- Flag.Arg1("--libc-include-dir"),
- Flag.Arg1("--output"),
-};
-
-fn cmdTranslateC(allocator: *Allocator, args: []const []const u8) !void {
- var flags = try Args.parse(allocator, args_translate_c_spec, args);
- defer flags.deinit();
-
- if (flags.present("help")) {
- try stderr.write(usage_translate_c);
- os.exit(0);
- }
-
- if (flags.positionals.len != 1) {
- try stderr.write("expected exactly one c source file\n");
- os.exit(1);
- }
-
- // set up codegen
-
- const zig_root_source_file = null;
-
- // NOTE: translate-c shouldn't require setting up the full codegen instance as it does in
- // the C++ compiler.
-
- // codegen_create(g);
- // codegen_set_out_name(g, null);
- // codegen_translate_c(g, flags.positional.at(0))
-
- var output_stream = stdout;
- if (flags.single("output")) |output_file| {
- var file = try os.File.openWrite(allocator, output_file);
- defer file.close();
-
- var file_stream = io.FileOutStream.init(&file);
- // TODO: Not being set correctly, still stdout
- output_stream = &file_stream.stream;
- }
-
- // ast_render(g, output_stream, g->root_import->root, 4);
- try output_stream.write("pub const example = 10;\n");
-
- if (flags.present("enable-timing-info")) {
- // codegen_print_timing_info(g, stdout);
- try stderr.write("printing timing info for translate-c\n");
- }
-}
-
-// cmd:help ////////////////////////////////////////////////////////////////////////////////////////
-
fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
- try stderr.write(usage);
+ try stdout.write(usage);
}
-// cmd:zen /////////////////////////////////////////////////////////////////////////////////////////
-
const info_zen =
\\
\\ * Communicate intent precisely.
@@ -1040,8 +703,6 @@ fn cmdZen(allocator: *Allocator, args: []const []const u8) !void {
try stdout.write(info_zen);
}
-// cmd:internal ////////////////////////////////////////////////////////////////////////////////////
-
const usage_internal =
\\usage: zig internal [subcommand]
\\
@@ -1095,3 +756,28 @@ fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void {
std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
);
}
+
+const CliPkg = struct {
+ name: []const u8,
+ path: []const u8,
+ children: ArrayList(*CliPkg),
+ parent: ?*CliPkg,
+
+ pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg {
+ var pkg = try allocator.create(CliPkg{
+ .name = name,
+ .path = path,
+ .children = ArrayList(*CliPkg).init(allocator),
+ .parent = parent,
+ });
+ return pkg;
+ }
+
+ pub fn deinit(self: *CliPkg) void {
+ for (self.children.toSliceConst()) |child| {
+ child.deinit();
+ }
+ self.children.deinit();
+ }
+};
+
diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig
index 5f02f1a832ce1d2693c5b32e34af378a4d3598f7..4da46cd38c5a9324a0a0f40dec7f490d97ee1b5c 100644
--- a/src-self-hosted/module.zig
+++ b/src-self-hosted/module.zig
@@ -103,30 +103,6 @@ pub const Module = struct {
LlvmIr,
};
- pub const CliPkg = struct {
- name: []const u8,
- path: []const u8,
- children: ArrayList(*CliPkg),
- parent: ?*CliPkg,
-
- pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg {
- var pkg = try allocator.create(CliPkg{
- .name = name,
- .path = path,
- .children = ArrayList(*CliPkg).init(allocator),
- .parent = parent,
- });
- return pkg;
- }
-
- pub fn deinit(self: *CliPkg) void {
- for (self.children.toSliceConst()) |child| {
- child.deinit();
- }
- self.children.deinit();
- }
- };
-
pub fn create(
allocator: *mem.Allocator,
name: []const u8,
diff --git a/test/cases/bugs/1111.zig b/test/cases/bugs/1111.zig
index 51ce90af52cdfc544cff017c9a3a3153a69f3105..f62107f9a3311864f7861e88713fc2b59a1f6297 100644
--- a/test/cases/bugs/1111.zig
+++ b/test/cases/bugs/1111.zig
@@ -5,8 +5,8 @@ const Foo = extern enum {
test "issue 1111 fixed" {
const v = Foo.Bar;
- switch(v) {
- Foo.Bar => return,
- else => return,
+ switch (v) {
+ Foo.Bar => return,
+ else => return,
}
}
--
2.54.0
From 3290e728339e49765b1adda78f173befb9fc12bf Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 25 Jun 2018 11:52:18 -0400
Subject: [PATCH 28/82] std.zig.ast: fix incorrect impl of FnProto.firstToken
closes #1151
---
std/zig/ast.zig | 1 +
std/zig/parser_test.zig | 9 +++++++++
2 files changed, 10 insertions(+)
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 4246a508618d18de1ac16bee5d02133b75189912..63518c51825cc7b0a901653d7467e06338364e82 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -858,6 +858,7 @@ pub const Node = struct {
pub fn firstToken(self: *FnProto) TokenIndex {
if (self.visib_token) |visib_token| return visib_token;
+ if (self.async_attr) |async_attr| return async_attr.firstToken();
if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
assert(self.lib_name == null);
if (self.cc_token) |cc_token| return cc_token;
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 09ea8aa1a15f529591c0e5b31c5faa1fd60c4d1a..21259bec3c55327f6b1a1135806a8511ee35713d 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,12 @@
+test "zig fmt: preserve space between async fn definitions" {
+ try testCanonical(
+ \\async fn a() void {}
+ \\
+ \\async fn b() void {}
+ \\
+ );
+}
+
test "zig fmt: comment to disable/enable zig fmt first" {
try testCanonical(
\\// Test trailing comma syntax
--
2.54.0
From 8e714289cac55fd6f793cf21cea5fa1930edb985 Mon Sep 17 00:00:00 2001
From: Isaac Hier
Date: Sun, 24 Jun 2018 20:27:18 -0400
Subject: [PATCH 29/82] Fix os_path_join for case where dirname is empty
---
src/os.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/os.cpp b/src/os.cpp
index b7d2fd1de026bfd68a0fa0a800b955232c7cff3d..d52295950d3f6c3d76fa7c0ea50273044336598a 100644
--- a/src/os.cpp
+++ b/src/os.cpp
@@ -225,6 +225,11 @@ void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname) {
}
void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) {
+ if (buf_len(dirname) == 0) {
+ buf_init_from_buf(out_full_path, basename);
+ return;
+ }
+
buf_init_from_buf(out_full_path, dirname);
uint8_t c = *(buf_ptr(out_full_path) + buf_len(out_full_path) - 1);
if (!os_is_sep(c))
--
2.54.0
From af95e1557214df4a1a34a712efc2f8dafb502c82 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 26 Jun 2018 15:10:11 -0400
Subject: [PATCH 30/82] rename get_maybe_type to get_optional_type
---
src/all_types.hpp | 2 +-
src/analyze.cpp | 12 ++++++------
src/analyze.hpp | 2 +-
src/ir.cpp | 38 +++++++++++++++++++-------------------
4 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 12e054cbebd5663da67a95c627474c82f058392f..019dcb182ef32bee918223192ba5fc3371e50357 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1233,7 +1233,7 @@ struct TypeTableEntry {
// use these fields to make sure we don't duplicate type table entries for the same type
TypeTableEntry *pointer_parent[2]; // [0 - mut, 1 - const]
- TypeTableEntry *maybe_parent;
+ TypeTableEntry *optional_parent;
TypeTableEntry *promise_parent;
TypeTableEntry *promise_frame_parent;
// If we generate a constant name value for this type, we memoize it here.
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 5160a19e8198bbd221fa545bc6b9133e59151b55..c018ee4e924e5882b9cef14c817fac8efc3f9cb7 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -482,7 +482,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
return return_type->promise_frame_parent;
}
- TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise);
+ TypeTableEntry *awaiter_handle_type = get_optional_type(g, g->builtin_types.entry_promise);
TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);
ZigList field_names = {};
@@ -513,9 +513,9 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
return entry;
}
-TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
- if (child_type->maybe_parent) {
- TypeTableEntry *entry = child_type->maybe_parent;
+TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type) {
+ if (child_type->optional_parent) {
+ TypeTableEntry *entry = child_type->optional_parent;
return entry;
} else {
ensure_complete_type(g, child_type);
@@ -592,7 +592,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
entry->data.maybe.child_type = child_type;
- child_type->maybe_parent = entry;
+ child_type->optional_parent = entry;
return entry;
}
}
@@ -2996,7 +2996,7 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
- TypeTableEntry *optional_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g));
+ TypeTableEntry *optional_ptr_to_stack_trace_type = get_optional_type(g, get_ptr_to_stack_trace_type(g));
if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 88e06b2390e99137cd016e56394c2a198c2c3ccf..c2730197e2d7769af7c771a9dedcdf0b004146da 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -24,7 +24,7 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type);
TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id);
-TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type);
+TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type);
TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size);
TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type);
TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
diff --git a/src/ir.cpp b/src/ir.cpp
index c6078e755de8289c89fec25edaa87cb95e0f5a1f..1930bbb248d3b947790d39573557cf7bff37b8d6 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -3044,7 +3044,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node,
- get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
+ get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
// TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig
IrInstruction *replacement_value = irb->exec->coro_handle;
IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node,
@@ -6654,7 +6654,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr);
IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle);
IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node,
- get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
+ get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, parent_scope, node,
promise_type_val, awaiter_field_ptr, nullptr, irb->exec->coro_handle, nullptr,
AtomicRmwOp_xchg, AtomicOrderSeqCst);
@@ -6988,7 +6988,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node);
IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node,
- get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
+ get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value);
irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var);
@@ -8762,7 +8762,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
} else if (prev_inst->value.type->id == TypeTableEntryIdOptional) {
return prev_inst->value.type;
} else {
- return get_maybe_type(ira->codegen, prev_inst->value.type);
+ return get_optional_type(ira->codegen, prev_inst->value.type);
}
} else {
return prev_inst->value.type;
@@ -12127,7 +12127,7 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
{
if (instruction->optional == IrInstructionErrorReturnTrace::Null) {
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
- TypeTableEntry *optional_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
+ TypeTableEntry *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type);
if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
assert(get_codegen_ptr_type(optional_type) != nullptr);
@@ -13105,7 +13105,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op
case TypeTableEntryIdPromise:
{
ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base);
- out_val->data.x_type = get_maybe_type(ira->codegen, type_entry);
+ out_val->data.x_type = get_optional_type(ira->codegen, type_entry);
return ira->codegen->builtin_types.entry_type;
}
case TypeTableEntryIdUnreachable:
@@ -16326,7 +16326,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
true, false, PtrLenUnknown,
get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8),
0, 0);
- fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
+ fn_def_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) {
fn_def_fields[6].data.x_optional = create_const_vals(1);
ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);
@@ -16609,7 +16609,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
// child: ?type
ensure_field_index(result->type, "child", 0);
fields[0].special = ConstValSpecialStatic;
- fields[0].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type);
+ fields[0].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
if (type_entry->data.promise.result_type == nullptr)
fields[0].data.x_optional = nullptr;
@@ -16763,7 +16763,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
// tag_type: ?type
ensure_field_index(result->type, "tag_type", 1);
fields[1].special = ConstValSpecialStatic;
- fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type);
+ fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
AstNode *union_decl_node = type_entry->data.unionation.decl_node;
if (union_decl_node->data.container_decl.auto_enum ||
@@ -16803,7 +16803,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
ConstExprValue *inner_fields = create_const_vals(3);
inner_fields[1].special = ConstValSpecialStatic;
- inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type);
+ inner_fields[1].type = get_optional_type(ira->codegen, type_info_enum_field_type);
if (fields[1].data.x_optional == nullptr) {
inner_fields[1].data.x_optional = nullptr;
@@ -16874,7 +16874,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
ConstExprValue *inner_fields = create_const_vals(3);
inner_fields[1].special = ConstValSpecialStatic;
- inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize);
+ inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_usize);
if (!type_has_bits(struct_field->type_entry)) {
inner_fields[1].data.x_optional = nullptr;
@@ -16934,7 +16934,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
// return_type: ?type
ensure_field_index(result->type, "return_type", 3);
fields[3].special = ConstValSpecialStatic;
- fields[3].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type);
+ fields[3].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
if (type_entry->data.fn.fn_type_id.return_type == nullptr)
fields[3].data.x_optional = nullptr;
else {
@@ -16947,7 +16947,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
// async_allocator_type: type
ensure_field_index(result->type, "async_allocator_type", 4);
fields[4].special = ConstValSpecialStatic;
- fields[4].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type);
+ fields[4].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr)
fields[4].data.x_optional = nullptr;
else {
@@ -16990,7 +16990,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
inner_fields[1].type = ira->codegen->builtin_types.entry_bool;
inner_fields[1].data.x_bool = fn_param_info->is_noalias;
inner_fields[2].special = ConstValSpecialStatic;
- inner_fields[2].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type);
+ inner_fields[2].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
if (arg_is_generic)
inner_fields[2].data.x_optional = nullptr;
@@ -17342,7 +17342,7 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct
IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak,
operand_type, success_order, failure_order);
- result->value.type = get_maybe_type(ira->codegen, operand_type);
+ result->value.type = get_optional_type(ira->codegen, operand_type);
ir_link_new_instruction(result, &instruction->base);
ir_add_alloca(ira, result, result->value.type);
return result->value.type;
@@ -19013,7 +19013,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
old_align_bytes = ptr_type->data.pointer.alignment;
TypeTableEntry *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes);
- result_type = get_maybe_type(ira->codegen, better_ptr_type);
+ result_type = get_optional_type(ira->codegen, better_ptr_type);
} else if (target_type->id == TypeTableEntryIdOptional &&
target_type->data.maybe.child_type->id == TypeTableEntryIdFn)
{
@@ -19021,7 +19021,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
old_align_bytes = fn_type_id.alignment;
fn_type_id.alignment = align_bytes;
TypeTableEntry *fn_type = get_fn_type(ira->codegen, &fn_type_id);
- result_type = get_maybe_type(ira->codegen, fn_type);
+ result_type = get_optional_type(ira->codegen, fn_type);
} else if (is_slice(target_type)) {
TypeTableEntry *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry;
old_align_bytes = slice_ptr_type->data.pointer.alignment;
@@ -19782,7 +19782,7 @@ static TypeTableEntry *ir_analyze_instruction_coro_free(IrAnalyze *ira, IrInstru
instruction->base.source_node, coro_id, coro_handle);
ir_link_new_instruction(result, &instruction->base);
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false);
- result->value.type = get_maybe_type(ira->codegen, ptr_type);
+ result->value.type = get_optional_type(ira->codegen, ptr_type);
return result->value.type;
}
@@ -19850,7 +19850,7 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira,
instruction->base.source_node, alloc_fn, coro_size);
ir_link_new_instruction(result, &instruction->base);
TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false);
- result->value.type = get_maybe_type(ira->codegen, u8_ptr_type);
+ result->value.type = get_optional_type(ira->codegen, u8_ptr_type);
return result->value.type;
}
--
2.54.0
From 11ca38a4e9c637bf6ff635f4f62634edaf89f853 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 26 Jun 2018 15:27:41 -0400
Subject: [PATCH 31/82] fix crash for optional pointer to empty struct
closes #1153
---
src/ir.cpp | 3 ++-
test/behavior.zig | 1 +
test/cases/optional.zig | 9 +++++++++
3 files changed, 12 insertions(+), 1 deletion(-)
create mode 100644 test/cases/optional.zig
diff --git a/src/ir.cpp b/src/ir.cpp
index 1930bbb248d3b947790d39573557cf7bff37b8d6..76178f2437cbeb0b29a0ebe98e23339a5593ca68 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -7985,9 +7985,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
// * and [*] can do a const-cast-only to ?* and ?[*], respectively
// but not if there is a mutable parent pointer
+ // and not if the pointer is zero bits
if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional &&
wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer &&
- actual_type->id == TypeTableEntryIdPointer)
+ actual_type->id == TypeTableEntryIdPointer && type_has_bits(actual_type))
{
ConstCastOnly child = types_match_const_cast_only(ira,
wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable);
diff --git a/test/behavior.zig b/test/behavior.zig
index 3a2f706ad4428ad504b6276779b23bd659a6907d..3766ed43052d95c1e935c75e1ba4348468c71e1a 100644
--- a/test/behavior.zig
+++ b/test/behavior.zig
@@ -35,6 +35,7 @@ comptime {
_ = @import("cases/math.zig");
_ = @import("cases/merge_error_sets.zig");
_ = @import("cases/misc.zig");
+ _ = @import("cases/optional.zig");
_ = @import("cases/namespace_depends_on_compile_var/index.zig");
_ = @import("cases/new_stack_call.zig");
_ = @import("cases/null.zig");
diff --git a/test/cases/optional.zig b/test/cases/optional.zig
new file mode 100644
index 0000000000000000000000000000000000000000..0129252dab658a1fd8b143318f2513c407a22500
--- /dev/null
+++ b/test/cases/optional.zig
@@ -0,0 +1,9 @@
+const assert = @import("std").debug.assert;
+
+pub const EmptyStruct = struct {};
+
+test "optional pointer to size zero struct" {
+ var e = EmptyStruct{};
+ var o: ?*EmptyStruct = &e;
+ assert(o != null);
+}
--
2.54.0
From 4de60dde6ed734acbc428887866ae3d528abbd37 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 26 Jun 2018 15:48:42 -0400
Subject: [PATCH 32/82] langref: explicit cast section
---
doc/langref.html.in | 31 ++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index b76fc693850bcb7913395e8fdfa2dedd042ba37f..8e24a4be2cf55ed0fc300b04b34c1a28a6b1f9ca 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -3602,6 +3602,10 @@ test "implicit cast - invoke a type as a function" {
var b = u16(a);
}
{#code_end#}
+
+ Implicit casts are only allowed when it is completely unambiguous how to get from one type to another,
+ and the transformation is guaranteed to be safe.
+
{#header_open|Implicit Cast: Stricter Qualification#}
Values which have the same representation at runtime can be cast to increase the strictness
@@ -3722,7 +3726,32 @@ test "float widening" {
{#header_close#}
{#header_open|Explicit Casts#}
-
TODO
+
+ Explicit casts are performed via {#link|Builtin Functions#}.
+ Some explicit casts are safe; some are not.
+ Some explicit casts perform language-level assertions; some do not.
+ Some explicit casts are no-ops at runtime; some are not.
+
+
+ - {#link|@bitCast#} - change type but maintain bit representation
+ - {#link|@alignCast#} - make a pointer have more alignment
+ - {#link|@boolToInt#} - convert true to 1 and false to 0
+ - {#link|@bytesToSlice#} - convert a slice of bytes to a slice of another type
+ - {#link|@enumToInt#} - obtain the integer tag value of an enum or tagged union
+ - {#link|@errSetCast#} - convert to a smaller error set
+ - {#link|@errorToInt#} - obtain the integer value of an error code
+ - {#link|@floatCast#} - convert a larger float to a smaller float
+ - {#link|@floatToInt#} - obtain the integer part of a float value
+ - {#link|@intCast#} - convert between integer types
+ - {#link|@intToEnum#} - obtain an enum value based on its integer tag value
+ - {#link|@intToError#} - obtain an error code based on its integer value
+ - {#link|@intToFloat#} - convert an integer to a float value
+ - {#link|@intToPtr#} - convert an address to a pointer
+ - {#link|@ptrCast#} - convert between pointer types
+ - {#link|@ptrToInt#} - obtain the address of a pointer
+ - {#link|@sliceToBytes#} - convert a slice of anything to a slice of bytes
+ - {#link|@truncate#} - convert between integer types, chopping off bits
+
{#header_close#}
{#header_open|Peer Type Resolution#}
--
2.54.0
From 0ebc7b66e6fe721d84f169ae714bbff7e82aa738 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Wed, 27 Jun 2018 16:20:04 +0200
Subject: [PATCH 33/82] scope variables in floating point cast tests
Fixes a bug where the result of a @floatCast wasn't actually checked; it
was checking the result from the previous @floatCast.
---
test/cases/cast.zig | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/test/cases/cast.zig b/test/cases/cast.zig
index 7b36bcd04a9aad48f028d2b3113c2a5cc22feff2..4209d87c1a1d03695e1909acbdced87bb1a63fcd 100644
--- a/test/cases/cast.zig
+++ b/test/cases/cast.zig
@@ -418,19 +418,24 @@ test "@intCast comptime_int" {
}
test "@floatCast comptime_int and comptime_float" {
- const result = @floatCast(f32, 1234);
- assert(@typeOf(result) == f32);
- assert(result == 1234.0);
-
- const result2 = @floatCast(f32, 1234.0);
- assert(@typeOf(result) == f32);
- assert(result == 1234.0);
+ {
+ const result = @floatCast(f32, 1234);
+ assert(@typeOf(result) == f32);
+ assert(result == 1234.0);
+ }
+ {
+ const result = @floatCast(f32, 1234.0);
+ assert(@typeOf(result) == f32);
+ assert(result == 1234.0);
+ }
}
test "comptime_int @intToFloat" {
- const result = @intToFloat(f32, 1234);
- assert(@typeOf(result) == f32);
- assert(result == 1234.0);
+ {
+ const result = @intToFloat(f32, 1234);
+ assert(@typeOf(result) == f32);
+ assert(result == 1234.0);
+ }
}
test "@bytesToSlice keeps pointer alignment" {
--
2.54.0
From 1f45075a0e1d86fa110011f6cedbef61a9f6f056 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Wed, 27 Jun 2018 16:20:04 +0200
Subject: [PATCH 34/82] dry floating-point type definitions
---
src/codegen.cpp | 57 ++++++++++++-------------------------------------
1 file changed, 14 insertions(+), 43 deletions(-)
diff --git a/src/codegen.cpp b/src/codegen.cpp
index c2406f0838455d3855c3a89e33f37f230256ef8e..abec5a8ec7def161bcb9c7d5e112586e22c6a815 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -6177,58 +6177,29 @@ static void define_builtin_types(CodeGen *g) {
g->builtin_types.entry_usize = entry;
}
}
- {
- TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
- entry->type_ref = LLVMFloatType();
- buf_init_from_str(&entry->name, "f32");
- entry->data.floating.bit_count = 32;
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
- entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
- debug_size_in_bits,
- ZigLLVMEncoding_DW_ATE_float());
- g->builtin_types.entry_f32 = entry;
- g->primitive_type_table.put(&entry->name, entry);
- }
- {
+ auto add_fp_entry = [] (CodeGen *g,
+ const char *name,
+ uint32_t bit_count,
+ LLVMTypeRef type_ref,
+ TypeTableEntry **field) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
- entry->type_ref = LLVMDoubleType();
- buf_init_from_str(&entry->name, "f64");
- entry->data.floating.bit_count = 64;
+ entry->type_ref = type_ref;
+ buf_init_from_str(&entry->name, name);
+ entry->data.floating.bit_count = bit_count;
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
debug_size_in_bits,
ZigLLVMEncoding_DW_ATE_float());
- g->builtin_types.entry_f64 = entry;
+ *field = entry;
g->primitive_type_table.put(&entry->name, entry);
- }
- {
- TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
- entry->type_ref = LLVMFP128Type();
- buf_init_from_str(&entry->name, "f128");
- entry->data.floating.bit_count = 128;
+ };
+ add_fp_entry(g, "f32", 32, LLVMFloatType(), &g->builtin_types.entry_f32);
+ add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64);
+ add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128);
+ add_fp_entry(g, "c_longdouble", 80, LLVMX86FP80Type(), &g->builtin_types.entry_c_longdouble);
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
- entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
- debug_size_in_bits,
- ZigLLVMEncoding_DW_ATE_float());
- g->builtin_types.entry_f128 = entry;
- g->primitive_type_table.put(&entry->name, entry);
- }
- {
- TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
- entry->type_ref = LLVMX86FP80Type();
- buf_init_from_str(&entry->name, "c_longdouble");
- entry->data.floating.bit_count = 80;
-
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
- entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
- debug_size_in_bits,
- ZigLLVMEncoding_DW_ATE_float());
- g->builtin_types.entry_c_longdouble = entry;
- g->primitive_type_table.put(&entry->name, entry);
- }
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
entry->type_ref = LLVMVoidType();
--
2.54.0
From fd75e73ee9818f12fd81d8fdb3cb949c492d664a Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Wed, 27 Jun 2018 16:20:04 +0200
Subject: [PATCH 35/82] add f16 type
Add support for half-precision floating point operations.
Introduce `__extendhfsf2` and `__truncsfhf2` in std/special/compiler_rt.
Add `__gnu_h2f_ieee` and `__gnu_f2h_ieee` as aliases that are used in
Windows builds.
The logic in std/special/compiler_rt/extendXfYf2.zig has been reworked
and can now operate on 16 bits floating point types.
`extendXfYf2()` and `truncXfYf2()` are marked `inline` to work around
a not entirely understood stack alignment issue on Windows when calling
the f16 versions of the builtins.
closes #1122
---
CMakeLists.txt | 16 ++
src/all_types.hpp | 2 +
src/analyze.cpp | 15 ++
src/bigfloat.cpp | 8 +
src/bigfloat.hpp | 2 +
src/codegen.cpp | 4 +
src/ir.cpp | 151 ++++++++++++++++++-
src/util.hpp | 19 +++
std/special/compiler_rt/extendXfYf2.zig | 56 +++----
std/special/compiler_rt/extendXfYf2_test.zig | 46 ++++++
std/special/compiler_rt/index.zig | 5 +
std/special/compiler_rt/truncXfYf2.zig | 111 ++++++++++++++
std/special/compiler_rt/truncXfYf2_test.zig | 64 ++++++++
test/cases/cast.zig | 28 +++-
test/cases/math.zig | 12 +-
test/cases/misc.zig | 1 +
16 files changed, 505 insertions(+), 35 deletions(-)
create mode 100644 std/special/compiler_rt/truncXfYf2.zig
create mode 100644 std/special/compiler_rt/truncXfYf2_test.zig
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 99de2328d2458039b394b79ddd6d49f1cda62511..789da4a8a65ae439e31edbed163635b7d2d6b858 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -261,12 +261,15 @@ endif()
set(EMBEDDED_SOFTFLOAT_SOURCES
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_add.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_div.c"
@@ -293,8 +296,20 @@ set(EMBEDDED_SOFTFLOAT_SOURCES
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_add.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_div.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_eq.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_lt.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_mul.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_rem.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_roundToInt.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sqrt.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sub.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f128M.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f64.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f32_to_f128M.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f128M.c"
+ "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f16.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_add256M.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addCarryM.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addComplCarryM.c"
@@ -572,6 +587,7 @@ set(ZIG_STD_FILES
"special/compiler_rt/floatuntidf.zig"
"special/compiler_rt/muloti4.zig"
"special/compiler_rt/index.zig"
+ "special/compiler_rt/truncXfYf2.zig"
"special/compiler_rt/udivmod.zig"
"special/compiler_rt/udivmoddi4.zig"
"special/compiler_rt/udivmodti4.zig"
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 019dcb182ef32bee918223192ba5fc3371e50357..5d449491c8e9fc854aaa2fe45cb36b8fec8260b0 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -258,6 +258,7 @@ struct ConstExprValue {
// populated if special == ConstValSpecialStatic
BigInt x_bigint;
BigFloat x_bigfloat;
+ float16_t x_f16;
float x_f32;
double x_f64;
float128_t x_f128;
@@ -1598,6 +1599,7 @@ struct CodeGen {
TypeTableEntry *entry_i128;
TypeTableEntry *entry_isize;
TypeTableEntry *entry_usize;
+ TypeTableEntry *entry_f16;
TypeTableEntry *entry_f32;
TypeTableEntry *entry_f64;
TypeTableEntry *entry_f128;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index c018ee4e924e5882b9cef14c817fac8efc3f9cb7..25cc1c79d0b81087a1f12ea2046edfa05ccaaf20 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -4668,6 +4668,13 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
}
case TypeTableEntryIdFloat:
switch (const_val->type->data.floating.bit_count) {
+ case 16:
+ {
+ uint16_t result;
+ static_assert(sizeof(result) == sizeof(const_val->data.x_f16), "");
+ memcpy(&result, &const_val->data.x_f16, sizeof(result));
+ return result * 65537u;
+ }
case 32:
{
uint32_t result;
@@ -5128,6 +5135,9 @@ void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double va
bigfloat_init_64(&const_val->data.x_bigfloat, value);
} else if (type->id == TypeTableEntryIdFloat) {
switch (type->data.floating.bit_count) {
+ case 16:
+ const_val->data.x_f16 = zig_double_to_f16(value);
+ break;
case 32:
const_val->data.x_f32 = value;
break;
@@ -5441,6 +5451,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
case TypeTableEntryIdFloat:
assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count);
switch (a->type->data.floating.bit_count) {
+ case 16:
+ return f16_eq(a->data.x_f16, b->data.x_f16);
case 32:
return a->data.x_f32 == b->data.x_f32;
case 64:
@@ -5614,6 +5626,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
return;
case TypeTableEntryIdFloat:
switch (type_entry->data.floating.bit_count) {
+ case 16:
+ buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16));
+ return;
case 32:
buf_appendf(buf, "%f", const_val->data.x_f32);
return;
diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp
index dcb6db61db3e5e5ecb1f68876af180a2468351f9..cc442fa3b7bba312557d755dfeff488f14cb49fb 100644
--- a/src/bigfloat.cpp
+++ b/src/bigfloat.cpp
@@ -18,6 +18,10 @@ void bigfloat_init_128(BigFloat *dest, float128_t x) {
dest->value = x;
}
+void bigfloat_init_16(BigFloat *dest, float16_t x) {
+ f16_to_f128M(x, &dest->value);
+}
+
void bigfloat_init_32(BigFloat *dest, float x) {
float32_t f32_val;
memcpy(&f32_val, &x, sizeof(float));
@@ -146,6 +150,10 @@ Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) {
}
}
+float16_t bigfloat_to_f16(const BigFloat *bigfloat) {
+ return f128M_to_f16(&bigfloat->value);
+}
+
float bigfloat_to_f32(const BigFloat *bigfloat) {
float32_t f32_value = f128M_to_f32(&bigfloat->value);
float result;
diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp
index e212c30c871fcbe69c738295f1f23a9f5c58ee35..c6ae567945d7354d071ffddf0bedd6aecdc7f585 100644
--- a/src/bigfloat.hpp
+++ b/src/bigfloat.hpp
@@ -22,6 +22,7 @@ struct BigFloat {
struct Buf;
+void bigfloat_init_16(BigFloat *dest, float16_t x);
void bigfloat_init_32(BigFloat *dest, float x);
void bigfloat_init_64(BigFloat *dest, double x);
void bigfloat_init_128(BigFloat *dest, float128_t x);
@@ -29,6 +30,7 @@ void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x);
void bigfloat_init_bigint(BigFloat *dest, const BigInt *op);
int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len);
+float16_t bigfloat_to_f16(const BigFloat *bigfloat);
float bigfloat_to_f32(const BigFloat *bigfloat);
double bigfloat_to_f64(const BigFloat *bigfloat);
float128_t bigfloat_to_f128(const BigFloat *bigfloat);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index abec5a8ec7def161bcb9c7d5e112586e22c6a815..4419f4fc8437f10acbfb9b7b9d7ce2ae2baa7dfb 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -17,6 +17,7 @@
#include "os.hpp"
#include "translate_c.hpp"
#include "target.hpp"
+#include "util.hpp"
#include "zig_llvm.h"
#include
@@ -5211,6 +5212,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
const_val->data.x_err_set->value, false);
case TypeTableEntryIdFloat:
switch (type_entry->data.floating.bit_count) {
+ case 16:
+ return LLVMConstReal(type_entry->type_ref, zig_f16_to_double(const_val->data.x_f16));
case 32:
return LLVMConstReal(type_entry->type_ref, const_val->data.x_f32);
case 64:
@@ -6195,6 +6198,7 @@ static void define_builtin_types(CodeGen *g) {
*field = entry;
g->primitive_type_table.put(&entry->name, entry);
};
+ add_fp_entry(g, "f16", 16, LLVMHalfType(), &g->builtin_types.entry_f16);
add_fp_entry(g, "f32", 32, LLVMFloatType(), &g->builtin_types.entry_f32);
add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64);
add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128);
diff --git a/src/ir.cpp b/src/ir.cpp
index 76178f2437cbeb0b29a0ebe98e23339a5593ca68..694f91214501f1489c3f8354ed90491f4e2a649c 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -11,9 +11,10 @@
#include "ir.hpp"
#include "ir_print.hpp"
#include "os.hpp"
-#include "translate_c.hpp"
#include "range_set.hpp"
#include "softfloat.hpp"
+#include "translate_c.hpp"
+#include "util.hpp"
struct IrExecContext {
ConstExprValue *mem_slot_list;
@@ -7238,6 +7239,11 @@ static bool float_has_fraction(ConstExprValue *const_val) {
return bigfloat_has_fraction(&const_val->data.x_bigfloat);
} else if (const_val->type->id == TypeTableEntryIdFloat) {
switch (const_val->type->data.floating.bit_count) {
+ case 16:
+ {
+ float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false);
+ return !f16_eq(floored, const_val->data.x_f16);
+ }
case 32:
return floorf(const_val->data.x_f32) != const_val->data.x_f32;
case 64:
@@ -7261,6 +7267,9 @@ static void float_append_buf(Buf *buf, ConstExprValue *const_val) {
bigfloat_append_buf(buf, &const_val->data.x_bigfloat);
} else if (const_val->type->id == TypeTableEntryIdFloat) {
switch (const_val->type->data.floating.bit_count) {
+ case 16:
+ buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16));
+ break;
case 32:
buf_appendf(buf, "%f", const_val->data.x_f32);
break;
@@ -7296,6 +7305,17 @@ static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) {
bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat);
} else if (const_val->type->id == TypeTableEntryIdFloat) {
switch (const_val->type->data.floating.bit_count) {
+ case 16:
+ {
+ double x = zig_f16_to_double(const_val->data.x_f16);
+ if (x >= 0) {
+ bigint_init_unsigned(bigint, (uint64_t)x);
+ } else {
+ bigint_init_unsigned(bigint, (uint64_t)-x);
+ bigint->is_negative = true;
+ }
+ break;
+ }
case 32:
if (const_val->data.x_f32 >= 0) {
bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32));
@@ -7332,6 +7352,9 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) {
bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat);
} else if (dest_val->type->id == TypeTableEntryIdFloat) {
switch (dest_val->type->data.floating.bit_count) {
+ case 16:
+ dest_val->data.x_f16 = bigfloat_to_f16(bigfloat);
+ break;
case 32:
dest_val->data.x_f32 = bigfloat_to_f32(bigfloat);
break;
@@ -7349,11 +7372,39 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) {
}
}
+static void float_init_f16(ConstExprValue *dest_val, float16_t x) {
+ if (dest_val->type->id == TypeTableEntryIdComptimeFloat) {
+ bigfloat_init_16(&dest_val->data.x_bigfloat, x);
+ } else if (dest_val->type->id == TypeTableEntryIdFloat) {
+ switch (dest_val->type->data.floating.bit_count) {
+ case 16:
+ dest_val->data.x_f16 = x;
+ break;
+ case 32:
+ dest_val->data.x_f32 = zig_f16_to_double(x);
+ break;
+ case 64:
+ dest_val->data.x_f64 = zig_f16_to_double(x);
+ break;
+ case 128:
+ f16_to_f128M(x, &dest_val->data.x_f128);
+ break;
+ default:
+ zig_unreachable();
+ }
+ } else {
+ zig_unreachable();
+ }
+}
+
static void float_init_f32(ConstExprValue *dest_val, float x) {
if (dest_val->type->id == TypeTableEntryIdComptimeFloat) {
bigfloat_init_32(&dest_val->data.x_bigfloat, x);
} else if (dest_val->type->id == TypeTableEntryIdFloat) {
switch (dest_val->type->data.floating.bit_count) {
+ case 16:
+ dest_val->data.x_f16 = zig_double_to_f16(x);
+ break;
case 32:
dest_val->data.x_f32 = x;
break;
@@ -7380,6 +7431,9 @@ static void float_init_f64(ConstExprValue *dest_val, double x) {
bigfloat_init_64(&dest_val->data.x_bigfloat, x);
} else if (dest_val->type->id == TypeTableEntryIdFloat) {
switch (dest_val->type->data.floating.bit_count) {
+ case 16:
+ dest_val->data.x_f16 = zig_double_to_f16(x);
+ break;
case 32:
dest_val->data.x_f32 = x;
break;
@@ -7406,6 +7460,9 @@ static void float_init_f128(ConstExprValue *dest_val, float128_t x) {
bigfloat_init_128(&dest_val->data.x_bigfloat, x);
} else if (dest_val->type->id == TypeTableEntryIdFloat) {
switch (dest_val->type->data.floating.bit_count) {
+ case 16:
+ dest_val->data.x_f16 = f128M_to_f16(&x);
+ break;
case 32:
{
float32_t f32_val = f128M_to_f32(&x);
@@ -7436,6 +7493,9 @@ static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val)
float_init_bigfloat(dest_val, &src_val->data.x_bigfloat);
} else if (src_val->type->id == TypeTableEntryIdFloat) {
switch (src_val->type->data.floating.bit_count) {
+ case 16:
+ float_init_f16(dest_val, src_val->data.x_f16);
+ break;
case 32:
float_init_f32(dest_val, src_val->data.x_f32);
break;
@@ -7459,6 +7519,14 @@ static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) {
return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
+ case 16:
+ if (f16_lt(op1->data.x_f16, op2->data.x_f16)) {
+ return CmpLT;
+ } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) {
+ return CmpGT;
+ } else {
+ return CmpEQ;
+ }
case 32:
if (op1->data.x_f32 > op2->data.x_f32) {
return CmpGT;
@@ -7496,6 +7564,17 @@ static Cmp float_cmp_zero(ConstExprValue *op) {
return bigfloat_cmp_zero(&op->data.x_bigfloat);
} else if (op->type->id == TypeTableEntryIdFloat) {
switch (op->type->data.floating.bit_count) {
+ case 16:
+ {
+ const float16_t zero = zig_double_to_f16(0);
+ if (f16_lt(op->data.x_f16, zero)) {
+ return CmpLT;
+ } else if (f16_lt(zero, op->data.x_f16)) {
+ return CmpGT;
+ } else {
+ return CmpEQ;
+ }
+ }
case 32:
if (op->data.x_f32 < 0.0) {
return CmpLT;
@@ -7537,6 +7616,9 @@ static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal
bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
+ case 16:
+ out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16);
+ return;
case 32:
out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32;
return;
@@ -7561,6 +7643,9 @@ static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal
bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
+ case 16:
+ out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16);
+ return;
case 32:
out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32;
return;
@@ -7585,6 +7670,9 @@ static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal
bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
+ case 16:
+ out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16);
+ return;
case 32:
out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32;
return;
@@ -7609,6 +7697,9 @@ static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal
bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
+ case 16:
+ out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16);
+ return;
case 32:
out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32;
return;
@@ -7633,6 +7724,19 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE
bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
+ case 16:
+ {
+ double a = zig_f16_to_double(op1->data.x_f16);
+ double b = zig_f16_to_double(op2->data.x_f16);
+ double c = a / b;
+ if (c >= 0.0) {
+ c = floor(c);
+ } else {
+ c = ceil(c);
+ }
+ out_val->data.x_f16 = zig_double_to_f16(c);
+ return;
+ }
case 32:
out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32;
if (out_val->data.x_f32 >= 0.0) {
@@ -7668,6 +7772,10 @@ static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstE
bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
+ case 16:
+ out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16);
+ out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false);
+ return;
case 32:
out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32);
return;
@@ -7693,6 +7801,9 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal
bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
+ case 16:
+ out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16);
+ return;
case 32:
out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32);
return;
@@ -7710,6 +7821,16 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal
}
}
+// c = a - b * trunc(a / b)
+static float16_t zig_f16_mod(float16_t a, float16_t b) {
+ float16_t c;
+ c = f16_div(a, b);
+ c = f16_roundToInt(c, softfloat_round_min, true);
+ c = f16_mul(b, c);
+ c = f16_sub(a, c);
+ return c;
+}
+
// c = a - b * trunc(a / b)
static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) {
f128M_div(a, b, c);
@@ -7725,6 +7846,9 @@ static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal
bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
+ case 16:
+ out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16);
+ return;
case 32:
out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32);
return;
@@ -7748,6 +7872,12 @@ static void float_negate(ConstExprValue *out_val, ConstExprValue *op) {
bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat);
} else if (op->type->id == TypeTableEntryIdFloat) {
switch (op->type->data.floating.bit_count) {
+ case 16:
+ {
+ const float16_t zero = zig_double_to_f16(0);
+ out_val->data.x_f16 = f16_sub(zero, op->data.x_f16);
+ return;
+ }
case 32:
out_val->data.x_f32 = -op->data.x_f32;
return;
@@ -7770,6 +7900,9 @@ static void float_negate(ConstExprValue *out_val, ConstExprValue *op) {
void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) {
if (op->type->id == TypeTableEntryIdFloat) {
switch (op->type->data.floating.bit_count) {
+ case 16:
+ memcpy(buf, &op->data.x_f16, 2); // TODO wrong when compiler is big endian
+ return;
case 32:
memcpy(buf, &op->data.x_f32, 4); // TODO wrong when compiler is big endian
return;
@@ -7790,6 +7923,9 @@ void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) {
void float_read_ieee597(ConstExprValue *val, uint8_t *buf, bool is_big_endian) {
if (val->type->id == TypeTableEntryIdFloat) {
switch (val->type->data.floating.bit_count) {
+ case 16:
+ memcpy(&val->data.x_f16, buf, 2); // TODO wrong when compiler is big endian
+ return;
case 32:
memcpy(&val->data.x_f32, buf, 4); // TODO wrong when compiler is big endian
return;
@@ -8817,6 +8953,9 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_
if (other_val->type->id == TypeTableEntryIdComptimeFloat) {
assert(new_type->id == TypeTableEntryIdFloat);
switch (new_type->data.floating.bit_count) {
+ case 16:
+ const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat);
+ break;
case 32:
const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat);
break;
@@ -8847,6 +8986,9 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_
BigFloat bigfloat;
bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint);
switch (new_type->data.floating.bit_count) {
+ case 16:
+ const_val->data.x_f16 = bigfloat_to_f16(&bigfloat);
+ break;
case 32:
const_val->data.x_f32 = bigfloat_to_f32(&bigfloat);
break;
@@ -20104,6 +20246,9 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction
bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
} else if (float_type->id == TypeTableEntryIdFloat) {
switch (float_type->data.floating.bit_count) {
+ case 16:
+ out_val->data.x_f16 = f16_sqrt(val->data.x_f16);
+ break;
case 32:
out_val->data.x_f32 = sqrtf(val->data.x_f32);
break;
@@ -20124,7 +20269,9 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction
}
assert(float_type->id == TypeTableEntryIdFloat);
- if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) {
+ if (float_type->data.floating.bit_count != 16 &&
+ float_type->data.floating.bit_count != 32 &&
+ float_type->data.floating.bit_count != 64) {
ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
diff --git a/src/util.hpp b/src/util.hpp
index 52baab7acea6fe7e8a52b1080ed292c5558665e5..b0402137bdab0ef648be01b850e7e40774fcd108 100644
--- a/src/util.hpp
+++ b/src/util.hpp
@@ -31,6 +31,8 @@
#endif
+#include "softfloat.hpp"
+
#define BREAKPOINT __asm("int $0x03")
ATTRIBUTE_COLD
@@ -165,4 +167,21 @@ static inline uint8_t log2_u64(uint64_t x) {
return (63 - clzll(x));
}
+static inline float16_t zig_double_to_f16(double x) {
+ float64_t y;
+ static_assert(sizeof(x) == sizeof(y), "");
+ memcpy(&y, &x, sizeof(x));
+ return f64_to_f16(y);
+}
+
+
+// Return value is safe to coerce to float even when |x| is NaN or Infinity.
+static inline double zig_f16_to_double(float16_t x) {
+ float64_t y = f16_to_f64(x);
+ double z;
+ static_assert(sizeof(y) == sizeof(z), "");
+ memcpy(&z, &y, sizeof(y));
+ return z;
+}
+
#endif
diff --git a/std/special/compiler_rt/extendXfYf2.zig b/std/special/compiler_rt/extendXfYf2.zig
index 6fa8cf4654a77d850781fab52c349ab614d9b256..099e27b74a5d58ce1360bc18fb7c613989fbeab7 100644
--- a/std/special/compiler_rt/extendXfYf2.zig
+++ b/std/special/compiler_rt/extendXfYf2.zig
@@ -10,9 +10,13 @@ pub extern fn __extendsftf2(a: f32) f128 {
return extendXfYf2(f128, f32, a);
}
+pub extern fn __extendhfsf2(a: u16) f32 {
+ return extendXfYf2(f32, f16, @bitCast(f16, a));
+}
+
const CHAR_BIT = 8;
-pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
+inline fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits);
const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits);
const srcSigBits = std.math.floatMantissaBits(src_t);
@@ -22,22 +26,22 @@ pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
// Various constants whose values follow from the type parameters.
// Any reasonable optimizer will fold and propagate all of these.
- const srcBits: i32 = @sizeOf(src_t) * CHAR_BIT;
- const srcExpBits: i32 = srcBits - srcSigBits - 1;
- const srcInfExp: i32 = (1 << srcExpBits) - 1;
- const srcExpBias: i32 = srcInfExp >> 1;
+ const srcBits = @sizeOf(src_t) * CHAR_BIT;
+ const srcExpBits = srcBits - srcSigBits - 1;
+ const srcInfExp = (1 << srcExpBits) - 1;
+ const srcExpBias = srcInfExp >> 1;
- const srcMinNormal: src_rep_t = src_rep_t(1) << srcSigBits;
- const srcInfinity: src_rep_t = src_rep_t(@bitCast(u32, srcInfExp)) << srcSigBits;
- const srcSignMask: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits +% srcExpBits);
- const srcAbsMask: src_rep_t = srcSignMask -% 1;
- const srcQNaN: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits -% 1);
- const srcNaNCode: src_rep_t = srcQNaN -% 1;
+ const srcMinNormal = 1 << srcSigBits;
+ const srcInfinity = srcInfExp << srcSigBits;
+ const srcSignMask = 1 << (srcSigBits + srcExpBits);
+ const srcAbsMask = srcSignMask - 1;
+ const srcQNaN = 1 << (srcSigBits - 1);
+ const srcNaNCode = srcQNaN - 1;
- const dstBits: i32 = @sizeOf(dst_t) * CHAR_BIT;
- const dstExpBits: i32 = dstBits - dstSigBits - 1;
- const dstInfExp: i32 = (1 << dstExpBits) - 1;
- const dstExpBias: i32 = dstInfExp >> 1;
+ const dstBits = @sizeOf(dst_t) * CHAR_BIT;
+ const dstExpBits = dstBits - dstSigBits - 1;
+ const dstInfExp = (1 << dstExpBits) - 1;
+ const dstExpBias = dstInfExp >> 1;
const dstMinNormal: dst_rep_t = dst_rep_t(1) << dstSigBits;
@@ -47,38 +51,36 @@ pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
const sign: src_rep_t = aRep & srcSignMask;
var absResult: dst_rep_t = undefined;
- // If @sizeOf(src_rep_t) < @sizeOf(int), the subtraction result is promoted
- // to (signed) int. To avoid that, explicitly cast to src_rep_t.
- if ((src_rep_t)(aAbs -% srcMinNormal) < srcInfinity -% srcMinNormal) {
+ if (aAbs -% srcMinNormal < srcInfinity - srcMinNormal) {
// a is a normal number.
// Extend to the destination type by shifting the significand and
// exponent into the proper position and rebiasing the exponent.
- absResult = dst_rep_t(aAbs) << (dstSigBits -% srcSigBits);
- absResult += dst_rep_t(@bitCast(u32, dstExpBias -% srcExpBias)) << dstSigBits;
+ absResult = dst_rep_t(aAbs) << (dstSigBits - srcSigBits);
+ absResult += (dstExpBias - srcExpBias) << dstSigBits;
} else if (aAbs >= srcInfinity) {
// a is NaN or infinity.
// Conjure the result by beginning with infinity, then setting the qNaN
// bit (if needed) and right-aligning the rest of the trailing NaN
// payload field.
- absResult = dst_rep_t(@bitCast(u32, dstInfExp)) << dstSigBits;
- absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits);
- absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits);
+ absResult = dstInfExp << dstSigBits;
+ absResult |= dst_rep_t(aAbs & srcQNaN) << (dstSigBits - srcSigBits);
+ absResult |= dst_rep_t(aAbs & srcNaNCode) << (dstSigBits - srcSigBits);
} else if (aAbs != 0) {
// a is denormal.
// renormalize the significand and clear the leading bit, then insert
// the correct adjusted exponent in the destination type.
- const scale: i32 = @clz(aAbs) - @clz(srcMinNormal);
+ const scale: u32 = @clz(aAbs) - @clz(src_rep_t(srcMinNormal));
absResult = dst_rep_t(aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale);
absResult ^= dstMinNormal;
- const resultExponent: i32 = dstExpBias - srcExpBias - scale + 1;
- absResult |= dst_rep_t(@bitCast(u32, resultExponent)) << @intCast(DstShift, dstSigBits);
+ const resultExponent: u32 = dstExpBias - srcExpBias - scale + 1;
+ absResult |= @intCast(dst_rep_t, resultExponent) << dstSigBits;
} else {
// a is zero.
absResult = 0;
}
// Apply the signbit to (dst_t)abs(a).
- const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << @intCast(DstShift, dstBits - srcBits);
+ const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << (dstBits - srcBits);
return @bitCast(dst_t, result);
}
diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig
index 84fb410fbb1405a0c7b1f8c1d80c3e6e4d106977..0168de12a543e6136a05a5a9982dc89f4c88e0a0 100644
--- a/std/special/compiler_rt/extendXfYf2_test.zig
+++ b/std/special/compiler_rt/extendXfYf2_test.zig
@@ -1,4 +1,5 @@
const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2;
+const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2;
const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2;
const assert = @import("std").debug.assert;
@@ -24,6 +25,22 @@ fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void {
@panic("__extenddftf2 test failure");
}
+fn test__extendhfsf2(a: u16, expected: u32) void {
+ const x = __extendhfsf2(a);
+ const rep = @bitCast(u32, x);
+
+ if (rep == expected) {
+ if (rep & 0x7fffffff > 0x7f800000) {
+ return; // NaN is always unequal.
+ }
+ if (x == @bitCast(f32, expected)) {
+ return;
+ }
+ }
+
+ @panic("__extendhfsf2 test failure");
+}
+
fn test__extendsftf2(a: f32, expectedHi: u64, expectedLo: u64) void {
const x = __extendsftf2(a);
@@ -68,6 +85,35 @@ test "extenddftf2" {
test__extenddftf2(0x1.edcba987654321fp-45, 0x3fd2edcba9876543, 0x2000000000000000);
}
+test "extendhfsf2" {
+ test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN
+ test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN
+
+ test__extendhfsf2(0, 0); // 0
+ test__extendhfsf2(0x8000, 0x80000000); // -0
+
+ test__extendhfsf2(0x7c00, 0x7f800000); // inf
+ test__extendhfsf2(0xfc00, 0xff800000); // -inf
+
+ test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-24
+ test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24
+
+ test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24
+ test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24
+
+ test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14
+ test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14
+
+ test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504
+ test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504
+
+ test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10
+ test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10
+
+ test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3
+ test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3
+}
+
test "extendsftf2" {
// qNaN
test__extendsftf2(makeQNaN32(), 0x7fff800000000000, 0x0);
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index c96e1587f88761192184f35eb8b87fa6707b1859..fda8d9d8af0faba5cd0ac2da7a8ce2f4e93e47dd 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -15,6 +15,8 @@ comptime {
@export("__lttf2", @import("comparetf2.zig").__letf2, linkage);
@export("__netf2", @import("comparetf2.zig").__letf2, linkage);
@export("__gttf2", @import("comparetf2.zig").__getf2, linkage);
+ @export("__gnu_h2f_ieee", @import("extendXfYf2.zig").__extendhfsf2, linkage);
+ @export("__gnu_f2h_ieee", @import("truncXfYf2.zig").__truncsfhf2, linkage);
}
@export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage);
@@ -22,6 +24,9 @@ comptime {
@export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage);
@export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage);
@export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage);
+ @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage);
+
+ @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage);
@export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage);
@export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage);
diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig
new file mode 100644
index 0000000000000000000000000000000000000000..f08c6ae34ff6b9af99c560404ea60a75bf1e6beb
--- /dev/null
+++ b/std/special/compiler_rt/truncXfYf2.zig
@@ -0,0 +1,111 @@
+const std = @import("std");
+
+pub extern fn __truncsfhf2(a: f32) u16 {
+ return @bitCast(u16, truncXfYf2(f16, f32, a));
+}
+
+const CHAR_BIT = 8;
+
+inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
+ const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits);
+ const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits);
+ const srcSigBits = std.math.floatMantissaBits(src_t);
+ const dstSigBits = std.math.floatMantissaBits(dst_t);
+ const SrcShift = std.math.Log2Int(src_rep_t);
+ const DstShift = std.math.Log2Int(dst_rep_t);
+
+ // Various constants whose values follow from the type parameters.
+ // Any reasonable optimizer will fold and propagate all of these.
+ const srcBits = @sizeOf(src_t) * CHAR_BIT;
+ const srcExpBits = srcBits - srcSigBits - 1;
+ const srcInfExp = (1 << srcExpBits) - 1;
+ const srcExpBias = srcInfExp >> 1;
+
+ const srcMinNormal = 1 << srcSigBits;
+ const srcSignificandMask = srcMinNormal - 1;
+ const srcInfinity = srcInfExp << srcSigBits;
+ const srcSignMask = 1 << (srcSigBits + srcExpBits);
+ const srcAbsMask = srcSignMask - 1;
+ const roundMask = (1 << (srcSigBits - dstSigBits)) - 1;
+ const halfway = 1 << (srcSigBits - dstSigBits - 1);
+ const srcQNaN = 1 << (srcSigBits - 1);
+ const srcNaNCode = srcQNaN - 1;
+
+ const dstBits = @sizeOf(dst_t) * CHAR_BIT;
+ const dstExpBits = dstBits - dstSigBits - 1;
+ const dstInfExp = (1 << dstExpBits) - 1;
+ const dstExpBias = dstInfExp >> 1;
+
+ const underflowExponent = srcExpBias + 1 - dstExpBias;
+ const overflowExponent = srcExpBias + dstInfExp - dstExpBias;
+ const underflow = underflowExponent << srcSigBits;
+ const overflow = overflowExponent << srcSigBits;
+
+ const dstQNaN = 1 << (dstSigBits - 1);
+ const dstNaNCode = dstQNaN - 1;
+
+ // Break a into a sign and representation of the absolute value
+ const aRep: src_rep_t = @bitCast(src_rep_t, a);
+ const aAbs: src_rep_t = aRep & srcAbsMask;
+ const sign: src_rep_t = aRep & srcSignMask;
+ var absResult: dst_rep_t = undefined;
+
+ if (aAbs -% underflow < aAbs -% overflow) {
+ // The exponent of a is within the range of normal numbers in the
+ // destination format. We can convert by simply right-shifting with
+ // rounding and adjusting the exponent.
+ absResult = @truncate(dst_rep_t, aAbs >> (srcSigBits - dstSigBits));
+ absResult -%= dst_rep_t(srcExpBias - dstExpBias) << dstSigBits;
+
+ const roundBits: src_rep_t = aAbs & roundMask;
+ if (roundBits > halfway) {
+ // Round to nearest
+ absResult += 1;
+ } else if (roundBits == halfway) {
+ // Ties to even
+ absResult += absResult & 1;
+ }
+ } else if (aAbs > srcInfinity) {
+ // a is NaN.
+ // Conjure the result by beginning with infinity, setting the qNaN
+ // bit and inserting the (truncated) trailing NaN field.
+ absResult = @intCast(dst_rep_t, dstInfExp) << dstSigBits;
+ absResult |= dstQNaN;
+ absResult |= @intCast(dst_rep_t, ((aAbs & srcNaNCode) >> (srcSigBits - dstSigBits)) & dstNaNCode);
+ } else if (aAbs >= overflow) {
+ // a overflows to infinity.
+ absResult = @intCast(dst_rep_t, dstInfExp) << dstSigBits;
+ } else {
+ // a underflows on conversion to the destination type or is an exact
+ // zero. The result may be a denormal or zero. Extract the exponent
+ // to get the shift amount for the denormalization.
+ const aExp: u32 = aAbs >> srcSigBits;
+ const shift: u32 = srcExpBias - dstExpBias - aExp + 1;
+
+ const significand: src_rep_t = (aRep & srcSignificandMask) | srcMinNormal;
+
+ // Right shift by the denormalization amount with sticky.
+ if (shift > srcSigBits) {
+ absResult = 0;
+ } else {
+ const sticky: src_rep_t = significand << @intCast(SrcShift, srcBits - shift);
+ const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky;
+ absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits));
+ const roundBits: src_rep_t = denormalizedSignificand & roundMask;
+ if (roundBits > halfway) {
+ // Round to nearest
+ absResult += 1;
+ } else if (roundBits == halfway) {
+ // Ties to even
+ absResult += absResult & 1;
+ }
+ }
+ }
+
+ const result: dst_rep_t align(@alignOf(dst_t)) = absResult | @truncate(dst_rep_t, sign >> @intCast(SrcShift, srcBits - dstBits));
+ return @bitCast(dst_t, result);
+}
+
+test "import truncXfYf2" {
+ _ = @import("truncXfYf2_test.zig");
+}
diff --git a/std/special/compiler_rt/truncXfYf2_test.zig b/std/special/compiler_rt/truncXfYf2_test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..e4dae7b5b0d6bf68911f9ab317cf1b074be29939
--- /dev/null
+++ b/std/special/compiler_rt/truncXfYf2_test.zig
@@ -0,0 +1,64 @@
+const __truncsfhf2 = @import("truncXfYf2.zig").__truncsfhf2;
+
+fn test__truncsfhf2(a: u32, expected: u16) void {
+ const actual = __truncsfhf2(@bitCast(f32, a));
+
+ if (actual == expected) {
+ return;
+ }
+
+ @panic("__truncsfhf2 test failure");
+}
+
+test "truncsfhf2" {
+ test__truncsfhf2(0x7fc00000, 0x7e00); // qNaN
+ test__truncsfhf2(0x7fe00000, 0x7f00); // sNaN
+
+ test__truncsfhf2(0, 0); // 0
+ test__truncsfhf2(0x80000000, 0x8000); // -0
+
+ test__truncsfhf2(0x7f800000, 0x7c00); // inf
+ test__truncsfhf2(0xff800000, 0xfc00); // -inf
+
+ test__truncsfhf2(0x477ff000, 0x7c00); // 65520 -> inf
+ test__truncsfhf2(0xc77ff000, 0xfc00); // -65520 -> -inf
+
+ test__truncsfhf2(0x71cc3892, 0x7c00); // 0x1.987124876876324p+100 -> inf
+ test__truncsfhf2(0xf1cc3892, 0xfc00); // -0x1.987124876876324p+100 -> -inf
+
+ test__truncsfhf2(0x38800000, 0x0400); // normal (min), 2**-14
+ test__truncsfhf2(0xb8800000, 0x8400); // normal (min), -2**-14
+
+ test__truncsfhf2(0x477fe000, 0x7bff); // normal (max), 65504
+ test__truncsfhf2(0xc77fe000, 0xfbff); // normal (max), -65504
+
+ test__truncsfhf2(0x477fe100, 0x7bff); // normal, 65505 -> 65504
+ test__truncsfhf2(0xc77fe100, 0xfbff); // normal, -65505 -> -65504
+
+ test__truncsfhf2(0x477fef00, 0x7bff); // normal, 65519 -> 65504
+ test__truncsfhf2(0xc77fef00, 0xfbff); // normal, -65519 -> -65504
+
+ test__truncsfhf2(0x3f802000, 0x3c01); // normal, 1 + 2**-10
+ test__truncsfhf2(0xbf802000, 0xbc01); // normal, -1 - 2**-10
+
+ test__truncsfhf2(0x3eaaa000, 0x3555); // normal, approx. 1/3
+ test__truncsfhf2(0xbeaaa000, 0xb555); // normal, approx. -1/3
+
+ test__truncsfhf2(0x40490fdb, 0x4248); // normal, 3.1415926535
+ test__truncsfhf2(0xc0490fdb, 0xc248); // normal, -3.1415926535
+
+ test__truncsfhf2(0x45cc3892, 0x6e62); // normal, 0x1.987124876876324p+12
+
+ test__truncsfhf2(0x3f800000, 0x3c00); // normal, 1
+ test__truncsfhf2(0x38800000, 0x0400); // normal, 0x1.0p-14
+
+ test__truncsfhf2(0x33800000, 0x0001); // denormal (min), 2**-24
+ test__truncsfhf2(0xb3800000, 0x8001); // denormal (min), -2**-24
+
+ test__truncsfhf2(0x387fc000, 0x03ff); // denormal (max), 2**-14 - 2**-24
+ test__truncsfhf2(0xb87fc000, 0x83ff); // denormal (max), -2**-14 + 2**-24
+
+ test__truncsfhf2(0x35800000, 0x0010); // denormal, 0x1.0p-20
+ test__truncsfhf2(0x33280000, 0x0001); // denormal, 0x1.5p-25 -> 0x1.0p-24
+ test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero
+}
diff --git a/test/cases/cast.zig b/test/cases/cast.zig
index 4209d87c1a1d03695e1909acbdced87bb1a63fcd..5688d90e114b18269fed414b4c25481c2fbec17b 100644
--- a/test/cases/cast.zig
+++ b/test/cases/cast.zig
@@ -350,13 +350,16 @@ fn testFloatToInts() void {
assert(x == 10000);
const y = @floatToInt(i32, f32(1e4));
assert(y == 10000);
- expectFloatToInt(u8, 255.1, 255);
- expectFloatToInt(i8, 127.2, 127);
- expectFloatToInt(i8, -128.2, -128);
+ expectFloatToInt(f16, 255.1, u8, 255);
+ expectFloatToInt(f16, 127.2, i8, 127);
+ expectFloatToInt(f16, -128.2, i8, -128);
+ expectFloatToInt(f32, 255.1, u8, 255);
+ expectFloatToInt(f32, 127.2, i8, 127);
+ expectFloatToInt(f32, -128.2, i8, -128);
}
-fn expectFloatToInt(comptime T: type, f: f32, i: T) void {
- assert(@floatToInt(T, f) == i);
+fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void {
+ assert(@floatToInt(I, f) == i);
}
test "cast u128 to f128 and back" {
@@ -418,6 +421,16 @@ test "@intCast comptime_int" {
}
test "@floatCast comptime_int and comptime_float" {
+ {
+ const result = @floatCast(f16, 1234);
+ assert(@typeOf(result) == f16);
+ assert(result == 1234.0);
+ }
+ {
+ const result = @floatCast(f16, 1234.0);
+ assert(@typeOf(result) == f16);
+ assert(result == 1234.0);
+ }
{
const result = @floatCast(f32, 1234);
assert(@typeOf(result) == f32);
@@ -431,6 +444,11 @@ test "@floatCast comptime_int and comptime_float" {
}
test "comptime_int @intToFloat" {
+ {
+ const result = @intToFloat(f16, 1234);
+ assert(@typeOf(result) == f16);
+ assert(result == 1234.0);
+ }
{
const result = @intToFloat(f32, 1234);
assert(@typeOf(result) == f32);
diff --git a/test/cases/math.zig b/test/cases/math.zig
index 08388d3df83362014d6674e6161c912fbae3571c..1807e5a1b07ca4d4507c2dc257e30b9090e5ca65 100644
--- a/test/cases/math.zig
+++ b/test/cases/math.zig
@@ -6,15 +6,20 @@ test "division" {
}
fn testDivision() void {
assert(div(u32, 13, 3) == 4);
+ assert(div(f16, 1.0, 2.0) == 0.5);
assert(div(f32, 1.0, 2.0) == 0.5);
assert(divExact(u32, 55, 11) == 5);
assert(divExact(i32, -55, 11) == -5);
+ assert(divExact(f16, 55.0, 11.0) == 5.0);
+ assert(divExact(f16, -55.0, 11.0) == -5.0);
assert(divExact(f32, 55.0, 11.0) == 5.0);
assert(divExact(f32, -55.0, 11.0) == -5.0);
assert(divFloor(i32, 5, 3) == 1);
assert(divFloor(i32, -5, 3) == -2);
+ assert(divFloor(f16, 5.0, 3.0) == 1.0);
+ assert(divFloor(f16, -5.0, 3.0) == -2.0);
assert(divFloor(f32, 5.0, 3.0) == 1.0);
assert(divFloor(f32, -5.0, 3.0) == -2.0);
assert(divFloor(i32, -0x80000000, -2) == 0x40000000);
@@ -24,6 +29,8 @@ fn testDivision() void {
assert(divTrunc(i32, 5, 3) == 1);
assert(divTrunc(i32, -5, 3) == -1);
+ assert(divTrunc(f16, 5.0, 3.0) == 1.0);
+ assert(divTrunc(f16, -5.0, 3.0) == -1.0);
assert(divTrunc(f32, 5.0, 3.0) == 1.0);
assert(divTrunc(f32, -5.0, 3.0) == -1.0);
@@ -435,10 +442,11 @@ test "comptime float rem int" {
}
test "remainder division" {
+ comptime remdiv(f16);
comptime remdiv(f32);
comptime remdiv(f64);
comptime remdiv(f128);
- remdiv(f32);
+ remdiv(f16);
remdiv(f64);
remdiv(f128);
}
@@ -453,6 +461,8 @@ test "@sqrt" {
comptime testSqrt(f64, 12.0);
testSqrt(f32, 13.0);
comptime testSqrt(f32, 13.0);
+ testSqrt(f16, 13.0);
+ comptime testSqrt(f16, 13.0);
const x = 14.0;
const y = x * x;
diff --git a/test/cases/misc.zig b/test/cases/misc.zig
index d539f79a57de3a53669c6bd1337ca9f4ed5001be..0f181a7b4eac2ce35a2e56cd160ac88e9400b041 100644
--- a/test/cases/misc.zig
+++ b/test/cases/misc.zig
@@ -53,6 +53,7 @@ test "@IntType builtin" {
}
test "floating point primitive bit counts" {
+ assert(f16.bit_count == 16);
assert(f32.bit_count == 32);
assert(f64.bit_count == 64);
}
--
2.54.0
From 440c1d52b4053c40c58c05116c8f6d9da8e35eed Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Wed, 27 Jun 2018 16:20:04 +0200
Subject: [PATCH 36/82] simplify comptime floating-point @divTrunc
Replace a conditional ceil/floor call with an unconditional trunc call.
---
src/ir.cpp | 29 +++++------------------------
test/cases/math.zig | 2 ++
2 files changed, 7 insertions(+), 24 deletions(-)
diff --git a/src/ir.cpp b/src/ir.cpp
index 694f91214501f1489c3f8354ed90491f4e2a649c..6e424980f845dad3ba67d5edc3bb93c73098d3c6 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -7725,33 +7725,14 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE
} else if (op1->type->id == TypeTableEntryIdFloat) {
switch (op1->type->data.floating.bit_count) {
case 16:
- {
- double a = zig_f16_to_double(op1->data.x_f16);
- double b = zig_f16_to_double(op2->data.x_f16);
- double c = a / b;
- if (c >= 0.0) {
- c = floor(c);
- } else {
- c = ceil(c);
- }
- out_val->data.x_f16 = zig_double_to_f16(c);
- return;
- }
+ out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16);
+ out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_minMag, false);
+ return;
case 32:
- out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32;
- if (out_val->data.x_f32 >= 0.0) {
- out_val->data.x_f32 = floorf(out_val->data.x_f32);
- } else {
- out_val->data.x_f32 = ceilf(out_val->data.x_f32);
- }
+ out_val->data.x_f32 = truncf(op1->data.x_f32 / op2->data.x_f32);
return;
case 64:
- out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64;
- if (out_val->data.x_f64 >= 0.0) {
- out_val->data.x_f64 = floor(out_val->data.x_f64);
- } else {
- out_val->data.x_f64 = ceil(out_val->data.x_f64);
- }
+ out_val->data.x_f64 = trunc(op1->data.x_f64 / op2->data.x_f64);
return;
case 128:
f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
diff --git a/test/cases/math.zig b/test/cases/math.zig
index 1807e5a1b07ca4d4507c2dc257e30b9090e5ca65..5931c5de312401dace68e82ac28dbad1d65ca2d5 100644
--- a/test/cases/math.zig
+++ b/test/cases/math.zig
@@ -33,6 +33,8 @@ fn testDivision() void {
assert(divTrunc(f16, -5.0, 3.0) == -1.0);
assert(divTrunc(f32, 5.0, 3.0) == 1.0);
assert(divTrunc(f32, -5.0, 3.0) == -1.0);
+ assert(divTrunc(f64, 5.0, 3.0) == 1.0);
+ assert(divTrunc(f64, -5.0, 3.0) == -1.0);
comptime {
assert(
--
2.54.0
From 3e94347e6152dd9a88f4bceb46bbc245ed7cef98 Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Wed, 27 Jun 2018 11:30:15 -0500
Subject: [PATCH 37/82] Fix up some std.rand syntax #1161 (#1162)
* Fix old syntax in rand
Ziggurat somehow did not get updated to latest syntax
* Fix broken float casts
f32 float casts somehow not updated to latest syntax
---
std/rand/index.zig | 4 ++--
std/rand/ziggurat.zig | 8 ++++++--
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/std/rand/index.zig b/std/rand/index.zig
index 13694f4c09a030524ac509f184e4f5bd5c8d7cc9..7daa558f135f6bd41cb4b612a53da5e2079f8233 100644
--- a/std/rand/index.zig
+++ b/std/rand/index.zig
@@ -116,7 +116,7 @@ pub const Random = struct {
pub fn floatNorm(r: *Random, comptime T: type) T {
const value = ziggurat.next_f64(r, ziggurat.NormDist);
switch (T) {
- f32 => return f32(value),
+ f32 => return @floatCast(f32, value),
f64 => return value,
else => @compileError("unknown floating point type"),
}
@@ -128,7 +128,7 @@ pub const Random = struct {
pub fn floatExp(r: *Random, comptime T: type) T {
const value = ziggurat.next_f64(r, ziggurat.ExpDist);
switch (T) {
- f32 => return f32(value),
+ f32 => return @floatCast(f32, value),
f64 => return value,
else => @compileError("unknown floating point type"),
}
diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig
index 774d3bd52a064a780c71c355010ee662c54d33d4..f7a1359f17662d8640dcdb8eaf8125f21e830b34 100644
--- a/std/rand/ziggurat.zig
+++ b/std/rand/ziggurat.zig
@@ -84,12 +84,12 @@ fn ZigTableGen(
for (tables.x[2..256]) |*entry, i| {
const last = tables.x[2 + i - 1];
- *entry = f_inv(v / last + f(last));
+ entry.* = f_inv(v / last + f(last));
}
tables.x[256] = 0;
for (tables.f[0..]) |*entry, i| {
- *entry = f(tables.x[i]);
+ entry.* = f(tables.x[i]);
}
return tables;
@@ -160,3 +160,7 @@ test "ziggurant exp dist sanity" {
_ = prng.random.floatExp(f64);
}
}
+
+test "ziggurat table gen" {
+ const table = NormDist;
+}
--
2.54.0
From 6f88ecc9b6ca4249912b7a9cffbad6d9b8819bc2 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 27 Jun 2018 12:59:12 -0400
Subject: [PATCH 38/82] add f16 to langref
---
doc/langref.html.in | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 8e24a4be2cf55ed0fc300b04b34c1a28a6b1f9ca..dbb4ea98060e59b8cb07e1c538dd89b6953d9b46 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -367,6 +367,11 @@ pub fn main() void {
for ABI compatibility with C |
+
+ f16 |
+ float |
+ 16-bit floating point (10-bit mantissa) IEEE-754-2008 binary16 |
+
f32 |
float |
@@ -654,6 +659,7 @@ fn divide(a: i32, b: i32) i32 {
{#header_open|Floats#}
Zig has the following floating point types:
+ f16 - IEEE-754-2008 binary16
f32 - IEEE-754-2008 binary32
f64 - IEEE-754-2008 binary64
f128 - IEEE-754-2008 binary128
@@ -3671,10 +3677,11 @@ test "implicit unsigned integer to signed integer" {
}
test "float widening" {
- var a: f32 = 12.34;
- var b: f64 = a;
- var c: f128 = b;
- assert(c == a);
+ var a: f16 = 12.34;
+ var b: f32 = a;
+ var c: f64 = b;
+ var d: f128 = c;
+ assert(d == a);
}
{#code_end#}
{#header_close#}
--
2.54.0
From 19961c50e4db10fc4ada428928a7f5d1a2966da6 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 27 Jun 2018 13:15:55 -0400
Subject: [PATCH 39/82] fix comptime @tagName crashing sometimes
closes #1118
---
src/analyze.cpp | 1 +
src/ir.cpp | 3 +++
test/cases/eval.zig | 5 +++++
test/cases/widening.zig | 9 +++++----
4 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 25cc1c79d0b81087a1f12ea2046edfa05ccaaf20..d5e69de1ebfd8e60ef73be457a26c4e398c2ea48 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -3728,6 +3728,7 @@ TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt
}
TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag) {
+ assert(enum_type->data.enumeration.zero_bits_known);
for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
TypeEnumField *field = &enum_type->data.enumeration.fields[i];
if (bigint_cmp(&field->value, tag) == CmpEQ) {
diff --git a/src/ir.cpp b/src/ir.cpp
index 6e424980f845dad3ba67d5edc3bb93c73098d3c6..9ba01d141181b1b67e1c93028bba2064105560b4 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -16053,6 +16053,9 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn
assert(target->value.type->id == TypeTableEntryIdEnum);
if (instr_is_comptime(target)) {
+ type_ensure_zero_bits_known(ira->codegen, target->value.type);
+ if (type_is_invalid(target->value.type))
+ return ira->codegen->builtin_types.entry_invalid;
TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint);
ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name);
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
diff --git a/test/cases/eval.zig b/test/cases/eval.zig
index 756ffe339adec38db8d4d98cab0aa5c90aaaf716..83d2e801766fe8749d2b2f1e6b19d03adb52af7d 100644
--- a/test/cases/eval.zig
+++ b/test/cases/eval.zig
@@ -637,3 +637,8 @@ test "call method with comptime pass-by-non-copying-value self parameter" {
var b = s.b();
assert(b == 2);
}
+
+test "@tagName of @typeId" {
+ const str = @tagName(@typeId(u8));
+ assert(std.mem.eql(u8, str, "Int"));
+}
diff --git a/test/cases/widening.zig b/test/cases/widening.zig
index 18c12806d34c5168f4f1f9f41e5cd60b4437e495..cf6ab4ca0fb4eeacf06e8d334554b106ff231686 100644
--- a/test/cases/widening.zig
+++ b/test/cases/widening.zig
@@ -19,8 +19,9 @@ test "implicit unsigned integer to signed integer" {
}
test "float widening" {
- var a: f32 = 12.34;
- var b: f64 = a;
- var c: f128 = b;
- assert(c == a);
+ var a: f16 = 12.34;
+ var b: f32 = a;
+ var c: f64 = b;
+ var d: f128 = c;
+ assert(d == a);
}
--
2.54.0
From 2fa588e81d60cfe319446bd0483c6bf296f40c40 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 27 Jun 2018 18:45:21 -0400
Subject: [PATCH 40/82] fix coroutine accessing freed memory
closes #1164
---
src/analyze.cpp | 2 +-
src/ir.cpp | 17 +++++++++++++---
test/cases/coroutines.zig | 41 ++++++++++++++++++++++++++++++---------
3 files changed, 47 insertions(+), 13 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index d5e69de1ebfd8e60ef73be457a26c4e398c2ea48..3c81d9ff9ae0b716f1c04890326ff798946ff5d7 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -5583,7 +5583,7 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeT
return;
}
case ConstPtrSpecialHardCodedAddr:
- buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
+ buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name),
const_val->data.x_ptr.data.hard_coded_addr.addr);
return;
case ConstPtrSpecialDiscard:
diff --git a/src/ir.cpp b/src/ir.cpp
index 9ba01d141181b1b67e1c93028bba2064105560b4..98ed53d839c6953b526d20a1f7b2cf18a8935dbc 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -7112,6 +7112,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr);
ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr);
}
+ // Before we destroy the coroutine frame, we need to load the target promise into
+ // a register or local variable which does not get spilled into the frame,
+ // otherwise llvm tries to access memory inside the destroyed frame.
+ IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node,
+ irb->exec->await_handle_var_ptr, false);
+ IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr);
ir_build_br(irb, scope, node, check_free_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);
@@ -7126,6 +7132,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
incoming_values[1] = const_bool_true;
IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
+ IrBasicBlock **merge_incoming_blocks = allocate(2);
+ IrInstruction **merge_incoming_values = allocate(2);
+ merge_incoming_blocks[0] = irb->exec->coro_final_cleanup_block;
+ merge_incoming_values[0] = ir_build_const_undefined(irb, scope, node);
+ merge_incoming_blocks[1] = irb->exec->coro_normal_final;
+ merge_incoming_values[1] = await_handle_in_block;
+ IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values);
+
Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME);
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,
ImplicitAllocatorIdLocalVar);
@@ -7152,9 +7166,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, resume_block);
- IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node,
- irb->exec->await_handle_var_ptr, false);
- IrInstruction *awaiter_handle = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr);
ir_build_coro_resume(irb, scope, node, awaiter_handle);
ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
}
diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
index 4d2aa54a699311888e00cab7fcd0df21a6a5130d..b3899b306b74bc40d312e8d7b32e1ab6554d8be7 100644
--- a/test/cases/coroutines.zig
+++ b/test/cases/coroutines.zig
@@ -5,7 +5,10 @@ const assert = std.debug.assert;
var x: i32 = 1;
test "create a coroutine and cancel it" {
- const p = try async simpleAsyncFn();
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+
+ const p = try async<&da.allocator> simpleAsyncFn();
comptime assert(@typeOf(p) == promise->void);
cancel p;
assert(x == 2);
@@ -17,8 +20,11 @@ async fn simpleAsyncFn() void {
}
test "coroutine suspend, resume, cancel" {
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+
seq('a');
- const p = try async testAsyncSeq();
+ const p = try async<&da.allocator> testAsyncSeq();
seq('c');
resume p;
seq('f');
@@ -43,7 +49,10 @@ fn seq(c: u8) void {
}
test "coroutine suspend with block" {
- const p = try async testSuspendBlock();
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+
+ const p = try async<&da.allocator> testSuspendBlock();
std.debug.assert(!result);
resume a_promise;
std.debug.assert(result);
@@ -64,8 +73,11 @@ var await_a_promise: promise = undefined;
var await_final_result: i32 = 0;
test "coroutine await" {
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+
await_seq('a');
- const p = async await_amain() catch unreachable;
+ const p = async<&da.allocator> await_amain() catch unreachable;
await_seq('f');
resume await_a_promise;
await_seq('i');
@@ -100,8 +112,11 @@ fn await_seq(c: u8) void {
var early_final_result: i32 = 0;
test "coroutine await early return" {
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+
early_seq('a');
- const p = async early_amain() catch unreachable;
+ const p = async<&da.allocator> early_amain() catch unreachable;
early_seq('f');
assert(early_final_result == 1234);
assert(std.mem.eql(u8, early_points, "abcdef"));
@@ -146,7 +161,9 @@ test "async function with dot syntax" {
suspend;
}
};
- const p = try async S.foo();
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+ const p = try async<&da.allocator> S.foo();
cancel p;
assert(S.y == 2);
}
@@ -157,7 +174,9 @@ test "async fn pointer in a struct field" {
bar: async<*std.mem.Allocator> fn (*i32) void,
};
var foo = Foo{ .bar = simpleAsyncFn2 };
- const p = (async foo.bar(&data)) catch unreachable;
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+ const p = (async<&da.allocator> foo.bar(&data)) catch unreachable;
assert(data == 2);
cancel p;
assert(data == 4);
@@ -169,7 +188,9 @@ async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void {
}
test "async fn with inferred error set" {
- const p = (async failing()) catch unreachable;
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+ const p = (async<&da.allocator> failing()) catch unreachable;
resume p;
cancel p;
}
@@ -181,7 +202,9 @@ async fn failing() !void {
test "error return trace across suspend points - early return" {
const p = nonFailing();
resume p;
- const p2 = try async printTrace(p);
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+ const p2 = try async<&da.allocator> printTrace(p);
cancel p2;
}
--
2.54.0
From 4a35d7eeebec3f345e2482bc189f07c19dcf6f8b Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 28 Jun 2018 20:12:03 +1200
Subject: [PATCH 41/82] Correct hex-float parsing
Unblocks #495.
---
src/tokenizer.cpp | 11 +++++++++--
test/cases/math.zig | 8 ++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 2950b4eb493f4317dc9e4d12bd4f693fdac12346..f7f41af8a6dc5a68c2f59f7312df31893bbf1d72 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -357,12 +357,19 @@ static void end_float_token(Tokenize *t) {
// Mask the sign bit to 0 since always non-negative lex
const uint64_t exp_mask = 0xffffull << exp_shift;
- if (shift >= 64) {
+ // must be special-cased to avoid undefined behavior on shift == 64
+ if (shift == 128) {
+ f_bits.repr[0] = 0;
+ f_bits.repr[1] = sig_bits[0];
+ } else if (shift == 0) {
+ f_bits.repr[0] = sig_bits[0];
+ f_bits.repr[1] = sig_bits[1];
+ } else if (shift >= 64) {
f_bits.repr[0] = 0;
f_bits.repr[1] = sig_bits[0] << (shift - 64);
} else {
f_bits.repr[0] = sig_bits[0] << shift;
- f_bits.repr[1] = ((sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift)));
+ f_bits.repr[1] = (sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift));
}
f_bits.repr[1] &= ~exp_mask;
diff --git a/test/cases/math.zig b/test/cases/math.zig
index 5931c5de312401dace68e82ac28dbad1d65ca2d5..195ada15ddab60a8decdf0e6cc36c3cdb312b1d0 100644
--- a/test/cases/math.zig
+++ b/test/cases/math.zig
@@ -296,6 +296,14 @@ test "quad hex float literal parsing in range" {
const d = 0x1.edcbff8ad76ab5bf46463233214fp-435;
}
+test "quad hex float literal parsing accurate" {
+ const a: f128 = 0x1.1111222233334444555566667777p+0;
+
+ // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing.
+ const expected: u128 = 0x3fff1111222233334444555566667777;
+ assert(@bitCast(u128, a) == expected);
+}
+
test "hex float literal within range" {
const a = 0x1.0p16383;
const b = 0x0.1p16387;
--
2.54.0
From 3ec38b249446d1a51391e263fbb8303af52e6751 Mon Sep 17 00:00:00 2001
From: Jimmi HC
Date: Thu, 28 Jun 2018 10:34:37 +0200
Subject: [PATCH 42/82] Implement const_values_equal for array type * This
allows arrays to be passed by value at comptime
---
src/analyze.cpp | 15 +++++++++++++--
test/cases/array.zig | 8 ++++++++
2 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 5160a19e8198bbd221fa545bc6b9133e59151b55..e9b74a9c26810c5c2b7f2a4206ff5d94f59302e1 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -5458,8 +5458,19 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
case TypeTableEntryIdPointer:
case TypeTableEntryIdFn:
return const_values_equal_ptr(a, b);
- case TypeTableEntryIdArray:
- zig_panic("TODO");
+ case TypeTableEntryIdArray: {
+ assert(a->type->data.array.len == b->type->data.array.len);
+ size_t len = a->type->data.array.len;
+ ConstExprValue *a_elems = a->data.x_array.s_none.elements;
+ ConstExprValue *b_elems = b->data.x_array.s_none.elements;
+
+ for (size_t i = 0; i < len; ++i) {
+ if (!const_values_equal(&a_elems[i], &b_elems[i]))
+ return false;
+ }
+
+ return true;
+ }
case TypeTableEntryIdStruct:
for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
ConstExprValue *field_a = &a->data.x_struct.fields[i];
diff --git a/test/cases/array.zig b/test/cases/array.zig
index b481261b4f2156a4815b870c10cf9f9006fdfc85..b72491bcc0a10d1d16e0984b14283bbf1cda558a 100644
--- a/test/cases/array.zig
+++ b/test/cases/array.zig
@@ -152,3 +152,11 @@ fn testImplicitCastSingleItemPtr() void {
slice[0] += 1;
assert(byte == 101);
}
+
+fn testArrayByValAtComptime(b: [2]u8) u8 { return b[0]; }
+
+test "comptime evalutating function that takes array by value" {
+ const arr = []u8{0,1};
+ _ = comptime testArrayByValAtComptime(arr);
+ _ = comptime testArrayByValAtComptime(arr);
+}
--
2.54.0
From b1128b18d5395d85f1c483d8b35e33c57be80722 Mon Sep 17 00:00:00 2001
From: Jimmi HC
Date: Fri, 29 Jun 2018 08:41:16 +0200
Subject: [PATCH 43/82] Assert that array is not ConstArraySpecialUndef in
const_values_equal
---
src/analyze.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index e9b74a9c26810c5c2b7f2a4206ff5d94f59302e1..b3a302a1d4cee5e1ce763da57f529385d7fb3388 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -5460,6 +5460,9 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
return const_values_equal_ptr(a, b);
case TypeTableEntryIdArray: {
assert(a->type->data.array.len == b->type->data.array.len);
+ assert(a->data.x_array.special != ConstArraySpecialUndef);
+ assert(b->data.x_array.special != ConstArraySpecialUndef);
+
size_t len = a->type->data.array.len;
ConstExprValue *a_elems = a->data.x_array.s_none.elements;
ConstExprValue *b_elems = b->data.x_array.s_none.elements;
--
2.54.0
From 4c3f27ce1ea17b5236a022971ebace73a02b7c2b Mon Sep 17 00:00:00 2001
From: Jimmi HC
Date: Fri, 29 Jun 2018 10:21:43 +0200
Subject: [PATCH 44/82] ir_resolve_const now checks recursivly for undef values
---
src/analyze.cpp | 135 ++++++++++++++++++++++++++++++++++++++++
src/analyze.hpp | 1 +
src/ir.cpp | 11 +++-
test/compile_errors.zig | 15 +++++
4 files changed, 160 insertions(+), 2 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index b3a302a1d4cee5e1ce763da57f529385d7fb3388..068ea48c0a84d4639b365faecc3100a216a8fde9 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -5288,6 +5288,141 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_
return const_val;
}
+bool contains_comptime_undefined_value(ConstExprValue *value) {
+ assert(value->special != ConstValSpecialRuntime);
+ if (value->special == ConstValSpecialUndef)
+ return true;
+
+ switch (value->type->id) {
+ case TypeTableEntryIdInvalid:
+ zig_unreachable();
+
+ case TypeTableEntryIdPointer: {
+ ConstPtrValue *ptr = &value->data.x_ptr;
+ if (ptr->mut == ConstPtrMutRuntimeVar)
+ return false;
+
+ switch (ptr->special) {
+ case ConstPtrSpecialInvalid:
+ zig_unreachable();
+ case ConstPtrSpecialRef:
+ return contains_comptime_undefined_value(ptr->data.ref.pointee);
+ case ConstPtrSpecialBaseArray: {
+ size_t index = ptr->data.base_array.elem_index;
+ ConstExprValue *arr = ptr->data.base_array.array_val;
+ if (arr->special == ConstValSpecialUndef)
+ return true;
+ if (arr->data.x_array.special == ConstArraySpecialUndef)
+ return true;
+
+ return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]);
+ }
+ case ConstPtrSpecialBaseStruct: {
+ size_t index = ptr->data.base_struct.field_index;
+ ConstExprValue *str = ptr->data.base_struct.struct_val;
+ if (str->special == ConstValSpecialUndef)
+ return true;
+
+ return contains_comptime_undefined_value(&str->data.x_struct.fields[index]);
+ }
+ case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value?
+ case ConstPtrSpecialDiscard:
+ case ConstPtrSpecialHardCodedAddr:
+ return false;
+ }
+ }
+ case TypeTableEntryIdArray: {
+ ConstArrayValue *arr = &value->data.x_array;
+ if (arr->special == ConstArraySpecialUndef)
+ return true;
+
+ for (size_t i = 0; i < value->type->data.array.len; ++i) {
+ if (contains_comptime_undefined_value(&arr->s_none.elements[i]))
+ return true;
+ }
+ return false;
+ }
+ case TypeTableEntryIdStruct: {
+ ConstStructValue *str = &value->data.x_struct;
+ if (value->type->data.structure.is_slice) {
+ ConstExprValue *len = &str->fields[slice_len_index];
+ ConstExprValue *ptr = &str->fields[slice_ptr_index];
+ if (len->special == ConstValSpecialUndef)
+ return true;
+ if (ptr->special == ConstValSpecialUndef)
+ return true;
+
+ switch (ptr->data.x_ptr.special) {
+ case ConstPtrSpecialRef:
+ return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee);
+ case ConstPtrSpecialBaseArray: {
+ size_t offset = ptr->data.x_ptr.data.base_array.elem_index;
+ ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val;
+ if (arr->special == ConstValSpecialUndef)
+ return true;
+ if (arr->data.x_array.special == ConstArraySpecialUndef)
+ return true;
+
+ uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint);
+ for (size_t i = 0; i < slice_len; ++i) {
+ if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset]))
+ return true;
+ }
+
+ return false;
+ }
+ case ConstPtrSpecialBaseStruct:
+ case ConstPtrSpecialInvalid:
+ case ConstPtrSpecialFunction:
+ case ConstPtrSpecialDiscard:
+ case ConstPtrSpecialHardCodedAddr:
+ zig_unreachable();
+ }
+ }
+
+ for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) {
+ if (contains_comptime_undefined_value(&str->fields[i]))
+ return true;
+ }
+ return false;
+ }
+ case TypeTableEntryIdOptional:
+ if (value->data.x_optional == nullptr)
+ return false;
+
+ return contains_comptime_undefined_value(value->data.x_optional);
+ case TypeTableEntryIdErrorUnion:
+ // TODO: Can error union error be undefined?
+ if (value->data.x_err_union.err != nullptr)
+ return false;
+
+ return contains_comptime_undefined_value(value->data.x_err_union.payload);
+ case TypeTableEntryIdUnion:
+ return contains_comptime_undefined_value(value->data.x_union.payload);
+
+ case TypeTableEntryIdArgTuple:
+ case TypeTableEntryIdVoid:
+ case TypeTableEntryIdBool:
+ case TypeTableEntryIdUnreachable:
+ case TypeTableEntryIdInt:
+ case TypeTableEntryIdFloat:
+ case TypeTableEntryIdComptimeFloat:
+ case TypeTableEntryIdComptimeInt:
+ case TypeTableEntryIdUndefined:
+ case TypeTableEntryIdNull:
+ case TypeTableEntryIdErrorSet:
+ case TypeTableEntryIdEnum:
+ case TypeTableEntryIdFn:
+ case TypeTableEntryIdNamespace:
+ case TypeTableEntryIdBlock:
+ case TypeTableEntryIdBoundFn:
+ case TypeTableEntryIdMetaType:
+ case TypeTableEntryIdOpaque:
+ case TypeTableEntryIdPromise:
+ return false;
+ }
+ zig_unreachable();
+}
void init_const_undefined(CodeGen *g, ConstExprValue *const_val) {
TypeTableEntry *wanted_type = const_val->type;
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 88e06b2390e99137cd016e56394c2a198c2c3ccf..100f85d4d9d663fa530397d6301b230f1726a327 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -93,6 +93,7 @@ void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry);
void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry);
void complete_enum(CodeGen *g, TypeTableEntry *enum_type);
bool ir_get_var_is_comptime(VariableTableEntry *var);
+bool contains_comptime_undefined_value(ConstExprValue *value);
bool const_values_equal(ConstExprValue *a, ConstExprValue *b);
void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max);
void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max);
diff --git a/src/ir.cpp b/src/ir.cpp
index c6078e755de8289c89fec25edaa87cb95e0f5a1f..2cce4a504472449b63dfa11313e09da66019a998 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -9148,8 +9148,15 @@ enum UndefAllowed {
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) {
switch (value->value.special) {
- case ConstValSpecialStatic:
- return &value->value;
+ case ConstValSpecialStatic: {
+ ConstExprValue *res = &value->value;
+ if (undef_allowed == UndefBad && contains_comptime_undefined_value(res)) {
+ ir_add_error(ira, value, buf_sprintf("use of undefined value"));
+ return nullptr;
+ }
+
+ return res;
+ }
case ConstValSpecialRuntime:
ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
return nullptr;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 2247f0af966cc4058b2d33c23bb072e50cfe1edc..8749f5b56060168c2b5422451540008e7da1fafd 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -4124,4 +4124,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
,
".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic",
);
+
+ cases.add(
+ "Trying to pass undefined array to function taking comptime array by value",
+ \\fn a(comptime b: [2]u8) u8 { return b[0]; }
+ \\
+ \\test "" {
+ \\ const arr: [2]u8 = undefined;
+ \\ _ = a(arr);
+ \\}
+ ,
+ ".tmp_source.zig:5:11: error: use of undefined value",
+ );
+
+
+
}
--
2.54.0
From 58b1692182dc2f8da5b535f59e9a89cfab10a7b6 Mon Sep 17 00:00:00 2001
From: Jimmi HC
Date: Fri, 29 Jun 2018 11:34:38 +0200
Subject: [PATCH 45/82] contains_comptime_undefined_value should not follow
pointers
---
src/analyze.cpp | 72 +------------------------------------------------
1 file changed, 1 insertion(+), 71 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 068ea48c0a84d4639b365faecc3100a216a8fde9..4c200888d8d27e82b5515e7bce202597ea7049a0 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -5296,41 +5296,6 @@ bool contains_comptime_undefined_value(ConstExprValue *value) {
switch (value->type->id) {
case TypeTableEntryIdInvalid:
zig_unreachable();
-
- case TypeTableEntryIdPointer: {
- ConstPtrValue *ptr = &value->data.x_ptr;
- if (ptr->mut == ConstPtrMutRuntimeVar)
- return false;
-
- switch (ptr->special) {
- case ConstPtrSpecialInvalid:
- zig_unreachable();
- case ConstPtrSpecialRef:
- return contains_comptime_undefined_value(ptr->data.ref.pointee);
- case ConstPtrSpecialBaseArray: {
- size_t index = ptr->data.base_array.elem_index;
- ConstExprValue *arr = ptr->data.base_array.array_val;
- if (arr->special == ConstValSpecialUndef)
- return true;
- if (arr->data.x_array.special == ConstArraySpecialUndef)
- return true;
-
- return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]);
- }
- case ConstPtrSpecialBaseStruct: {
- size_t index = ptr->data.base_struct.field_index;
- ConstExprValue *str = ptr->data.base_struct.struct_val;
- if (str->special == ConstValSpecialUndef)
- return true;
-
- return contains_comptime_undefined_value(&str->data.x_struct.fields[index]);
- }
- case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value?
- case ConstPtrSpecialDiscard:
- case ConstPtrSpecialHardCodedAddr:
- return false;
- }
- }
case TypeTableEntryIdArray: {
ConstArrayValue *arr = &value->data.x_array;
if (arr->special == ConstArraySpecialUndef)
@@ -5344,42 +5309,6 @@ bool contains_comptime_undefined_value(ConstExprValue *value) {
}
case TypeTableEntryIdStruct: {
ConstStructValue *str = &value->data.x_struct;
- if (value->type->data.structure.is_slice) {
- ConstExprValue *len = &str->fields[slice_len_index];
- ConstExprValue *ptr = &str->fields[slice_ptr_index];
- if (len->special == ConstValSpecialUndef)
- return true;
- if (ptr->special == ConstValSpecialUndef)
- return true;
-
- switch (ptr->data.x_ptr.special) {
- case ConstPtrSpecialRef:
- return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee);
- case ConstPtrSpecialBaseArray: {
- size_t offset = ptr->data.x_ptr.data.base_array.elem_index;
- ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val;
- if (arr->special == ConstValSpecialUndef)
- return true;
- if (arr->data.x_array.special == ConstArraySpecialUndef)
- return true;
-
- uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint);
- for (size_t i = 0; i < slice_len; ++i) {
- if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset]))
- return true;
- }
-
- return false;
- }
- case ConstPtrSpecialBaseStruct:
- case ConstPtrSpecialInvalid:
- case ConstPtrSpecialFunction:
- case ConstPtrSpecialDiscard:
- case ConstPtrSpecialHardCodedAddr:
- zig_unreachable();
- }
- }
-
for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) {
if (contains_comptime_undefined_value(&str->fields[i]))
return true;
@@ -5400,6 +5329,7 @@ bool contains_comptime_undefined_value(ConstExprValue *value) {
case TypeTableEntryIdUnion:
return contains_comptime_undefined_value(value->data.x_union.payload);
+ case TypeTableEntryIdPointer:
case TypeTableEntryIdArgTuple:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
--
2.54.0
From 0874a5ba77a1d049a0e9e7f9f249605c109a731c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 29 Jun 2018 14:45:01 -0400
Subject: [PATCH 46/82] std.atomic.queue - document limitation and add MPSC
queue
---
CMakeLists.txt | 3 +-
std/atomic/index.zig | 8 +-
std/atomic/queue_mpmc.zig | 214 +++++++++++++++++++++++
std/atomic/{queue.zig => queue_mpsc.zig} | 62 ++++---
4 files changed, 254 insertions(+), 33 deletions(-)
create mode 100644 std/atomic/queue_mpmc.zig
rename std/atomic/{queue.zig => queue_mpsc.zig} (70%)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 789da4a8a65ae439e31edbed163635b7d2d6b858..4838aeb7970ef65e56af40c1a1ed663c19f7031e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -431,7 +431,8 @@ set(ZIG_CPP_SOURCES
set(ZIG_STD_FILES
"array_list.zig"
"atomic/index.zig"
- "atomic/queue.zig"
+ "atomic/queue_mpmc.zig"
+ "atomic/queue_mpsc.zig"
"atomic/stack.zig"
"base64.zig"
"buf_map.zig"
diff --git a/std/atomic/index.zig b/std/atomic/index.zig
index 9d556a641503c36c2e0cedebea08406f0289d1db..c0ea5be183adbf66870b10aa2e87253f7dc15413 100644
--- a/std/atomic/index.zig
+++ b/std/atomic/index.zig
@@ -1,7 +1,9 @@
pub const Stack = @import("stack.zig").Stack;
-pub const Queue = @import("queue.zig").Queue;
+pub const QueueMpsc = @import("queue_mpsc.zig").QueueMpsc;
+pub const QueueMpmc = @import("queue_mpmc.zig").QueueMpmc;
test "std.atomic" {
- _ = @import("stack.zig").Stack;
- _ = @import("queue.zig").Queue;
+ _ = @import("stack.zig");
+ _ = @import("queue_mpsc.zig");
+ _ = @import("queue_mpmc.zig");
}
diff --git a/std/atomic/queue_mpmc.zig b/std/atomic/queue_mpmc.zig
new file mode 100644
index 0000000000000000000000000000000000000000..7ffc9f9ccb4d551f36dccfc538ee0f0f6f3707c9
--- /dev/null
+++ b/std/atomic/queue_mpmc.zig
@@ -0,0 +1,214 @@
+const builtin = @import("builtin");
+const AtomicOrder = builtin.AtomicOrder;
+const AtomicRmwOp = builtin.AtomicRmwOp;
+
+/// Many producer, many consumer, non-allocating, thread-safe, lock-free
+/// This implementation has a crippling limitation - it hangs onto node
+/// memory for 1 extra get() and 1 extra put() operation - when get() returns a node, that
+/// node must not be freed until both the next get() and the next put() completes.
+pub fn QueueMpmc(comptime T: type) type {
+ return struct {
+ head: *Node,
+ tail: *Node,
+ root: Node,
+
+ pub const Self = this;
+
+ pub const Node = struct {
+ next: ?*Node,
+ data: T,
+ };
+
+ /// TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287
+ pub fn init(self: *Self) void {
+ self.root.next = null;
+ self.head = &self.root;
+ self.tail = &self.root;
+ }
+
+ pub fn put(self: *Self, node: *Node) void {
+ node.next = null;
+
+ const tail = @atomicRmw(*Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
+ _ = @atomicRmw(?*Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
+ }
+
+ /// node must not be freed until both the next get() and the next put() complete
+ pub fn get(self: *Self) ?*Node {
+ var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst);
+ while (true) {
+ const node = head.next orelse return null;
+ head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node;
+ }
+ }
+
+ ///// This is a debug function that is not thread-safe.
+ pub fn dump(self: *Self) void {
+ std.debug.warn("head: ");
+ dumpRecursive(self.head, 0);
+ std.debug.warn("tail: ");
+ dumpRecursive(self.tail, 0);
+ }
+
+ fn dumpRecursive(optional_node: ?*Node, indent: usize) void {
+ var stderr_file = std.io.getStdErr() catch return;
+ const stderr = &std.io.FileOutStream.init(&stderr_file).stream;
+ stderr.writeByteNTimes(' ', indent) catch return;
+ if (optional_node) |node| {
+ std.debug.warn("0x{x}={}\n", @ptrToInt(node), node.data);
+ dumpRecursive(node.next, indent + 1);
+ } else {
+ std.debug.warn("(null)\n");
+ }
+ }
+ };
+}
+
+const std = @import("std");
+const assert = std.debug.assert;
+
+const Context = struct {
+ allocator: *std.mem.Allocator,
+ queue: *QueueMpmc(i32),
+ put_sum: isize,
+ get_sum: isize,
+ get_count: usize,
+ puts_done: u8, // TODO make this a bool
+};
+
+// TODO add lazy evaluated build options and then put puts_per_thread behind
+// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
+// CI we would use a less aggressive setting since at 1 core, while we still
+// want this test to pass, we need a smaller value since there is so much thrashing
+// we would also use a less aggressive setting when running in valgrind
+const puts_per_thread = 500;
+const put_thread_count = 3;
+
+test "std.atomic.queue_mpmc" {
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
+ defer direct_allocator.allocator.free(plenty_of_memory);
+
+ var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
+ var a = &fixed_buffer_allocator.allocator;
+
+ var queue: QueueMpmc(i32) = undefined;
+ queue.init();
+ var context = Context{
+ .allocator = a,
+ .queue = &queue,
+ .put_sum = 0,
+ .get_sum = 0,
+ .puts_done = 0,
+ .get_count = 0,
+ };
+
+ var putters: [put_thread_count]*std.os.Thread = undefined;
+ for (putters) |*t| {
+ t.* = try std.os.spawnThread(&context, startPuts);
+ }
+ var getters: [put_thread_count]*std.os.Thread = undefined;
+ for (getters) |*t| {
+ t.* = try std.os.spawnThread(&context, startGets);
+ }
+
+ for (putters) |t|
+ t.wait();
+ _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
+ for (getters) |t|
+ t.wait();
+
+ if (context.put_sum != context.get_sum) {
+ std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
+ }
+
+ if (context.get_count != puts_per_thread * put_thread_count) {
+ std.debug.panic(
+ "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
+ context.get_count,
+ u32(puts_per_thread),
+ u32(put_thread_count),
+ );
+ }
+}
+
+fn startPuts(ctx: *Context) u8 {
+ var put_count: usize = puts_per_thread;
+ var r = std.rand.DefaultPrng.init(0xdeadbeef);
+ while (put_count != 0) : (put_count -= 1) {
+ std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
+ const x = @bitCast(i32, r.random.scalar(u32));
+ const node = ctx.allocator.create(QueueMpmc(i32).Node{
+ .next = undefined,
+ .data = x,
+ }) catch unreachable;
+ ctx.queue.put(node);
+ _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
+ }
+ return 0;
+}
+
+fn startGets(ctx: *Context) u8 {
+ while (true) {
+ const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
+
+ while (ctx.queue.get()) |node| {
+ std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
+ _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
+ _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
+ }
+
+ if (last) return 0;
+ }
+}
+
+test "std.atomic.queue_mpmc single-threaded" {
+ var queue: QueueMpmc(i32) = undefined;
+ queue.init();
+
+ var node_0 = QueueMpmc(i32).Node{
+ .data = 0,
+ .next = undefined,
+ };
+ queue.put(&node_0);
+
+ var node_1 = QueueMpmc(i32).Node{
+ .data = 1,
+ .next = undefined,
+ };
+ queue.put(&node_1);
+
+ assert(queue.get().?.data == 0);
+
+ var node_2 = QueueMpmc(i32).Node{
+ .data = 2,
+ .next = undefined,
+ };
+ queue.put(&node_2);
+
+ var node_3 = QueueMpmc(i32).Node{
+ .data = 3,
+ .next = undefined,
+ };
+ queue.put(&node_3);
+
+ assert(queue.get().?.data == 1);
+
+ assert(queue.get().?.data == 2);
+
+ var node_4 = QueueMpmc(i32).Node{
+ .data = 4,
+ .next = undefined,
+ };
+ queue.put(&node_4);
+
+ assert(queue.get().?.data == 3);
+ // if we were to set node_3.next to null here, it would cause this test
+ // to fail. this demonstrates the limitation of hanging on to extra memory.
+
+ assert(queue.get().?.data == 4);
+
+ assert(queue.get() == null);
+}
diff --git a/std/atomic/queue.zig b/std/atomic/queue_mpsc.zig
similarity index 70%
rename from std/atomic/queue.zig
rename to std/atomic/queue_mpsc.zig
index 16dc9f6cc3ec546b6e08c77cd6262a5eed34c81c..66eb4573df3e96b40e7c639a27836f9f88dbc90e 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue_mpsc.zig
@@ -1,49 +1,54 @@
+const std = @import("std");
+const assert = std.debug.assert;
const builtin = @import("builtin");
const AtomicOrder = builtin.AtomicOrder;
const AtomicRmwOp = builtin.AtomicRmwOp;
-/// Many reader, many writer, non-allocating, thread-safe, lock-free
-pub fn Queue(comptime T: type) type {
+/// Many producer, single consumer, non-allocating, thread-safe, lock-free
+pub fn QueueMpsc(comptime T: type) type {
return struct {
- head: *Node,
- tail: *Node,
- root: Node,
+ inboxes: [2]std.atomic.Stack(T),
+ outbox: std.atomic.Stack(T),
+ inbox_index: usize,
pub const Self = this;
- pub const Node = struct {
- next: ?*Node,
- data: T,
- };
+ pub const Node = std.atomic.Stack(T).Node;
- // TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287
- pub fn init(self: *Self) void {
- self.root.next = null;
- self.head = &self.root;
- self.tail = &self.root;
+ pub fn init() Self {
+ return Self{
+ .inboxes = []std.atomic.Stack(T){
+ std.atomic.Stack(T).init(),
+ std.atomic.Stack(T).init(),
+ },
+ .outbox = std.atomic.Stack(T).init(),
+ .inbox_index = 0,
+ };
}
pub fn put(self: *Self, node: *Node) void {
- node.next = null;
-
- const tail = @atomicRmw(*Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
- _ = @atomicRmw(?*Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
+ const inbox_index = @atomicLoad(usize, &self.inbox_index, AtomicOrder.SeqCst);
+ const inbox = &self.inboxes[inbox_index];
+ inbox.push(node);
}
pub fn get(self: *Self) ?*Node {
- var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst);
- while (true) {
- const node = head.next orelse return null;
- head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node;
+ if (self.outbox.pop()) |node| {
+ return node;
}
+ const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst);
+ const prev_inbox = &self.inboxes[prev_inbox_index];
+ while (prev_inbox.pop()) |node| {
+ self.outbox.push(node);
+ }
+ return self.outbox.pop();
}
};
}
-const std = @import("std");
const Context = struct {
allocator: *std.mem.Allocator,
- queue: *Queue(i32),
+ queue: *QueueMpsc(i32),
put_sum: isize,
get_sum: isize,
get_count: usize,
@@ -58,7 +63,7 @@ const Context = struct {
const puts_per_thread = 500;
const put_thread_count = 3;
-test "std.atomic.queue" {
+test "std.atomic.queue_mpsc" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
@@ -68,8 +73,7 @@ test "std.atomic.queue" {
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
var a = &fixed_buffer_allocator.allocator;
- var queue: Queue(i32) = undefined;
- queue.init();
+ var queue = QueueMpsc(i32).init();
var context = Context{
.allocator = a,
.queue = &queue,
@@ -83,7 +87,7 @@ test "std.atomic.queue" {
for (putters) |*t| {
t.* = try std.os.spawnThread(&context, startPuts);
}
- var getters: [put_thread_count]*std.os.Thread = undefined;
+ var getters: [1]*std.os.Thread = undefined;
for (getters) |*t| {
t.* = try std.os.spawnThread(&context, startGets);
}
@@ -114,7 +118,7 @@ fn startPuts(ctx: *Context) u8 {
while (put_count != 0) : (put_count -= 1) {
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
const x = @bitCast(i32, r.random.scalar(u32));
- const node = ctx.allocator.create(Queue(i32).Node{
+ const node = ctx.allocator.create(QueueMpsc(i32).Node{
.next = undefined,
.data = x,
}) catch unreachable;
--
2.54.0
From f1c56f7f225f2f3054abd8c9e6330a0f1e20b2a6 Mon Sep 17 00:00:00 2001
From: isaachier
Date: Fri, 29 Jun 2018 14:52:25 -0400
Subject: [PATCH 47/82] Clarify reason implicit cast does not work for large
RHS (#1168)
* Clarify reason implicit cast does not work for large RHS
---
src/ir.cpp | 20 ++++++++++++++++++++
test/compile_errors.zig | 12 ++++++++++++
2 files changed, 32 insertions(+)
diff --git a/src/ir.cpp b/src/ir.cpp
index 98ed53d839c6953b526d20a1f7b2cf18a8935dbc..a450b6d14e857c487698111aa6cf6ca160a9f0fb 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -11432,6 +11432,26 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *
} else {
TypeTableEntry *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen,
op1->value.type->data.integral.bit_count - 1);
+ if (bin_op_instruction->op_id == IrBinOpBitShiftLeftLossy &&
+ op2->value.type->id == TypeTableEntryIdComptimeInt) {
+ if (!bigint_fits_in_bits(&op2->value.data.x_bigint,
+ shift_amt_type->data.integral.bit_count,
+ op2->value.data.x_bigint.is_negative)) {
+ Buf *val_buf = buf_alloc();
+ bigint_append_buf(val_buf, &op2->value.data.x_bigint, 10);
+ ErrorMsg* msg = ir_add_error(ira,
+ &bin_op_instruction->base,
+ buf_sprintf("RHS of shift is too large for LHS type"));
+ add_error_note(
+ ira->codegen,
+ msg,
+ op2->source_node,
+ buf_sprintf("value %s cannot fit into type %s",
+ buf_ptr(val_buf),
+ buf_ptr(&shift_amt_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ }
casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type);
if (casted_op2 == ira->codegen->invalid_instruction)
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 2247f0af966cc4058b2d33c23bb072e50cfe1edc..cfe4a2ef5f96e16727e147081e9b5dcbdf175711 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1677,6 +1677,18 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
".tmp_source.zig:1:16: error: integer value 300 cannot be implicitly casted to type 'u8'",
);
+ cases.add(
+ "invalid shift amount error",
+ \\const x : u8 = 2;
+ \\fn f() u16 {
+ \\ return x << 8;
+ \\}
+ \\export fn entry() u16 { return f(); }
+ ,
+ ".tmp_source.zig:3:14: error: RHS of shift is too large for LHS type",
+ ".tmp_source.zig:3:17: note: value 8 cannot fit into type u3",
+ );
+
cases.add(
"incompatible number literals",
\\const x = 2 == 2.0;
--
2.54.0
From 03f66825d6b6922da250cdb99c2996455541e0f9 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Fri, 29 Jun 2018 23:28:42 +0200
Subject: [PATCH 48/82] support --emit in 'test' command
Support the `--emit` switch in `zig --emit asm test file.zig`.
The command fails because no tests run (no executable is created) but
it emits the requested file. That seems like a good tradeoff.
---
src/main.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/main.cpp b/src/main.cpp
index 0fe12bb0cb67c19f6c9403dda13ae4a53195da88..a409778a78d0689d97774b4b99008b9cba3b0dc8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -924,6 +924,8 @@ int main(int argc, char **argv) {
codegen_print_timing_report(g, stdout);
return EXIT_SUCCESS;
} else if (cmd == CmdTest) {
+ codegen_set_emit_file_type(g, emit_file_type);
+
ZigTarget native;
get_native_target(&native);
--
2.54.0
From 61df5bc142253b0d33b77bceecfaef4c767e7feb Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 49/82] add std.math f16 constants
refs #1122
---
std/math/index.zig | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/std/math/index.zig b/std/math/index.zig
index 04133dc1dce2966faa82ad85bb0f8942a1c80f2f..e987274f71af06aacab384b48cf4be54c6b26bc2 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -19,6 +19,12 @@ pub const f32_max = 3.40282346638528859812e+38;
pub const f32_epsilon = 1.1920928955078125e-07;
pub const f32_toint = 1.0 / f32_epsilon;
+pub const f16_true_min = 0.000000059604644775390625; // 2**-24
+pub const f16_min = 0.00006103515625; // 2**-14
+pub const f16_max = 65504;
+pub const f16_epsilon = 0.0009765625; // 2**-10
+pub const f16_toint = 1.0 / f16_epsilon;
+
pub const nan_u32 = u32(0x7F800001);
pub const nan_f32 = @bitCast(f32, nan_u32);
--
2.54.0
From 27b02413dc3dacc7d784fe84ff8ba6cb0361842d Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 50/82] add std.math f16 nan support
refs #1122
---
std/math/index.zig | 3 +++
std/math/isnan.zig | 6 ++++++
std/math/nan.zig | 2 ++
std/special/builtin.zig | 4 +++-
std/special/compiler_rt/extendXfYf2_test.zig | 1 +
5 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/std/math/index.zig b/std/math/index.zig
index e987274f71af06aacab384b48cf4be54c6b26bc2..df8271fbcf5615065ab1717ef0df9882ea5d928b 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -25,6 +25,9 @@ pub const f16_max = 65504;
pub const f16_epsilon = 0.0009765625; // 2**-10
pub const f16_toint = 1.0 / f16_epsilon;
+pub const nan_u16 = u16(0x7C01);
+pub const nan_f16 = @bitCast(f16, nan_u16);
+
pub const nan_u32 = u32(0x7F800001);
pub const nan_f32 = @bitCast(f32, nan_u32);
diff --git a/std/math/isnan.zig b/std/math/isnan.zig
index 67971e3d0c15e624d2f3d0e06b16cac1009c9bf8..ef3002d8e197a4723d6cee13932ef0a05563c9e8 100644
--- a/std/math/isnan.zig
+++ b/std/math/isnan.zig
@@ -5,6 +5,10 @@ const assert = std.debug.assert;
pub fn isNan(x: var) bool {
const T = @typeOf(x);
switch (T) {
+ f16 => {
+ const bits = @bitCast(u16, x);
+ return (bits & 0x7fff) > 0x7c00;
+ },
f32 => {
const bits = @bitCast(u32, x);
return bits & 0x7FFFFFFF > 0x7F800000;
@@ -26,8 +30,10 @@ pub fn isSignalNan(x: var) bool {
}
test "math.isNan" {
+ assert(isNan(math.nan(f16)));
assert(isNan(math.nan(f32)));
assert(isNan(math.nan(f64)));
+ assert(!isNan(f16(1.0)));
assert(!isNan(f32(1.0)));
assert(!isNan(f64(1.0)));
}
diff --git a/std/math/nan.zig b/std/math/nan.zig
index 22461711d0a59d54d631feb8a9bcbcf1867ae9bf..2cbcbee81b974159a0bf5ca99bc81faa3b159a43 100644
--- a/std/math/nan.zig
+++ b/std/math/nan.zig
@@ -2,6 +2,7 @@ const math = @import("index.zig");
pub fn nan(comptime T: type) T {
return switch (T) {
+ f16 => @bitCast(f16, math.nan_u16),
f32 => @bitCast(f32, math.nan_u32),
f64 => @bitCast(f64, math.nan_u64),
else => @compileError("nan not implemented for " ++ @typeName(T)),
@@ -12,6 +13,7 @@ pub fn nan(comptime T: type) T {
// representation in the future when required.
pub fn snan(comptime T: type) T {
return switch (T) {
+ f16 => @bitCast(f16, math.nan_u16),
f32 => @bitCast(f32, math.nan_u32),
f64 => @bitCast(f64, math.nan_u64),
else => @compileError("snan not implemented for " ++ @typeName(T)),
diff --git a/std/special/builtin.zig b/std/special/builtin.zig
index 07e735d93175c8d35c7ef8d3848bec1f9c1bfd95..56e578030b51db012085ac5a00f650d2f47a475c 100644
--- a/std/special/builtin.zig
+++ b/std/special/builtin.zig
@@ -210,7 +210,9 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {
}
fn isNan(comptime T: type, bits: T) bool {
- if (T == u32) {
+ if (T == u16) {
+ return (bits & 0x7fff) > 0x7c00;
+ } else if (T == u32) {
return (bits & 0x7fffffff) > 0x7f800000;
} else if (T == u64) {
return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) << 52);
diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig
index 0168de12a543e6136a05a5a9982dc89f4c88e0a0..185c83a0eff2a14945fa799199d7ec9e879772fa 100644
--- a/std/special/compiler_rt/extendXfYf2_test.zig
+++ b/std/special/compiler_rt/extendXfYf2_test.zig
@@ -88,6 +88,7 @@ test "extenddftf2" {
test "extendhfsf2" {
test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN
test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN
+ test__extendhfsf2(0x7c01, 0x7f802000); // sNaN
test__extendhfsf2(0, 0); // 0
test__extendhfsf2(0x8000, 0x80000000); // -0
--
2.54.0
From a36d7b613185c28c508bcc7b0e8b580a0ffd5e28 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 51/82] add std.math f16 inf support
refs #1122
---
std/math/index.zig | 3 +++
std/math/inf.zig | 2 +-
std/math/isinf.zig | 22 ++++++++++++++++++++++
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/std/math/index.zig b/std/math/index.zig
index df8271fbcf5615065ab1717ef0df9882ea5d928b..1926943cc630f4887cc07d0cf41cc993a2341c18 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -28,6 +28,9 @@ pub const f16_toint = 1.0 / f16_epsilon;
pub const nan_u16 = u16(0x7C01);
pub const nan_f16 = @bitCast(f16, nan_u16);
+pub const inf_u16 = u16(0x7C00);
+pub const inf_f16 = @bitCast(f16, inf_u16);
+
pub const nan_u32 = u32(0x7F800001);
pub const nan_f32 = @bitCast(f32, nan_u32);
diff --git a/std/math/inf.zig b/std/math/inf.zig
index bde90b2be1d89b43072bf90ef21c90de505ef6fc..62f5ef7c0da91c8c9c485501e41f605c9660c97a 100644
--- a/std/math/inf.zig
+++ b/std/math/inf.zig
@@ -1,9 +1,9 @@
const std = @import("../index.zig");
const math = std.math;
-const assert = std.debug.assert;
pub fn inf(comptime T: type) T {
return switch (T) {
+ f16 => @bitCast(f16, math.inf_u16),
f32 => @bitCast(f32, math.inf_u32),
f64 => @bitCast(f64, math.inf_u64),
else => @compileError("inf not implemented for " ++ @typeName(T)),
diff --git a/std/math/isinf.zig b/std/math/isinf.zig
index a976fb73d24c47f18d2430d5e78c443cbb537851..cf68b5769cbb401633a967cc6b96431207109385 100644
--- a/std/math/isinf.zig
+++ b/std/math/isinf.zig
@@ -5,6 +5,10 @@ const assert = std.debug.assert;
pub fn isInf(x: var) bool {
const T = @typeOf(x);
switch (T) {
+ f16 => {
+ const bits = @bitCast(u16, x);
+ return bits & 0x7FFF == 0x7C00;
+ },
f32 => {
const bits = @bitCast(u32, x);
return bits & 0x7FFFFFFF == 0x7F800000;
@@ -22,6 +26,9 @@ pub fn isInf(x: var) bool {
pub fn isPositiveInf(x: var) bool {
const T = @typeOf(x);
switch (T) {
+ f16 => {
+ return @bitCast(u16, x) == 0x7C00;
+ },
f32 => {
return @bitCast(u32, x) == 0x7F800000;
},
@@ -37,6 +44,9 @@ pub fn isPositiveInf(x: var) bool {
pub fn isNegativeInf(x: var) bool {
const T = @typeOf(x);
switch (T) {
+ f16 => {
+ return @bitCast(u16, x) == 0xFC00;
+ },
f32 => {
return @bitCast(u32, x) == 0xFF800000;
},
@@ -50,10 +60,14 @@ pub fn isNegativeInf(x: var) bool {
}
test "math.isInf" {
+ assert(!isInf(f16(0.0)));
+ assert(!isInf(f16(-0.0)));
assert(!isInf(f32(0.0)));
assert(!isInf(f32(-0.0)));
assert(!isInf(f64(0.0)));
assert(!isInf(f64(-0.0)));
+ assert(isInf(math.inf(f16)));
+ assert(isInf(-math.inf(f16)));
assert(isInf(math.inf(f32)));
assert(isInf(-math.inf(f32)));
assert(isInf(math.inf(f64)));
@@ -61,10 +75,14 @@ test "math.isInf" {
}
test "math.isPositiveInf" {
+ assert(!isPositiveInf(f16(0.0)));
+ assert(!isPositiveInf(f16(-0.0)));
assert(!isPositiveInf(f32(0.0)));
assert(!isPositiveInf(f32(-0.0)));
assert(!isPositiveInf(f64(0.0)));
assert(!isPositiveInf(f64(-0.0)));
+ assert(isPositiveInf(math.inf(f16)));
+ assert(!isPositiveInf(-math.inf(f16)));
assert(isPositiveInf(math.inf(f32)));
assert(!isPositiveInf(-math.inf(f32)));
assert(isPositiveInf(math.inf(f64)));
@@ -72,10 +90,14 @@ test "math.isPositiveInf" {
}
test "math.isNegativeInf" {
+ assert(!isNegativeInf(f16(0.0)));
+ assert(!isNegativeInf(f16(-0.0)));
assert(!isNegativeInf(f32(0.0)));
assert(!isNegativeInf(f32(-0.0)));
assert(!isNegativeInf(f64(0.0)));
assert(!isNegativeInf(f64(-0.0)));
+ assert(!isNegativeInf(math.inf(f16)));
+ assert(isNegativeInf(-math.inf(f16)));
assert(!isNegativeInf(math.inf(f32)));
assert(isNegativeInf(-math.inf(f32)));
assert(!isNegativeInf(math.inf(f64)));
--
2.54.0
From 30b75ae3539128cf7f12ef84db15ad91f54f0de2 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 52/82] add std.math f16 isfinite support
refs #1122
---
std/math/isfinite.zig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/std/math/isfinite.zig b/std/math/isfinite.zig
index 37ead03bba002af38fc58bd3c49306f078619494..3a5d4f01bb261e8bc10b194ad936bb343c80809c 100644
--- a/std/math/isfinite.zig
+++ b/std/math/isfinite.zig
@@ -5,6 +5,10 @@ const assert = std.debug.assert;
pub fn isFinite(x: var) bool {
const T = @typeOf(x);
switch (T) {
+ f16 => {
+ const bits = @bitCast(u16, x);
+ return bits & 0x7FFF < 0x7C00;
+ },
f32 => {
const bits = @bitCast(u32, x);
return bits & 0x7FFFFFFF < 0x7F800000;
@@ -20,10 +24,14 @@ pub fn isFinite(x: var) bool {
}
test "math.isFinite" {
+ assert(isFinite(f16(0.0)));
+ assert(isFinite(f16(-0.0)));
assert(isFinite(f32(0.0)));
assert(isFinite(f32(-0.0)));
assert(isFinite(f64(0.0)));
assert(isFinite(f64(-0.0)));
+ assert(!isFinite(math.inf(f16)));
+ assert(!isFinite(-math.inf(f16)));
assert(!isFinite(math.inf(f32)));
assert(!isFinite(-math.inf(f32)));
assert(!isFinite(math.inf(f64)));
--
2.54.0
From f36b095b5ff2176f357d84a9924740324b069765 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 53/82] add std.math f16 isnormal support
refs #1122
---
std/math/isnormal.zig | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/std/math/isnormal.zig b/std/math/isnormal.zig
index d5c1061cb18406de0915ed5d0d70ee0effebc94d..22109936c4849cc5884e91476c6419c218a8b6e8 100644
--- a/std/math/isnormal.zig
+++ b/std/math/isnormal.zig
@@ -5,6 +5,10 @@ const assert = std.debug.assert;
pub fn isNormal(x: var) bool {
const T = @typeOf(x);
switch (T) {
+ f16 => {
+ const bits = @bitCast(u16, x);
+ return (bits + 1024) & 0x7FFF >= 2048;
+ },
f32 => {
const bits = @bitCast(u32, x);
return (bits + 0x00800000) & 0x7FFFFFFF >= 0x01000000;
@@ -20,8 +24,13 @@ pub fn isNormal(x: var) bool {
}
test "math.isNormal" {
+ assert(!isNormal(math.nan(f16)));
assert(!isNormal(math.nan(f32)));
assert(!isNormal(math.nan(f64)));
+ assert(!isNormal(f16(0)));
+ assert(!isNormal(f32(0)));
+ assert(!isNormal(f64(0)));
+ assert(isNormal(f16(1.0)));
assert(isNormal(f32(1.0)));
assert(isNormal(f64(1.0)));
}
--
2.54.0
From 1abc9252922f242b801fe88533b482a18724237d Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 54/82] add std.math f16 fabs support
refs #1122
---
std/math/fabs.zig | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/std/math/fabs.zig b/std/math/fabs.zig
index 821624e1bc2cd7cae6e8bca163e03bd446b47940..ae8f9616a867793c6e709513268ad186aea73e26 100644
--- a/std/math/fabs.zig
+++ b/std/math/fabs.zig
@@ -10,12 +10,19 @@ const assert = std.debug.assert;
pub fn fabs(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
+ f16 => fabs16(x),
f32 => fabs32(x),
f64 => fabs64(x),
else => @compileError("fabs not implemented for " ++ @typeName(T)),
};
}
+fn fabs16(x: f16) f16 {
+ var u = @bitCast(u16, x);
+ u &= 0x7FFF;
+ return @bitCast(f16, u);
+}
+
fn fabs32(x: f32) f32 {
var u = @bitCast(u32, x);
u &= 0x7FFFFFFF;
@@ -29,10 +36,16 @@ fn fabs64(x: f64) f64 {
}
test "math.fabs" {
+ assert(fabs(f16(1.0)) == fabs16(1.0));
assert(fabs(f32(1.0)) == fabs32(1.0));
assert(fabs(f64(1.0)) == fabs64(1.0));
}
+test "math.fabs16" {
+ assert(fabs16(1.0) == 1.0);
+ assert(fabs16(-1.0) == 1.0);
+}
+
test "math.fabs32" {
assert(fabs32(1.0) == 1.0);
assert(fabs32(-1.0) == 1.0);
@@ -43,6 +56,12 @@ test "math.fabs64" {
assert(fabs64(-1.0) == 1.0);
}
+test "math.fabs16.special" {
+ assert(math.isPositiveInf(fabs(math.inf(f16))));
+ assert(math.isPositiveInf(fabs(-math.inf(f16))));
+ assert(math.isNan(fabs(math.nan(f16))));
+}
+
test "math.fabs32.special" {
assert(math.isPositiveInf(fabs(math.inf(f32))));
assert(math.isPositiveInf(fabs(-math.inf(f32))));
--
2.54.0
From d293f1a0edf0e2e42f03ad416c7f5d649776ae88 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 55/82] add std.math f16 floor support
refs #1122
---
std/math/floor.zig | 50 ++++++++++++++++++++++++++++++++++++++++++++++
std/math/index.zig | 5 +++++
2 files changed, 55 insertions(+)
diff --git a/std/math/floor.zig b/std/math/floor.zig
index 79d1097d083404e7b8df12bf344812550bd870ec..0858598eeafdbac4a215f4e554ececc66e19735a 100644
--- a/std/math/floor.zig
+++ b/std/math/floor.zig
@@ -12,12 +12,47 @@ const math = std.math;
pub fn floor(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
+ f16 => floor16(x),
f32 => floor32(x),
f64 => floor64(x),
else => @compileError("floor not implemented for " ++ @typeName(T)),
};
}
+fn floor16(x: f16) f16 {
+ var u = @bitCast(u16, x);
+ const e = @intCast(i16, (u >> 10) & 31) - 15;
+ var m: u16 = undefined;
+
+ // TODO: Shouldn't need this explicit check.
+ if (x == 0.0) {
+ return x;
+ }
+
+ if (e >= 10) {
+ return x;
+ }
+
+ if (e >= 0) {
+ m = u16(1023) >> @intCast(u4, e);
+ if (u & m == 0) {
+ return x;
+ }
+ math.forceEval(x + 0x1.0p120);
+ if (u >> 15 != 0) {
+ u += m;
+ }
+ return @bitCast(f16, u & ~m);
+ } else {
+ math.forceEval(x + 0x1.0p120);
+ if (u >> 15 == 0) {
+ return 0.0;
+ } else {
+ return -1.0;
+ }
+ }
+}
+
fn floor32(x: f32) f32 {
var u = @bitCast(u32, x);
const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F;
@@ -84,10 +119,17 @@ fn floor64(x: f64) f64 {
}
test "math.floor" {
+ assert(floor(f16(1.3)) == floor16(1.3));
assert(floor(f32(1.3)) == floor32(1.3));
assert(floor(f64(1.3)) == floor64(1.3));
}
+test "math.floor16" {
+ assert(floor16(1.3) == 1.0);
+ assert(floor16(-1.3) == -2.0);
+ assert(floor16(0.2) == 0.0);
+}
+
test "math.floor32" {
assert(floor32(1.3) == 1.0);
assert(floor32(-1.3) == -2.0);
@@ -100,6 +142,14 @@ test "math.floor64" {
assert(floor64(0.2) == 0.0);
}
+test "math.floor16.special" {
+ assert(floor16(0.0) == 0.0);
+ assert(floor16(-0.0) == -0.0);
+ assert(math.isPositiveInf(floor16(math.inf(f16))));
+ assert(math.isNegativeInf(floor16(-math.inf(f16))));
+ assert(math.isNan(floor16(math.nan(f16))));
+}
+
test "math.floor32.special" {
assert(floor32(0.0) == 0.0);
assert(floor32(-0.0) == -0.0);
diff --git a/std/math/index.zig b/std/math/index.zig
index 1926943cc630f4887cc07d0cf41cc993a2341c18..17b66f55680d718414282395ab65d21b10a04e00 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -56,6 +56,11 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool {
pub fn forceEval(value: var) void {
const T = @typeOf(value);
switch (T) {
+ f16 => {
+ var x: f16 = undefined;
+ const p = @ptrCast(*volatile f16, &x);
+ p.* = x;
+ },
f32 => {
var x: f32 = undefined;
const p = @ptrCast(*volatile f32, &x);
--
2.54.0
From ca444e6191138e6f9977cf2fbceb071134ed6aff Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 56/82] add std.math f16 copysign support
refs #1122
---
std/math/copysign.zig | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/std/math/copysign.zig b/std/math/copysign.zig
index 4ca8f82f4b4a66859de8e707138cf29596009ce2..8c71dcb0bc4c201e7d54e95c1f53c4c1040008f7 100644
--- a/std/math/copysign.zig
+++ b/std/math/copysign.zig
@@ -4,12 +4,22 @@ const assert = std.debug.assert;
pub fn copysign(comptime T: type, x: T, y: T) T {
return switch (T) {
+ f16 => copysign16(x, y),
f32 => copysign32(x, y),
f64 => copysign64(x, y),
else => @compileError("copysign not implemented for " ++ @typeName(T)),
};
}
+fn copysign16(x: f16, y: f16) f16 {
+ const ux = @bitCast(u16, x);
+ const uy = @bitCast(u16, y);
+
+ const h1 = ux & (@maxValue(u16) / 2);
+ const h2 = uy & (u16(1) << 15);
+ return @bitCast(f16, h1 | h2);
+}
+
fn copysign32(x: f32, y: f32) f32 {
const ux = @bitCast(u32, x);
const uy = @bitCast(u32, y);
@@ -29,10 +39,18 @@ fn copysign64(x: f64, y: f64) f64 {
}
test "math.copysign" {
+ assert(copysign(f16, 1.0, 1.0) == copysign16(1.0, 1.0));
assert(copysign(f32, 1.0, 1.0) == copysign32(1.0, 1.0));
assert(copysign(f64, 1.0, 1.0) == copysign64(1.0, 1.0));
}
+test "math.copysign16" {
+ assert(copysign16(5.0, 1.0) == 5.0);
+ assert(copysign16(5.0, -1.0) == -5.0);
+ assert(copysign16(-5.0, -1.0) == -5.0);
+ assert(copysign16(-5.0, 1.0) == 5.0);
+}
+
test "math.copysign32" {
assert(copysign32(5.0, 1.0) == 5.0);
assert(copysign32(5.0, -1.0) == -5.0);
--
2.54.0
From be361790645971446b64dabf21c27a595a40c8fd Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 57/82] add std.math f16 signbit support
refs #1122
---
std/math/signbit.zig | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/std/math/signbit.zig b/std/math/signbit.zig
index a0191bed5c5a0150512bc40100ff3e98d8d1828e..8c6829dfcd7906b9fc0b77fe008acb4c413931a0 100644
--- a/std/math/signbit.zig
+++ b/std/math/signbit.zig
@@ -5,12 +5,18 @@ const assert = std.debug.assert;
pub fn signbit(x: var) bool {
const T = @typeOf(x);
return switch (T) {
+ f16 => signbit16(x),
f32 => signbit32(x),
f64 => signbit64(x),
else => @compileError("signbit not implemented for " ++ @typeName(T)),
};
}
+fn signbit16(x: f16) bool {
+ const bits = @bitCast(u16, x);
+ return bits >> 15 != 0;
+}
+
fn signbit32(x: f32) bool {
const bits = @bitCast(u32, x);
return bits >> 31 != 0;
@@ -22,10 +28,16 @@ fn signbit64(x: f64) bool {
}
test "math.signbit" {
+ assert(signbit(f16(4.0)) == signbit16(4.0));
assert(signbit(f32(4.0)) == signbit32(4.0));
assert(signbit(f64(4.0)) == signbit64(4.0));
}
+test "math.signbit16" {
+ assert(!signbit16(4.0));
+ assert(signbit16(-3.0));
+}
+
test "math.signbit32" {
assert(!signbit32(4.0));
assert(signbit32(-3.0));
--
2.54.0
From 30cfc0ab2c56ad73dca9b9935731d10010c93b32 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 30 Jun 2018 01:44:54 +0200
Subject: [PATCH 58/82] test std.math f16 sqrt support
refs #1122
---
std/math/sqrt.zig | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig
index 599008acff27ddf5eab8291d2bbc12b799a5fbc4..e12ecf9683becf2a2a3cd3055f9c8fc3b8d677a8 100644
--- a/std/math/sqrt.zig
+++ b/std/math/sqrt.zig
@@ -31,10 +31,25 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ
}
test "math.sqrt" {
+ assert(sqrt(f16(0.0)) == @sqrt(f16, 0.0));
assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0));
assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0));
}
+test "math.sqrt16" {
+ const epsilon = 0.000001;
+
+ assert(@sqrt(f16, 0.0) == 0.0);
+ assert(math.approxEq(f16, @sqrt(f16, 2.0), 1.414214, epsilon));
+ assert(math.approxEq(f16, @sqrt(f16, 3.6), 1.897367, epsilon));
+ assert(@sqrt(f16, 4.0) == 2.0);
+ assert(math.approxEq(f16, @sqrt(f16, 7.539840), 2.745877, epsilon));
+ assert(math.approxEq(f16, @sqrt(f16, 19.230934), 4.385309, epsilon));
+ assert(@sqrt(f16, 64.0) == 8.0);
+ assert(math.approxEq(f16, @sqrt(f16, 64.1), 8.006248, epsilon));
+ assert(math.approxEq(f16, @sqrt(f16, 8942.230469), 94.563370, epsilon));
+}
+
test "math.sqrt32" {
const epsilon = 0.000001;
@@ -63,6 +78,14 @@ test "math.sqrt64" {
assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon));
}
+test "math.sqrt16.special" {
+ assert(math.isPositiveInf(@sqrt(f16, math.inf(f16))));
+ assert(@sqrt(f16, 0.0) == 0.0);
+ assert(@sqrt(f16, -0.0) == -0.0);
+ assert(math.isNan(@sqrt(f16, -1.0)));
+ assert(math.isNan(@sqrt(f16, math.nan(f16))));
+}
+
test "math.sqrt32.special" {
assert(math.isPositiveInf(@sqrt(f32, math.inf(f32))));
assert(@sqrt(f32, 0.0) == 0.0);
--
2.54.0
From 25bbb1a8ff7074a56b4da98de24a549e223d0009 Mon Sep 17 00:00:00 2001
From: Jay Weisskopf
Date: Fri, 29 Jun 2018 22:22:04 -0400
Subject: [PATCH 59/82] Fix version detection for out-of-source builds
Git was called in the build directory and not the source directory.
This works fine when the build directory resides within the source
repository, but doesn't work for out-of-source builds. Example:
```
~/zigbuild$ cmake ../zig
fatal: not a git repository (or any of the parent directories): .git
Configuring zig version 0.2.0+
```
Use Git's `-C ` flag to always point to the source directory so
that it doesn't matter where the build directory lives.
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4838aeb7970ef65e56af40c1a1ed663c19f7031e..e8873d2e6726ea22861f6460be8963f9db478c4c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,7 +22,7 @@ set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}"
find_program(GIT_EXE NAMES git)
if(GIT_EXE)
execute_process(
- COMMAND ${GIT_EXE} name-rev HEAD --tags --name-only --no-undefined --always
+ COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always
OUTPUT_VARIABLE ZIG_GIT_REV
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(ZIG_GIT_REV MATCHES "\\^0$")
--
2.54.0
From 379950f81debb1e4df4e69511fbebd61911013b4 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 28 Jun 2018 20:26:35 +1200
Subject: [PATCH 60/82] compiler_rt: Add trunc f128 narrowing functions
---
std/special/compiler_rt/truncXfYf2.zig | 16 ++-
std/special/compiler_rt/truncXfYf2_test.zig | 138 +++++++++++++++-----
2 files changed, 115 insertions(+), 39 deletions(-)
diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig
index f08c6ae34ff6b9af99c560404ea60a75bf1e6beb..04b815e86b7da35bef764b4b6fc63aa634355e4e 100644
--- a/std/special/compiler_rt/truncXfYf2.zig
+++ b/std/special/compiler_rt/truncXfYf2.zig
@@ -4,7 +4,13 @@ pub extern fn __truncsfhf2(a: f32) u16 {
return @bitCast(u16, truncXfYf2(f16, f32, a));
}
-const CHAR_BIT = 8;
+pub extern fn __trunctfsf2(a: f128) f32 {
+ return truncXfYf2(f32, f128, a);
+}
+
+pub extern fn __trunctfdf2(a: f128) f64 {
+ return truncXfYf2(f64, f128, a);
+}
inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits);
@@ -16,7 +22,7 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t
// Various constants whose values follow from the type parameters.
// Any reasonable optimizer will fold and propagate all of these.
- const srcBits = @sizeOf(src_t) * CHAR_BIT;
+ const srcBits = src_t.bit_count;
const srcExpBits = srcBits - srcSigBits - 1;
const srcInfExp = (1 << srcExpBits) - 1;
const srcExpBias = srcInfExp >> 1;
@@ -31,7 +37,7 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t
const srcQNaN = 1 << (srcSigBits - 1);
const srcNaNCode = srcQNaN - 1;
- const dstBits = @sizeOf(dst_t) * CHAR_BIT;
+ const dstBits = dst_t.bit_count;
const dstExpBits = dstBits - dstSigBits - 1;
const dstInfExp = (1 << dstExpBits) - 1;
const dstExpBias = dstInfExp >> 1;
@@ -79,8 +85,8 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t
// a underflows on conversion to the destination type or is an exact
// zero. The result may be a denormal or zero. Extract the exponent
// to get the shift amount for the denormalization.
- const aExp: u32 = aAbs >> srcSigBits;
- const shift: u32 = srcExpBias - dstExpBias - aExp + 1;
+ const aExp = aAbs >> srcSigBits;
+ const shift = srcExpBias - dstExpBias - aExp + 1;
const significand: src_rep_t = (aRep & srcSignificandMask) | srcMinNormal;
diff --git a/std/special/compiler_rt/truncXfYf2_test.zig b/std/special/compiler_rt/truncXfYf2_test.zig
index e4dae7b5b0d6bf68911f9ab317cf1b074be29939..c4bf2db7333b8e911fbe4d2d467dcfb9be0a7f16 100644
--- a/std/special/compiler_rt/truncXfYf2_test.zig
+++ b/std/special/compiler_rt/truncXfYf2_test.zig
@@ -11,54 +11,124 @@ fn test__truncsfhf2(a: u32, expected: u16) void {
}
test "truncsfhf2" {
- test__truncsfhf2(0x7fc00000, 0x7e00); // qNaN
- test__truncsfhf2(0x7fe00000, 0x7f00); // sNaN
+ test__truncsfhf2(0x7fc00000, 0x7e00); // qNaN
+ test__truncsfhf2(0x7fe00000, 0x7f00); // sNaN
- test__truncsfhf2(0, 0); // 0
- test__truncsfhf2(0x80000000, 0x8000); // -0
+ test__truncsfhf2(0, 0); // 0
+ test__truncsfhf2(0x80000000, 0x8000); // -0
- test__truncsfhf2(0x7f800000, 0x7c00); // inf
- test__truncsfhf2(0xff800000, 0xfc00); // -inf
+ test__truncsfhf2(0x7f800000, 0x7c00); // inf
+ test__truncsfhf2(0xff800000, 0xfc00); // -inf
- test__truncsfhf2(0x477ff000, 0x7c00); // 65520 -> inf
- test__truncsfhf2(0xc77ff000, 0xfc00); // -65520 -> -inf
+ test__truncsfhf2(0x477ff000, 0x7c00); // 65520 -> inf
+ test__truncsfhf2(0xc77ff000, 0xfc00); // -65520 -> -inf
- test__truncsfhf2(0x71cc3892, 0x7c00); // 0x1.987124876876324p+100 -> inf
- test__truncsfhf2(0xf1cc3892, 0xfc00); // -0x1.987124876876324p+100 -> -inf
+ test__truncsfhf2(0x71cc3892, 0x7c00); // 0x1.987124876876324p+100 -> inf
+ test__truncsfhf2(0xf1cc3892, 0xfc00); // -0x1.987124876876324p+100 -> -inf
- test__truncsfhf2(0x38800000, 0x0400); // normal (min), 2**-14
- test__truncsfhf2(0xb8800000, 0x8400); // normal (min), -2**-14
+ test__truncsfhf2(0x38800000, 0x0400); // normal (min), 2**-14
+ test__truncsfhf2(0xb8800000, 0x8400); // normal (min), -2**-14
- test__truncsfhf2(0x477fe000, 0x7bff); // normal (max), 65504
- test__truncsfhf2(0xc77fe000, 0xfbff); // normal (max), -65504
+ test__truncsfhf2(0x477fe000, 0x7bff); // normal (max), 65504
+ test__truncsfhf2(0xc77fe000, 0xfbff); // normal (max), -65504
- test__truncsfhf2(0x477fe100, 0x7bff); // normal, 65505 -> 65504
- test__truncsfhf2(0xc77fe100, 0xfbff); // normal, -65505 -> -65504
+ test__truncsfhf2(0x477fe100, 0x7bff); // normal, 65505 -> 65504
+ test__truncsfhf2(0xc77fe100, 0xfbff); // normal, -65505 -> -65504
- test__truncsfhf2(0x477fef00, 0x7bff); // normal, 65519 -> 65504
- test__truncsfhf2(0xc77fef00, 0xfbff); // normal, -65519 -> -65504
+ test__truncsfhf2(0x477fef00, 0x7bff); // normal, 65519 -> 65504
+ test__truncsfhf2(0xc77fef00, 0xfbff); // normal, -65519 -> -65504
- test__truncsfhf2(0x3f802000, 0x3c01); // normal, 1 + 2**-10
- test__truncsfhf2(0xbf802000, 0xbc01); // normal, -1 - 2**-10
+ test__truncsfhf2(0x3f802000, 0x3c01); // normal, 1 + 2**-10
+ test__truncsfhf2(0xbf802000, 0xbc01); // normal, -1 - 2**-10
- test__truncsfhf2(0x3eaaa000, 0x3555); // normal, approx. 1/3
- test__truncsfhf2(0xbeaaa000, 0xb555); // normal, approx. -1/3
+ test__truncsfhf2(0x3eaaa000, 0x3555); // normal, approx. 1/3
+ test__truncsfhf2(0xbeaaa000, 0xb555); // normal, approx. -1/3
- test__truncsfhf2(0x40490fdb, 0x4248); // normal, 3.1415926535
- test__truncsfhf2(0xc0490fdb, 0xc248); // normal, -3.1415926535
+ test__truncsfhf2(0x40490fdb, 0x4248); // normal, 3.1415926535
+ test__truncsfhf2(0xc0490fdb, 0xc248); // normal, -3.1415926535
- test__truncsfhf2(0x45cc3892, 0x6e62); // normal, 0x1.987124876876324p+12
+ test__truncsfhf2(0x45cc3892, 0x6e62); // normal, 0x1.987124876876324p+12
- test__truncsfhf2(0x3f800000, 0x3c00); // normal, 1
- test__truncsfhf2(0x38800000, 0x0400); // normal, 0x1.0p-14
+ test__truncsfhf2(0x3f800000, 0x3c00); // normal, 1
+ test__truncsfhf2(0x38800000, 0x0400); // normal, 0x1.0p-14
- test__truncsfhf2(0x33800000, 0x0001); // denormal (min), 2**-24
- test__truncsfhf2(0xb3800000, 0x8001); // denormal (min), -2**-24
+ test__truncsfhf2(0x33800000, 0x0001); // denormal (min), 2**-24
+ test__truncsfhf2(0xb3800000, 0x8001); // denormal (min), -2**-24
- test__truncsfhf2(0x387fc000, 0x03ff); // denormal (max), 2**-14 - 2**-24
- test__truncsfhf2(0xb87fc000, 0x83ff); // denormal (max), -2**-14 + 2**-24
+ test__truncsfhf2(0x387fc000, 0x03ff); // denormal (max), 2**-14 - 2**-24
+ test__truncsfhf2(0xb87fc000, 0x83ff); // denormal (max), -2**-14 + 2**-24
- test__truncsfhf2(0x35800000, 0x0010); // denormal, 0x1.0p-20
- test__truncsfhf2(0x33280000, 0x0001); // denormal, 0x1.5p-25 -> 0x1.0p-24
- test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero
+ test__truncsfhf2(0x35800000, 0x0010); // denormal, 0x1.0p-20
+ test__truncsfhf2(0x33280000, 0x0001); // denormal, 0x1.5p-25 -> 0x1.0p-24
+ test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero
+}
+
+const __trunctfsf2 = @import("truncXfYf2.zig").__trunctfsf2;
+
+fn test__trunctfsf2(a: f128, expected: u32) void {
+ const x = __trunctfsf2(a);
+
+ const rep = @bitCast(u32, x);
+ if (rep == expected) {
+ return;
+ }
+ // test other possible NaN representation(signal NaN)
+ else if (expected == 0x7fc00000) {
+ if ((rep & 0x7f800000) == 0x7f800000 and (rep & 0x7fffff) > 0) {
+ return;
+ }
+ }
+
+ @panic("__trunctfsf2 test failure");
+}
+
+test "trunctfsf2" {
+ // qnan
+ test__trunctfsf2(@bitCast(f128, u128(0x7fff800000000000 << 64)), 0x7fc00000);
+ // nan
+ test__trunctfsf2(@bitCast(f128, u128((0x7fff000000000000 | (0x810000000000 & 0xffffffffffff)) << 64)), 0x7fc08000);
+ // inf
+ test__trunctfsf2(@bitCast(f128, u128(0x7fff000000000000 << 64)), 0x7f800000);
+ // zero
+ test__trunctfsf2(0.0, 0x0);
+
+ test__trunctfsf2(0x1.23a2abb4a2ddee355f36789abcdep+5, 0x4211d156);
+ test__trunctfsf2(0x1.e3d3c45bd3abfd98b76a54cc321fp-9, 0x3b71e9e2);
+ test__trunctfsf2(0x1.234eebb5faa678f4488693abcdefp+4534, 0x7f800000);
+ test__trunctfsf2(0x1.edcba9bb8c76a5a43dd21f334634p-435, 0x0);
+}
+
+const __trunctfdf2 = @import("truncXfYf2.zig").__trunctfdf2;
+
+fn test__trunctfdf2(a: f128, expected: u64) void {
+ const x = __trunctfdf2(a);
+
+ const rep = @bitCast(u64, x);
+ if (rep == expected) {
+ return;
+ }
+ // test other possible NaN representation(signal NaN)
+ else if (expected == 0x7ff8000000000000) {
+ if ((rep & 0x7ff0000000000000) == 0x7ff0000000000000 and (rep & 0xfffffffffffff) > 0) {
+ return;
+ }
+ }
+
+ @panic("__trunctfsf2 test failure");
+}
+
+test "trunctfdf2" {
+ // qnan
+ test__trunctfdf2(@bitCast(f128, u128(0x7fff800000000000 << 64)), 0x7ff8000000000000);
+ // nan
+ test__trunctfdf2(@bitCast(f128, u128((0x7fff000000000000 | (0x810000000000 & 0xffffffffffff)) << 64)), 0x7ff8100000000000);
+ // inf
+ test__trunctfdf2(@bitCast(f128, u128(0x7fff000000000000 << 64)), 0x7ff0000000000000);
+ // zero
+ test__trunctfdf2(0.0, 0x0);
+
+ test__trunctfdf2(0x1.af23456789bbaaab347645365cdep+5, 0x404af23456789bbb);
+ test__trunctfdf2(0x1.dedafcff354b6ae9758763545432p-9, 0x3f6dedafcff354b7);
+ test__trunctfdf2(0x1.2f34dd5f437e849b4baab754cdefp+4534, 0x7ff0000000000000);
+ test__trunctfdf2(0x1.edcbff8ad76ab5bf46463233214fp-435, 0x24cedcbff8ad76ab);
}
--
2.54.0
From c32b2e45efb0d0ced14c76d5221b2db636e40246 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 28 Jun 2018 20:40:11 +1200
Subject: [PATCH 61/82] compiler_rt: Add floatuntisf
---
std/special/compiler_rt/floatuntisf.zig | 59 ++++++++++++++++
std/special/compiler_rt/floatuntisf_test.zig | 72 ++++++++++++++++++++
2 files changed, 131 insertions(+)
create mode 100644 std/special/compiler_rt/floatuntisf.zig
create mode 100644 std/special/compiler_rt/floatuntisf_test.zig
diff --git a/std/special/compiler_rt/floatuntisf.zig b/std/special/compiler_rt/floatuntisf.zig
new file mode 100644
index 0000000000000000000000000000000000000000..e83affd87c99d2202a28171a394529d585c2dcf4
--- /dev/null
+++ b/std/special/compiler_rt/floatuntisf.zig
@@ -0,0 +1,59 @@
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+const FLT_MANT_DIG = 24;
+
+pub extern fn __floatuntisf(arg: u128) f32 {
+ @setRuntimeSafety(is_test);
+
+ if (arg == 0)
+ return 0.0;
+
+ var a = arg;
+ const N: u32 = @sizeOf(u128) * 8;
+ const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits
+ var e: i32 = sd -% 1; // exponent
+ if (sd > FLT_MANT_DIG) {
+ // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
+ // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
+ // 12345678901234567890123456
+ // 1 = msb 1 bit
+ // P = bit FLT_MANT_DIG-1 bits to the right of 1
+ // Q = bit FLT_MANT_DIG bits to the right of 1
+ // R = "or" of all bits to the right of Q
+ switch (sd) {
+ FLT_MANT_DIG + 1 => {
+ a <<= 1;
+ },
+ FLT_MANT_DIG + 2 => {},
+ else => {
+ const shift_amt = @bitCast(i32, N +% (FLT_MANT_DIG + 2)) -% sd;
+ const shift_amt_u7 = @intCast(u7, shift_amt);
+ a = (a >> @intCast(u7, sd -% (FLT_MANT_DIG + 2))) |
+ @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0);
+ },
+ }
+ // finish
+ a |= @boolToInt((a & 4) != 0); // Or P into R
+ a +%= 1; // round - this step may add a significant bit
+ a >>= 2; // dump Q and R
+ // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits
+ if ((a & (u128(1) << FLT_MANT_DIG)) != 0) {
+ a >>= 1;
+ e +%= 1;
+ }
+ // a is now rounded to FLT_MANT_DIG bits
+ } else {
+ a <<= @intCast(u7, FLT_MANT_DIG -% sd);
+ // a is now rounded to FLT_MANT_DIG bits
+ }
+
+ const high = @bitCast(u32, (e +% 127) << 23); // exponent
+ const low = @truncate(u32, a) & 0x007fffff; // mantissa
+
+ return @bitCast(f32, high | low);
+}
+
+test "import floatuntisf" {
+ _ = @import("floatuntisf_test.zig");
+}
diff --git a/std/special/compiler_rt/floatuntisf_test.zig b/std/special/compiler_rt/floatuntisf_test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..7f84c1f963066697745a8645fd96d37c51544791
--- /dev/null
+++ b/std/special/compiler_rt/floatuntisf_test.zig
@@ -0,0 +1,72 @@
+const __floatuntisf = @import("floatuntisf.zig").__floatuntisf;
+const assert = @import("std").debug.assert;
+
+fn test__floatuntisf(a: u128, expected: f32) void {
+ const x = __floatuntisf(a);
+ assert(x == expected);
+}
+
+test "floatuntisf" {
+ test__floatuntisf(0, 0.0);
+
+ test__floatuntisf(1, 1.0);
+ test__floatuntisf(2, 2.0);
+ test__floatuntisf(20, 20.0);
+
+ test__floatuntisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62);
+ test__floatuntisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62);
+
+ test__floatuntisf(make_ti(0x8000008000000000, 0), 0x1.000001p+127);
+ test__floatuntisf(make_ti(0x8000000000000800, 0), 0x1.0p+127);
+ test__floatuntisf(make_ti(0x8000010000000000, 0), 0x1.000002p+127);
+
+ test__floatuntisf(make_ti(0x8000000000000000, 0), 0x1.000000p+127);
+
+ test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
+
+ test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBA8p+50);
+ test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBACp+50);
+
+ test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBBp+50);
+
+ test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCB98p+50);
+ test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50);
+ test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB9p+50);
+
+ test__floatuntisf(0xFFFFFFFFFFFFFFFE, 0x1p+64);
+ test__floatuntisf(0xFFFFFFFFFFFFFFFF, 0x1p+64);
+
+ test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
+
+ test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBAp+50);
+ test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBAp+50);
+ test__floatuntisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50);
+ test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBCp+50);
+ test__floatuntisf(0x0007FB72E8000001, 0x1.FEDCBAp+50);
+
+ test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCBAp+50);
+ test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCBAp+50);
+ test__floatuntisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50);
+ test__floatuntisf(0x0007FB72E4000001, 0x1.FEDCBAp+50);
+ test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB8p+50);
+
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCB90000000000001), 0x1.FEDCBAp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBA0000000000000), 0x1.FEDCBAp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBAFFFFFFFFFFFFF), 0x1.FEDCBAp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBB0000000000000), 0x1.FEDCBCp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBB0000000000001), 0x1.FEDCBCp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBBFFFFFFFFFFFFF), 0x1.FEDCBCp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBC0000000000000), 0x1.FEDCBCp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBC0000000000001), 0x1.FEDCBCp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBD0000000000000), 0x1.FEDCBCp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBD0000000000001), 0x1.FEDCBEp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBDFFFFFFFFFFFFF), 0x1.FEDCBEp+76);
+ test__floatuntisf(make_ti(0x0000000000001FED, 0xCBE0000000000000), 0x1.FEDCBEp+76);
+}
+
+fn make_ti(high: u64, low: u64) u128 {
+ var result: u128 = high;
+ result <<= 64;
+ result |= low;
+ return result;
+}
--
2.54.0
From 61ebfe6603c8fef26008683f96b62dab2c502429 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 28 Jun 2018 21:12:47 +1200
Subject: [PATCH 62/82] compiler_rt: Add floatunditf and floatunsitf
---
std/special/compiler_rt/floatunditf.zig | 28 +++++++++++++++++
std/special/compiler_rt/floatunditf_test.zig | 33 ++++++++++++++++++++
std/special/compiler_rt/floatunsitf.zig | 29 +++++++++++++++++
std/special/compiler_rt/floatunsitf_test.zig | 29 +++++++++++++++++
std/special/compiler_rt/truncXfYf2.zig | 4 +--
5 files changed, 121 insertions(+), 2 deletions(-)
create mode 100644 std/special/compiler_rt/floatunditf.zig
create mode 100644 std/special/compiler_rt/floatunditf_test.zig
create mode 100644 std/special/compiler_rt/floatunsitf.zig
create mode 100644 std/special/compiler_rt/floatunsitf_test.zig
diff --git a/std/special/compiler_rt/floatunditf.zig b/std/special/compiler_rt/floatunditf.zig
new file mode 100644
index 0000000000000000000000000000000000000000..b8b097581f997714cc61b3ac7dce390f3e16ec0c
--- /dev/null
+++ b/std/special/compiler_rt/floatunditf.zig
@@ -0,0 +1,28 @@
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+const std = @import("../../index.zig");
+
+pub extern fn __floatunditf(a: u128) f128 {
+ @setRuntimeSafety(is_test);
+
+ if (a == 0) {
+ return 0;
+ }
+
+ const mantissa_bits = std.math.floatMantissaBits(f128);
+ const exponent_bits = std.math.floatExponentBits(f128);
+ const exponent_bias = (1 << (exponent_bits - 1)) - 1;
+ const implicit_bit = 1 << mantissa_bits;
+
+ const exp = (u128.bit_count - 1) - @clz(a);
+ const shift = mantissa_bits - @intCast(u7, exp);
+
+ var result: u128 = (a << shift) ^ implicit_bit;
+ result += (@intCast(u128, exp) + exponent_bias) << mantissa_bits;
+
+ return @bitCast(f128, result);
+}
+
+test "import floatunditf" {
+ _ = @import("floatunditf_test.zig");
+}
diff --git a/std/special/compiler_rt/floatunditf_test.zig b/std/special/compiler_rt/floatunditf_test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..8533c750708bfc91fe4b856e2c8ba65d1eef69d3
--- /dev/null
+++ b/std/special/compiler_rt/floatunditf_test.zig
@@ -0,0 +1,33 @@
+const __floatunditf = @import("floatunditf.zig").__floatunditf;
+const assert = @import("std").debug.assert;
+
+fn test__floatunditf(a: u128, expected_hi: u64, expected_lo: u64) void {
+ const x = __floatunditf(a);
+
+ const x_repr = @bitCast(u128, x);
+ const x_hi = @intCast(u64, x_repr >> 64);
+ const x_lo = @truncate(u64, x_repr);
+
+ if (x_hi == expected_hi and x_lo == expected_lo) {
+ return;
+ }
+ // nan repr
+ else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) {
+ if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) {
+ return;
+ }
+ }
+
+ @panic("__floatunditf test failure");
+}
+
+test "floatunditf" {
+ test__floatunditf(0xffffffffffffffff, 0x403effffffffffff, 0xfffe000000000000);
+ test__floatunditf(0xfffffffffffffffe, 0x403effffffffffff, 0xfffc000000000000);
+ test__floatunditf(0x8000000000000000, 0x403e000000000000, 0x0);
+ test__floatunditf(0x7fffffffffffffff, 0x403dffffffffffff, 0xfffc000000000000);
+ test__floatunditf(0x123456789abcdef1, 0x403b23456789abcd, 0xef10000000000000);
+ test__floatunditf(0x2, 0x4000000000000000, 0x0);
+ test__floatunditf(0x1, 0x3fff000000000000, 0x0);
+ test__floatunditf(0x0, 0x0, 0x0);
+}
diff --git a/std/special/compiler_rt/floatunsitf.zig b/std/special/compiler_rt/floatunsitf.zig
new file mode 100644
index 0000000000000000000000000000000000000000..dfee826b32ac568d557cb60df17693be17c5b4fa
--- /dev/null
+++ b/std/special/compiler_rt/floatunsitf.zig
@@ -0,0 +1,29 @@
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+const std = @import("../../index.zig");
+
+pub extern fn __floatunsitf(a: u64) f128 {
+ @setRuntimeSafety(is_test);
+
+ if (a == 0) {
+ return 0;
+ }
+
+ const mantissa_bits = std.math.floatMantissaBits(f128);
+ const exponent_bits = std.math.floatExponentBits(f128);
+ const exponent_bias = (1 << (exponent_bits - 1)) - 1;
+ const implicit_bit = 1 << mantissa_bits;
+
+ const exp = (u64.bit_count - 1) - @clz(a);
+ const shift = mantissa_bits - @intCast(u7, exp);
+
+ // TODO: @bitCast alignment error
+ var result align(16) = (@intCast(u128, a) << shift) ^ implicit_bit;
+ result += (@intCast(u128, exp) + exponent_bias) << mantissa_bits;
+
+ return @bitCast(f128, result);
+}
+
+test "import floatunsitf" {
+ _ = @import("floatunsitf_test.zig");
+}
diff --git a/std/special/compiler_rt/floatunsitf_test.zig b/std/special/compiler_rt/floatunsitf_test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..06f54cde037e7cda6a1b439b822b7de915535874
--- /dev/null
+++ b/std/special/compiler_rt/floatunsitf_test.zig
@@ -0,0 +1,29 @@
+const __floatunsitf = @import("floatunsitf.zig").__floatunsitf;
+const assert = @import("std").debug.assert;
+
+fn test__floatunsitf(a: u64, expected_hi: u64, expected_lo: u64) void {
+ const x = __floatunsitf(a);
+
+ const x_repr = @bitCast(u128, x);
+ const x_hi = @intCast(u64, x_repr >> 64);
+ const x_lo = @truncate(u64, x_repr);
+
+ if (x_hi == expected_hi and x_lo == expected_lo) {
+ return;
+ }
+ // nan repr
+ else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) {
+ if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) {
+ return;
+ }
+ }
+
+ @panic("__floatunsitf test failure");
+}
+
+test "floatunsitf" {
+ test__floatunsitf(0x7fffffff, 0x401dfffffffc0000, 0x0);
+ test__floatunsitf(0, 0x0, 0x0);
+ test__floatunsitf(0xffffffff, 0x401efffffffe0000, 0x0);
+ test__floatunsitf(0x12345678, 0x401b234567800000, 0x0);
+}
diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig
index 04b815e86b7da35bef764b4b6fc63aa634355e4e..5cb2f615688f2c1aa192b10358bf46a6a646f2a0 100644
--- a/std/special/compiler_rt/truncXfYf2.zig
+++ b/std/special/compiler_rt/truncXfYf2.zig
@@ -85,8 +85,8 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t
// a underflows on conversion to the destination type or is an exact
// zero. The result may be a denormal or zero. Extract the exponent
// to get the shift amount for the denormalization.
- const aExp = aAbs >> srcSigBits;
- const shift = srcExpBias - dstExpBias - aExp + 1;
+ const aExp = @intCast(u32, aAbs >> srcSigBits);
+ const shift = @intCast(u32, srcExpBias - dstExpBias - aExp + 1);
const significand: src_rep_t = (aRep & srcSignificandMask) | srcMinNormal;
--
2.54.0
From cb7bdc2da1b2d7a3e78b272928ced77ccdd12148 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 28 Jun 2018 21:37:58 +1200
Subject: [PATCH 63/82] compiler_rt: Add floatuntitf
---
std/special/compiler_rt/floatuntitf.zig | 60 ++++++++++++
std/special/compiler_rt/floatuntitf_test.zig | 99 ++++++++++++++++++++
2 files changed, 159 insertions(+)
create mode 100644 std/special/compiler_rt/floatuntitf.zig
create mode 100644 std/special/compiler_rt/floatuntitf_test.zig
diff --git a/std/special/compiler_rt/floatuntitf.zig b/std/special/compiler_rt/floatuntitf.zig
new file mode 100644
index 0000000000000000000000000000000000000000..a64dd96a343c0a2f6deca1b6870a0c0558148b0f
--- /dev/null
+++ b/std/special/compiler_rt/floatuntitf.zig
@@ -0,0 +1,60 @@
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+const LDBL_MANT_DIG = 113;
+
+pub extern fn __floatuntitf(arg: u128) f128 {
+ @setRuntimeSafety(is_test);
+
+ if (arg == 0)
+ return 0.0;
+
+ var a = arg;
+ const N: u32 = @sizeOf(u128) * 8;
+ const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits
+ var e: i32 = sd -% 1; // exponent
+ if (sd > LDBL_MANT_DIG) {
+ // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
+ // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
+ // 12345678901234567890123456
+ // 1 = msb 1 bit
+ // P = bit LDBL_MANT_DIG-1 bits to the right of 1
+ // Q = bit LDBL_MANT_DIG bits to the right of 1
+ // R = "or" of all bits to the right of Q
+ switch (sd) {
+ LDBL_MANT_DIG + 1 => {
+ a <<= 1;
+ },
+ LDBL_MANT_DIG + 2 => {},
+ else => {
+ const shift_amt = @bitCast(i32, N +% (LDBL_MANT_DIG + 2)) -% sd;
+ const shift_amt_u7 = @intCast(u7, shift_amt);
+ a = (a >> @intCast(u7, sd -% (LDBL_MANT_DIG + 2))) |
+ @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0);
+ },
+ }
+ // finish
+ a |= @boolToInt((a & 4) != 0); // Or P into R
+ a +%= 1; // round - this step may add a significant bit
+ a >>= 2; // dump Q and R
+ // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits
+ if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) {
+ a >>= 1;
+ e +%= 1;
+ }
+ // a is now rounded to LDBL_MANT_DIG bits
+ } else {
+ a <<= @intCast(u7, LDBL_MANT_DIG -% sd);
+ // a is now rounded to LDBL_MANT_DIG bits
+ }
+
+ const high: u128 = (@intCast(u64, (e +% 16383)) << 48) | // exponent
+ (@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high
+ const low = @truncate(u64, a); // mantissa-low
+
+ return @bitCast(f128, low | (high << 64));
+}
+
+test "import floatuntitf" {
+ _ = @import("floatuntitf_test.zig");
+}
diff --git a/std/special/compiler_rt/floatuntitf_test.zig b/std/special/compiler_rt/floatuntitf_test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..8e67fee108519a23d0705346d09afaaf41f8695b
--- /dev/null
+++ b/std/special/compiler_rt/floatuntitf_test.zig
@@ -0,0 +1,99 @@
+const __floatuntitf = @import("floatuntitf.zig").__floatuntitf;
+const assert = @import("std").debug.assert;
+
+fn test__floatuntitf(a: u128, expected: f128) void {
+ const x = __floatuntitf(a);
+ assert(x == expected);
+}
+
+test "floatuntitf" {
+ test__floatuntitf(0, 0.0);
+
+ test__floatuntitf(1, 1.0);
+ test__floatuntitf(2, 2.0);
+ test__floatuntitf(20, 20.0);
+
+ test__floatuntitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62);
+ test__floatuntitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62);
+ test__floatuntitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62);
+ test__floatuntitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62);
+ test__floatuntitf(0x7FFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFEp+59);
+ test__floatuntitf(0xFFFFFFFFFFFFFFFE, 0xF.FFFFFFFFFFFFFFEp+60);
+ test__floatuntitf(0xFFFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFFp+60);
+
+ test__floatuntitf(0x8000008000000000, 0x8.000008p+60);
+ test__floatuntitf(0x8000000000000800, 0x8.0000000000008p+60);
+ test__floatuntitf(0x8000010000000000, 0x8.00001p+60);
+ test__floatuntitf(0x8000000000001000, 0x8.000000000001p+60);
+
+ test__floatuntitf(0x8000000000000000, 0x8p+60);
+ test__floatuntitf(0x8000000000000001, 0x8.000000000000001p+60);
+
+ test__floatuntitf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
+
+ test__floatuntitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50);
+ test__floatuntitf(0x0007FB72EB000000, 0x1.FEDCBACp+50);
+ test__floatuntitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50);
+ test__floatuntitf(0x0007FB72EC000000, 0x1.FEDCBBp+50);
+ test__floatuntitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50);
+
+ test__floatuntitf(0x0007FB72E6000000, 0x1.FEDCB98p+50);
+ test__floatuntitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50);
+ test__floatuntitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50);
+ test__floatuntitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50);
+ test__floatuntitf(0x0007FB72E4000000, 0x1.FEDCB9p+50);
+
+ test__floatuntitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57);
+ test__floatuntitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57);
+ test__floatuntitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57);
+ test__floatuntitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57);
+ test__floatuntitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57);
+ test__floatuntitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57);
+ test__floatuntitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57);
+ test__floatuntitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57);
+ test__floatuntitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57);
+ test__floatuntitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57);
+ test__floatuntitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57);
+ test__floatuntitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57);
+ test__floatuntitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57);
+ test__floatuntitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57);
+ test__floatuntitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57);
+
+ test__floatuntitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121);
+ test__floatuntitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121);
+
+ test__floatuntitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63);
+
+ test__floatuntitf(make_ti(0xFFFFFFFFFFFFFFFF, 0x0000000000000000), 0x1.FFFFFFFFFFFFFFFEp+127);
+ test__floatuntitf(make_ti(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), 0x1.0000000000000000p+128);
+
+ test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124);
+ test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124);
+ test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124);
+ test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124);
+ test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124);
+ test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124);
+ test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124);
+ test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124);
+ test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124);
+}
+
+fn make_ti(high: u64, low: u64) u128 {
+ var result: u128 = high;
+ result <<= 64;
+ result |= low;
+ return result;
+}
--
2.54.0
From e19fc4a0a3899ce1a3cb476617b9c19b605877bb Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 28 Jun 2018 21:41:35 +1200
Subject: [PATCH 64/82] compiler_rt: Add missing exports
---
std/special/compiler_rt/index.zig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index fda8d9d8af0faba5cd0ac2da7a8ce2f4e93e47dd..0eca064ddac56c2448b50df7c320144c6b8f1cca 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -21,12 +21,20 @@ comptime {
@export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage);
+ @export("__floatunditf", @import("floatunditf.zig").__floatunditf, linkage);
+ @export("__floatunsitf", @import("floatunsitf.zig").__floatunsitf, linkage);
+
+ @export("__floatuntitf", @import("floatuntitf.zig").__floatuntitf, linkage);
@export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage);
+ @export("__floatuntisf", @import("floatuntisf.zig").__floatuntisf, linkage);
+
@export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage);
@export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage);
@export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage);
@export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage);
+ @export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage);
+ @export("__trunctfsf2", @import("truncXfYf2.zig").__trunctfsf2, linkage);
@export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage);
@export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage);
--
2.54.0
From 53fef94b9fb2b04d208a0671aa58e90f93f412cf Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 28 Jun 2018 23:14:40 +1200
Subject: [PATCH 65/82] compiler_rt: Add missing install targets
---
CMakeLists.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4838aeb7970ef65e56af40c1a1ed663c19f7031e..9957a740cf3d4b16f258b3cb1a10e6b0aa5e00ee 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -585,7 +585,11 @@ set(ZIG_STD_FILES
"special/compiler_rt/fixunstfdi.zig"
"special/compiler_rt/fixunstfsi.zig"
"special/compiler_rt/fixunstfti.zig"
+ "special/compiler_rt/floatunditf.zig"
+ "special/compiler_rt/floatunsitf.zig"
"special/compiler_rt/floatuntidf.zig"
+ "special/compiler_rt/floatuntisf.zig"
+ "special/compiler_rt/floatuntitf.zig"
"special/compiler_rt/muloti4.zig"
"special/compiler_rt/index.zig"
"special/compiler_rt/truncXfYf2.zig"
--
2.54.0
From 814a34f263cbfeabbf7a898b3b70fa781baaccac Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sat, 30 Jun 2018 19:57:17 +1200
Subject: [PATCH 66/82] compiler_rt: Add floattitf/floattidf/floattisf
---
std/special/compiler_rt/floattidf.zig | 69 ++++++++++++++++
std/special/compiler_rt/floattidf_test.zig | 84 +++++++++++++++++++
std/special/compiler_rt/floattisf.zig | 69 ++++++++++++++++
std/special/compiler_rt/floattisf_test.zig | 60 ++++++++++++++
std/special/compiler_rt/floattitf.zig | 69 ++++++++++++++++
std/special/compiler_rt/floattitf_test.zig | 96 ++++++++++++++++++++++
std/special/compiler_rt/index.zig | 4 +
7 files changed, 451 insertions(+)
create mode 100644 std/special/compiler_rt/floattidf.zig
create mode 100644 std/special/compiler_rt/floattidf_test.zig
create mode 100644 std/special/compiler_rt/floattisf.zig
create mode 100644 std/special/compiler_rt/floattisf_test.zig
create mode 100644 std/special/compiler_rt/floattitf.zig
create mode 100644 std/special/compiler_rt/floattitf_test.zig
diff --git a/std/special/compiler_rt/floattidf.zig b/std/special/compiler_rt/floattidf.zig
new file mode 100644
index 0000000000000000000000000000000000000000..8a627fdc04fdfd739868563a40efabd8bab1f457
--- /dev/null
+++ b/std/special/compiler_rt/floattidf.zig
@@ -0,0 +1,69 @@
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+const DBL_MANT_DIG = 53;
+
+pub extern fn __floattidf(arg: i128) f64 {
+ @setRuntimeSafety(is_test);
+
+ if (arg == 0)
+ return 0.0;
+
+ var ai = arg;
+ const N: u32 = 128;
+ const si = ai >> @intCast(u7, (N - 1));
+ ai = ((ai ^ si) -% si);
+ var a = @bitCast(u128, ai);
+
+ const sd = @bitCast(i32, N - @clz(a)); // number of significant digits
+ var e: i32 = sd - 1; // exponent
+ if (sd > DBL_MANT_DIG) {
+ // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
+ // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
+ // 12345678901234567890123456
+ // 1 = msb 1 bit
+ // P = bit DBL_MANT_DIG-1 bits to the right of 1
+ // Q = bit DBL_MANT_DIG bits to the right of 1
+ // R = "or" of all bits to the right of Q
+ switch (sd) {
+ DBL_MANT_DIG + 1 => {
+ a <<= 1;
+ },
+ DBL_MANT_DIG + 2 => {},
+ else => {
+ const shift1_amt = @intCast(i32, sd - (DBL_MANT_DIG + 2));
+ const shift1_amt_u7 = @intCast(u7, shift1_amt);
+
+ const shift2_amt = @intCast(i32, N + (DBL_MANT_DIG + 2)) - sd;
+ const shift2_amt_u7 = @intCast(u7, shift2_amt);
+
+ a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, @maxValue(u128)) >> shift2_amt_u7)) != 0);
+ },
+ }
+ // finish
+ a |= @boolToInt((a & 4) != 0); // Or P into R
+ a +%= 1; // round - this step may add a significant bit
+ a >>= 2; // dump Q and R
+ // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits
+ if ((a & (u128(1) << DBL_MANT_DIG)) != 0) {
+ a >>= 1;
+ e +%= 1;
+ }
+ // a is now rounded to DBL_MANT_DIG bits
+ } else {
+ a <<= @intCast(u7, DBL_MANT_DIG - sd);
+ // a is now rounded to DBL_MANT_DIG bits
+ }
+
+ const s = @bitCast(u128, arg) >> (128 - 32);
+ const high: u64 = (@intCast(u64, s) & 0x80000000) | // sign
+ (@intCast(u32, (e + 1023)) << 20) | // exponent
+ (@truncate(u32, a >> 32) & 0x000fffff); // mantissa-high
+ const low: u64 = @truncate(u32, a); // mantissa-low
+
+ return @bitCast(f64, low | (high << 32));
+}
+
+test "import floattidf" {
+ _ = @import("floattidf_test.zig");
+}
diff --git a/std/special/compiler_rt/floattidf_test.zig b/std/special/compiler_rt/floattidf_test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..25dc595052307004dcb8da0310979e1df03337e0
--- /dev/null
+++ b/std/special/compiler_rt/floattidf_test.zig
@@ -0,0 +1,84 @@
+const __floattidf = @import("floattidf.zig").__floattidf;
+const assert = @import("std").debug.assert;
+
+fn test__floattidf(a: i128, expected: f64) void {
+ const x = __floattidf(a);
+ assert(x == expected);
+}
+
+test "floattidf" {
+ test__floattidf(0, 0.0);
+
+ test__floattidf(1, 1.0);
+ test__floattidf(2, 2.0);
+ test__floattidf(20, 20.0);
+ test__floattidf(-1, -1.0);
+ test__floattidf(-2, -2.0);
+ test__floattidf(-20, -20.0);
+
+ test__floattidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62);
+ test__floattidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62);
+ test__floattidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62);
+ test__floattidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62);
+
+ test__floattidf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126);
+ test__floattidf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126);
+ test__floattidf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126);
+ test__floattidf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126);
+
+ test__floattidf(make_ti(0x8000000000000000, 0), -0x1.000000p+127);
+ test__floattidf(make_ti(0x8000000000000001, 0), -0x1.000000p+127);
+
+ test__floattidf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
+
+ test__floattidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50);
+ test__floattidf(0x0007FB72EB000000, 0x1.FEDCBACp+50);
+ test__floattidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50);
+ test__floattidf(0x0007FB72EC000000, 0x1.FEDCBBp+50);
+ test__floattidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50);
+
+ test__floattidf(0x0007FB72E6000000, 0x1.FEDCB98p+50);
+ test__floattidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50);
+ test__floattidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50);
+ test__floattidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50);
+ test__floattidf(0x0007FB72E4000000, 0x1.FEDCB9p+50);
+
+ test__floattidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57);
+ test__floattidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57);
+ test__floattidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57);
+ test__floattidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57);
+ test__floattidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57);
+ test__floattidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57);
+
+ test__floattidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121);
+ test__floattidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121);
+ test__floattidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121);
+ test__floattidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121);
+ test__floattidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121);
+ test__floattidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121);
+}
+
+fn make_ti(high: u64, low: u64) i128 {
+ var result: u128 = high;
+ result <<= 64;
+ result |= low;
+ return @bitCast(i128, result);
+}
diff --git a/std/special/compiler_rt/floattisf.zig b/std/special/compiler_rt/floattisf.zig
new file mode 100644
index 0000000000000000000000000000000000000000..cbdd30418fd25778fb37fb6633c26b9a13f714e4
--- /dev/null
+++ b/std/special/compiler_rt/floattisf.zig
@@ -0,0 +1,69 @@
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+const FLT_MANT_DIG = 24;
+
+pub extern fn __floattisf(arg: i128) f32 {
+ @setRuntimeSafety(is_test);
+
+ if (arg == 0)
+ return 0.0;
+
+ var ai = arg;
+ const N: u32 = 128;
+ const si = ai >> @intCast(u7, (N - 1));
+ ai = ((ai ^ si) -% si);
+ var a = @bitCast(u128, ai);
+
+ const sd = @bitCast(i32, N - @clz(a)); // number of significant digits
+ var e: i32 = sd - 1; // exponent
+
+ if (sd > FLT_MANT_DIG) {
+ // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
+ // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
+ // 12345678901234567890123456
+ // 1 = msb 1 bit
+ // P = bit FLT_MANT_DIG-1 bits to the right of 1
+ // Q = bit FLT_MANT_DIG bits to the right of 1
+ // R = "or" of all bits to the right of Q
+ switch (sd) {
+ FLT_MANT_DIG + 1 => {
+ a <<= 1;
+ },
+ FLT_MANT_DIG + 2 => {},
+ else => {
+ const shift1_amt = @intCast(i32, sd - (FLT_MANT_DIG + 2));
+ const shift1_amt_u7 = @intCast(u7, shift1_amt);
+
+ const shift2_amt = @intCast(i32, N + (FLT_MANT_DIG + 2)) - sd;
+ const shift2_amt_u7 = @intCast(u7, shift2_amt);
+
+ a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, @maxValue(u128)) >> shift2_amt_u7)) != 0);
+ },
+ }
+ // finish
+ a |= @boolToInt((a & 4) != 0); // Or P into R
+ a +%= 1; // round - this step may add a significant bit
+ a >>= 2; // dump Q and R
+ // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits
+ if ((a & (u128(1) << FLT_MANT_DIG)) != 0) {
+ a >>= 1;
+ e +%= 1;
+ }
+ // a is now rounded to FLT_MANT_DIG bits
+ } else {
+ a <<= @intCast(u7, FLT_MANT_DIG - sd);
+ // a is now rounded to FLT_MANT_DIG bits
+ }
+
+ const s = @bitCast(u128, arg) >> (128 - 32);
+ const r = (@intCast(u32, s) & 0x80000000) | // sign
+ (@intCast(u32, (e + 127)) << 23) | // exponent
+ (@truncate(u32, a) & 0x007fffff); // mantissa-high
+
+ return @bitCast(f32, r);
+}
+
+test "import floattisf" {
+ _ = @import("floattisf_test.zig");
+}
diff --git a/std/special/compiler_rt/floattisf_test.zig b/std/special/compiler_rt/floattisf_test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..ecb8eac60a8ac6b9c66caeba34d5f34e05e3153d
--- /dev/null
+++ b/std/special/compiler_rt/floattisf_test.zig
@@ -0,0 +1,60 @@
+const __floattisf = @import("floattisf.zig").__floattisf;
+const assert = @import("std").debug.assert;
+
+fn test__floattisf(a: i128, expected: f32) void {
+ const x = __floattisf(a);
+ assert(x == expected);
+}
+
+test "floattisf" {
+ test__floattisf(0, 0.0);
+
+ test__floattisf(1, 1.0);
+ test__floattisf(2, 2.0);
+ test__floattisf(-1, -1.0);
+ test__floattisf(-2, -2.0);
+
+ test__floattisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62);
+ test__floattisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62);
+
+ test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000008000000000), -0x1.FFFFFEp+62);
+ test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000010000000000), -0x1.FFFFFCp+62);
+
+ test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000000), -0x1.000000p+63);
+ test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000001), -0x1.000000p+63);
+
+ test__floattisf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
+
+ test__floattisf(0x0007FB72EA000000, 0x1.FEDCBAp+50);
+ test__floattisf(0x0007FB72EB000000, 0x1.FEDCBAp+50);
+ test__floattisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50);
+ test__floattisf(0x0007FB72EC000000, 0x1.FEDCBCp+50);
+ test__floattisf(0x0007FB72E8000001, 0x1.FEDCBAp+50);
+
+ test__floattisf(0x0007FB72E6000000, 0x1.FEDCBAp+50);
+ test__floattisf(0x0007FB72E7000000, 0x1.FEDCBAp+50);
+ test__floattisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50);
+ test__floattisf(0x0007FB72E4000001, 0x1.FEDCBAp+50);
+ test__floattisf(0x0007FB72E4000000, 0x1.FEDCB8p+50);
+
+ test__floattisf(make_ti(0x0007FB72E8000000, 0), 0x1.FEDCBAp+114);
+
+ test__floattisf(make_ti(0x0007FB72EA000000, 0), 0x1.FEDCBAp+114);
+ test__floattisf(make_ti(0x0007FB72EB000000, 0), 0x1.FEDCBAp+114);
+ test__floattisf(make_ti(0x0007FB72EBFFFFFF, 0), 0x1.FEDCBAp+114);
+ test__floattisf(make_ti(0x0007FB72EC000000, 0), 0x1.FEDCBCp+114);
+ test__floattisf(make_ti(0x0007FB72E8000001, 0), 0x1.FEDCBAp+114);
+
+ test__floattisf(make_ti(0x0007FB72E6000000, 0), 0x1.FEDCBAp+114);
+ test__floattisf(make_ti(0x0007FB72E7000000, 0), 0x1.FEDCBAp+114);
+ test__floattisf(make_ti(0x0007FB72E7FFFFFF, 0), 0x1.FEDCBAp+114);
+ test__floattisf(make_ti(0x0007FB72E4000001, 0), 0x1.FEDCBAp+114);
+ test__floattisf(make_ti(0x0007FB72E4000000, 0), 0x1.FEDCB8p+114);
+}
+
+fn make_ti(high: u64, low: u64) i128 {
+ var result: u128 = high;
+ result <<= 64;
+ result |= low;
+ return @bitCast(i128, result);
+}
diff --git a/std/special/compiler_rt/floattitf.zig b/std/special/compiler_rt/floattitf.zig
new file mode 100644
index 0000000000000000000000000000000000000000..531634e4949a01b2d26d2704c6ecd06c96f6bc7d
--- /dev/null
+++ b/std/special/compiler_rt/floattitf.zig
@@ -0,0 +1,69 @@
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+const LDBL_MANT_DIG = 113;
+
+pub extern fn __floattitf(arg: i128) f128 {
+ @setRuntimeSafety(is_test);
+
+ if (arg == 0)
+ return 0.0;
+
+ var ai = arg;
+ const N: u32 = 128;
+ const si = ai >> @intCast(u7, (N - 1));
+ ai = ((ai ^ si) -% si);
+ var a = @bitCast(u128, ai);
+
+ const sd = @bitCast(i32, N - @clz(a)); // number of significant digits
+ var e: i32 = sd - 1; // exponent
+ if (sd > LDBL_MANT_DIG) {
+ // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
+ // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
+ // 12345678901234567890123456
+ // 1 = msb 1 bit
+ // P = bit LDBL_MANT_DIG-1 bits to the right of 1
+ // Q = bit LDBL_MANT_DIG bits to the right of 1
+ // R = "or" of all bits to the right of Q
+ switch (sd) {
+ LDBL_MANT_DIG + 1 => {
+ a <<= 1;
+ },
+ LDBL_MANT_DIG + 2 => {},
+ else => {
+ const shift1_amt = @intCast(i32, sd - (LDBL_MANT_DIG + 2));
+ const shift1_amt_u7 = @intCast(u7, shift1_amt);
+
+ const shift2_amt = @intCast(i32, N + (LDBL_MANT_DIG + 2)) - sd;
+ const shift2_amt_u7 = @intCast(u7, shift2_amt);
+
+ a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, @maxValue(u128)) >> shift2_amt_u7)) != 0);
+ },
+ }
+ // finish
+ a |= @boolToInt((a & 4) != 0); // Or P into R
+ a +%= 1; // round - this step may add a significant bit
+ a >>= 2; // dump Q and R
+ // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits
+ if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) {
+ a >>= 1;
+ e +%= 1;
+ }
+ // a is now rounded to LDBL_MANT_DIG bits
+ } else {
+ a <<= @intCast(u7, LDBL_MANT_DIG - sd);
+ // a is now rounded to LDBL_MANT_DIG bits
+ }
+
+ const s = @bitCast(u128, arg) >> (128 - 64);
+ const high: u128 = (@intCast(u64, s) & 0x8000000000000000) | // sign
+ (@intCast(u64, (e + 16383)) << 48) | // exponent
+ (@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high
+ const low = @truncate(u64, a); // mantissa-low
+
+ return @bitCast(f128, low | (high << 64));
+}
+
+test "import floattitf" {
+ _ = @import("floattitf_test.zig");
+}
diff --git a/std/special/compiler_rt/floattitf_test.zig b/std/special/compiler_rt/floattitf_test.zig
new file mode 100644
index 0000000000000000000000000000000000000000..da2ccc8b355ee9ca7f4c31c40f92d9b86ad97586
--- /dev/null
+++ b/std/special/compiler_rt/floattitf_test.zig
@@ -0,0 +1,96 @@
+const __floattitf = @import("floattitf.zig").__floattitf;
+const assert = @import("std").debug.assert;
+
+fn test__floattitf(a: i128, expected: f128) void {
+ const x = __floattitf(a);
+ assert(x == expected);
+}
+
+test "floattitf" {
+ test__floattitf(0, 0.0);
+
+ test__floattitf(1, 1.0);
+ test__floattitf(2, 2.0);
+ test__floattitf(20, 20.0);
+ test__floattitf(-1, -1.0);
+ test__floattitf(-2, -2.0);
+ test__floattitf(-20, -20.0);
+
+ test__floattitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62);
+ test__floattitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62);
+ test__floattitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62);
+ test__floattitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62);
+
+ test__floattitf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126);
+ test__floattitf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126);
+ test__floattitf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126);
+ test__floattitf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126);
+
+ test__floattitf(make_ti(0x8000000000000000, 0), -0x1.000000p+127);
+ test__floattitf(make_ti(0x8000000000000001, 0), -0x1.FFFFFFFFFFFFFFFCp+126);
+
+ test__floattitf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
+
+ test__floattitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50);
+ test__floattitf(0x0007FB72EB000000, 0x1.FEDCBACp+50);
+ test__floattitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50);
+ test__floattitf(0x0007FB72EC000000, 0x1.FEDCBBp+50);
+ test__floattitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50);
+
+ test__floattitf(0x0007FB72E6000000, 0x1.FEDCB98p+50);
+ test__floattitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50);
+ test__floattitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50);
+ test__floattitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50);
+ test__floattitf(0x0007FB72E4000000, 0x1.FEDCB9p+50);
+
+ test__floattitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57);
+ test__floattitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57);
+ test__floattitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57);
+ test__floattitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57);
+ test__floattitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57);
+ test__floattitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57);
+ test__floattitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57);
+ test__floattitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57);
+ test__floattitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57);
+ test__floattitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57);
+ test__floattitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57);
+ test__floattitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57);
+ test__floattitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57);
+ test__floattitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57);
+ test__floattitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57);
+
+ test__floattitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121);
+ test__floattitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121);
+ test__floattitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121);
+ test__floattitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121);
+ test__floattitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121);
+ test__floattitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121);
+ test__floattitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121);
+ test__floattitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121);
+ test__floattitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121);
+ test__floattitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121);
+ test__floattitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121);
+ test__floattitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121);
+ test__floattitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121);
+ test__floattitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121);
+ test__floattitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121);
+
+ test__floattitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63);
+
+ test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124);
+ test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124);
+ test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124);
+ test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124);
+ test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124);
+ test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124);
+ test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124);
+ test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124);
+ test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124);
+}
+
+fn make_ti(high: u64, low: u64) i128 {
+ var result: u128 = high;
+ result <<= 64;
+ result |= low;
+ return @bitCast(i128, result);
+}
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index 0eca064ddac56c2448b50df7c320144c6b8f1cca..54a461d0f1526787b8a71681e18054f4364b3b24 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -21,6 +21,10 @@ comptime {
@export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage);
+ @export("__floattitf", @import("floattitf.zig").__floattitf, linkage);
+ @export("__floattidf", @import("floattidf.zig").__floattidf, linkage);
+ @export("__floattisf", @import("floattisf.zig").__floattisf, linkage);
+
@export("__floatunditf", @import("floatunditf.zig").__floatunditf, linkage);
@export("__floatunsitf", @import("floatunsitf.zig").__floatunsitf, linkage);
--
2.54.0
From 9f48b2ab48bcd6cfba45b8dfc60d0ad9633b294e Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sat, 30 Jun 2018 20:06:30 +1200
Subject: [PATCH 67/82] compiler_rt: Remove wrapping add/sub operators where
unneeded
Closes #495.
---
std/special/compiler_rt/floattidf.zig | 4 ++--
std/special/compiler_rt/floattisf.zig | 4 ++--
std/special/compiler_rt/floattitf.zig | 4 ++--
std/special/compiler_rt/floatunditf.zig | 2 +-
std/special/compiler_rt/floatunsitf.zig | 2 +-
std/special/compiler_rt/floatuntidf.zig | 16 ++++++++--------
std/special/compiler_rt/floatuntisf.zig | 16 ++++++++--------
std/special/compiler_rt/floatuntitf.zig | 16 ++++++++--------
8 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/std/special/compiler_rt/floattidf.zig b/std/special/compiler_rt/floattidf.zig
index 8a627fdc04fdfd739868563a40efabd8bab1f457..2a24c64efe827b002ae4bc27838b11c30e0334c6 100644
--- a/std/special/compiler_rt/floattidf.zig
+++ b/std/special/compiler_rt/floattidf.zig
@@ -42,12 +42,12 @@ pub extern fn __floattidf(arg: i128) f64 {
}
// finish
a |= @boolToInt((a & 4) != 0); // Or P into R
- a +%= 1; // round - this step may add a significant bit
+ a += 1; // round - this step may add a significant bit
a >>= 2; // dump Q and R
// a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits
if ((a & (u128(1) << DBL_MANT_DIG)) != 0) {
a >>= 1;
- e +%= 1;
+ e += 1;
}
// a is now rounded to DBL_MANT_DIG bits
} else {
diff --git a/std/special/compiler_rt/floattisf.zig b/std/special/compiler_rt/floattisf.zig
index cbdd30418fd25778fb37fb6633c26b9a13f714e4..4618a8644491a5597ff061a9729807458d15f01e 100644
--- a/std/special/compiler_rt/floattisf.zig
+++ b/std/special/compiler_rt/floattisf.zig
@@ -43,12 +43,12 @@ pub extern fn __floattisf(arg: i128) f32 {
}
// finish
a |= @boolToInt((a & 4) != 0); // Or P into R
- a +%= 1; // round - this step may add a significant bit
+ a += 1; // round - this step may add a significant bit
a >>= 2; // dump Q and R
// a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits
if ((a & (u128(1) << FLT_MANT_DIG)) != 0) {
a >>= 1;
- e +%= 1;
+ e += 1;
}
// a is now rounded to FLT_MANT_DIG bits
} else {
diff --git a/std/special/compiler_rt/floattitf.zig b/std/special/compiler_rt/floattitf.zig
index 531634e4949a01b2d26d2704c6ecd06c96f6bc7d..4da2c145fa04f0a4485083efb5713abae2606058 100644
--- a/std/special/compiler_rt/floattitf.zig
+++ b/std/special/compiler_rt/floattitf.zig
@@ -42,12 +42,12 @@ pub extern fn __floattitf(arg: i128) f128 {
}
// finish
a |= @boolToInt((a & 4) != 0); // Or P into R
- a +%= 1; // round - this step may add a significant bit
+ a += 1; // round - this step may add a significant bit
a >>= 2; // dump Q and R
// a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits
if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) {
a >>= 1;
- e +%= 1;
+ e += 1;
}
// a is now rounded to LDBL_MANT_DIG bits
} else {
diff --git a/std/special/compiler_rt/floatunditf.zig b/std/special/compiler_rt/floatunditf.zig
index b8b097581f997714cc61b3ac7dce390f3e16ec0c..12d6a5613fea33ffe7484fd91c01cbac81aea814 100644
--- a/std/special/compiler_rt/floatunditf.zig
+++ b/std/special/compiler_rt/floatunditf.zig
@@ -1,6 +1,6 @@
const builtin = @import("builtin");
const is_test = builtin.is_test;
-const std = @import("../../index.zig");
+const std = @import("std");
pub extern fn __floatunditf(a: u128) f128 {
@setRuntimeSafety(is_test);
diff --git a/std/special/compiler_rt/floatunsitf.zig b/std/special/compiler_rt/floatunsitf.zig
index dfee826b32ac568d557cb60df17693be17c5b4fa..625f90a3d099587eb94b22ec0a92f00e36770748 100644
--- a/std/special/compiler_rt/floatunsitf.zig
+++ b/std/special/compiler_rt/floatunsitf.zig
@@ -1,6 +1,6 @@
const builtin = @import("builtin");
const is_test = builtin.is_test;
-const std = @import("../../index.zig");
+const std = @import("std");
pub extern fn __floatunsitf(a: u64) f128 {
@setRuntimeSafety(is_test);
diff --git a/std/special/compiler_rt/floatuntidf.zig b/std/special/compiler_rt/floatuntidf.zig
index 3aabcb7b8aa3179405a5a8a95f3549db3b0fb3fb..1101733825e1c9dddd85f6c5de9e50cd42fd72a2 100644
--- a/std/special/compiler_rt/floatuntidf.zig
+++ b/std/special/compiler_rt/floatuntidf.zig
@@ -11,8 +11,8 @@ pub extern fn __floatuntidf(arg: u128) f64 {
var a = arg;
const N: u32 = @sizeOf(u128) * 8;
- const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits
- var e: i32 = sd -% 1; // exponent
+ const sd = @bitCast(i32, N - @clz(a)); // number of significant digits
+ var e: i32 = sd - 1; // exponent
if (sd > DBL_MANT_DIG) {
// start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
// finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
@@ -27,28 +27,28 @@ pub extern fn __floatuntidf(arg: u128) f64 {
},
DBL_MANT_DIG + 2 => {},
else => {
- const shift_amt = @bitCast(i32, N +% (DBL_MANT_DIG + 2)) -% sd;
+ const shift_amt = @bitCast(i32, N + (DBL_MANT_DIG + 2)) - sd;
const shift_amt_u7 = @intCast(u7, shift_amt);
- a = (a >> @intCast(u7, sd -% (DBL_MANT_DIG + 2))) |
+ a = (a >> @intCast(u7, sd - (DBL_MANT_DIG + 2))) |
@boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0);
},
}
// finish
a |= @boolToInt((a & 4) != 0); // Or P into R
- a +%= 1; // round - this step may add a significant bit
+ a += 1; // round - this step may add a significant bit
a >>= 2; // dump Q and R
// a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits
if ((a & (u128(1) << DBL_MANT_DIG)) != 0) {
a >>= 1;
- e +%= 1;
+ e += 1;
}
// a is now rounded to DBL_MANT_DIG bits
} else {
- a <<= @intCast(u7, DBL_MANT_DIG -% sd);
+ a <<= @intCast(u7, DBL_MANT_DIG - sd);
// a is now rounded to DBL_MANT_DIG bits
}
- const high: u64 = @bitCast(u32, (e +% 1023) << 20) | // exponent
+ const high: u64 = @bitCast(u32, (e + 1023) << 20) | // exponent
(@truncate(u32, a >> 32) & 0x000FFFFF); // mantissa-high
const low = @truncate(u32, a); // mantissa-low
diff --git a/std/special/compiler_rt/floatuntisf.zig b/std/special/compiler_rt/floatuntisf.zig
index e83affd87c99d2202a28171a394529d585c2dcf4..f85c22578e2e245e583a26330ab03a5d10232494 100644
--- a/std/special/compiler_rt/floatuntisf.zig
+++ b/std/special/compiler_rt/floatuntisf.zig
@@ -11,8 +11,8 @@ pub extern fn __floatuntisf(arg: u128) f32 {
var a = arg;
const N: u32 = @sizeOf(u128) * 8;
- const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits
- var e: i32 = sd -% 1; // exponent
+ const sd = @bitCast(i32, N - @clz(a)); // number of significant digits
+ var e: i32 = sd - 1; // exponent
if (sd > FLT_MANT_DIG) {
// start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
// finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
@@ -27,28 +27,28 @@ pub extern fn __floatuntisf(arg: u128) f32 {
},
FLT_MANT_DIG + 2 => {},
else => {
- const shift_amt = @bitCast(i32, N +% (FLT_MANT_DIG + 2)) -% sd;
+ const shift_amt = @bitCast(i32, N + (FLT_MANT_DIG + 2)) - sd;
const shift_amt_u7 = @intCast(u7, shift_amt);
- a = (a >> @intCast(u7, sd -% (FLT_MANT_DIG + 2))) |
+ a = (a >> @intCast(u7, sd - (FLT_MANT_DIG + 2))) |
@boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0);
},
}
// finish
a |= @boolToInt((a & 4) != 0); // Or P into R
- a +%= 1; // round - this step may add a significant bit
+ a += 1; // round - this step may add a significant bit
a >>= 2; // dump Q and R
// a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits
if ((a & (u128(1) << FLT_MANT_DIG)) != 0) {
a >>= 1;
- e +%= 1;
+ e += 1;
}
// a is now rounded to FLT_MANT_DIG bits
} else {
- a <<= @intCast(u7, FLT_MANT_DIG -% sd);
+ a <<= @intCast(u7, FLT_MANT_DIG - sd);
// a is now rounded to FLT_MANT_DIG bits
}
- const high = @bitCast(u32, (e +% 127) << 23); // exponent
+ const high = @bitCast(u32, (e + 127) << 23); // exponent
const low = @truncate(u32, a) & 0x007fffff; // mantissa
return @bitCast(f32, high | low);
diff --git a/std/special/compiler_rt/floatuntitf.zig b/std/special/compiler_rt/floatuntitf.zig
index a64dd96a343c0a2f6deca1b6870a0c0558148b0f..6354c89287cfe1832707498d29410799d028cb20 100644
--- a/std/special/compiler_rt/floatuntitf.zig
+++ b/std/special/compiler_rt/floatuntitf.zig
@@ -11,8 +11,8 @@ pub extern fn __floatuntitf(arg: u128) f128 {
var a = arg;
const N: u32 = @sizeOf(u128) * 8;
- const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits
- var e: i32 = sd -% 1; // exponent
+ const sd = @bitCast(i32, N - @clz(a)); // number of significant digits
+ var e: i32 = sd - 1; // exponent
if (sd > LDBL_MANT_DIG) {
// start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
// finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
@@ -27,28 +27,28 @@ pub extern fn __floatuntitf(arg: u128) f128 {
},
LDBL_MANT_DIG + 2 => {},
else => {
- const shift_amt = @bitCast(i32, N +% (LDBL_MANT_DIG + 2)) -% sd;
+ const shift_amt = @bitCast(i32, N + (LDBL_MANT_DIG + 2)) - sd;
const shift_amt_u7 = @intCast(u7, shift_amt);
- a = (a >> @intCast(u7, sd -% (LDBL_MANT_DIG + 2))) |
+ a = (a >> @intCast(u7, sd - (LDBL_MANT_DIG + 2))) |
@boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0);
},
}
// finish
a |= @boolToInt((a & 4) != 0); // Or P into R
- a +%= 1; // round - this step may add a significant bit
+ a += 1; // round - this step may add a significant bit
a >>= 2; // dump Q and R
// a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits
if ((a & (u128(1) << LDBL_MANT_DIG)) != 0) {
a >>= 1;
- e +%= 1;
+ e += 1;
}
// a is now rounded to LDBL_MANT_DIG bits
} else {
- a <<= @intCast(u7, LDBL_MANT_DIG -% sd);
+ a <<= @intCast(u7, LDBL_MANT_DIG - sd);
// a is now rounded to LDBL_MANT_DIG bits
}
- const high: u128 = (@intCast(u64, (e +% 16383)) << 48) | // exponent
+ const high: u128 = (@intCast(u64, (e + 16383)) << 48) | // exponent
(@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high
const low = @truncate(u64, a); // mantissa-low
--
2.54.0
From 951512f5ae52e41d3f2bdcb9a533668bcca3a9cd Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sat, 30 Jun 2018 20:11:21 +1200
Subject: [PATCH 68/82] compiler_rt: Add CMake entries
---
CMakeLists.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9957a740cf3d4b16f258b3cb1a10e6b0aa5e00ee..87c0351059be306a2e9fd83f69837e032dcbfb2f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -590,6 +590,9 @@ set(ZIG_STD_FILES
"special/compiler_rt/floatuntidf.zig"
"special/compiler_rt/floatuntisf.zig"
"special/compiler_rt/floatuntitf.zig"
+ "special/compiler_rt/floattidf.zig"
+ "special/compiler_rt/floattisf.zig"
+ "special/compiler_rt/floattitf.zig"
"special/compiler_rt/muloti4.zig"
"special/compiler_rt/index.zig"
"special/compiler_rt/truncXfYf2.zig"
--
2.54.0
From 887c97742f86071ffab2a79443d48c4153b63ad6 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sat, 30 Jun 2018 20:15:02 +1200
Subject: [PATCH 69/82] Alignment fix and allow rudimentary f128 float printing
---
std/fmt/index.zig | 2 +-
std/special/compiler_rt/floatunditf.zig | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index f4dfa0e3246bac951bbe38def0f3b31b5408d08b..bf12e86fef1a5dd16838baa286544884588262ec 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -327,7 +327,7 @@ pub fn formatFloatScientific(
comptime Errors: type,
output: fn (@typeOf(context), []const u8) Errors!void,
) Errors!void {
- var x = f64(value);
+ var x = @floatCast(f64, value);
// Errol doesn't handle these special cases.
if (math.signbit(x)) {
diff --git a/std/special/compiler_rt/floatunditf.zig b/std/special/compiler_rt/floatunditf.zig
index 12d6a5613fea33ffe7484fd91c01cbac81aea814..afc545448a9b91f94718a09d8606006b4efb02ad 100644
--- a/std/special/compiler_rt/floatunditf.zig
+++ b/std/special/compiler_rt/floatunditf.zig
@@ -17,7 +17,7 @@ pub extern fn __floatunditf(a: u128) f128 {
const exp = (u128.bit_count - 1) - @clz(a);
const shift = mantissa_bits - @intCast(u7, exp);
- var result: u128 = (a << shift) ^ implicit_bit;
+ var result: u128 align(16) = (a << shift) ^ implicit_bit;
result += (@intCast(u128, exp) + exponent_bias) << mantissa_bits;
return @bitCast(f128, result);
--
2.54.0
From 616fe798c801baa5fa7238f5fc576a5090938999 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 30 Jun 2018 17:35:05 +0200
Subject: [PATCH 70/82] Revert "contains_comptime_undefined_value should not
follow pointers"
This reverts commit 58b1692182dc2f8da5b535f59e9a89cfab10a7b6.
---
src/analyze.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 71 insertions(+), 1 deletion(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 4c200888d8d27e82b5515e7bce202597ea7049a0..068ea48c0a84d4639b365faecc3100a216a8fde9 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -5296,6 +5296,41 @@ bool contains_comptime_undefined_value(ConstExprValue *value) {
switch (value->type->id) {
case TypeTableEntryIdInvalid:
zig_unreachable();
+
+ case TypeTableEntryIdPointer: {
+ ConstPtrValue *ptr = &value->data.x_ptr;
+ if (ptr->mut == ConstPtrMutRuntimeVar)
+ return false;
+
+ switch (ptr->special) {
+ case ConstPtrSpecialInvalid:
+ zig_unreachable();
+ case ConstPtrSpecialRef:
+ return contains_comptime_undefined_value(ptr->data.ref.pointee);
+ case ConstPtrSpecialBaseArray: {
+ size_t index = ptr->data.base_array.elem_index;
+ ConstExprValue *arr = ptr->data.base_array.array_val;
+ if (arr->special == ConstValSpecialUndef)
+ return true;
+ if (arr->data.x_array.special == ConstArraySpecialUndef)
+ return true;
+
+ return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]);
+ }
+ case ConstPtrSpecialBaseStruct: {
+ size_t index = ptr->data.base_struct.field_index;
+ ConstExprValue *str = ptr->data.base_struct.struct_val;
+ if (str->special == ConstValSpecialUndef)
+ return true;
+
+ return contains_comptime_undefined_value(&str->data.x_struct.fields[index]);
+ }
+ case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value?
+ case ConstPtrSpecialDiscard:
+ case ConstPtrSpecialHardCodedAddr:
+ return false;
+ }
+ }
case TypeTableEntryIdArray: {
ConstArrayValue *arr = &value->data.x_array;
if (arr->special == ConstArraySpecialUndef)
@@ -5309,6 +5344,42 @@ bool contains_comptime_undefined_value(ConstExprValue *value) {
}
case TypeTableEntryIdStruct: {
ConstStructValue *str = &value->data.x_struct;
+ if (value->type->data.structure.is_slice) {
+ ConstExprValue *len = &str->fields[slice_len_index];
+ ConstExprValue *ptr = &str->fields[slice_ptr_index];
+ if (len->special == ConstValSpecialUndef)
+ return true;
+ if (ptr->special == ConstValSpecialUndef)
+ return true;
+
+ switch (ptr->data.x_ptr.special) {
+ case ConstPtrSpecialRef:
+ return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee);
+ case ConstPtrSpecialBaseArray: {
+ size_t offset = ptr->data.x_ptr.data.base_array.elem_index;
+ ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val;
+ if (arr->special == ConstValSpecialUndef)
+ return true;
+ if (arr->data.x_array.special == ConstArraySpecialUndef)
+ return true;
+
+ uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint);
+ for (size_t i = 0; i < slice_len; ++i) {
+ if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset]))
+ return true;
+ }
+
+ return false;
+ }
+ case ConstPtrSpecialBaseStruct:
+ case ConstPtrSpecialInvalid:
+ case ConstPtrSpecialFunction:
+ case ConstPtrSpecialDiscard:
+ case ConstPtrSpecialHardCodedAddr:
+ zig_unreachable();
+ }
+ }
+
for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) {
if (contains_comptime_undefined_value(&str->fields[i]))
return true;
@@ -5329,7 +5400,6 @@ bool contains_comptime_undefined_value(ConstExprValue *value) {
case TypeTableEntryIdUnion:
return contains_comptime_undefined_value(value->data.x_union.payload);
- case TypeTableEntryIdPointer:
case TypeTableEntryIdArgTuple:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
--
2.54.0
From 01bd5c46e177ae59f72197063c374e845eea3ff3 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 30 Jun 2018 17:35:06 +0200
Subject: [PATCH 71/82] Revert "ir_resolve_const now checks recursivly for
undef values"
This reverts commit 4c3f27ce1ea17b5236a022971ebace73a02b7c2b.
---
src/analyze.cpp | 135 ----------------------------------------
src/analyze.hpp | 1 -
src/ir.cpp | 11 +---
test/compile_errors.zig | 15 -----
4 files changed, 2 insertions(+), 160 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 068ea48c0a84d4639b365faecc3100a216a8fde9..b3a302a1d4cee5e1ce763da57f529385d7fb3388 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -5288,141 +5288,6 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_
return const_val;
}
-bool contains_comptime_undefined_value(ConstExprValue *value) {
- assert(value->special != ConstValSpecialRuntime);
- if (value->special == ConstValSpecialUndef)
- return true;
-
- switch (value->type->id) {
- case TypeTableEntryIdInvalid:
- zig_unreachable();
-
- case TypeTableEntryIdPointer: {
- ConstPtrValue *ptr = &value->data.x_ptr;
- if (ptr->mut == ConstPtrMutRuntimeVar)
- return false;
-
- switch (ptr->special) {
- case ConstPtrSpecialInvalid:
- zig_unreachable();
- case ConstPtrSpecialRef:
- return contains_comptime_undefined_value(ptr->data.ref.pointee);
- case ConstPtrSpecialBaseArray: {
- size_t index = ptr->data.base_array.elem_index;
- ConstExprValue *arr = ptr->data.base_array.array_val;
- if (arr->special == ConstValSpecialUndef)
- return true;
- if (arr->data.x_array.special == ConstArraySpecialUndef)
- return true;
-
- return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]);
- }
- case ConstPtrSpecialBaseStruct: {
- size_t index = ptr->data.base_struct.field_index;
- ConstExprValue *str = ptr->data.base_struct.struct_val;
- if (str->special == ConstValSpecialUndef)
- return true;
-
- return contains_comptime_undefined_value(&str->data.x_struct.fields[index]);
- }
- case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value?
- case ConstPtrSpecialDiscard:
- case ConstPtrSpecialHardCodedAddr:
- return false;
- }
- }
- case TypeTableEntryIdArray: {
- ConstArrayValue *arr = &value->data.x_array;
- if (arr->special == ConstArraySpecialUndef)
- return true;
-
- for (size_t i = 0; i < value->type->data.array.len; ++i) {
- if (contains_comptime_undefined_value(&arr->s_none.elements[i]))
- return true;
- }
- return false;
- }
- case TypeTableEntryIdStruct: {
- ConstStructValue *str = &value->data.x_struct;
- if (value->type->data.structure.is_slice) {
- ConstExprValue *len = &str->fields[slice_len_index];
- ConstExprValue *ptr = &str->fields[slice_ptr_index];
- if (len->special == ConstValSpecialUndef)
- return true;
- if (ptr->special == ConstValSpecialUndef)
- return true;
-
- switch (ptr->data.x_ptr.special) {
- case ConstPtrSpecialRef:
- return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee);
- case ConstPtrSpecialBaseArray: {
- size_t offset = ptr->data.x_ptr.data.base_array.elem_index;
- ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val;
- if (arr->special == ConstValSpecialUndef)
- return true;
- if (arr->data.x_array.special == ConstArraySpecialUndef)
- return true;
-
- uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint);
- for (size_t i = 0; i < slice_len; ++i) {
- if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset]))
- return true;
- }
-
- return false;
- }
- case ConstPtrSpecialBaseStruct:
- case ConstPtrSpecialInvalid:
- case ConstPtrSpecialFunction:
- case ConstPtrSpecialDiscard:
- case ConstPtrSpecialHardCodedAddr:
- zig_unreachable();
- }
- }
-
- for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) {
- if (contains_comptime_undefined_value(&str->fields[i]))
- return true;
- }
- return false;
- }
- case TypeTableEntryIdOptional:
- if (value->data.x_optional == nullptr)
- return false;
-
- return contains_comptime_undefined_value(value->data.x_optional);
- case TypeTableEntryIdErrorUnion:
- // TODO: Can error union error be undefined?
- if (value->data.x_err_union.err != nullptr)
- return false;
-
- return contains_comptime_undefined_value(value->data.x_err_union.payload);
- case TypeTableEntryIdUnion:
- return contains_comptime_undefined_value(value->data.x_union.payload);
-
- case TypeTableEntryIdArgTuple:
- case TypeTableEntryIdVoid:
- case TypeTableEntryIdBool:
- case TypeTableEntryIdUnreachable:
- case TypeTableEntryIdInt:
- case TypeTableEntryIdFloat:
- case TypeTableEntryIdComptimeFloat:
- case TypeTableEntryIdComptimeInt:
- case TypeTableEntryIdUndefined:
- case TypeTableEntryIdNull:
- case TypeTableEntryIdErrorSet:
- case TypeTableEntryIdEnum:
- case TypeTableEntryIdFn:
- case TypeTableEntryIdNamespace:
- case TypeTableEntryIdBlock:
- case TypeTableEntryIdBoundFn:
- case TypeTableEntryIdMetaType:
- case TypeTableEntryIdOpaque:
- case TypeTableEntryIdPromise:
- return false;
- }
- zig_unreachable();
-}
void init_const_undefined(CodeGen *g, ConstExprValue *const_val) {
TypeTableEntry *wanted_type = const_val->type;
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 100f85d4d9d663fa530397d6301b230f1726a327..88e06b2390e99137cd016e56394c2a198c2c3ccf 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -93,7 +93,6 @@ void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry);
void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry);
void complete_enum(CodeGen *g, TypeTableEntry *enum_type);
bool ir_get_var_is_comptime(VariableTableEntry *var);
-bool contains_comptime_undefined_value(ConstExprValue *value);
bool const_values_equal(ConstExprValue *a, ConstExprValue *b);
void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max);
void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max);
diff --git a/src/ir.cpp b/src/ir.cpp
index 2cce4a504472449b63dfa11313e09da66019a998..c6078e755de8289c89fec25edaa87cb95e0f5a1f 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -9148,15 +9148,8 @@ enum UndefAllowed {
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) {
switch (value->value.special) {
- case ConstValSpecialStatic: {
- ConstExprValue *res = &value->value;
- if (undef_allowed == UndefBad && contains_comptime_undefined_value(res)) {
- ir_add_error(ira, value, buf_sprintf("use of undefined value"));
- return nullptr;
- }
-
- return res;
- }
+ case ConstValSpecialStatic:
+ return &value->value;
case ConstValSpecialRuntime:
ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
return nullptr;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 8749f5b56060168c2b5422451540008e7da1fafd..2247f0af966cc4058b2d33c23bb072e50cfe1edc 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -4124,19 +4124,4 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
,
".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic",
);
-
- cases.add(
- "Trying to pass undefined array to function taking comptime array by value",
- \\fn a(comptime b: [2]u8) u8 { return b[0]; }
- \\
- \\test "" {
- \\ const arr: [2]u8 = undefined;
- \\ _ = a(arr);
- \\}
- ,
- ".tmp_source.zig:5:11: error: use of undefined value",
- );
-
-
-
}
--
2.54.0
From ecd5e60be9cab03449e0d40a770c5a0c5582198d Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 30 Jun 2018 20:50:09 +0200
Subject: [PATCH 72/82] Expanded the list of operators that catch undefined
values at comptime
---
src/ir.cpp | 134 ++++++++++---
test/compile_errors.zig | 420 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 523 insertions(+), 31 deletions(-)
diff --git a/src/ir.cpp b/src/ir.cpp
index c6078e755de8289c89fec25edaa87cb95e0f5a1f..0f1e63229919d390f77944a275d720ef449b18f2 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -10773,10 +10773,15 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
- ConstExprValue *op1_val = &casted_op1->value;
- ConstExprValue *op2_val = &casted_op2->value;
- if (op1_val->special != ConstValSpecialRuntime && op2_val->special != ConstValSpecialRuntime) {
+ if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
+ ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
+ if (op1_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
+ if (op2_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
assert(casted_op1->value.type->id == TypeTableEntryIdBool);
assert(casted_op2->value.type->id == TypeTableEntryIdBool);
@@ -10926,9 +10931,14 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
}
}
- ConstExprValue *op1_val = &op1->value;
- ConstExprValue *op2_val = &op2->value;
- if (value_is_comptime(op1_val) && value_is_comptime(op2_val)) {
+ if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
+ ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
+ if (op1_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+ ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
+ if (op2_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
bool answer;
bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value;
if (op_id == IrBinOpCmpEq) {
@@ -11017,10 +11027,15 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
- ConstExprValue *op1_val = &casted_op1->value;
- ConstExprValue *op2_val = &casted_op2->value;
bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type);
- if (one_possible_value || (value_is_comptime(op1_val) && value_is_comptime(op2_val))) {
+ if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) {
+ ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
+ if (op1_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+ ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
+ if (op2_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
bool answer;
if (resolved_type->id == TypeTableEntryIdComptimeFloat || resolved_type->id == TypeTableEntryIdFloat) {
Cmp cmp_result = float_cmp(op1_val, op2_val);
@@ -11048,11 +11063,17 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
if (resolved_type->id == TypeTableEntryIdInt && !resolved_type->data.integral.is_signed) {
ConstExprValue *known_left_val;
IrBinOp flipped_op_id;
- if (value_is_comptime(op1_val)) {
- known_left_val = op1_val;
+ if (instr_is_comptime(casted_op1)) {
+ known_left_val = ir_resolve_const(ira, casted_op1, UndefBad);
+ if (known_left_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
flipped_op_id = op_id;
- } else if (value_is_comptime(op2_val)) {
- known_left_val = op2_val;
+ } else if (instr_is_comptime(casted_op2)) {
+ known_left_val = ir_resolve_const(ira, casted_op2, UndefBad);
+ if (known_left_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
if (op_id == IrBinOpCmpLessThan) {
flipped_op_id = IrBinOpCmpGreaterThan;
} else if (op_id == IrBinOpCmpGreaterThan) {
@@ -11304,8 +11325,14 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *
}
if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) {
- ConstExprValue *op1_val = &op1->value;
- ConstExprValue *op2_val = &casted_op2->value;
+ ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
+ if (op1_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
+ if (op2_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
IrInstruction *result_instruction = ir_get_const(ira, &bin_op_instruction->base);
ir_link_new_instruction(result_instruction, &bin_op_instruction->base);
ConstExprValue *out_val = &result_instruction->value;
@@ -11384,7 +11411,15 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
if (is_signed_div) {
bool ok = false;
if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
- if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) {
+ ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
+ if (op1_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
+ if (op2_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) {
// the division by zero error will be caught later, but we don't have a
// division function ambiguity problem.
op_id = IrBinOpDivTrunc;
@@ -11392,8 +11427,8 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
} else {
BigInt trunc_result;
BigInt floor_result;
- bigint_div_trunc(&trunc_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint);
- bigint_div_floor(&floor_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint);
+ bigint_div_trunc(&trunc_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+ bigint_div_floor(&floor_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) {
ok = true;
op_id = IrBinOpDivTrunc;
@@ -11414,7 +11449,15 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
if (is_signed_div && (is_int || is_float)) {
bool ok = false;
if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
+ ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
+ if (op1_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
if (is_int) {
+ ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
+ if (op2_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) {
// the division by zero error will be caught later, but we don't
// have a remainder function ambiguity problem
@@ -11422,14 +11465,19 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
} else {
BigInt rem_result;
BigInt mod_result;
- bigint_rem(&rem_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint);
- bigint_mod(&mod_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint);
+ bigint_rem(&rem_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
+ bigint_mod(&mod_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ;
}
} else {
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid;
+
+ ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
+ if (op2_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
if (float_cmp_zero(&casted_op2->value) == CmpEQ) {
// the division by zero error will be caught later, but we don't
// have a remainder function ambiguity problem
@@ -11437,8 +11485,8 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
} else {
ConstExprValue rem_result;
ConstExprValue mod_result;
- float_rem(&rem_result, &op1->value, &casted_op2->value);
- float_mod(&mod_result, &op1->value, &casted_op2->value);
+ float_rem(&rem_result, op1_val, op2_val);
+ float_mod(&mod_result, op1_val, op2_val);
ok = float_cmp(&rem_result, &mod_result) == CmpEQ;
}
}
@@ -11496,8 +11544,13 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
return ira->codegen->builtin_types.entry_invalid;
if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
- ConstExprValue *op1_val = &casted_op1->value;
- ConstExprValue *op2_val = &casted_op2->value;
+ ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
+ if (op1_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+ ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
+ if (op2_val == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
IrInstruction *result_instruction = ir_get_const(ira, &bin_op_instruction->base);
ir_link_new_instruction(result_instruction, &bin_op_instruction->base);
ConstExprValue *out_val = &result_instruction->value;
@@ -11672,9 +11725,16 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *
out_val->data.x_ptr.data.base_array.array_val = out_array_val;
out_val->data.x_ptr.data.base_array.elem_index = 0;
}
+
+ if (op1_array_val->data.x_array.special == ConstArraySpecialUndef &&
+ op2_array_val->data.x_array.special == ConstArraySpecialUndef) {
+ out_array_val->data.x_array.special = ConstArraySpecialUndef;
+ return result_type;
+ }
+
out_array_val->data.x_array.s_none.elements = create_const_vals(new_len);
-
expand_undef_array(ira->codegen, op1_array_val);
+ expand_undef_array(ira->codegen, op2_array_val);
size_t next_index = 0;
for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) {
@@ -11726,11 +11786,15 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp
}
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+ if (array_val->data.x_array.special == ConstArraySpecialUndef) {
+ out_val->data.x_array.special = ConstArraySpecialUndef;
+
+ TypeTableEntry *child_type = array_type->data.array.child_type;
+ return get_array_type(ira->codegen, child_type, new_array_len);
+ }
out_val->data.x_array.s_none.elements = create_const_vals(new_array_len);
- expand_undef_array(ira->codegen, array_val);
-
uint64_t i = 0;
for (uint64_t x = 0; x < mult_amt; x += 1) {
for (uint64_t y = 0; y < old_array_len; y += 1) {
@@ -13056,7 +13120,11 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp
// one of the ptr instructions
if (instr_is_comptime(value)) {
- ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value);
+ ConstExprValue *comptime_value = ir_resolve_const(ira, value, UndefBad);
+ if (comptime_value == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ ConstExprValue *pointee = const_ptr_pointee(ira->codegen, comptime_value);
if (pointee->type == child_type) {
ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base);
copy_const_val(out_val, pointee, value->value.data.x_ptr.mut == ConstPtrMutComptimeConst);
@@ -13173,7 +13241,7 @@ static TypeTableEntry *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *ins
if (expr_type->id == TypeTableEntryIdInt) {
if (instr_is_comptime(value)) {
ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad);
- if (!target_const_val)
+ if (target_const_val == nullptr)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
@@ -17750,9 +17818,13 @@ static TypeTableEntry *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstruc
if (type_is_invalid(casted_value->value.type))
return ira->codegen->builtin_types.entry_invalid;
- if (casted_value->value.special != ConstValSpecialRuntime) {
+ if (instr_is_comptime(casted_value)) {
+ ConstExprValue *value = ir_resolve_const(ira, casted_value, UndefBad);
+ if (value == nullptr)
+ return ira->codegen->builtin_types.entry_invalid;
+
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
- out_val->data.x_bool = !casted_value->value.data.x_bool;
+ out_val->data.x_bool = !value->data.x_bool;
return bool_type;
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 2247f0af966cc4058b2d33c23bb072e50cfe1edc..2562424ee0c6bd85d72b947b3f8b2b511fe56a19 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1893,6 +1893,426 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
".tmp_source.zig:1:15: error: use of undefined value",
);
+ cases.add(
+ "div on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a / a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "div assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a /= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "mod on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a % a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "mod assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a %= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "add on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a + a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "add assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a += a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "add wrap on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a +% a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "add wrap assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a +%= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "sub on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a - a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "sub assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a -= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "sub wrap on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a -% a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "sub wrap assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a -%= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "mult on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a * a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "mult assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a *= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "mult wrap on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a *% a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "mult wrap assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a *%= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "shift left on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a << 2;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "shift left assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a <<= 2;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "shift right on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a >> 2;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "shift left assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a >>= 2;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "bin and on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a & a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "bin and assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a &= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "bin or on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a | a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "bin or assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a |= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "bin xor on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a ^ a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "bin xor assign on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ a ^= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: use of undefined value",
+ );
+
+ cases.add(
+ "equal on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a == a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "not equal on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a != a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "greater than on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a > a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "greater than equal on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a >= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "less than on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a < a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "less than equal on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = a <= a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "and on undefined value",
+ \\comptime {
+ \\ var a: bool = undefined;
+ \\ _ = a and a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "or on undefined value",
+ \\comptime {
+ \\ var a: bool = undefined;
+ \\ _ = a or a;
+ \\}
+ ,
+ ".tmp_source.zig:3:9: error: use of undefined value",
+ );
+
+ cases.add(
+ "negate on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = -a;
+ \\}
+ ,
+ ".tmp_source.zig:3:10: error: use of undefined value",
+ );
+
+ cases.add(
+ "negate wrap on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = -%a;
+ \\}
+ ,
+ ".tmp_source.zig:3:11: error: use of undefined value",
+ );
+
+ cases.add(
+ "bin not on undefined value",
+ \\comptime {
+ \\ var a: i64 = undefined;
+ \\ _ = ~a;
+ \\}
+ ,
+ ".tmp_source.zig:3:10: error: use of undefined value",
+ );
+
+ cases.add(
+ "bool not on undefined value",
+ \\comptime {
+ \\ var a: bool = undefined;
+ \\ _ = !a;
+ \\}
+ ,
+ ".tmp_source.zig:3:10: error: use of undefined value",
+ );
+
+ cases.add(
+ "orelse on undefined value",
+ \\comptime {
+ \\ var a: ?bool = undefined;
+ \\ _ = a orelse false;
+ \\}
+ ,
+ ".tmp_source.zig:3:11: error: use of undefined value",
+ );
+
+ cases.add(
+ "catch on undefined value",
+ \\comptime {
+ \\ var a: error!bool = undefined;
+ \\ _ = a catch |err| false;
+ \\}
+ ,
+ ".tmp_source.zig:3:11: error: use of undefined value",
+ );
+
+ cases.add(
+ "deref on undefined value",
+ \\comptime {
+ \\ var a: *u8 = undefined;
+ \\ _ = a.*;
+ \\}
+ ,
+ ".tmp_source.zig:3:11: error: use of undefined value",
+ );
+
+ cases.add(
+ "unwrap on undefined value",
+ \\comptime {
+ \\ var a: ?u8 = undefined;
+ \\ _ = a.?;
+ \\}
+ ,
+ ".tmp_source.zig:3:11: error: use of undefined value",
+ );
+
cases.add(
"endless loop in function evaluation",
\\const seventh_fib_number = fibbonaci(7);
--
2.54.0
From 055e0fef4eaa2d8c6dcbd83180baa44d88be3b4d Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 30 Jun 2018 21:22:26 +0200
Subject: [PATCH 73/82] Avoid resolve_const in cmp when instr are not comptime
---
src/ir.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/ir.cpp b/src/ir.cpp
index 0f1e63229919d390f77944a275d720ef449b18f2..ce2e333227779898af19ab98ede854eef7befc44 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -11029,10 +11029,10 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type);
if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) {
- ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
+ ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad);
if (op1_val == nullptr)
return ira->codegen->builtin_types.entry_invalid;
- ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
+ ConstExprValue *op2_val = one_possible_value ? &casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad);
if (op2_val == nullptr)
return ira->codegen->builtin_types.entry_invalid;
--
2.54.0
From b182151de5a215b97de3ef3742ebdc5af7e66119 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 30 Jun 2018 21:59:14 +0200
Subject: [PATCH 74/82] Fixed line numbers for tests
---
test/compile_errors.zig | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 2562424ee0c6bd85d72b947b3f8b2b511fe56a19..3b69fb633b5b5c0a756ec04abb3a818253390f49 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -2300,17 +2300,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a.*;
\\}
,
- ".tmp_source.zig:3:11: error: use of undefined value",
- );
-
- cases.add(
- "unwrap on undefined value",
- \\comptime {
- \\ var a: ?u8 = undefined;
- \\ _ = a.?;
- \\}
- ,
- ".tmp_source.zig:3:11: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value",
);
cases.add(
--
2.54.0
From e833a5a24c1ad58af0bb56e89dbcc0b9ec38c020 Mon Sep 17 00:00:00 2001
From: Josh Wolfe
Date: Sun, 1 Jul 2018 13:47:29 -0400
Subject: [PATCH 75/82] gitignore docgen test artifacts
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index 20b208975adc79b2e86bc06beaf560c9ce287920..5616da8e586f115a9bfc1fe07355a7ffdf75f60b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
zig-cache/
build/
build-*/
+docgen_tmp/
--
2.54.0
From 0206b76351a50e154181d5bff1d134656e412bfb Mon Sep 17 00:00:00 2001
From: Josh Wolfe
Date: Sun, 1 Jul 2018 22:03:51 -0400
Subject: [PATCH 76/82] syntax in build.zig example doc
---
doc/langref.html.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index dbb4ea98060e59b8cb07e1c538dd89b6953d9b46..15e04459bd91a3512568fb933bdc3f12ea69f363 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -6067,7 +6067,7 @@ pub const TypeInfo = union(TypeId) {
{#code_begin|syntax#}
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) void {
+pub fn build(b: *Builder) void {
const exe = b.addExecutable("example", "example.zig");
exe.setBuildMode(b.standardReleaseOptions());
b.default_step.dependOn(&exe.step);
--
2.54.0
From 2759c7951da050d825cf765c4b660f5562fb01a4 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 2 Jul 2018 14:10:27 -0400
Subject: [PATCH 77/82] always link against compiler_rt.o even when linking
libc
sometimes libgcc is missing things we need, so we always link
compiler_rt and rely on weak linkage to allow libgcc to override.
---
src/link.cpp | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/link.cpp b/src/link.cpp
index a4631b1dafbdc85f0d1780835a4c9f479a083d00..2d9a79585f2c852b5968556cfb2aa2bdbe36db8a 100644
--- a/src/link.cpp
+++ b/src/link.cpp
@@ -325,10 +325,13 @@ static void construct_linker_job_elf(LinkJob *lj) {
lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
}
- if (g->libc_link_lib == nullptr && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) {
- Buf *builtin_o_path = build_o(g, "builtin");
- lj->args.append(buf_ptr(builtin_o_path));
+ if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
+ if (g->libc_link_lib == nullptr) {
+ Buf *builtin_o_path = build_o(g, "builtin");
+ lj->args.append(buf_ptr(builtin_o_path));
+ }
+ // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
Buf *compiler_rt_o_path = build_compiler_rt(g);
lj->args.append(buf_ptr(compiler_rt_o_path));
}
@@ -554,7 +557,7 @@ static void construct_linker_job_coff(LinkJob *lj) {
lj->args.append(buf_ptr(builtin_o_path));
}
- // msvc compiler_rt is missing some stuff, so we still build it and rely on LinkOnce
+ // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
Buf *compiler_rt_o_path = build_compiler_rt(g);
lj->args.append(buf_ptr(compiler_rt_o_path));
}
--
2.54.0
From a3f55aaf34f0a459c8aec4b35e55ad4534eaca30 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 29 Jun 2018 15:39:55 -0400
Subject: [PATCH 78/82] add event loop Channel abstraction
This is akin to channels in Go, except:
* implemented in userland
* they are lock-free and thread-safe
* they integrate with the userland event loop
The self hosted compiler is changed to use a channel for events,
and made to stay alive, watching files and performing builds when
things change, however the main.zig file exits after 1 build.
Note that nothing is actually built yet, it just parses the input
and then declares that the build succeeded.
Next items to do:
* add windows and macos support for std.event.Loop
* improve the event loop stop() operation
* make the event loop multiplex coroutines onto kernel threads
* watch source file for updates, and provide AST diffs
(at least list the top level declaration changes)
* top level declaration analysis
---
src-self-hosted/main.zig | 37 ++++-
src-self-hosted/module.zig | 135 +++++++++++++-----
std/atomic/queue_mpsc.zig | 2 +-
std/event.zig | 279 ++++++++++++++++++++++++++++++++++++-
std/fmt/index.zig | 3 +
std/heap.zig | 1 +
6 files changed, 416 insertions(+), 41 deletions(-)
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index 6dabddaefb7f64940bcbf6de9a2cf73e5f2f65a4..d17fc94c82a072f0e939bbcda5cbc0864f47cf05 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const builtin = @import("builtin");
+const event = std.event;
const os = std.os;
const io = std.io;
const mem = std.mem;
@@ -43,6 +44,9 @@ const Command = struct {
};
pub fn main() !void {
+ // This allocator needs to be thread-safe because we use it for the event.Loop
+ // which multiplexes coroutines onto kernel threads.
+ // libc allocator is guaranteed to have this property.
const allocator = std.heap.c_allocator;
var stdout_file = try std.io.getStdOut();
@@ -380,8 +384,10 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
defer allocator.free(zig_lib_dir);
+ var loop = try event.Loop.init(allocator);
+
var module = try Module.create(
- allocator,
+ &loop,
root_name,
root_source_file,
Target.Native,
@@ -471,9 +477,35 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
module.emit_file_type = emit_type;
module.link_objects = link_objects;
module.assembly_files = assembly_files;
+ module.link_out_file = flags.single("out-file");
try module.build();
- try module.link(flags.single("out-file"));
+ const process_build_events_handle = try async processBuildEvents(module, true);
+ defer cancel process_build_events_handle;
+ loop.run();
+}
+
+async fn processBuildEvents(module: *Module, watch: bool) void {
+ while (watch) {
+ // TODO directly awaiting async should guarantee memory allocation elision
+ const build_event = await (async module.events.get() catch unreachable);
+
+ switch (build_event) {
+ Module.Event.Ok => {
+ std.debug.warn("Build succeeded\n");
+ // for now we stop after 1
+ module.loop.stop();
+ return;
+ },
+ Module.Event.Error => |err| {
+ std.debug.warn("build failed: {}\n", @errorName(err));
+ @panic("TODO error return trace");
+ },
+ Module.Event.Fail => |errs| {
+ @panic("TODO print compile error messages");
+ },
+ }
+ }
}
fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void {
@@ -780,4 +812,3 @@ const CliPkg = struct {
self.children.deinit();
}
};
-
diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig
index 4da46cd38c5a9324a0a0f40dec7f490d97ee1b5c..4fac7607902fc053def8c85728c02cb3ecce704b 100644
--- a/src-self-hosted/module.zig
+++ b/src-self-hosted/module.zig
@@ -11,9 +11,11 @@ const warn = std.debug.warn;
const Token = std.zig.Token;
const ArrayList = std.ArrayList;
const errmsg = @import("errmsg.zig");
+const ast = std.zig.ast;
+const event = std.event;
pub const Module = struct {
- allocator: *mem.Allocator,
+ loop: *event.Loop,
name: Buffer,
root_src_path: ?[]const u8,
module: llvm.ModuleRef,
@@ -76,6 +78,50 @@ pub const Module = struct {
kind: Kind,
+ link_out_file: ?[]const u8,
+ events: *event.Channel(Event),
+
+ // TODO handle some of these earlier and report them in a way other than error codes
+ pub const BuildError = error{
+ OutOfMemory,
+ EndOfStream,
+ BadFd,
+ Io,
+ IsDir,
+ Unexpected,
+ SystemResources,
+ SharingViolation,
+ PathAlreadyExists,
+ FileNotFound,
+ AccessDenied,
+ PipeBusy,
+ FileTooBig,
+ SymLinkLoop,
+ ProcessFdQuotaExceeded,
+ NameTooLong,
+ SystemFdQuotaExceeded,
+ NoDevice,
+ PathNotFound,
+ NoSpaceLeft,
+ NotDir,
+ FileSystem,
+ OperationAborted,
+ IoPending,
+ BrokenPipe,
+ WouldBlock,
+ FileClosed,
+ DestinationAddressRequired,
+ DiskQuota,
+ InputOutput,
+ NoStdHandles,
+ };
+
+ pub const Event = union(enum) {
+ Ok,
+ Fail: []errmsg.Msg,
+ Error: BuildError,
+ };
+
pub const DarwinVersionMin = union(enum) {
None,
MacOS: []const u8,
@@ -104,7 +150,7 @@ pub const Module = struct {
};
pub fn create(
- allocator: *mem.Allocator,
+ loop: *event.Loop,
name: []const u8,
root_src_path: ?[]const u8,
target: *const Target,
@@ -113,7 +159,7 @@ pub const Module = struct {
zig_lib_dir: []const u8,
cache_dir: []const u8,
) !*Module {
- var name_buffer = try Buffer.init(allocator, name);
+ var name_buffer = try Buffer.init(loop.allocator, name);
errdefer name_buffer.deinit();
const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
@@ -125,8 +171,12 @@ pub const Module = struct {
const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
errdefer c.LLVMDisposeBuilder(builder);
- const module_ptr = try allocator.create(Module{
- .allocator = allocator,
+ const events = try event.Channel(Event).create(loop, 0);
+ errdefer events.destroy();
+
+ return loop.allocator.create(Module{
+ .loop = loop,
+ .events = events,
.name = name_buffer,
.root_src_path = root_src_path,
.module = module,
@@ -171,7 +221,7 @@ pub const Module = struct {
.link_objects = [][]const u8{},
.windows_subsystem_windows = false,
.windows_subsystem_console = false,
- .link_libs_list = ArrayList(*LinkLib).init(allocator),
+ .link_libs_list = ArrayList(*LinkLib).init(loop.allocator),
.libc_link_lib = null,
.err_color = errmsg.Color.Auto,
.darwin_frameworks = [][]const u8{},
@@ -179,9 +229,8 @@ pub const Module = struct {
.test_filters = [][]const u8{},
.test_name_prefix = null,
.emit_file_type = Emit.Binary,
+ .link_out_file = null,
});
- errdefer allocator.destroy(module_ptr);
- return module_ptr;
}
fn dump(self: *Module) void {
@@ -189,58 +238,70 @@ pub const Module = struct {
}
pub fn destroy(self: *Module) void {
+ self.events.destroy();
c.LLVMDisposeBuilder(self.builder);
c.LLVMDisposeModule(self.module);
c.LLVMContextDispose(self.context);
self.name.deinit();
- self.allocator.destroy(self);
+ self.a().destroy(self);
}
pub fn build(self: *Module) !void {
if (self.llvm_argv.len != 0) {
- var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator, [][]const []const u8{
+ var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{
[][]const u8{"zig (LLVM option parsing)"},
self.llvm_argv,
});
defer c_compatible_args.deinit();
+ // TODO this sets global state
c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
}
+ _ = try async self.buildAsync();
+ }
+
+ async fn buildAsync(self: *Module) void {
+ while (true) {
+ // TODO directly awaiting async should guarantee memory allocation elision
+ // TODO also async before suspending should guarantee memory allocation elision
+ (await (async self.addRootSrc() catch unreachable)) catch |err| {
+ await (async self.events.put(Event{ .Error = err }) catch unreachable);
+ return;
+ };
+ await (async self.events.put(Event.Ok) catch unreachable);
+ }
+ }
+
+ async fn addRootSrc(self: *Module) !void {
const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
- const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| {
+ const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {
try printError("unable to get real path '{}': {}", root_src_path, err);
return err;
};
- errdefer self.allocator.free(root_src_real_path);
+ errdefer self.a().free(root_src_real_path);
- const source_code = io.readFileAlloc(self.allocator, root_src_real_path) catch |err| {
+ const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {
try printError("unable to open '{}': {}", root_src_real_path, err);
return err;
};
- errdefer self.allocator.free(source_code);
+ errdefer self.a().free(source_code);
- warn("====input:====\n");
-
- warn("{}", source_code);
-
- warn("====parse:====\n");
-
- var tree = try std.zig.parse(self.allocator, source_code);
+ var tree = try std.zig.parse(self.a(), source_code);
defer tree.deinit();
- var stderr_file = try std.io.getStdErr();
- var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
- const out_stream = &stderr_file_out_stream.stream;
-
- warn("====fmt:====\n");
- _ = try std.zig.render(self.allocator, out_stream, &tree);
-
- warn("====ir:====\n");
- warn("TODO\n\n");
-
- warn("====llvm ir:====\n");
- self.dump();
+ //var it = tree.root_node.decls.iterator();
+ //while (it.next()) |decl_ptr| {
+ // const decl = decl_ptr.*;
+ // switch (decl.id) {
+ // ast.Node.Comptime => @panic("TODO"),
+ // ast.Node.VarDecl => @panic("TODO"),
+ // ast.Node.UseDecl => @panic("TODO"),
+ // ast.Node.FnDef => @panic("TODO"),
+ // ast.Node.TestDecl => @panic("TODO"),
+ // else => unreachable,
+ // }
+ //}
}
pub fn link(self: *Module, out_file: ?[]const u8) !void {
@@ -263,11 +324,11 @@ pub const Module = struct {
}
}
- const link_lib = try self.allocator.create(LinkLib{
+ const link_lib = try self.a().create(LinkLib{
.name = name,
.path = null,
.provided_explicitly = provided_explicitly,
- .symbols = ArrayList([]u8).init(self.allocator),
+ .symbols = ArrayList([]u8).init(self.a()),
});
try self.link_libs_list.append(link_lib);
if (is_libc) {
@@ -275,6 +336,10 @@ pub const Module = struct {
}
return link_lib;
}
+
+ fn a(self: Module) *mem.Allocator {
+ return self.loop.allocator;
+ }
};
fn printError(comptime format: []const u8, args: ...) !void {
diff --git a/std/atomic/queue_mpsc.zig b/std/atomic/queue_mpsc.zig
index 66eb4573df3e96b40e7c639a27836f9f88dbc90e..8030565d7ae17f53dcf455cbb2df83e3dfb77364 100644
--- a/std/atomic/queue_mpsc.zig
+++ b/std/atomic/queue_mpsc.zig
@@ -1,4 +1,4 @@
-const std = @import("std");
+const std = @import("../index.zig");
const assert = std.debug.assert;
const builtin = @import("builtin");
const AtomicOrder = builtin.AtomicOrder;
diff --git a/std/event.zig b/std/event.zig
index 0821c789b7ea7d1993c157406a01710fe7edb32f..7f823bc732e5bb900f95ee503ea1167958918b96 100644
--- a/std/event.zig
+++ b/std/event.zig
@@ -4,6 +4,8 @@ const assert = std.debug.assert;
const event = this;
const mem = std.mem;
const posix = std.os.posix;
+const AtomicRmwOp = builtin.AtomicRmwOp;
+const AtomicOrder = builtin.AtomicOrder;
pub const TcpServer = struct {
handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void,
@@ -95,16 +97,29 @@ pub const Loop = struct {
allocator: *mem.Allocator,
epollfd: i32,
keep_running: bool,
+ next_tick_queue: std.atomic.QueueMpsc(promise),
- fn init(allocator: *mem.Allocator) !Loop {
+ pub const NextTickNode = std.atomic.QueueMpsc(promise).Node;
+
+ /// The allocator must be thread-safe because we use it for multiplexing
+ /// coroutines onto kernel threads.
+ pub fn init(allocator: *mem.Allocator) !Loop {
const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
+ errdefer std.os.close(epollfd);
+
return Loop{
.keep_running = true,
.allocator = allocator,
.epollfd = epollfd,
+ .next_tick_queue = std.atomic.QueueMpsc(promise).init(),
};
}
+ /// must call stop before deinit
+ pub fn deinit(self: *Loop) void {
+ std.os.close(self.epollfd);
+ }
+
pub fn addFd(self: *Loop, fd: i32, prom: promise) !void {
var ev = std.os.linux.epoll_event{
.events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET,
@@ -126,11 +141,21 @@ pub const Loop = struct {
pub fn stop(self: *Loop) void {
// TODO make atomic
self.keep_running = false;
- // TODO activate an fd in the epoll set
+ // TODO activate an fd in the epoll set which should cancel all the promises
+ }
+
+ /// bring your own linked list node. this means it can't fail.
+ pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
+ self.next_tick_queue.put(node);
}
pub fn run(self: *Loop) void {
while (self.keep_running) {
+ // TODO multiplex the next tick queue and the epoll event results onto a thread pool
+ while (self.next_tick_queue.get()) |node| {
+ resume node.data;
+ }
+ if (!self.keep_running) break;
var events: [16]std.os.linux.epoll_event = undefined;
const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1);
for (events[0..count]) |ev| {
@@ -141,6 +166,215 @@ pub const Loop = struct {
}
};
+/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size
+/// when buffer is empty, consumers suspend and are resumed by producers
+/// when buffer is full, producers suspend and are resumed by consumers
+pub fn Channel(comptime T: type) type {
+ return struct {
+ loop: *Loop,
+
+ getters: std.atomic.QueueMpsc(GetNode),
+ putters: std.atomic.QueueMpsc(PutNode),
+ get_count: usize,
+ put_count: usize,
+ dispatch_lock: u8, // TODO make this a bool
+ need_dispatch: u8, // TODO make this a bool
+
+ // simple fixed size ring buffer
+ buffer_nodes: []T,
+ buffer_index: usize,
+ buffer_len: usize,
+
+ const SelfChannel = this;
+ const GetNode = struct {
+ ptr: *T,
+ tick_node: *Loop.NextTickNode,
+ };
+ const PutNode = struct {
+ data: T,
+ tick_node: *Loop.NextTickNode,
+ };
+
+ /// call destroy when done
+ pub fn create(loop: *Loop, capacity: usize) !*SelfChannel {
+ const buffer_nodes = try loop.allocator.alloc(T, capacity);
+ errdefer loop.allocator.free(buffer_nodes);
+
+ const self = try loop.allocator.create(SelfChannel{
+ .loop = loop,
+ .buffer_len = 0,
+ .buffer_nodes = buffer_nodes,
+ .buffer_index = 0,
+ .dispatch_lock = 0,
+ .need_dispatch = 0,
+ .getters = std.atomic.QueueMpsc(GetNode).init(),
+ .putters = std.atomic.QueueMpsc(PutNode).init(),
+ .get_count = 0,
+ .put_count = 0,
+ });
+ errdefer loop.allocator.destroy(self);
+
+ return self;
+ }
+
+ /// must be called when all calls to put and get have suspended and no more calls occur
+ pub fn destroy(self: *SelfChannel) void {
+ while (self.getters.get()) |get_node| {
+ cancel get_node.data.tick_node.data;
+ }
+ while (self.putters.get()) |put_node| {
+ cancel put_node.data.tick_node.data;
+ }
+ self.loop.allocator.free(self.buffer_nodes);
+ self.loop.allocator.destroy(self);
+ }
+
+ /// puts a data item in the channel. The promise completes when the value has been added to the
+ /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
+ pub async fn put(self: *SelfChannel, data: T) void {
+ // TODO should be able to group memory allocation failure before first suspend point
+ // so that the async invocation catches it
+ var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined;
+ _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable;
+
+ suspend |handle| {
+ var my_tick_node = Loop.NextTickNode{
+ .next = undefined,
+ .data = handle,
+ };
+ var queue_node = std.atomic.QueueMpsc(PutNode).Node{
+ .data = PutNode{
+ .tick_node = &my_tick_node,
+ .data = data,
+ },
+ .next = undefined,
+ };
+ self.putters.put(&queue_node);
+ _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
+
+ self.loop.onNextTick(dispatch_tick_node_ptr);
+ }
+ }
+
+ /// await this function to get an item from the channel. If the buffer is empty, the promise will
+ /// complete when the next item is put in the channel.
+ pub async fn get(self: *SelfChannel) T {
+ // TODO should be able to group memory allocation failure before first suspend point
+ // so that the async invocation catches it
+ var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined;
+ _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable;
+
+ // TODO integrate this function with named return values
+ // so we can get rid of this extra result copy
+ var result: T = undefined;
+ var debug_handle: usize = undefined;
+ suspend |handle| {
+ debug_handle = @ptrToInt(handle);
+ var my_tick_node = Loop.NextTickNode{
+ .next = undefined,
+ .data = handle,
+ };
+ var queue_node = std.atomic.QueueMpsc(GetNode).Node{
+ .data = GetNode{
+ .ptr = &result,
+ .tick_node = &my_tick_node,
+ },
+ .next = undefined,
+ };
+ self.getters.put(&queue_node);
+ _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
+
+ self.loop.onNextTick(dispatch_tick_node_ptr);
+ }
+ return result;
+ }
+
+ async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void {
+ // resumed by onNextTick
+ suspend |handle| {
+ var tick_node = Loop.NextTickNode{
+ .data = handle,
+ .next = undefined,
+ };
+ tick_node_ptr.* = &tick_node;
+ }
+
+ // set the "need dispatch" flag
+ _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
+
+ lock: while (true) {
+ // set the lock flag
+ const prev_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
+ if (prev_lock != 0) return;
+
+ // clear the need_dispatch flag since we're about to do it
+ _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
+
+ while (true) {
+ one_dispatch: {
+ // later we correct these extra subtractions
+ var get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
+ var put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
+
+ // transfer self.buffer to self.getters
+ while (self.buffer_len != 0) {
+ if (get_count == 0) break :one_dispatch;
+
+ const get_node = &self.getters.get().?.data;
+ get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
+ self.loop.onNextTick(get_node.tick_node);
+ self.buffer_len -= 1;
+
+ get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
+ }
+
+ // direct transfer self.putters to self.getters
+ while (get_count != 0 and put_count != 0) {
+ const get_node = &self.getters.get().?.data;
+ const put_node = &self.putters.get().?.data;
+
+ get_node.ptr.* = put_node.data;
+ self.loop.onNextTick(get_node.tick_node);
+ self.loop.onNextTick(put_node.tick_node);
+
+ get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
+ put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
+ }
+
+ // transfer self.putters to self.buffer
+ while (self.buffer_len != self.buffer_nodes.len and put_count != 0) {
+ const put_node = &self.putters.get().?.data;
+
+ self.buffer_nodes[self.buffer_index] = put_node.data;
+ self.loop.onNextTick(put_node.tick_node);
+ self.buffer_index +%= 1;
+ self.buffer_len += 1;
+
+ put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
+ }
+ }
+
+ // undo the extra subtractions
+ _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
+ _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
+
+ // clear need-dispatch flag
+ const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
+ if (need_dispatch != 0) continue;
+
+ const my_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
+ assert(my_lock != 0);
+
+ // we have to check again now that we unlocked
+ if (@atomicLoad(u8, &self.need_dispatch, AtomicOrder.SeqCst) != 0) continue :lock;
+
+ return;
+ }
+ }
+ }
+ };
+}
+
pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File {
var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733
@@ -199,6 +433,7 @@ test "listen on a port, send bytes, receive bytes" {
defer cancel p;
loop.run();
}
+
async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void {
errdefer @panic("test failure");
@@ -211,3 +446,43 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void {
assert(mem.eql(u8, msg, "hello from server\n"));
loop.stop();
}
+
+test "std.event.Channel" {
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+
+ const allocator = &da.allocator;
+
+ var loop = try Loop.init(allocator);
+ defer loop.deinit();
+
+ const channel = try Channel(i32).create(&loop, 0);
+ defer channel.destroy();
+
+ const handle = try async testChannelGetter(&loop, channel);
+ defer cancel handle;
+
+ const putter = try async testChannelPutter(channel);
+ defer cancel putter;
+
+ loop.run();
+}
+
+async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
+ errdefer @panic("test failed");
+
+ const value1_promise = try async channel.get();
+ const value1 = await value1_promise;
+ assert(value1 == 1234);
+
+ const value2_promise = try async channel.get();
+ const value2 = await value2_promise;
+ assert(value2 == 4567);
+
+ loop.stop();
+}
+
+async fn testChannelPutter(channel: *Channel(i32)) void {
+ await (async channel.put(1234) catch @panic("out of memory"));
+ await (async channel.put(4567) catch @panic("out of memory"));
+}
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index bf12e86fef1a5dd16838baa286544884588262ec..c3c17f53220197d8d6fc02004da79bcd89de0b51 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -130,6 +130,9 @@ pub fn formatType(
try output(context, "error.");
return output(context, @errorName(value));
},
+ builtin.TypeId.Promise => {
+ return format(context, Errors, output, "promise@{x}", @ptrToInt(value));
+ },
builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {
builtin.TypeId.Array => |info| {
diff --git a/std/heap.zig b/std/heap.zig
index 41d7802fdd03efeebb7f730c86c6fda6cae47ce5..2e02733da1cd6e460651c492ce7fd57daef4e0e0 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -38,6 +38,7 @@ fn cFree(self: *Allocator, old_mem: []u8) void {
}
/// This allocator makes a syscall directly for every allocation and free.
+/// TODO make this thread-safe. The windows implementation will need some atomics.
pub const DirectAllocator = struct {
allocator: Allocator,
heap_handle: ?HeapHandle,
--
2.54.0
From 96a6bc57d20cff5171437a483422f53b0231a03d Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 2 Jul 2018 14:30:51 -0400
Subject: [PATCH 79/82] modify std.event.Loop to work for windows and macos
---
std/event.zig | 67 +++++++++++++++++++++++++++++++++++++++------------
1 file changed, 52 insertions(+), 15 deletions(-)
diff --git a/std/event.zig b/std/event.zig
index 7f823bc732e5bb900f95ee503ea1167958918b96..c6ac04a9d030de304e351fd7bccce2a53488d49a 100644
--- a/std/event.zig
+++ b/std/event.zig
@@ -95,29 +95,56 @@ pub const TcpServer = struct {
pub const Loop = struct {
allocator: *mem.Allocator,
- epollfd: i32,
keep_running: bool,
next_tick_queue: std.atomic.QueueMpsc(promise),
+ os_data: OsData,
+
+ const OsData = switch (builtin.os) {
+ builtin.Os.linux => struct {
+ epollfd: i32,
+ },
+ else => struct {},
+ };
pub const NextTickNode = std.atomic.QueueMpsc(promise).Node;
/// The allocator must be thread-safe because we use it for multiplexing
/// coroutines onto kernel threads.
pub fn init(allocator: *mem.Allocator) !Loop {
- const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
- errdefer std.os.close(epollfd);
-
- return Loop{
+ var self = Loop{
.keep_running = true,
.allocator = allocator,
- .epollfd = epollfd,
+ .os_data = undefined,
.next_tick_queue = std.atomic.QueueMpsc(promise).init(),
};
+ try self.initOsData();
+ errdefer self.deinitOsData();
+
+ return self;
}
/// must call stop before deinit
pub fn deinit(self: *Loop) void {
- std.os.close(self.epollfd);
+ self.deinitOsData();
+ }
+
+ const InitOsDataError = std.os.LinuxEpollCreateError;
+
+ fn initOsData(self: *Loop) InitOsDataError!void {
+ switch (builtin.os) {
+ builtin.Os.linux => {
+ self.os_data.epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
+ errdefer std.os.close(self.os_data.epollfd);
+ },
+ else => {},
+ }
+ }
+
+ fn deinitOsData(self: *Loop) void {
+ switch (builtin.os) {
+ builtin.Os.linux => std.os.close(self.os_data.epollfd),
+ else => {},
+ }
}
pub fn addFd(self: *Loop, fd: i32, prom: promise) !void {
@@ -125,11 +152,11 @@ pub const Loop = struct {
.events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET,
.data = std.os.linux.epoll_data{ .ptr = @ptrToInt(prom) },
};
- try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev);
+ try std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev);
}
pub fn removeFd(self: *Loop, fd: i32) void {
- std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
+ std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
}
async fn waitFd(self: *Loop, fd: i32) !void {
defer self.removeFd(fd);
@@ -156,12 +183,22 @@ pub const Loop = struct {
resume node.data;
}
if (!self.keep_running) break;
- var events: [16]std.os.linux.epoll_event = undefined;
- const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1);
- for (events[0..count]) |ev| {
- const p = @intToPtr(promise, ev.data.ptr);
- resume p;
- }
+
+ self.dispatchOsEvents();
+ }
+ }
+
+ fn dispatchOsEvents(self: *Loop) void {
+ switch (builtin.os) {
+ builtin.Os.linux => {
+ var events: [16]std.os.linux.epoll_event = undefined;
+ const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
+ for (events[0..count]) |ev| {
+ const p = @intToPtr(promise, ev.data.ptr);
+ resume p;
+ }
+ },
+ else => {},
}
}
};
--
2.54.0
From 2da999372a1f7848af59b07fe14ef025354f4c51 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 2 Jul 2018 15:25:23 -0400
Subject: [PATCH 80/82] add another BuildError code
---
src-self-hosted/module.zig | 1 +
1 file changed, 1 insertion(+)
diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig
index 4fac7607902fc053def8c85728c02cb3ecce704b..c984610257b0554986625d6dc6058c5065bab080 100644
--- a/src-self-hosted/module.zig
+++ b/src-self-hosted/module.zig
@@ -114,6 +114,7 @@ pub const Module = struct {
DiskQuota,
InputOutput,
NoStdHandles,
+ Overflow,
};
pub const Event = union(enum) {
--
2.54.0
From 35463526cceb91243410bdab4d74e2d5b3c60f66 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 2 Jul 2018 15:49:49 -0400
Subject: [PATCH 81/82] add runtime safety for `@intToEnum`; add docs for
runtime safety
See #367
---
doc/langref.html.in | 225 +++++++++++++++++++++++++++++++++++-----
src/codegen.cpp | 19 +++-
test/runtime_safety.zig | 18 ++++
3 files changed, 233 insertions(+), 29 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 15e04459bd91a3512568fb933bdc3f12ea69f363..1da4205b89aceb5f8fc72b5942e8433479651840 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -6144,7 +6144,14 @@ fn assert(ok: bool) void {
if (!ok) unreachable; // assertion failure
}
{#code_end#}
- At runtime crashes with the message reached unreachable code and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ std.debug.assert(false);
+}
+ {#code_end#}
{#header_close#}
{#header_open|Index out of Bounds#}
At compile-time:
@@ -6154,7 +6161,16 @@ comptime {
const garbage = array[5];
}
{#code_end#}
- At runtime crashes with the message index out of bounds and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+pub fn main() void {
+ var x = foo("hello");
+}
+
+fn foo(x: []const u8) u8 {
+ return x[5];
+}
+ {#code_end#}
{#header_close#}
{#header_open|Cast Negative Number to Unsigned Integer#}
At compile-time:
@@ -6164,10 +6180,18 @@ comptime {
const unsigned = @intCast(u32, value);
}
{#code_end#}
- At runtime crashes with the message attempt to cast negative value to unsigned integer and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ var value: i32 = -1;
+ var unsigned = @intCast(u32, value);
+ std.debug.warn("value: {}\n", unsigned);
+}
+ {#code_end#}
- If you are trying to obtain the maximum value of an unsigned integer, use @maxValue(T),
- where T is the integer type, such as u32.
+ To obtain the maximum value of an unsigned integer, use {#link|@maxValue#}.
{#header_close#}
{#header_open|Cast Truncates Data#}
@@ -6178,11 +6202,18 @@ comptime {
const byte = @intCast(u8, spartan_count);
}
{#code_end#}
- At runtime crashes with the message integer cast truncated bits and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ var spartan_count: u16 = 300;
+ const byte = @intCast(u8, spartan_count);
+ std.debug.warn("value: {}\n", byte);
+}
+ {#code_end#}
- If you are trying to truncate bits, use @truncate(T, value),
- where T is the integer type, such as u32, and value
- is the value you want to truncate.
+ To truncate bits, use {#link|@truncate#}.
{#header_close#}
{#header_open|Integer Overflow#}
@@ -6194,9 +6225,9 @@ comptime {
- (negation)
* (multiplication)
/ (division)
- @divTrunc (division)
- @divFloor (division)
- @divExact (division)
+ - {#link|@divTrunc#} (division)
+ - {#link|@divFloor#} (division)
+ - {#link|@divExact#} (division)
Example with addition at compile-time:
{#code_begin|test_err|operation caused overflow#}
@@ -6205,7 +6236,16 @@ comptime {
byte += 1;
}
{#code_end#}
- At runtime crashes with the message integer overflow and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ var byte: u8 = 255;
+ byte += 1;
+ std.debug.warn("value: {}\n", byte);
+}
+ {#code_end#}
{#header_close#}
{#header_open|Standard Library Math Functions#}
These functions provided by the standard library return possible errors.
@@ -6240,13 +6280,13 @@ pub fn main() !void {
occurred, as well as returning the overflowed bits:
- @addWithOverflow
- @subWithOverflow
- @mulWithOverflow
- @shlWithOverflow
+ - {#link|@addWithOverflow#}
+ - {#link|@subWithOverflow#}
+ - {#link|@mulWithOverflow#}
+ - {#link|@shlWithOverflow#}
- Example of @addWithOverflow:
+ Example of {#link|@addWithOverflow#}:
{#code_begin|exe#}
const warn = @import("std").debug.warn;
@@ -6292,7 +6332,16 @@ comptime {
const x = @shlExact(u8(0b01010101), 2);
}
{#code_end#}
- At runtime crashes with the message left shift overflowed bits and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ var x: u8 = 0b01010101;
+ var y = @shlExact(x, 2);
+ std.debug.warn("value: {}\n", y);
+}
+ {#code_end#}
{#header_close#}
{#header_open|Exact Right Shift Overflow#}
At compile-time:
@@ -6301,7 +6350,16 @@ comptime {
const x = @shrExact(u8(0b10101010), 2);
}
{#code_end#}
- At runtime crashes with the message right shift overflowed bits and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ var x: u8 = 0b10101010;
+ var y = @shrExact(x, 2);
+ std.debug.warn("value: {}\n", y);
+}
+ {#code_end#}
{#header_close#}
{#header_open|Division by Zero#}
At compile-time:
@@ -6312,8 +6370,17 @@ comptime {
const c = a / b;
}
{#code_end#}
- At runtime crashes with the message division by zero and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+pub fn main() void {
+ var a: u32 = 1;
+ var b: u32 = 0;
+ var c = a / b;
+ std.debug.warn("value: {}\n", c);
+}
+ {#code_end#}
{#header_close#}
{#header_open|Remainder Division by Zero#}
At compile-time:
@@ -6324,14 +6391,57 @@ comptime {
const c = a % b;
}
{#code_end#}
- At runtime crashes with the message remainder division by zero and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+pub fn main() void {
+ var a: u32 = 10;
+ var b: u32 = 0;
+ var c = a % b;
+ std.debug.warn("value: {}\n", c);
+}
+ {#code_end#}
{#header_close#}
{#header_open|Exact Division Remainder#}
- TODO
+ At compile-time:
+ {#code_begin|test_err|exact division had a remainder#}
+comptime {
+ const a: u32 = 10;
+ const b: u32 = 3;
+ const c = @divExact(a, b);
+}
+ {#code_end#}
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ var a: u32 = 10;
+ var b: u32 = 3;
+ var c = @divExact(a, b);
+ std.debug.warn("value: {}\n", c);
+}
+ {#code_end#}
{#header_close#}
{#header_open|Slice Widen Remainder#}
- TODO
+ At compile-time:
+ {#code_begin|test_err|unable to convert#}
+comptime {
+ var bytes = [5]u8{ 1, 2, 3, 4, 5 };
+ var slice = @bytesToSlice(u32, bytes);
+}
+ {#code_end#}
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ var bytes = [5]u8{ 1, 2, 3, 4, 5 };
+ var slice = @bytesToSlice(u32, bytes[0..]);
+ std.debug.warn("value: {}\n", slice[0]);
+}
+ {#code_end#}
{#header_close#}
{#header_open|Attempt to Unwrap Null#}
At compile-time:
@@ -6341,7 +6451,16 @@ comptime {
const number = optional_number.?;
}
{#code_end#}
- At runtime crashes with the message attempt to unwrap null and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ var optional_number: ?i32 = null;
+ var number = optional_number.?;
+ std.debug.warn("value: {}\n", number);
+}
+ {#code_end#}
One way to avoid this crash is to test for null instead of assuming non-null, with
the if expression:
{#code_begin|exe|test#}
@@ -6356,6 +6475,7 @@ pub fn main() void {
}
}
{#code_end#}
+ {#see_also|Optionals#}
{#header_close#}
{#header_open|Attempt to Unwrap Error#}
At compile-time:
@@ -6368,7 +6488,19 @@ fn getNumberOrFail() !i32 {
return error.UnableToReturnNumber;
}
{#code_end#}
- At runtime crashes with the message attempt to unwrap error: ErrorCode and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ const number = getNumberOrFail() catch unreachable;
+ std.debug.warn("value: {}\n", number);
+}
+
+fn getNumberOrFail() !i32 {
+ return error.UnableToReturnNumber;
+}
+ {#code_end#}
One way to avoid this crash is to test for an error instead of assuming a successful result, with
the if expression:
{#code_begin|exe#}
@@ -6388,6 +6520,7 @@ fn getNumberOrFail() !i32 {
return error.UnableToReturnNumber;
}
{#code_end#}
+ {#see_also|Errors#}
{#header_close#}
{#header_open|Invalid Error Code#}
At compile-time:
@@ -6398,11 +6531,47 @@ comptime {
const invalid_err = @intToError(number);
}
{#code_end#}
- At runtime crashes with the message invalid error code and a stack trace.
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+
+pub fn main() void {
+ var err = error.AnError;
+ var number = @errorToInt(err) + 500;
+ var invalid_err = @intToError(number);
+ std.debug.warn("value: {}\n", number);
+}
+ {#code_end#}
{#header_close#}
{#header_open|Invalid Enum Cast#}
- TODO
+ At compile-time:
+ {#code_begin|test_err|has no tag matching integer value 3#}
+const Foo = enum {
+ A,
+ B,
+ C,
+};
+comptime {
+ const a: u2 = 3;
+ const b = @intToEnum(Foo, a);
+}
+ {#code_end#}
+ At runtime:
+ {#code_begin|exe_err#}
+const std = @import("std");
+const Foo = enum {
+ A,
+ B,
+ C,
+};
+
+pub fn main() void {
+ var a: u2 = 3;
+ var b = @intToEnum(Foo, a);
+ std.debug.warn("value: {}\n", @tagName(b));
+}
+ {#code_end#}
{#header_close#}
{#header_open|Invalid Error Set Cast#}
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 4419f4fc8437f10acbfb9b7b9d7ce2ae2baa7dfb..9c37c174d695811f0ef002ca14a151896461f2ab 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -2673,8 +2673,25 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable,
TypeTableEntry *tag_int_type = wanted_type->data.enumeration.tag_int_type;
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
- return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
+ LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
instruction->target->value.type, tag_int_type, target_val);
+
+ if (ir_want_runtime_safety(g, &instruction->base)) {
+ LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue");
+ LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue");
+ size_t field_count = wanted_type->data.enumeration.src_field_count;
+ LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count);
+ for (size_t field_i = 0; field_i < field_count; field_i += 1) {
+ LLVMValueRef this_tag_int_value = bigint_to_llvm_const(tag_int_type->type_ref,
+ &wanted_type->data.enumeration.fields[field_i].value);
+ LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block);
+ }
+ LLVMPositionBuilderAtEnd(g->builder, bad_value_block);
+ gen_safety_crash(g, PanicMsgIdBadEnumValue);
+
+ LLVMPositionBuilderAtEnd(g->builder, ok_value_block);
+ }
+ return tag_int_value;
}
static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) {
diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig
index a7e8d6dc0e3080afbb3716c4edbbe923dd909d04..3d58dfe7487199c2946e28d369a753e926824a4b 100644
--- a/test/runtime_safety.zig
+++ b/test/runtime_safety.zig
@@ -1,6 +1,24 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompareOutputContext) void {
+ cases.addRuntimeSafety("@intToEnum - no matching tag value",
+ \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\const Foo = enum {
+ \\ A,
+ \\ B,
+ \\ C,
+ \\};
+ \\pub fn main() void {
+ \\ baz(bar(3));
+ \\}
+ \\fn bar(a: u2) Foo {
+ \\ return @intToEnum(Foo, a);
+ \\}
+ \\fn baz(a: Foo) void {}
+ );
+
cases.addRuntimeSafety("@floatToInt cannot fit - negative to unsigned",
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
--
2.54.0
From 06e8c2e5194439ce5b66f18fcf60108604449957 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 2 Jul 2018 17:55:32 -0400
Subject: [PATCH 82/82] fix stage2 macos build
See #1173
---
src-self-hosted/module.zig | 1 +
1 file changed, 1 insertion(+)
diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig
index c984610257b0554986625d6dc6058c5065bab080..cf27c826c8d33c6f3b718aed476030723f571fdd 100644
--- a/src-self-hosted/module.zig
+++ b/src-self-hosted/module.zig
@@ -115,6 +115,7 @@ pub const Module = struct {
InputOutput,
NoStdHandles,
Overflow,
+ NotSupported,
};
pub const Event = union(enum) {
--
2.54.0