authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-21 14:43:55-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-21 14:44:35-04:00
log5f38d6e2e97829ed74f06a96b5d07a2c68516063
tree6278f9d0ffeb86329094d8d0715dee8201c6ca65
parent47dd1049c8ef4064084af05e7b62becf502e41e6

add casting docs, __extenddftf2, and __extendsftf2


8 files changed, 384 insertions(+), 18 deletions(-)

CMakeLists.txt+1
......@@ -558,6 +558,7 @@ set(ZIG_STD_FILES
558558 "special/compiler_rt/aullrem.zig"
559559 "special/compiler_rt/comparetf2.zig"
560560 "special/compiler_rt/divti3.zig"
561 "special/compiler_rt/extendXfYf2.zig"
561562 "special/compiler_rt/fixuint.zig"
562563 "special/compiler_rt/fixunsdfdi.zig"
563564 "special/compiler_rt/fixunsdfsi.zig"
doc/langref.html.in+156-15
......@@ -3573,14 +3573,161 @@ const optional_value: ?i32 = null;
35733573 {#header_close#}
35743574 {#header_close#}
35753575 {#header_open|Casting#}
3576 <p>TODO: explain implicit vs explicit casting</p>
3577 <p>TODO: resolve peer types builtin</p>
3578 <p>TODO: truncate builtin</p>
3579 <p>TODO: bitcast builtin</p>
3580 <p>TODO: int to ptr builtin</p>
3581 <p>TODO: ptr to int builtin</p>
3582 <p>TODO: ptrcast builtin</p>
3583 <p>TODO: explain number literals vs concrete types</p>
3576 <p>
3577 A <strong>type cast</strong> converts a value of one type to another.
3578 Zig has {#link|Implicit Casts#} for conversions that are known to be completely safe and unambiguous,
3579 and {#link|Explicit Casts#} for conversions that one would not want to happen on accident.
3580 There is also a third kind of type conversion called {#link|Peer Type Resolution#} for
3581 the case when a result type must be decided given multiple operand types.
3582 </p>
3583 {#header_open|Implicit Casts#}
3584 <p>
3585 An implicit cast occurs when one type is expected, but different type is provided:
3586 </p>
3587 {#code_begin|test#}
3588test "implicit cast - variable declaration" {
3589 var a: u8 = 1;
3590 var b: u16 = a;
3591}
3592
3593test "implicit cast - function call" {
3594 var a: u8 = 1;
3595 foo(a);
3596}
3597
3598fn foo(b: u16) void {}
3599
3600test "implicit cast - invoke a type as a function" {
3601 var a: u8 = 1;
3602 var b = u16(a);
3603}
3604 {#code_end#}
3605 {#header_open|Implicit Cast: Stricter Qualification#}
3606 <p>
3607 Values which have the same representation at runtime can be cast to increase the strictness
3608 of the qualifiers, no matter how nested the qualifiers are:
3609 </p>
3610 <ul>
3611 <li><code>const</code> - non-const to const is allowed</li>
3612 <li><code>volatile</code> - non-volatile to volatile is allowed</li>
3613 <li><code>align</code> - bigger to smaller alignment is allowed </li>
3614 <li>{#link|error sets|Error Set Type#} to supersets is allowed</li>
3615 </ul>
3616 <p>
3617 These casts are no-ops at runtime since the value representation does not change.
3618 </p>
3619 {#code_begin|test#}
3620test "implicit cast - const qualification" {
3621 var a: i32 = 1;
3622 var b: *i32 = &a;
3623 foo(b);
3624}
3625
3626fn foo(a: *const i32) void {}
3627 {#code_end#}
3628 <p>
3629 In addition, pointers implicitly cast to const optional pointers:
3630 </p>
3631 {#code_begin|test#}
3632const std = @import("std");
3633const assert = std.debug.assert;
3634const mem = std.mem;
3635
3636test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
3637 const window_name = [1][*]const u8{c"window name"};
3638 const x: [*]const ?[*]const u8 = &window_name;
3639 assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name"));
3640}
3641 {#code_end#}
3642 {#header_close#}
3643 {#header_open|Implicit Cast: Integer and Float Widening#}
3644 <p>
3645 {#link|Integers#} implicitly cast to integer types which can represent every value of the old type, and likewise
3646 {#link|Floats#} implicitly cast to float types which can represent every value of the old type.
3647 </p>
3648 {#code_begin|test#}
3649const std = @import("std");
3650const assert = std.debug.assert;
3651const mem = std.mem;
3652
3653test "integer widening" {
3654 var a: u8 = 250;
3655 var b: u16 = a;
3656 var c: u32 = b;
3657 var d: u64 = c;
3658 var e: u64 = d;
3659 var f: u128 = e;
3660 assert(f == a);
3661}
3662
3663test "implicit unsigned integer to signed integer" {
3664 var a: u8 = 250;
3665 var b: i16 = a;
3666 assert(b == 250);
3667}
3668
3669test "float widening" {
3670 var a: f32 = 12.34;
3671 var b: f64 = a;
3672 var c: f128 = b;
3673 assert(c == a);
3674}
3675 {#code_end#}
3676 {#header_close#}
3677 {#header_open|Implicit Cast: Arrays#}
3678 <p>TODO: [N]T to []const T</p>
3679 <p>TODO: *const [N]T to []const T</p>
3680 <p>TODO: [N]T to *const []const T</p>
3681 <p>TODO: [N]T to ?[]const T</p>
3682 <p>TODO: *[N]T to []T</p>
3683 <p>TODO: *[N]T to [*]T</p>
3684 <p>TODO: *T to *[1]T</p>
3685 <p>TODO: [N]T to E![]const T</p>
3686 {#header_close#}
3687 {#header_open|Implicit Cast: Optionals#}
3688 <p>TODO: T to ?T</p>
3689 <p>TODO: T to E!?T</p>
3690 <p>TODO: null to ?T</p>
3691 {#header_close#}
3692 {#header_open|Implicit Cast: T to E!T#}
3693 <p>TODO</p>
3694 {#header_close#}
3695 {#header_open|Implicit Cast: E to E!T#}
3696 <p>TODO</p>
3697 {#header_close#}
3698 {#header_open|Implicit Cast: comptime_int to *const integer#}
3699 <p>TODO</p>
3700 {#header_close#}
3701 {#header_open|Implicit Cast: comptime_float to *const float#}
3702 <p>TODO</p>
3703 {#header_close#}
3704 {#header_open|Implicit Cast: compile-time known numbers#}
3705 <p>TODO</p>
3706 {#header_close#}
3707 {#header_open|Implicit Cast: union to enum#}
3708 <p>TODO</p>
3709 {#header_close#}
3710 {#header_open|Implicit Cast: enum to union#}
3711 <p>TODO</p>
3712 {#header_close#}
3713 {#header_open|Implicit Cast: T to *T when @sizeOf(T) == 0#}
3714 <p>TODO</p>
3715 {#header_close#}
3716 {#header_open|Implicit Cast: undefined#}
3717 <p>TODO</p>
3718 {#header_close#}
3719 {#header_open|Implicit Cast: T to *const T#}
3720 <p>TODO</p>
3721 {#header_close#}
3722 {#header_close#}
3723
3724 {#header_open|Explicit Casts#}
3725 <p>TODO</p>
3726 {#header_close#}
3727
3728 {#header_open|Peer Type Resolution#}
3729 <p>TODO</p>
3730 {#header_close#}
35843731 {#header_close#}
35853732
35863733 {#header_open|void#}
......@@ -5522,12 +5669,6 @@ pub const FloatMode = enum {
55225669 </p>
55235670 {#see_also|Compile Variables#}
55245671 {#header_close#}
5525 {#header_open|@setGlobalSection#}
5526 <pre><code class="zig">@setGlobalSection(global_variable_name, comptime section_name: []const u8) bool</code></pre>
5527 <p>
5528 Puts the global variable in the specified section.
5529 </p>
5530 {#header_close#}
55315672 {#header_open|@shlExact#}
55325673 <pre><code class="zig">@shlExact(value: T, shift_amt: Log2T) T</code></pre>
55335674 <p>
......@@ -6928,7 +7069,7 @@ hljs.registerLanguage("zig", function(t) {
69287069 a = t.IR + "\\s*\\(",
69297070 c = {
69307071 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",
6931 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",
7072 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",
69327073 literal: "true false null undefined"
69337074 },
69347075 n = [e, t.CLCM, t.CBCM, s, r];
src/ir.cpp+3-3
......@@ -10092,7 +10092,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1009210092 }
1009310093 }
1009410094
10095 // cast from &const [N]T to []const T
10095 // cast from *const [N]T to []const T
1009610096 if (is_slice(wanted_type) &&
1009710097 actual_type->id == TypeTableEntryIdPointer &&
1009810098 actual_type->data.pointer.is_const &&
......@@ -10111,7 +10111,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1011110111 }
1011210112 }
1011310113
10114 // cast from [N]T to &const []const T
10114 // cast from [N]T to *const []const T
1011510115 if (wanted_type->id == TypeTableEntryIdPointer &&
1011610116 wanted_type->data.pointer.is_const &&
1011710117 is_slice(wanted_type->data.pointer.child_type) &&
......@@ -10136,7 +10136,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1013610136 }
1013710137 }
1013810138
10139 // cast from [N]T to ?[]const N
10139 // cast from [N]T to ?[]const T
1014010140 if (wanted_type->id == TypeTableEntryIdOptional &&
1014110141 is_slice(wanted_type->data.maybe.child_type) &&
1014210142 actual_type->id == TypeTableEntryIdArray)
std/special/compiler_rt/extendXfYf2.zig created+87
......@@ -0,0 +1,87 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const is_test = builtin.is_test;
4
5pub extern fn __extenddftf2(a: f64) f128 {
6 return extendXfYf2(f128, f64, a);
7}
8
9pub extern fn __extendsftf2(a: f32) f128 {
10 return extendXfYf2(f128, f32, a);
11}
12
13const CHAR_BIT = 8;
14
15pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
16 const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits);
17 const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits);
18 const srcSigBits = std.math.floatMantissaBits(src_t);
19 const dstSigBits = std.math.floatMantissaBits(dst_t);
20 const SrcShift = std.math.Log2Int(src_rep_t);
21 const DstShift = std.math.Log2Int(dst_rep_t);
22
23 // Various constants whose values follow from the type parameters.
24 // Any reasonable optimizer will fold and propagate all of these.
25 const srcBits: i32 = @sizeOf(src_t) * CHAR_BIT;
26 const srcExpBits: i32 = srcBits - srcSigBits - 1;
27 const srcInfExp: i32 = (1 << srcExpBits) - 1;
28 const srcExpBias: i32 = srcInfExp >> 1;
29
30 const srcMinNormal: src_rep_t = src_rep_t(1) << srcSigBits;
31 const srcInfinity: src_rep_t = src_rep_t(@bitCast(u32, srcInfExp)) << srcSigBits;
32 const srcSignMask: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits +% srcExpBits);
33 const srcAbsMask: src_rep_t = srcSignMask -% 1;
34 const srcQNaN: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits -% 1);
35 const srcNaNCode: src_rep_t = srcQNaN -% 1;
36
37 const dstBits: i32 = @sizeOf(dst_t) * CHAR_BIT;
38 const dstExpBits: i32 = dstBits - dstSigBits - 1;
39 const dstInfExp: i32 = (1 << dstExpBits) - 1;
40 const dstExpBias: i32 = dstInfExp >> 1;
41
42 const dstMinNormal: dst_rep_t = dst_rep_t(1) << dstSigBits;
43
44 // Break a into a sign and representation of the absolute value
45 const aRep: src_rep_t = @bitCast(src_rep_t, a);
46 const aAbs: src_rep_t = aRep & srcAbsMask;
47 const sign: src_rep_t = aRep & srcSignMask;
48 var absResult: dst_rep_t = undefined;
49
50 // If @sizeOf(src_rep_t) < @sizeOf(int), the subtraction result is promoted
51 // to (signed) int. To avoid that, explicitly cast to src_rep_t.
52 if ((src_rep_t)(aAbs -% srcMinNormal) < srcInfinity -% srcMinNormal) {
53 // a is a normal number.
54 // Extend to the destination type by shifting the significand and
55 // exponent into the proper position and rebiasing the exponent.
56 absResult = dst_rep_t(aAbs) << (dstSigBits -% srcSigBits);
57 absResult += dst_rep_t(@bitCast(u32, dstExpBias -% srcExpBias)) << dstSigBits;
58 } else if (aAbs >= srcInfinity) {
59 // a is NaN or infinity.
60 // Conjure the result by beginning with infinity, then setting the qNaN
61 // bit (if needed) and right-aligning the rest of the trailing NaN
62 // payload field.
63 absResult = dst_rep_t(@bitCast(u32, dstInfExp)) << dstSigBits;
64 absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits);
65 absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits);
66 } else if (aAbs != 0) {
67 // a is denormal.
68 // renormalize the significand and clear the leading bit, then insert
69 // the correct adjusted exponent in the destination type.
70 const scale: i32 = @clz(aAbs) - @clz(srcMinNormal);
71 absResult = dst_rep_t(aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale);
72 absResult ^= dstMinNormal;
73 const resultExponent: i32 = dstExpBias - srcExpBias - scale + 1;
74 absResult |= dst_rep_t(@bitCast(u32, resultExponent)) << @intCast(DstShift, dstSigBits);
75 } else {
76 // a is zero.
77 absResult = 0;
78 }
79
80 // Apply the signbit to (dst_t)abs(a).
81 const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << @intCast(DstShift, dstBits - srcBits);
82 return @bitCast(dst_t, result);
83}
84
85test "import extendXfYf2" {
86 _ = @import("extendXfYf2_test.zig");
87}
std/special/compiler_rt/extendXfYf2_test.zig created+108
......@@ -0,0 +1,108 @@
1const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2;
2const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2;
3const assert = @import("std").debug.assert;
4
5fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void {
6 const x = __extenddftf2(a);
7
8 const rep = @bitCast(u128, x);
9 const hi = @intCast(u64, rep >> 64);
10 const lo = @truncate(u64, rep);
11
12 if (hi == expectedHi and lo == expectedLo)
13 return;
14
15 // test other possible NaN representation(signal NaN)
16 if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) {
17 if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and
18 ((hi & 0xffffffffffff) > 0 or lo > 0))
19 {
20 return;
21 }
22 }
23
24 @panic("__extenddftf2 test failure");
25}
26
27fn test__extendsftf2(a: f32, expectedHi: u64, expectedLo: u64) void {
28 const x = __extendsftf2(a);
29
30 const rep = @bitCast(u128, x);
31 const hi = @intCast(u64, rep >> 64);
32 const lo = @truncate(u64, rep);
33
34 if (hi == expectedHi and lo == expectedLo)
35 return;
36
37 // test other possible NaN representation(signal NaN)
38 if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) {
39 if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and
40 ((hi & 0xffffffffffff) > 0 or lo > 0))
41 {
42 return;
43 }
44 }
45
46 @panic("__extendsftf2 test failure");
47}
48
49test "extenddftf2" {
50 // qNaN
51 test__extenddftf2(makeQNaN64(), 0x7fff800000000000, 0x0);
52
53 // NaN
54 test__extenddftf2(makeNaN64(0x7100000000000), 0x7fff710000000000, 0x0);
55
56 // inf
57 test__extenddftf2(makeInf64(), 0x7fff000000000000, 0x0);
58
59 // zero
60 test__extenddftf2(0.0, 0x0, 0x0);
61
62 test__extenddftf2(0x1.23456789abcdefp+5, 0x400423456789abcd, 0xf000000000000000);
63
64 test__extenddftf2(0x1.edcba987654321fp-9, 0x3ff6edcba9876543, 0x2000000000000000);
65
66 test__extenddftf2(0x1.23456789abcdefp+45, 0x402c23456789abcd, 0xf000000000000000);
67
68 test__extenddftf2(0x1.edcba987654321fp-45, 0x3fd2edcba9876543, 0x2000000000000000);
69}
70
71test "extendsftf2" {
72 // qNaN
73 test__extendsftf2(makeQNaN32(), 0x7fff800000000000, 0x0);
74 // NaN
75 test__extendsftf2(makeNaN32(0x410000), 0x7fff820000000000, 0x0);
76 // inf
77 test__extendsftf2(makeInf32(), 0x7fff000000000000, 0x0);
78 // zero
79 test__extendsftf2(0.0, 0x0, 0x0);
80 test__extendsftf2(0x1.23456p+5, 0x4004234560000000, 0x0);
81 test__extendsftf2(0x1.edcbap-9, 0x3ff6edcba0000000, 0x0);
82 test__extendsftf2(0x1.23456p+45, 0x402c234560000000, 0x0);
83 test__extendsftf2(0x1.edcbap-45, 0x3fd2edcba0000000, 0x0);
84}
85
86fn makeQNaN64() f64 {
87 return @bitCast(f64, u64(0x7ff8000000000000));
88}
89
90fn makeInf64() f64 {
91 return @bitCast(f64, u64(0x7ff0000000000000));
92}
93
94fn makeNaN64(rand: u64) f64 {
95 return @bitCast(f64, 0x7ff0000000000000 | (rand & 0xfffffffffffff));
96}
97
98fn makeQNaN32() f32 {
99 return @bitCast(f32, u32(0x7fc00000));
100}
101
102fn makeNaN32(rand: u32) f32 {
103 return @bitCast(f32, 0x7f800000 | (rand & 0x7fffff));
104}
105
106fn makeInf32() f32 {
107 return @bitCast(f32, u32(0x7f800000));
108}
std/special/compiler_rt/index.zig+2
......@@ -20,6 +20,8 @@ comptime {
2020 @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage);
2121
2222 @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage);
23 @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage);
24 @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage);
2325
2426 @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage);
2527 @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage);
test/behavior.zig+1
......@@ -59,4 +59,5 @@ comptime {
5959 _ = @import("cases/var_args.zig");
6060 _ = @import("cases/void.zig");
6161 _ = @import("cases/while.zig");
62 _ = @import("cases/widening.zig");
6263}
test/cases/widening.zig created+26
......@@ -0,0 +1,26 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const mem = std.mem;
4
5test "integer widening" {
6 var a: u8 = 250;
7 var b: u16 = a;
8 var c: u32 = b;
9 var d: u64 = c;
10 var e: u64 = d;
11 var f: u128 = e;
12 assert(f == a);
13}
14
15test "implicit unsigned integer to signed integer" {
16 var a: u8 = 250;
17 var b: i16 = a;
18 assert(b == 250);
19}
20
21test "float widening" {
22 var a: f32 = 12.34;
23 var b: f64 = a;
24 var c: f128 = b;
25 assert(c == a);
26}