Merge pull request #11155 from ziglang/stage2-float-fixes

stage2 float fixes
This commit is contained in:
Andrew Kelley 2022-03-14 12:38:56 -04:00 committed by GitHub
commit 5919b10048
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 394 additions and 128 deletions

View File

@ -2515,13 +2515,14 @@ test "null terminated array" {
{#header_open|Vectors#}
<p>
A vector is a group of booleans, {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on
in parallel using SIMD instructions. Vector types are created with the builtin function {#link|@Type#},
or using the shorthand function {#syntax#}std.meta.Vector{#endsyntax#}.
A vector is a group of booleans, {#link|Integers#}, {#link|Floats#}, or
{#link|Pointers#} which are operated on in parallel, using SIMD instructions if possible.
Vector types are created with the builtin function {#link|@Vector#}.
</p>
<p>
Vectors support the same builtin operators as their underlying base types. These operations are performed
element-wise, and return a vector of the same length as the input vectors. This includes:
Vectors support the same builtin operators as their underlying base types.
These operations are performed element-wise, and return a vector of the same length
as the input vectors. This includes:
</p>
<ul>
<li>Arithmetic ({#syntax#}+{#endsyntax#}, {#syntax#}-{#endsyntax#}, {#syntax#}/{#endsyntax#}, {#syntax#}*{#endsyntax#},
@ -2532,10 +2533,11 @@ test "null terminated array" {
<li>Comparison operators ({#syntax#}<{#endsyntax#}, {#syntax#}>{#endsyntax#}, {#syntax#}=={#endsyntax#}, etc.)</li>
</ul>
<p>
It is prohibited to use a math operator on a mixture of scalars (individual numbers) and vectors.
Zig provides the {#link|@splat#} builtin to easily convert from scalars to vectors, and it supports {#link|@reduce#}
and array indexing syntax to convert from vectors to scalars. Vectors also support assignment to and from
fixed-length arrays with comptime known length.
It is prohibited to use a math operator on a mixture of scalars (individual numbers)
and vectors. Zig provides the {#link|@splat#} builtin to easily convert from scalars
to vectors, and it supports {#link|@reduce#} and array indexing syntax to convert
from vectors to scalars. Vectors also support assignment to and from fixed-length
arrays with comptime known length.
</p>
<p>
For rearranging elements within and between vectors, Zig provides the {#link|@shuffle#} and {#link|@select#} functions.
@ -2550,16 +2552,14 @@ test "null terminated array" {
</p>
{#code_begin|test|vector_example#}
const std = @import("std");
const Vector = std.meta.Vector;
const expectEqual = std.testing.expectEqual;
test "Basic vector usage" {
// Vectors have a compile-time known length and base type,
// and can be assigned to using array literal syntax
const a: Vector(4, i32) = [_]i32{ 1, 2, 3, 4 };
const b: Vector(4, i32) = [_]i32{ 5, 6, 7, 8 };
// Vectors have a compile-time known length and base type.
const a = @Vector(4, i32){ 1, 2, 3, 4 };
const b = @Vector(4, i32){ 5, 6, 7, 8 };
// Math operations take place element-wise
// Math operations take place element-wise.
const c = a + b;
// Individual vector elements can be accessed using array indexing syntax.
@ -2572,19 +2572,19 @@ test "Basic vector usage" {
test "Conversion between vectors, arrays, and slices" {
// Vectors and fixed-length arrays can be automatically assigned back and forth
var arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };
var vec: Vector(4, f32) = arr1;
var vec: @Vector(4, f32) = arr1;
var arr2: [4]f32 = vec;
try expectEqual(arr1, arr2);
// You can also assign from a slice with comptime-known length to a vector using .*
const vec2: Vector(2, f32) = arr1[1..3].*;
const vec2: @Vector(2, f32) = arr1[1..3].*;
var slice: []const f32 = &arr1;
var offset: u32 = 1;
// To extract a comptime-known length from a runtime-known offset,
// first extract a new slice from the starting offset, then an array of
// comptime known length
const vec3: Vector(2, f32) = slice[offset..][0..2].*;
const vec3: @Vector(2, f32) = slice[offset..][0..2].*;
try expectEqual(slice[offset], vec2[0]);
try expectEqual(slice[offset + 1], vec2[1]);
try expectEqual(vec2, vec3);
@ -9084,7 +9084,7 @@ pub const PrefetchOptions = struct {
{#header_close#}
{#header_open|@select#}
<pre>{#syntax#}@select(comptime T: type, pred: std.meta.Vector(len, bool), a: std.meta.Vector(len, T), b: std.meta.Vector(len, T)) std.meta.Vector(len, T){#endsyntax#}</pre>
<pre>{#syntax#}@select(comptime T: type, pred: @Vector(len, bool), a: @Vector(len, T), b: @Vector(len, T)) @Vector(len, T){#endsyntax#}</pre>
<p>
Selects values element-wise from {#syntax#}a{#endsyntax#} or {#syntax#}b{#endsyntax#} based on {#syntax#}pred{#endsyntax#}. If {#syntax#}pred[i]{#endsyntax#} is {#syntax#}true{#endsyntax#}, the corresponding element in the result will be {#syntax#}a[i]{#endsyntax#} and otherwise {#syntax#}b[i]{#endsyntax#}.
</p>
@ -9252,7 +9252,7 @@ test "@setRuntimeSafety" {
{#header_close#}
{#header_open|@shuffle#}
<pre>{#syntax#}@shuffle(comptime E: type, a: std.meta.Vector(a_len, E), b: std.meta.Vector(b_len, E), comptime mask: std.meta.Vector(mask_len, i32)) std.meta.Vector(mask_len, E){#endsyntax#}</pre>
<pre>{#syntax#}@shuffle(comptime E: type, a: @Vector(a_len, E), b: @Vector(b_len, E), comptime mask: @Vector(mask_len, i32)) @Vector(mask_len, E){#endsyntax#}</pre>
<p>
Constructs a new {#link|vector|Vectors#} by selecting elements from {#syntax#}a{#endsyntax#} and
{#syntax#}b{#endsyntax#} based on {#syntax#}mask{#endsyntax#}.
@ -9287,22 +9287,21 @@ test "@setRuntimeSafety" {
</p>
{#code_begin|test|vector_shuffle#}
const std = @import("std");
const Vector = std.meta.Vector;
const expect = std.testing.expect;
test "vector @shuffle" {
const a: Vector(7, u8) = [_]u8{ 'o', 'l', 'h', 'e', 'r', 'z', 'w' };
const b: Vector(4, u8) = [_]u8{ 'w', 'd', '!', 'x' };
const a = @Vector(7, u8){ 'o', 'l', 'h', 'e', 'r', 'z', 'w' };
const b = @Vector(4, u8){ 'w', 'd', '!', 'x' };
// To shuffle within a single vector, pass undefined as the second argument.
// Notice that we can re-order, duplicate, or omit elements of the input vector
const mask1: Vector(5, i32) = [_]i32{ 2, 3, 1, 1, 0 };
const res1: Vector(5, u8) = @shuffle(u8, a, undefined, mask1);
const mask1 = @Vector(5, i32){ 2, 3, 1, 1, 0 };
const res1: @Vector(5, u8) = @shuffle(u8, a, undefined, mask1);
try expect(std.mem.eql(u8, &@as([5]u8, res1), "hello"));
// Combining two vectors
const mask2: Vector(6, i32) = [_]i32{ -1, 0, 4, 1, -2, -3 };
const res2: Vector(6, u8) = @shuffle(u8, a, b, mask2);
const mask2 = @Vector(6, i32){ -1, 0, 4, 1, -2, -3 };
const res2: @Vector(6, u8) = @shuffle(u8, a, b, mask2);
try expect(std.mem.eql(u8, &@as([6]u8, res2), "world!"));
}
{#code_end#}
@ -9329,7 +9328,7 @@ test "vector @shuffle" {
{#header_close#}
{#header_open|@splat#}
<pre>{#syntax#}@splat(comptime len: u32, scalar: anytype) std.meta.Vector(len, @TypeOf(scalar)){#endsyntax#}</pre>
<pre>{#syntax#}@splat(comptime len: u32, scalar: anytype) @Vector(len, @TypeOf(scalar)){#endsyntax#}</pre>
<p>
Produces a vector of length {#syntax#}len{#endsyntax#} where each element is the value
{#syntax#}scalar{#endsyntax#}:
@ -9341,7 +9340,7 @@ const expect = std.testing.expect;
test "vector @splat" {
const scalar: u32 = 5;
const result = @splat(4, scalar);
comptime try expect(@TypeOf(result) == std.meta.Vector(4, u32));
comptime try expect(@TypeOf(result) == @Vector(4, u32));
try expect(std.mem.eql(u32, &@as([4]u32, result), &[_]u32{ 5, 5, 5, 5 }));
}
{#code_end#}
@ -9381,10 +9380,10 @@ const std = @import("std");
const expect = std.testing.expect;
test "vector @reduce" {
const value: std.meta.Vector(4, i32) = [_]i32{ 1, -1, 1, -1 };
const value = @Vector(4, i32){ 1, -1, 1, -1 };
const result = value > @splat(4, @as(i32, 0));
// result is { true, false, true, false };
comptime try expect(@TypeOf(result) == std.meta.Vector(4, bool));
comptime try expect(@TypeOf(result) == @Vector(4, bool));
const is_all_true = @reduce(.And, result);
comptime try expect(@TypeOf(is_all_true) == bool);
try expect(is_all_true == false);
@ -9743,6 +9742,12 @@ fn foo(comptime T: type, ptr: *T) T {
{#syntax#}@unionInit{#endsyntax#} forwards its {#link|result location|Result Location Semantics#} to {#syntax#}init_expr{#endsyntax#}.
</p>
{#header_close#}
{#header_open|@Vector#}
<pre>{#syntax#}@Vector(len: comptime_int, Element: type) type{#endsyntax#}</pre>
<p>Creates {#link|Vectors#}.</p>
{#header_close#}
{#header_close#}
{#header_open|Build Mode#}

View File

@ -20,6 +20,10 @@ pub fn round(x: anytype) @TypeOf(x) {
f32 => round32(x),
f64 => round64(x),
f128 => round128(x),
// TODO this is not correct for some targets
c_longdouble => @floatCast(c_longdouble, round128(x)),
else => @compileError("round not implemented for " ++ @typeName(T)),
};
}

View File

@ -930,6 +930,7 @@ test "std.meta.Float" {
try testing.expectEqual(f128, Float(128));
}
/// Deprecated. Use `@Vector`.
pub fn Vector(comptime len: u32, comptime child: type) type {
return @Type(.{
.Vector = .{

View File

@ -421,7 +421,7 @@ pub fn MultiArrayList(comptime S: type) type {
}
fn capacityInBytes(capacity: usize) usize {
const sizes_vector: std.meta.Vector(sizes.bytes.len, usize) = sizes.bytes;
const sizes_vector: @Vector(sizes.bytes.len, usize) = sizes.bytes;
const capacity_vector = @splat(sizes.bytes.len, capacity);
return @reduce(.Add, capacity_vector * sizes_vector);
}

View File

@ -98,6 +98,7 @@ comptime {
@export(round, .{ .name = "round", .linkage = .Strong });
@export(roundf, .{ .name = "roundf", .linkage = .Strong });
@export(roundl, .{ .name = "roundl", .linkage = .Strong });
@export(fmin, .{ .name = "fmin", .linkage = .Strong });
@export(fminf, .{ .name = "fminf", .linkage = .Strong });
@ -575,11 +576,18 @@ fn fabsf(a: f32) callconv(.C) f32 {
return math.fabs(a);
}
fn roundf(a: f32) callconv(.C) f32 {
return math.round(a);
}
fn round(a: f64) callconv(.C) f64 {
return math.round(a);
}
fn roundf(a: f32) callconv(.C) f32 {
fn roundl(a: c_longdouble) callconv(.C) c_longdouble {
if (!long_double_is_f128) {
@panic("TODO implement this");
}
return math.round(a);
}

View File

@ -4195,7 +4195,15 @@ fn zirDbgVar(
const str_op = sema.code.instructions.items(.data)[inst].str_op;
const operand = sema.resolveInst(str_op.operand);
const operand_ty = sema.typeOf(operand);
if (!(try sema.typeHasRuntimeBits(block, sema.src, operand_ty))) return;
switch (air_tag) {
.dbg_var_ptr => {
if (!(try sema.typeHasRuntimeBits(block, sema.src, operand_ty.childType()))) return;
},
.dbg_var_val => {
if (!(try sema.typeHasRuntimeBits(block, sema.src, operand_ty))) return;
},
else => unreachable,
}
const name = str_op.getStr(sema.code);
// Add the name to the AIR.
@ -13268,7 +13276,7 @@ fn checkFloatType(
ty: Type,
) CompileError!void {
switch (ty.zigTypeTag()) {
.ComptimeFloat, .Float => {},
.ComptimeInt, .ComptimeFloat, .Float => {},
else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty}),
}
}
@ -17169,10 +17177,25 @@ fn storePtr2(
return;
}
// TODO do the same thing for anon structs as for tuples above.
// Detect if we are storing an array operand to a bitcasted vector pointer.
// If so, we instead reach through the bitcasted pointer to the vector pointer,
// bitcast the array operand to a vector, and then lower this as a store of
// a vector value to a vector pointer. This generally results in better code,
// as well as working around an LLVM bug:
// https://github.com/ziglang/zig/issues/11154
if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
const vector_ty = sema.typeOf(vector_ptr).childType();
const vector = try sema.coerce(block, vector_ty, uncasted_operand, operand_src);
try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store);
return;
}
const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand);
const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand);
const operand_val = maybe_operand_val orelse {
try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
break :rs operand_src;
@ -17195,6 +17218,39 @@ fn storePtr2(
_ = try block.addBinOp(air_tag, ptr, operand);
}
/// Traverse an arbitrary number of bitcasted pointers and return the underyling vector
/// pointer. Only if the final element type matches the vector element type, and the
/// lengths match.
fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref {
const array_ty = sema.typeOf(ptr).childType();
if (array_ty.zigTypeTag() != .Array) return null;
var ptr_inst = Air.refToIndex(ptr) orelse return null;
const air_datas = sema.air_instructions.items(.data);
const air_tags = sema.air_instructions.items(.tag);
const prev_ptr = while (air_tags[ptr_inst] == .bitcast) {
const prev_ptr = air_datas[ptr_inst].ty_op.operand;
const prev_ptr_ty = sema.typeOf(prev_ptr);
const prev_ptr_child_ty = switch (prev_ptr_ty.tag()) {
.single_mut_pointer => prev_ptr_ty.castTag(.single_mut_pointer).?.data,
.pointer => prev_ptr_ty.castTag(.pointer).?.data.pointee_type,
else => return null,
};
if (prev_ptr_child_ty.zigTypeTag() == .Vector) break prev_ptr;
ptr_inst = Air.refToIndex(prev_ptr) orelse return null;
} else return null;
// We have a pointer-to-array and a pointer-to-vector. If the elements and
// lengths match, return the result.
const vector_ty = sema.typeOf(prev_ptr).childType();
if (array_ty.childType().eql(vector_ty.childType()) and
array_ty.arrayLen() == vector_ty.vectorLen())
{
return prev_ptr;
} else {
return null;
}
}
/// Call when you have Value objects rather than Air instructions, and you want to
/// assert the store must be done at comptime.
fn storePtrVal(

View File

@ -3709,10 +3709,11 @@ pub const FuncGen = struct {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const operand_ty = self.air.typeOf(ty_op.operand);
const dest_ty = self.air.typeOfIndex(inst);
const dest_llvm_ty = try self.dg.llvmType(dest_ty);
if (dest_ty.isSignedInt()) {
if (operand_ty.isSignedInt()) {
return self.builder.buildSIToFP(operand, dest_llvm_ty, "");
} else {
return self.builder.buildUIToFP(operand, dest_llvm_ty, "");
@ -3984,13 +3985,14 @@ pub const FuncGen = struct {
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const operand = try self.resolveInst(pl_op.operand);
const name = self.air.nullTerminatedString(pl_op.payload);
const ptr_ty = self.air.typeOf(pl_op.operand);
const di_local_var = dib.createAutoVariable(
self.di_scope.?,
name.ptr,
self.di_file.?,
self.prev_dbg_line,
try self.dg.lowerDebugType(self.air.typeOf(pl_op.operand)),
try self.dg.lowerDebugType(ptr_ty.childType()),
true, // always preserve
0, // flags
);

View File

@ -2573,15 +2573,14 @@ pub const Type = extern union {
.array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data + 1,
.array, .vector => {
const payload = self.cast(Payload.Array).?.data;
const elem_size = @maximum(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
const elem_size = payload.elem_type.abiSize(target);
assert(elem_size >= payload.elem_type.abiAlignment(target));
return payload.len * elem_size;
},
.array_sentinel => {
const payload = self.castTag(.array_sentinel).?.data;
const elem_size = std.math.max(
payload.elem_type.abiAlignment(target),
payload.elem_type.abiSize(target),
);
const elem_size = payload.elem_type.abiSize(target);
assert(elem_size >= payload.elem_type.abiAlignment(target));
return (payload.len + 1) * elem_size;
},
.i16, .u16 => return 2,

View File

@ -3931,15 +3931,12 @@ pub const Value = extern union {
},
80 => {
if (true) {
@panic("TODO implement compiler_rt fabs for f80");
@panic("TODO implement compiler_rt fabs for f80 (__fabsx)");
}
const f = val.toFloat(f80);
return Value.Tag.float_80.create(arena, @fabs(f));
},
128 => {
if (true) {
@panic("TODO implement compiler_rt fabs for f128");
}
const f = val.toFloat(f128);
return Value.Tag.float_128.create(arena, @fabs(f));
},
@ -3963,15 +3960,12 @@ pub const Value = extern union {
},
80 => {
if (true) {
@panic("TODO implement compiler_rt floor for f80");
@panic("TODO implement compiler_rt floor for f80 (__floorx)");
}
const f = val.toFloat(f80);
return Value.Tag.float_80.create(arena, @floor(f));
},
128 => {
if (true) {
@panic("TODO implement compiler_rt floor for f128");
}
const f = val.toFloat(f128);
return Value.Tag.float_128.create(arena, @floor(f));
},
@ -4001,9 +3995,6 @@ pub const Value = extern union {
return Value.Tag.float_80.create(arena, @ceil(f));
},
128 => {
if (true) {
@panic("TODO implement compiler_rt ceil for f128");
}
const f = val.toFloat(f128);
return Value.Tag.float_128.create(arena, @ceil(f));
},
@ -4033,9 +4024,6 @@ pub const Value = extern union {
return Value.Tag.float_80.create(arena, @round(f));
},
128 => {
if (true) {
@panic("TODO implement compiler_rt round for f128");
}
const f = val.toFloat(f128);
return Value.Tag.float_128.create(arena, @round(f));
},
@ -4065,9 +4053,6 @@ pub const Value = extern union {
return Value.Tag.float_80.create(arena, @trunc(f));
},
128 => {
if (true) {
@panic("TODO implement compiler_rt trunc for f128");
}
const f = val.toFloat(f128);
return Value.Tag.float_128.create(arena, @trunc(f));
},

View File

@ -95,8 +95,29 @@ test "comptime_int @intToFloat" {
}
}
test "@intToFloat" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
try testIntToFloat(-2);
}
fn testIntToFloat(k: i32) !void {
const f = @intToFloat(f32, k);
const i = @floatToInt(i32, f);
try expect(i == k);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "@floatToInt" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
@ -1007,8 +1028,6 @@ test "peer type resolve array pointer and unknown pointer" {
}
test "comptime float casts" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const a = @intToFloat(comptime_float, 1);
try expect(a == 1);
try expect(@TypeOf(a) == comptime_float);

View File

@ -333,7 +333,6 @@ fn testLog() !void {
}
test "@log with vectors" {
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
@ -343,15 +342,19 @@ test "@log with vectors" {
{
var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
var result = @log(v);
try expect(math.approxEqAbs(f32, @log(@as(f32, 1.1)), result[0], epsilon));
try expect(math.approxEqAbs(f32, @log(@as(f32, 2.2)), result[1], epsilon));
try expect(@log(@as(f32, 1.1)) == result[0]);
try expect(@log(@as(f32, 2.2)) == result[1]);
try expect(@log(@as(f32, 0.3)) == result[2]);
try expect(math.approxEqAbs(f32, @log(@as(f32, 0.4)), result[3], epsilon));
try expect(@log(@as(f32, 0.4)) == result[3]);
}
}
test "@log2" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
comptime try testLog2();
try testLog2();
@ -368,15 +371,19 @@ fn testLog2() !void {
{
var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
var result = @log2(v);
try expect(math.approxEqAbs(f32, @log2(@as(f32, 1.1)), result[0], epsilon));
try expect(math.approxEqAbs(f32, @log2(@as(f32, 2.2)), result[1], epsilon));
try expect(math.approxEqAbs(f32, @log2(@as(f32, 0.3)), result[2], epsilon));
try expect(math.approxEqAbs(f32, @log2(@as(f32, 0.4)), result[3], epsilon));
try expect(@log2(@as(f32, 1.1)) == result[0]);
try expect(@log2(@as(f32, 2.2)) == result[1]);
try expect(@log2(@as(f32, 0.3)) == result[2]);
try expect(@log2(@as(f32, 0.4)) == result[3]);
}
}
test "@log10" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
comptime try testLog10();
try testLog10();
@ -393,10 +400,10 @@ fn testLog10() !void {
{
var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
var result = @log10(v);
try expect(math.approxEqAbs(f32, @log10(@as(f32, 1.1)), result[0], epsilon));
try expect(math.approxEqAbs(f32, @log10(@as(f32, 2.2)), result[1], epsilon));
try expect(math.approxEqAbs(f32, @log10(@as(f32, 0.3)), result[2], epsilon));
try expect(math.approxEqAbs(f32, @log10(@as(f32, 0.4)), result[3], epsilon));
try expect(@log10(@as(f32, 1.1)) == result[0]);
try expect(@log10(@as(f32, 2.2)) == result[1]);
try expect(@log10(@as(f32, 0.3)) == result[2]);
try expect(@log10(@as(f32, 0.4)) == result[3]);
}
}
@ -537,7 +544,71 @@ fn testTrunc() !void {
}
}
test "negation" {
test "negation f16" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.os.tag == .freebsd) {
// TODO file issue to track this failure
return error.SkipZigTest;
}
const S = struct {
fn doTheTest() !void {
var a: f16 = 1;
a = -a;
try expect(a == -1);
a = -a;
try expect(a == 1);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "negation f32" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var a: f32 = 1;
a = -a;
try expect(a == -1);
a = -a;
try expect(a == 1);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "negation f64" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var a: f64 = 1;
a = -a;
try expect(a == -1);
a = -a;
try expect(a == 1);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "negation f80" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.os.tag == .freebsd) {
@ -547,11 +618,37 @@ test "negation" {
const S = struct {
fn doTheTest() !void {
inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
var a: T = 1;
a = -a;
try expect(a == -1);
}
var a: f80 = 1;
a = -a;
try expect(a == -1);
a = -a;
try expect(a == 1);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "negation f128" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.os.tag == .freebsd) {
// TODO file issue to track this failure
return error.SkipZigTest;
}
const S = struct {
fn doTheTest() !void {
var a: f128 = 1;
a = -a;
try expect(a == -1);
a = -a;
try expect(a == 1);
}
};
@ -583,7 +680,13 @@ test "float literal at compile time not lossy" {
}
test "f128 at compile time is lossy" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend != .stage1) {
// this one is happening because we represent comptime-known f128 integers with
// Value.Tag.bigint and only convert to f128 representation if it stops being an
// integer. Is this something we want? need to have a lang spec discussion on this
// topic.
return error.SkipZigTest; // TODO
}
try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0);
}

View File

@ -6,7 +6,6 @@ const expectEqualSlices = std.testing.expectEqualSlices;
const maxInt = std.math.maxInt;
const minInt = std.math.minInt;
const mem = std.mem;
const has_f80_rt = builtin.cpu.arch == .x86_64;
test "assignment operators" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
@ -1046,12 +1045,14 @@ fn testSqrt(comptime T: type, x: T) !void {
}
test "@fabs" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
try testFabs(f128, 12.0);
comptime try testFabs(f128, 12.0);
if (has_f80_rt) try testFabs(f80, 12.0);
// comptime try testFabs(f80, 12.0);
try testFabs(f64, 12.0);
comptime try testFabs(f64, 12.0);
try testFabs(f32, 12.0);
@ -1065,20 +1066,25 @@ test "@fabs" {
comptime try expectEqual(x, z);
}
test "@fabs f80" {
if (true) {
// https://github.com/ziglang/zig/issues/11030
return error.SkipZigTest;
}
try testFabs(f80, 12.0);
comptime try testFabs(f80, 12.0);
}
fn testFabs(comptime T: type, x: T) !void {
const y = -x;
const z = @fabs(y);
try expectEqual(x, z);
try expect(x == z);
}
test "@floor" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// FIXME: Generates a floorl function call
// testFloor(f128, 12.0);
comptime try testFloor(f128, 12.0);
// try testFloor(f80, 12.0);
comptime try testFloor(f80, 12.0);
try testFloor(f64, 12.0);
comptime try testFloor(f64, 12.0);
try testFloor(f32, 12.0);
@ -1089,23 +1095,39 @@ test "@floor" {
const x = 14.0;
const y = x + 0.7;
const z = @floor(y);
comptime try expectEqual(x, z);
comptime try expect(x == z);
}
test "@floor f80" {
if (true) {
// https://github.com/ziglang/zig/issues/11030
return error.SkipZigTest;
}
try testFloor(f80, 12.0);
comptime try testFloor(f80, 12.0);
}
test "@floor f128" {
if (builtin.zig_backend == .stage1) {
// Fails because it incorrectly lowers to a floorl function call.
return error.SkipZigTest;
}
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
testFloor(f128, 12.0);
comptime try testFloor(f128, 12.0);
}
fn testFloor(comptime T: type, x: T) !void {
const y = x + 0.6;
const z = @floor(y);
try expectEqual(x, z);
try expect(x == z);
}
test "@ceil" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// FIXME: Generates a ceill function call
//testCeil(f128, 12.0);
comptime try testCeil(f128, 12.0);
// try testCeil(f80, 12.0);
comptime try testCeil(f80, 12.0);
try testCeil(f64, 12.0);
comptime try testCeil(f64, 12.0);
try testCeil(f32, 12.0);
@ -1116,29 +1138,40 @@ test "@ceil" {
const x = 14.0;
const y = x - 0.7;
const z = @ceil(y);
comptime try expectEqual(x, z);
comptime try expect(x == z);
}
test "@ceil f80" {
if (true) {
// https://github.com/ziglang/zig/issues/11030
return error.SkipZigTest;
}
try testCeil(f80, 12.0);
comptime try testCeil(f80, 12.0);
}
test "@ceil f128" {
if (builtin.zig_backend == .stage1) {
// Fails because it incorrectly lowers to a ceill function call.
return error.SkipZigTest;
}
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
testCeil(f128, 12.0);
comptime try testCeil(f128, 12.0);
}
fn testCeil(comptime T: type, x: T) !void {
const y = x - 0.8;
const z = @ceil(y);
try expectEqual(x, z);
try expect(x == z);
}
test "@trunc" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// FIXME: Generates a truncl function call
//testTrunc(f128, 12.0);
comptime try testTrunc(f128, 12.0);
// try testTrunc(f80, 12.0);
// comptime try testTrunc(f80, 12.0);
comptime {
const x: f80 = 12.0;
const y = x + 0.8;
const z = @trunc(y);
try expectEqual(x, z);
}
try testTrunc(f64, 12.0);
comptime try testTrunc(f64, 12.0);
try testTrunc(f32, 12.0);
@ -1149,31 +1182,54 @@ test "@trunc" {
const x = 14.0;
const y = x + 0.7;
const z = @trunc(y);
comptime try expectEqual(x, z);
comptime try expect(x == z);
}
test "@trunc f80" {
if (true) {
// https://github.com/ziglang/zig/issues/11030
return error.SkipZigTest;
}
try testTrunc(f80, 12.0);
comptime try testTrunc(f80, 12.0);
comptime {
const x: f80 = 12.0;
const y = x + 0.8;
const z = @trunc(y);
try expect(x == z);
}
}
test "@trunc f128" {
if (builtin.zig_backend == .stage1) {
// Fails because it incorrectly lowers to a truncl function call.
return error.SkipZigTest;
}
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
testTrunc(f128, 12.0);
comptime try testTrunc(f128, 12.0);
}
fn testTrunc(comptime T: type, x: T) !void {
{
const y = x + 0.8;
const z = @trunc(y);
try expectEqual(x, z);
try expect(x == z);
}
{
const y = -x - 0.8;
const z = @trunc(y);
try expectEqual(-x, z);
try expect(-x == z);
}
}
test "@round" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// FIXME: Generates a roundl function call
//testRound(f128, 12.0);
comptime try testRound(f128, 12.0);
// try testRound(f80, 12.0);
comptime try testRound(f80, 12.0);
try testRound(f64, 12.0);
comptime try testRound(f64, 12.0);
try testRound(f32, 12.0);
@ -1184,13 +1240,35 @@ test "@round" {
const x = 14.0;
const y = x + 0.4;
const z = @round(y);
comptime try expectEqual(x, z);
comptime try expect(x == z);
}
test "@round f80" {
if (true) {
// https://github.com/ziglang/zig/issues/11030
return error.SkipZigTest;
}
try testRound(f80, 12.0);
comptime try testRound(f80, 12.0);
}
test "@round f128" {
if (builtin.zig_backend == .stage1) {
// Fails because it incorrectly lowers to a roundl function call.
return error.SkipZigTest;
}
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
testRound(f128, 12.0);
comptime try testRound(f128, 12.0);
}
fn testRound(comptime T: type, x: T) !void {
const y = x - 0.5;
const z = @round(y);
try expectEqual(x, z);
try expect(x == z);
}
test "vector integer addition" {
@ -1225,10 +1303,15 @@ test "NaN comparison" {
comptime try testNanEqNan(f32);
comptime try testNanEqNan(f64);
comptime try testNanEqNan(f128);
}
// TODO https://github.com/ziglang/zig/issues/11030
// try testNanEqNan(f80);
// comptime try testNanEqNan(f80);
test "NaN comparison f80" {
if (true) {
// https://github.com/ziglang/zig/issues/11030
return error.SkipZigTest;
}
try testNanEqNan(f80);
comptime try testNanEqNan(f80);
}
fn testNanEqNan(comptime F: type) !void {

View File

@ -16,5 +16,6 @@ pub fn addCases(ctx: *TestContext) !void {
try @import("stage2/riscv64.zig").addCases(ctx);
try @import("stage2/plan9.zig").addCases(ctx);
try @import("stage2/x86_64.zig").addCases(ctx);
try @import("stage2/nvptx.zig").addCases(ctx);
// https://github.com/ziglang/zig/issues/10968
//try @import("stage2/nvptx.zig").addCases(ctx);
}