| author | |
| committer | |
| log | b2a1b4c085b93d508c51307f40444252b8cd4d52 |
| tree | 307c504b85c9aee16602e667db1a5db5c25e1fff |
| parent | eee989d2a00f99d95900515b48f981f2ea6bbe78 |
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.
See #111545 files changed, 324 insertions(+), 86 deletions(-)
src/Sema.zig+50-2| ... | @@ -13276,7 +13276,7 @@ fn checkFloatType( | ... | @@ -13276,7 +13276,7 @@ fn checkFloatType( |
| 13276 | ty: Type, | 13276 | ty: Type, |
| 13277 | ) CompileError!void { | 13277 | ) CompileError!void { |
| 13278 | switch (ty.zigTypeTag()) { | 13278 | switch (ty.zigTypeTag()) { |
| 13279 | .ComptimeFloat, .Float => {}, | 13279 | .ComptimeInt, .ComptimeFloat, .Float => {}, |
| 13280 | else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty}), | 13280 | else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty}), |
| 13281 | } | 13281 | } |
| 13282 | } | 13282 | } |
| ... | @@ -17177,10 +17177,25 @@ fn storePtr2( | ... | @@ -17177,10 +17177,25 @@ fn storePtr2( |
| 17177 | return; | 17177 | return; |
| 17178 | } | 17178 | } |
| 17179 | 17179 | ||
| 17180 | // TODO do the same thing for anon structs as for tuples above. | ||
| 17181 | |||
| 17182 | // Detect if we are storing an array operand to a bitcasted vector pointer. | ||
| 17183 | // If so, we instead reach through the bitcasted pointer to the vector pointer, | ||
| 17184 | // bitcast the array operand to a vector, and then lower this as a store of | ||
| 17185 | // a vector value to a vector pointer. This generally results in better code, | ||
| 17186 | // as well as working around an LLVM bug: | ||
| 17187 | // https://github.com/ziglang/zig/issues/11154 | ||
| 17188 | if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| { | ||
| 17189 | const vector_ty = sema.typeOf(vector_ptr).childType(); | ||
| 17190 | const vector = try sema.coerce(block, vector_ty, uncasted_operand, operand_src); | ||
| 17191 | try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store); | ||
| 17192 | return; | ||
| 17193 | } | ||
| 17194 | |||
| 17180 | const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src); | 17195 | const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src); |
| 17196 | const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand); | ||
| 17181 | 17197 | ||
| 17182 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { | 17198 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { |
| 17183 | const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand); | ||
| 17184 | const operand_val = maybe_operand_val orelse { | 17199 | const operand_val = maybe_operand_val orelse { |
| 17185 | try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); | 17200 | try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); |
| 17186 | break :rs operand_src; | 17201 | break :rs operand_src; |
| ... | @@ -17203,6 +17218,39 @@ fn storePtr2( | ... | @@ -17203,6 +17218,39 @@ fn storePtr2( |
| 17203 | _ = try block.addBinOp(air_tag, ptr, operand); | 17218 | _ = try block.addBinOp(air_tag, ptr, operand); |
| 17204 | } | 17219 | } |
| 17205 | 17220 | ||
| 17221 | /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector | ||
| 17222 | /// pointer. Only if the final element type matches the vector element type, and the | ||
| 17223 | /// lengths match. | ||
| 17224 | fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref { | ||
| 17225 | const array_ty = sema.typeOf(ptr).childType(); | ||
| 17226 | if (array_ty.zigTypeTag() != .Array) return null; | ||
| 17227 | var ptr_inst = Air.refToIndex(ptr) orelse return null; | ||
| 17228 | const air_datas = sema.air_instructions.items(.data); | ||
| 17229 | const air_tags = sema.air_instructions.items(.tag); | ||
| 17230 | const prev_ptr = while (air_tags[ptr_inst] == .bitcast) { | ||
| 17231 | const prev_ptr = air_datas[ptr_inst].ty_op.operand; | ||
| 17232 | const prev_ptr_ty = sema.typeOf(prev_ptr); | ||
| 17233 | const prev_ptr_child_ty = switch (prev_ptr_ty.tag()) { | ||
| 17234 | .single_mut_pointer => prev_ptr_ty.castTag(.single_mut_pointer).?.data, | ||
| 17235 | .pointer => prev_ptr_ty.castTag(.pointer).?.data.pointee_type, | ||
| 17236 | else => return null, | ||
| 17237 | }; | ||
| 17238 | if (prev_ptr_child_ty.zigTypeTag() == .Vector) break prev_ptr; | ||
| 17239 | ptr_inst = Air.refToIndex(prev_ptr) orelse return null; | ||
| 17240 | } else return null; | ||
| 17241 | |||
| 17242 | // We have a pointer-to-array and a pointer-to-vector. If the elements and | ||
| 17243 | // lengths match, return the result. | ||
| 17244 | const vector_ty = sema.typeOf(prev_ptr).childType(); | ||
| 17245 | if (array_ty.childType().eql(vector_ty.childType()) and | ||
| 17246 | array_ty.arrayLen() == vector_ty.vectorLen()) | ||
| 17247 | { | ||
| 17248 | return prev_ptr; | ||
| 17249 | } else { | ||
| 17250 | return null; | ||
| 17251 | } | ||
| 17252 | } | ||
| 17253 | |||
| 17206 | /// Call when you have Value objects rather than Air instructions, and you want to | 17254 | /// Call when you have Value objects rather than Air instructions, and you want to |
| 17207 | /// assert the store must be done at comptime. | 17255 | /// assert the store must be done at comptime. |
| 17208 | fn storePtrVal( | 17256 | fn storePtrVal( |
src/value.zig+2-17| ... | @@ -3931,15 +3931,12 @@ pub const Value = extern union { | ... | @@ -3931,15 +3931,12 @@ pub const Value = extern union { |
| 3931 | }, | 3931 | }, |
| 3932 | 80 => { | 3932 | 80 => { |
| 3933 | if (true) { | 3933 | if (true) { |
| 3934 | @panic("TODO implement compiler_rt fabs for f80"); | 3934 | @panic("TODO implement compiler_rt fabs for f80 (__fabsx)"); |
| 3935 | } | 3935 | } |
| 3936 | const f = val.toFloat(f80); | 3936 | const f = val.toFloat(f80); |
| 3937 | return Value.Tag.float_80.create(arena, @fabs(f)); | 3937 | return Value.Tag.float_80.create(arena, @fabs(f)); |
| 3938 | }, | 3938 | }, |
| 3939 | 128 => { | 3939 | 128 => { |
| 3940 | if (true) { | ||
| 3941 | @panic("TODO implement compiler_rt fabs for f128"); | ||
| 3942 | } | ||
| 3943 | const f = val.toFloat(f128); | 3940 | const f = val.toFloat(f128); |
| 3944 | return Value.Tag.float_128.create(arena, @fabs(f)); | 3941 | return Value.Tag.float_128.create(arena, @fabs(f)); |
| 3945 | }, | 3942 | }, |
| ... | @@ -3963,15 +3960,12 @@ pub const Value = extern union { | ... | @@ -3963,15 +3960,12 @@ pub const Value = extern union { |
| 3963 | }, | 3960 | }, |
| 3964 | 80 => { | 3961 | 80 => { |
| 3965 | if (true) { | 3962 | if (true) { |
| 3966 | @panic("TODO implement compiler_rt floor for f80"); | 3963 | @panic("TODO implement compiler_rt floor for f80 (__floorx)"); |
| 3967 | } | 3964 | } |
| 3968 | const f = val.toFloat(f80); | 3965 | const f = val.toFloat(f80); |
| 3969 | return Value.Tag.float_80.create(arena, @floor(f)); | 3966 | return Value.Tag.float_80.create(arena, @floor(f)); |
| 3970 | }, | 3967 | }, |
| 3971 | 128 => { | 3968 | 128 => { |
| 3972 | if (true) { | ||
| 3973 | @panic("TODO implement compiler_rt floor for f128"); | ||
| 3974 | } | ||
| 3975 | const f = val.toFloat(f128); | 3969 | const f = val.toFloat(f128); |
| 3976 | return Value.Tag.float_128.create(arena, @floor(f)); | 3970 | return Value.Tag.float_128.create(arena, @floor(f)); |
| 3977 | }, | 3971 | }, |
| ... | @@ -4001,9 +3995,6 @@ pub const Value = extern union { | ... | @@ -4001,9 +3995,6 @@ pub const Value = extern union { |
| 4001 | return Value.Tag.float_80.create(arena, @ceil(f)); | 3995 | return Value.Tag.float_80.create(arena, @ceil(f)); |
| 4002 | }, | 3996 | }, |
| 4003 | 128 => { | 3997 | 128 => { |
| 4004 | if (true) { | ||
| 4005 | @panic("TODO implement compiler_rt ceil for f128"); | ||
| 4006 | } | ||
| 4007 | const f = val.toFloat(f128); | 3998 | const f = val.toFloat(f128); |
| 4008 | return Value.Tag.float_128.create(arena, @ceil(f)); | 3999 | return Value.Tag.float_128.create(arena, @ceil(f)); |
| 4009 | }, | 4000 | }, |
| ... | @@ -4033,9 +4024,6 @@ pub const Value = extern union { | ... | @@ -4033,9 +4024,6 @@ pub const Value = extern union { |
| 4033 | return Value.Tag.float_80.create(arena, @round(f)); | 4024 | return Value.Tag.float_80.create(arena, @round(f)); |
| 4034 | }, | 4025 | }, |
| 4035 | 128 => { | 4026 | 128 => { |
| 4036 | if (true) { | ||
| 4037 | @panic("TODO implement compiler_rt round for f128"); | ||
| 4038 | } | ||
| 4039 | const f = val.toFloat(f128); | 4027 | const f = val.toFloat(f128); |
| 4040 | return Value.Tag.float_128.create(arena, @round(f)); | 4028 | return Value.Tag.float_128.create(arena, @round(f)); |
| 4041 | }, | 4029 | }, |
| ... | @@ -4065,9 +4053,6 @@ pub const Value = extern union { | ... | @@ -4065,9 +4053,6 @@ pub const Value = extern union { |
| 4065 | return Value.Tag.float_80.create(arena, @trunc(f)); | 4053 | return Value.Tag.float_80.create(arena, @trunc(f)); |
| 4066 | }, | 4054 | }, |
| 4067 | 128 => { | 4055 | 128 => { |
| 4068 | if (true) { | ||
| 4069 | @panic("TODO implement compiler_rt trunc for f128"); | ||
| 4070 | } | ||
| 4071 | const f = val.toFloat(f128); | 4056 | const f = val.toFloat(f128); |
| 4072 | return Value.Tag.float_128.create(arena, @trunc(f)); | 4057 | return Value.Tag.float_128.create(arena, @trunc(f)); |
| 4073 | }, | 4058 | }, |
test/behavior/cast.zig+22-3| ... | @@ -95,8 +95,29 @@ test "comptime_int @intToFloat" { | ... | @@ -95,8 +95,29 @@ test "comptime_int @intToFloat" { |
| 95 | } | 95 | } |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | test "@intToFloat" { | ||
| 99 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 100 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 101 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 102 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 103 | |||
| 104 | const S = struct { | ||
| 105 | fn doTheTest() !void { | ||
| 106 | try testIntToFloat(-2); | ||
| 107 | } | ||
| 108 | |||
| 109 | fn testIntToFloat(k: i32) !void { | ||
| 110 | const f = @intToFloat(f32, k); | ||
| 111 | const i = @floatToInt(i32, f); | ||
| 112 | try expect(i == k); | ||
| 113 | } | ||
| 114 | }; | ||
| 115 | try S.doTheTest(); | ||
| 116 | comptime try S.doTheTest(); | ||
| 117 | } | ||
| 118 | |||
| 98 | test "@floatToInt" { | 119 | test "@floatToInt" { |
| 99 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 120 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 100 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 121 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 101 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 122 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 102 | 123 | ||
| ... | @@ -1007,8 +1028,6 @@ test "peer type resolve array pointer and unknown pointer" { | ... | @@ -1007,8 +1028,6 @@ test "peer type resolve array pointer and unknown pointer" { |
| 1007 | } | 1028 | } |
| 1008 | 1029 | ||
| 1009 | test "comptime float casts" { | 1030 | test "comptime float casts" { |
| 1010 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 1011 | |||
| 1012 | const a = @intToFloat(comptime_float, 1); | 1031 | const a = @intToFloat(comptime_float, 1); |
| 1013 | try expect(a == 1); | 1032 | try expect(a == 1); |
| 1014 | try expect(@TypeOf(a) == comptime_float); | 1033 | try expect(@TypeOf(a) == comptime_float); |
test/behavior/floatop.zig+124-21| ... | @@ -333,7 +333,6 @@ fn testLog() !void { | ... | @@ -333,7 +333,6 @@ fn testLog() !void { |
| 333 | } | 333 | } |
| 334 | 334 | ||
| 335 | test "@log with vectors" { | 335 | test "@log with vectors" { |
| 336 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO | ||
| 337 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 336 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 338 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 337 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 339 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 338 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | @@ -343,15 +342,19 @@ test "@log with vectors" { | ... | @@ -343,15 +342,19 @@ test "@log with vectors" { |
| 343 | { | 342 | { |
| 344 | var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; | 343 | var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; |
| 345 | var result = @log(v); | 344 | var result = @log(v); |
| 346 | try expect(math.approxEqAbs(f32, @log(@as(f32, 1.1)), result[0], epsilon)); | 345 | try expect(@log(@as(f32, 1.1)) == result[0]); |
| 347 | try expect(math.approxEqAbs(f32, @log(@as(f32, 2.2)), result[1], epsilon)); | 346 | try expect(@log(@as(f32, 2.2)) == result[1]); |
| 348 | try expect(@log(@as(f32, 0.3)) == result[2]); | 347 | try expect(@log(@as(f32, 0.3)) == result[2]); |
| 349 | try expect(math.approxEqAbs(f32, @log(@as(f32, 0.4)), result[3], epsilon)); | 348 | try expect(@log(@as(f32, 0.4)) == result[3]); |
| 350 | } | 349 | } |
| 351 | } | 350 | } |
| 352 | 351 | ||
| 353 | test "@log2" { | 352 | test "@log2" { |
| 354 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 353 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 354 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 355 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 356 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 357 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 355 | 358 | ||
| 356 | comptime try testLog2(); | 359 | comptime try testLog2(); |
| 357 | try testLog2(); | 360 | try testLog2(); |
| ... | @@ -368,15 +371,19 @@ fn testLog2() !void { | ... | @@ -368,15 +371,19 @@ fn testLog2() !void { |
| 368 | { | 371 | { |
| 369 | var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; | 372 | var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; |
| 370 | var result = @log2(v); | 373 | var result = @log2(v); |
| 371 | try expect(math.approxEqAbs(f32, @log2(@as(f32, 1.1)), result[0], epsilon)); | 374 | try expect(@log2(@as(f32, 1.1)) == result[0]); |
| 372 | try expect(math.approxEqAbs(f32, @log2(@as(f32, 2.2)), result[1], epsilon)); | 375 | try expect(@log2(@as(f32, 2.2)) == result[1]); |
| 373 | try expect(math.approxEqAbs(f32, @log2(@as(f32, 0.3)), result[2], epsilon)); | 376 | try expect(@log2(@as(f32, 0.3)) == result[2]); |
| 374 | try expect(math.approxEqAbs(f32, @log2(@as(f32, 0.4)), result[3], epsilon)); | 377 | try expect(@log2(@as(f32, 0.4)) == result[3]); |
| 375 | } | 378 | } |
| 376 | } | 379 | } |
| 377 | 380 | ||
| 378 | test "@log10" { | 381 | test "@log10" { |
| 379 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 382 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 383 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 384 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 385 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 386 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 380 | 387 | ||
| 381 | comptime try testLog10(); | 388 | comptime try testLog10(); |
| 382 | try testLog10(); | 389 | try testLog10(); |
| ... | @@ -393,10 +400,10 @@ fn testLog10() !void { | ... | @@ -393,10 +400,10 @@ fn testLog10() !void { |
| 393 | { | 400 | { |
| 394 | var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; | 401 | var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; |
| 395 | var result = @log10(v); | 402 | var result = @log10(v); |
| 396 | try expect(math.approxEqAbs(f32, @log10(@as(f32, 1.1)), result[0], epsilon)); | 403 | try expect(@log10(@as(f32, 1.1)) == result[0]); |
| 397 | try expect(math.approxEqAbs(f32, @log10(@as(f32, 2.2)), result[1], epsilon)); | 404 | try expect(@log10(@as(f32, 2.2)) == result[1]); |
| 398 | try expect(math.approxEqAbs(f32, @log10(@as(f32, 0.3)), result[2], epsilon)); | 405 | try expect(@log10(@as(f32, 0.3)) == result[2]); |
| 399 | try expect(math.approxEqAbs(f32, @log10(@as(f32, 0.4)), result[3], epsilon)); | 406 | try expect(@log10(@as(f32, 0.4)) == result[3]); |
| 400 | } | 407 | } |
| 401 | } | 408 | } |
| 402 | 409 | ||
| ... | @@ -537,7 +544,71 @@ fn testTrunc() !void { | ... | @@ -537,7 +544,71 @@ fn testTrunc() !void { |
| 537 | } | 544 | } |
| 538 | } | 545 | } |
| 539 | 546 | ||
| 540 | test "negation" { | 547 | test "negation f16" { |
| 548 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 549 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 550 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 551 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 552 | |||
| 553 | if (builtin.os.tag == .freebsd) { | ||
| 554 | // TODO file issue to track this failure | ||
| 555 | return error.SkipZigTest; | ||
| 556 | } | ||
| 557 | |||
| 558 | const S = struct { | ||
| 559 | fn doTheTest() !void { | ||
| 560 | var a: f16 = 1; | ||
| 561 | a = -a; | ||
| 562 | try expect(a == -1); | ||
| 563 | a = -a; | ||
| 564 | try expect(a == 1); | ||
| 565 | } | ||
| 566 | }; | ||
| 567 | |||
| 568 | try S.doTheTest(); | ||
| 569 | comptime try S.doTheTest(); | ||
| 570 | } | ||
| 571 | |||
| 572 | test "negation f32" { | ||
| 573 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 574 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 575 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 576 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 577 | |||
| 578 | const S = struct { | ||
| 579 | fn doTheTest() !void { | ||
| 580 | var a: f32 = 1; | ||
| 581 | a = -a; | ||
| 582 | try expect(a == -1); | ||
| 583 | a = -a; | ||
| 584 | try expect(a == 1); | ||
| 585 | } | ||
| 586 | }; | ||
| 587 | |||
| 588 | try S.doTheTest(); | ||
| 589 | comptime try S.doTheTest(); | ||
| 590 | } | ||
| 591 | |||
| 592 | test "negation f64" { | ||
| 593 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 594 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 595 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 596 | |||
| 597 | const S = struct { | ||
| 598 | fn doTheTest() !void { | ||
| 599 | var a: f64 = 1; | ||
| 600 | a = -a; | ||
| 601 | try expect(a == -1); | ||
| 602 | a = -a; | ||
| 603 | try expect(a == 1); | ||
| 604 | } | ||
| 605 | }; | ||
| 606 | |||
| 607 | try S.doTheTest(); | ||
| 608 | comptime try S.doTheTest(); | ||
| 609 | } | ||
| 610 | |||
| 611 | test "negation f80" { | ||
| 541 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 612 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO |
| 542 | 613 | ||
| 543 | if (builtin.os.tag == .freebsd) { | 614 | if (builtin.os.tag == .freebsd) { |
| ... | @@ -547,11 +618,37 @@ test "negation" { | ... | @@ -547,11 +618,37 @@ test "negation" { |
| 547 | 618 | ||
| 548 | const S = struct { | 619 | const S = struct { |
| 549 | fn doTheTest() !void { | 620 | fn doTheTest() !void { |
| 550 | inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { | 621 | var a: f80 = 1; |
| 551 | var a: T = 1; | 622 | a = -a; |
| 552 | a = -a; | 623 | try expect(a == -1); |
| 553 | try expect(a == -1); | 624 | a = -a; |
| 554 | } | 625 | try expect(a == 1); |
| 626 | } | ||
| 627 | }; | ||
| 628 | |||
| 629 | try S.doTheTest(); | ||
| 630 | comptime try S.doTheTest(); | ||
| 631 | } | ||
| 632 | |||
| 633 | test "negation f128" { | ||
| 634 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 635 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 636 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 637 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 638 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 639 | |||
| 640 | if (builtin.os.tag == .freebsd) { | ||
| 641 | // TODO file issue to track this failure | ||
| 642 | return error.SkipZigTest; | ||
| 643 | } | ||
| 644 | |||
| 645 | const S = struct { | ||
| 646 | fn doTheTest() !void { | ||
| 647 | var a: f128 = 1; | ||
| 648 | a = -a; | ||
| 649 | try expect(a == -1); | ||
| 650 | a = -a; | ||
| 651 | try expect(a == 1); | ||
| 555 | } | 652 | } |
| 556 | }; | 653 | }; |
| 557 | 654 | ||
| ... | @@ -583,7 +680,13 @@ test "float literal at compile time not lossy" { | ... | @@ -583,7 +680,13 @@ test "float literal at compile time not lossy" { |
| 583 | } | 680 | } |
| 584 | 681 | ||
| 585 | test "f128 at compile time is lossy" { | 682 | test "f128 at compile time is lossy" { |
| 586 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 683 | if (builtin.zig_backend != .stage1) { |
| 684 | // this one is happening because we represent comptime-known f128 integers with | ||
| 685 | // Value.Tag.bigint and only convert to f128 representation if it stops being an | ||
| 686 | // integer. Is this something we want? need to have a lang spec discussion on this | ||
| 687 | // topic. | ||
| 688 | return error.SkipZigTest; // TODO | ||
| 689 | } | ||
| 587 | 690 | ||
| 588 | try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); | 691 | try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); |
| 589 | } | 692 | } |
test/behavior/math.zig+126-43| ... | @@ -6,7 +6,6 @@ const expectEqualSlices = std.testing.expectEqualSlices; | ... | @@ -6,7 +6,6 @@ const expectEqualSlices = std.testing.expectEqualSlices; |
| 6 | const maxInt = std.math.maxInt; | 6 | const maxInt = std.math.maxInt; |
| 7 | const minInt = std.math.minInt; | 7 | const minInt = std.math.minInt; |
| 8 | const mem = std.mem; | 8 | const mem = std.mem; |
| 9 | const has_f80_rt = builtin.cpu.arch == .x86_64; | ||
| 10 | 9 | ||
| 11 | test "assignment operators" { | 10 | test "assignment operators" { |
| 12 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 11 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| ... | @@ -1046,12 +1045,14 @@ fn testSqrt(comptime T: type, x: T) !void { | ... | @@ -1046,12 +1045,14 @@ fn testSqrt(comptime T: type, x: T) !void { |
| 1046 | } | 1045 | } |
| 1047 | 1046 | ||
| 1048 | test "@fabs" { | 1047 | test "@fabs" { |
| 1049 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 1048 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1049 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 1050 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 1051 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1052 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1050 | 1053 | ||
| 1051 | try testFabs(f128, 12.0); | 1054 | try testFabs(f128, 12.0); |
| 1052 | comptime try testFabs(f128, 12.0); | 1055 | comptime try testFabs(f128, 12.0); |
| 1053 | if (has_f80_rt) try testFabs(f80, 12.0); | ||
| 1054 | // comptime try testFabs(f80, 12.0); | ||
| 1055 | try testFabs(f64, 12.0); | 1056 | try testFabs(f64, 12.0); |
| 1056 | comptime try testFabs(f64, 12.0); | 1057 | comptime try testFabs(f64, 12.0); |
| 1057 | try testFabs(f32, 12.0); | 1058 | try testFabs(f32, 12.0); |
| ... | @@ -1065,20 +1066,25 @@ test "@fabs" { | ... | @@ -1065,20 +1066,25 @@ test "@fabs" { |
| 1065 | comptime try expectEqual(x, z); | 1066 | comptime try expectEqual(x, z); |
| 1066 | } | 1067 | } |
| 1067 | 1068 | ||
| 1069 | test "@fabs f80" { | ||
| 1070 | if (true) { | ||
| 1071 | // https://github.com/ziglang/zig/issues/11030 | ||
| 1072 | return error.SkipZigTest; | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | try testFabs(f80, 12.0); | ||
| 1076 | comptime try testFabs(f80, 12.0); | ||
| 1077 | } | ||
| 1078 | |||
| 1068 | fn testFabs(comptime T: type, x: T) !void { | 1079 | fn testFabs(comptime T: type, x: T) !void { |
| 1069 | const y = -x; | 1080 | const y = -x; |
| 1070 | const z = @fabs(y); | 1081 | const z = @fabs(y); |
| 1071 | try expectEqual(x, z); | 1082 | try expect(x == z); |
| 1072 | } | 1083 | } |
| 1073 | 1084 | ||
| 1074 | test "@floor" { | 1085 | test "@floor" { |
| 1075 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 1086 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO |
| 1076 | 1087 | ||
| 1077 | // FIXME: Generates a floorl function call | ||
| 1078 | // testFloor(f128, 12.0); | ||
| 1079 | comptime try testFloor(f128, 12.0); | ||
| 1080 | // try testFloor(f80, 12.0); | ||
| 1081 | comptime try testFloor(f80, 12.0); | ||
| 1082 | try testFloor(f64, 12.0); | 1088 | try testFloor(f64, 12.0); |
| 1083 | comptime try testFloor(f64, 12.0); | 1089 | comptime try testFloor(f64, 12.0); |
| 1084 | try testFloor(f32, 12.0); | 1090 | try testFloor(f32, 12.0); |
| ... | @@ -1089,23 +1095,39 @@ test "@floor" { | ... | @@ -1089,23 +1095,39 @@ test "@floor" { |
| 1089 | const x = 14.0; | 1095 | const x = 14.0; |
| 1090 | const y = x + 0.7; | 1096 | const y = x + 0.7; |
| 1091 | const z = @floor(y); | 1097 | const z = @floor(y); |
| 1092 | comptime try expectEqual(x, z); | 1098 | comptime try expect(x == z); |
| 1099 | } | ||
| 1100 | |||
| 1101 | test "@floor f80" { | ||
| 1102 | if (true) { | ||
| 1103 | // https://github.com/ziglang/zig/issues/11030 | ||
| 1104 | return error.SkipZigTest; | ||
| 1105 | } | ||
| 1106 | try testFloor(f80, 12.0); | ||
| 1107 | comptime try testFloor(f80, 12.0); | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | test "@floor f128" { | ||
| 1111 | if (builtin.zig_backend == .stage1) { | ||
| 1112 | // Fails because it incorrectly lowers to a floorl function call. | ||
| 1113 | return error.SkipZigTest; | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 1117 | |||
| 1118 | testFloor(f128, 12.0); | ||
| 1119 | comptime try testFloor(f128, 12.0); | ||
| 1093 | } | 1120 | } |
| 1094 | 1121 | ||
| 1095 | fn testFloor(comptime T: type, x: T) !void { | 1122 | fn testFloor(comptime T: type, x: T) !void { |
| 1096 | const y = x + 0.6; | 1123 | const y = x + 0.6; |
| 1097 | const z = @floor(y); | 1124 | const z = @floor(y); |
| 1098 | try expectEqual(x, z); | 1125 | try expect(x == z); |
| 1099 | } | 1126 | } |
| 1100 | 1127 | ||
| 1101 | test "@ceil" { | 1128 | test "@ceil" { |
| 1102 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 1129 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO |
| 1103 | 1130 | ||
| 1104 | // FIXME: Generates a ceill function call | ||
| 1105 | //testCeil(f128, 12.0); | ||
| 1106 | comptime try testCeil(f128, 12.0); | ||
| 1107 | // try testCeil(f80, 12.0); | ||
| 1108 | comptime try testCeil(f80, 12.0); | ||
| 1109 | try testCeil(f64, 12.0); | 1131 | try testCeil(f64, 12.0); |
| 1110 | comptime try testCeil(f64, 12.0); | 1132 | comptime try testCeil(f64, 12.0); |
| 1111 | try testCeil(f32, 12.0); | 1133 | try testCeil(f32, 12.0); |
| ... | @@ -1116,29 +1138,40 @@ test "@ceil" { | ... | @@ -1116,29 +1138,40 @@ test "@ceil" { |
| 1116 | const x = 14.0; | 1138 | const x = 14.0; |
| 1117 | const y = x - 0.7; | 1139 | const y = x - 0.7; |
| 1118 | const z = @ceil(y); | 1140 | const z = @ceil(y); |
| 1119 | comptime try expectEqual(x, z); | 1141 | comptime try expect(x == z); |
| 1142 | } | ||
| 1143 | |||
| 1144 | test "@ceil f80" { | ||
| 1145 | if (true) { | ||
| 1146 | // https://github.com/ziglang/zig/issues/11030 | ||
| 1147 | return error.SkipZigTest; | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | try testCeil(f80, 12.0); | ||
| 1151 | comptime try testCeil(f80, 12.0); | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | test "@ceil f128" { | ||
| 1155 | if (builtin.zig_backend == .stage1) { | ||
| 1156 | // Fails because it incorrectly lowers to a ceill function call. | ||
| 1157 | return error.SkipZigTest; | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 1161 | |||
| 1162 | testCeil(f128, 12.0); | ||
| 1163 | comptime try testCeil(f128, 12.0); | ||
| 1120 | } | 1164 | } |
| 1121 | 1165 | ||
| 1122 | fn testCeil(comptime T: type, x: T) !void { | 1166 | fn testCeil(comptime T: type, x: T) !void { |
| 1123 | const y = x - 0.8; | 1167 | const y = x - 0.8; |
| 1124 | const z = @ceil(y); | 1168 | const z = @ceil(y); |
| 1125 | try expectEqual(x, z); | 1169 | try expect(x == z); |
| 1126 | } | 1170 | } |
| 1127 | 1171 | ||
| 1128 | test "@trunc" { | 1172 | test "@trunc" { |
| 1129 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 1173 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO |
| 1130 | 1174 | ||
| 1131 | // FIXME: Generates a truncl function call | ||
| 1132 | //testTrunc(f128, 12.0); | ||
| 1133 | comptime try testTrunc(f128, 12.0); | ||
| 1134 | // try testTrunc(f80, 12.0); | ||
| 1135 | // comptime try testTrunc(f80, 12.0); | ||
| 1136 | comptime { | ||
| 1137 | const x: f80 = 12.0; | ||
| 1138 | const y = x + 0.8; | ||
| 1139 | const z = @trunc(y); | ||
| 1140 | try expectEqual(x, z); | ||
| 1141 | } | ||
| 1142 | try testTrunc(f64, 12.0); | 1175 | try testTrunc(f64, 12.0); |
| 1143 | comptime try testTrunc(f64, 12.0); | 1176 | comptime try testTrunc(f64, 12.0); |
| 1144 | try testTrunc(f32, 12.0); | 1177 | try testTrunc(f32, 12.0); |
| ... | @@ -1149,31 +1182,54 @@ test "@trunc" { | ... | @@ -1149,31 +1182,54 @@ test "@trunc" { |
| 1149 | const x = 14.0; | 1182 | const x = 14.0; |
| 1150 | const y = x + 0.7; | 1183 | const y = x + 0.7; |
| 1151 | const z = @trunc(y); | 1184 | const z = @trunc(y); |
| 1152 | comptime try expectEqual(x, z); | 1185 | comptime try expect(x == z); |
| 1186 | } | ||
| 1187 | |||
| 1188 | test "@trunc f80" { | ||
| 1189 | if (true) { | ||
| 1190 | // https://github.com/ziglang/zig/issues/11030 | ||
| 1191 | return error.SkipZigTest; | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | try testTrunc(f80, 12.0); | ||
| 1195 | comptime try testTrunc(f80, 12.0); | ||
| 1196 | comptime { | ||
| 1197 | const x: f80 = 12.0; | ||
| 1198 | const y = x + 0.8; | ||
| 1199 | const z = @trunc(y); | ||
| 1200 | try expect(x == z); | ||
| 1201 | } | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | test "@trunc f128" { | ||
| 1205 | if (builtin.zig_backend == .stage1) { | ||
| 1206 | // Fails because it incorrectly lowers to a truncl function call. | ||
| 1207 | return error.SkipZigTest; | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 1211 | |||
| 1212 | testTrunc(f128, 12.0); | ||
| 1213 | comptime try testTrunc(f128, 12.0); | ||
| 1153 | } | 1214 | } |
| 1154 | 1215 | ||
| 1155 | fn testTrunc(comptime T: type, x: T) !void { | 1216 | fn testTrunc(comptime T: type, x: T) !void { |
| 1156 | { | 1217 | { |
| 1157 | const y = x + 0.8; | 1218 | const y = x + 0.8; |
| 1158 | const z = @trunc(y); | 1219 | const z = @trunc(y); |
| 1159 | try expectEqual(x, z); | 1220 | try expect(x == z); |
| 1160 | } | 1221 | } |
| 1161 | 1222 | ||
| 1162 | { | 1223 | { |
| 1163 | const y = -x - 0.8; | 1224 | const y = -x - 0.8; |
| 1164 | const z = @trunc(y); | 1225 | const z = @trunc(y); |
| 1165 | try expectEqual(-x, z); | 1226 | try expect(-x == z); |
| 1166 | } | 1227 | } |
| 1167 | } | 1228 | } |
| 1168 | 1229 | ||
| 1169 | test "@round" { | 1230 | test "@round" { |
| 1170 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | 1231 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO |
| 1171 | 1232 | ||
| 1172 | // FIXME: Generates a roundl function call | ||
| 1173 | //testRound(f128, 12.0); | ||
| 1174 | comptime try testRound(f128, 12.0); | ||
| 1175 | // try testRound(f80, 12.0); | ||
| 1176 | comptime try testRound(f80, 12.0); | ||
| 1177 | try testRound(f64, 12.0); | 1233 | try testRound(f64, 12.0); |
| 1178 | comptime try testRound(f64, 12.0); | 1234 | comptime try testRound(f64, 12.0); |
| 1179 | try testRound(f32, 12.0); | 1235 | try testRound(f32, 12.0); |
| ... | @@ -1184,13 +1240,35 @@ test "@round" { | ... | @@ -1184,13 +1240,35 @@ test "@round" { |
| 1184 | const x = 14.0; | 1240 | const x = 14.0; |
| 1185 | const y = x + 0.4; | 1241 | const y = x + 0.4; |
| 1186 | const z = @round(y); | 1242 | const z = @round(y); |
| 1187 | comptime try expectEqual(x, z); | 1243 | comptime try expect(x == z); |
| 1244 | } | ||
| 1245 | |||
| 1246 | test "@round f80" { | ||
| 1247 | if (true) { | ||
| 1248 | // https://github.com/ziglang/zig/issues/11030 | ||
| 1249 | return error.SkipZigTest; | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | try testRound(f80, 12.0); | ||
| 1253 | comptime try testRound(f80, 12.0); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | test "@round f128" { | ||
| 1257 | if (builtin.zig_backend == .stage1) { | ||
| 1258 | // Fails because it incorrectly lowers to a roundl function call. | ||
| 1259 | return error.SkipZigTest; | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 1263 | |||
| 1264 | testRound(f128, 12.0); | ||
| 1265 | comptime try testRound(f128, 12.0); | ||
| 1188 | } | 1266 | } |
| 1189 | 1267 | ||
| 1190 | fn testRound(comptime T: type, x: T) !void { | 1268 | fn testRound(comptime T: type, x: T) !void { |
| 1191 | const y = x - 0.5; | 1269 | const y = x - 0.5; |
| 1192 | const z = @round(y); | 1270 | const z = @round(y); |
| 1193 | try expectEqual(x, z); | 1271 | try expect(x == z); |
| 1194 | } | 1272 | } |
| 1195 | 1273 | ||
| 1196 | test "vector integer addition" { | 1274 | test "vector integer addition" { |
| ... | @@ -1225,10 +1303,15 @@ test "NaN comparison" { | ... | @@ -1225,10 +1303,15 @@ test "NaN comparison" { |
| 1225 | comptime try testNanEqNan(f32); | 1303 | comptime try testNanEqNan(f32); |
| 1226 | comptime try testNanEqNan(f64); | 1304 | comptime try testNanEqNan(f64); |
| 1227 | comptime try testNanEqNan(f128); | 1305 | comptime try testNanEqNan(f128); |
| 1306 | } | ||
| 1228 | 1307 | ||
| 1229 | // TODO https://github.com/ziglang/zig/issues/11030 | 1308 | test "NaN comparison f80" { |
| 1230 | // try testNanEqNan(f80); | 1309 | if (true) { |
| 1231 | // comptime try testNanEqNan(f80); | 1310 | // https://github.com/ziglang/zig/issues/11030 |
| 1311 | return error.SkipZigTest; | ||
| 1312 | } | ||
| 1313 | try testNanEqNan(f80); | ||
| 1314 | comptime try testNanEqNan(f80); | ||
| 1232 | } | 1315 | } |
| 1233 | 1316 | ||
| 1234 | fn testNanEqNan(comptime F: type) !void { | 1317 | fn testNanEqNan(comptime F: type) !void { |