From 63f9ee164575fc87345f0ed1d0a8c275273289f6 Mon Sep 17 00:00:00 2001 From: Michael Starzinger Date: Mon, 25 Sep 2017 14:19:05 +0200 Subject: [PATCH] [asm.js] Fix Math.min/max signatures to take signed. This fixes the signature of "Math.min" and "Math.max" for integer values from "(int, int...) -> signed" to "(signed, signed..) -> signed" which properly distinguishes signed from unsigned values now. This is in sync with the spec errata (and ECMAScript semantics). R=clemensh@chromium.org TEST=mjsunit/regress/regress-6838-1 BUG=v8:6838 Change-Id: Id72836513dd86e93472a22cf1ac2e2d382ed4f23 Reviewed-on: https://chromium-review.googlesource.com/681357 Commit-Queue: Michael Starzinger Reviewed-by: Clemens Hammacher Cr-Commit-Position: refs/heads/master@{#48139} --- src/asmjs/asm-parser.cc | 13 +++-- test/mjsunit/asm/math-max.js | 78 ++++++++++++++++++++++++++ test/mjsunit/asm/math-min.js | 78 ++++++++++++++++++++++++++ test/mjsunit/regress/regress-6838-1.js | 33 +++++++++++ 4 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 test/mjsunit/asm/math-max.js create mode 100644 test/mjsunit/asm/math-min.js create mode 100644 test/mjsunit/regress/regress-6838-1.js diff --git a/src/asmjs/asm-parser.cc b/src/asmjs/asm-parser.cc index 0502c993d7..c7170e75a4 100644 --- a/src/asmjs/asm-parser.cc +++ b/src/asmjs/asm-parser.cc @@ -119,13 +119,16 @@ void AsmJsParser::InitializeStdlibTypes() { stdlib_ii2s_->AsFunctionType()->AddArgument(i); stdlib_ii2s_->AsFunctionType()->AddArgument(i); + // The signatures in "9 Standard Library" of the spec draft are outdated and + // have been superseded with the following by an errata: + // - Math.min/max : (signed, signed...) -> signed + // (double, double...) -> double + // (float, float...) -> float auto* minmax_d = AsmType::MinMaxType(zone(), d, d); - // *VIOLATION* The float variant is not part of the spec, but firefox accepts - // it. auto* minmax_f = AsmType::MinMaxType(zone(), f, f); - auto* minmax_i = AsmType::MinMaxType(zone(), s, i); + auto* minmax_s = AsmType::MinMaxType(zone(), s, s); stdlib_minmax_ = AsmType::OverloadedFunction(zone()); - stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_i); + stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_s); stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_f); stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_d); @@ -2248,7 +2251,7 @@ AsmType* AsmJsParser::ValidateCall() { current_function_builder_->Emit(kExprF32Max); } } - } else if (param_specific_types[0]->IsA(AsmType::Int())) { + } else if (param_specific_types[0]->IsA(AsmType::Signed())) { TemporaryVariableScope tmp_x(this); TemporaryVariableScope tmp_y(this); for (size_t i = 1; i < param_specific_types.size(); ++i) { diff --git a/test/mjsunit/asm/math-max.js b/test/mjsunit/asm/math-max.js new file mode 100644 index 0000000000..d5dc0d1617 --- /dev/null +++ b/test/mjsunit/asm/math-max.js @@ -0,0 +1,78 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function Module(stdlib) { + "use asm"; + + var max = stdlib.Math.max; + var fround = stdlib.Math.fround; + + // f: double, double -> double + function f(a, b) { + a = +a; b = +b; + return +max(a, b); + } + + // g: signed, signed -> signed + function g(a, b) { + a = a | 0; b = b | 0; + return max(a >> 0, b >> 0) | 0; + } + + // h: float, float -> float + function h(a, b) { + a = fround(a); b = fround(b); + return fround(max(a, b)); + } + + return { f: f, g: g, h: h }; +} + +var m = Module({ Math: Math }); +var f = m.f; +var g = m.g; +var h = m.h; + +assertTrue(isNaN(f(0, NaN))); +assertFalse(isFinite(f(0, Infinity))); +assertTrue(isFinite(f(0, -Infinity))); + +assertTrue(Object.is(+0, f(-0, +0))); +assertTrue(Object.is(+0, f(+0, -0))); + +assertEquals(0.1, f( 0, 0.1)); +assertEquals(0.5, f( 0.1, 0.5)); +assertEquals(0.5, f( 0.5, -0.1)); +assertEquals(-0.1, f(-0.1, -0.5)); +assertEquals(1, f(-0.5, 1)); +assertEquals(1.1, f( 1, 1.1)); +assertEquals(1.1, f( 1.1, -1)); +assertEquals(-1, f(-1, -1.1)); +assertEquals(0, f(-1.1, 0)); + +assertEquals( 1, g( 0, 1)); +assertEquals( 5, g( 1, 5)); +assertEquals( 5, g( 5, -1)); +assertEquals(-1, g(-1, -5)); +assertEquals( 1, g(-5, 1)); +assertEquals( 1, g( 1, -1)); +assertEquals( 0, g(-1, 0)); + +assertEquals(Math.fround(0.1), h( 0, 0.1)); +assertEquals(Math.fround(0.5), h( 0.1, 0.5)); +assertEquals(Math.fround(0.5), h( 0.5, -0.1)); +assertEquals(Math.fround(-0.1), h(-0.1, -0.5)); +assertEquals(Math.fround(1), h(-0.5, 1)); +assertEquals(Math.fround(1.1), h( 1, 1.1)); +assertEquals(Math.fround(1.1), h( 1.1, -1)); +assertEquals(Math.fround(-1), h(-1, -1.1)); +assertEquals(Math.fround(0), h(-1.1, 0)); + +assertEquals(1, g(0, Number.MIN_SAFE_INTEGER)); +assertEquals(0, g(0, Number.MAX_SAFE_INTEGER)); + +assertEquals(Number.MAX_VALUE, f(Number.MIN_VALUE, Number.MAX_VALUE)); +assertEquals(Number.MAX_VALUE, f(Number.MAX_VALUE, Number.MIN_VALUE)); +assertEquals(Number.POSITIVE_INFINITY, f(Number.POSITIVE_INFINITY, 0)); +assertEquals(0, f(Number.NEGATIVE_INFINITY, 0)); diff --git a/test/mjsunit/asm/math-min.js b/test/mjsunit/asm/math-min.js new file mode 100644 index 0000000000..5923d267d0 --- /dev/null +++ b/test/mjsunit/asm/math-min.js @@ -0,0 +1,78 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function Module(stdlib) { + "use asm"; + + var min = stdlib.Math.min; + var fround = stdlib.Math.fround; + + // f: double, double -> double + function f(a, b) { + a = +a; b = +b; + return +min(a, b); + } + + // g: signed, signed -> signed + function g(a, b) { + a = a | 0; b = b | 0; + return min(a >> 0, b >> 0) | 0; + } + + // h: float, float -> float + function h(a, b) { + a = fround(a); b = fround(b); + return fround(min(a, b)); + } + + return { f: f, g: g, h: h }; +} + +var m = Module({ Math: Math }); +var f = m.f; +var g = m.g; +var h = m.h; + +assertTrue(isNaN(f(0, NaN))); +assertTrue(isFinite(f(0, Infinity))); +assertFalse(isFinite(f(0, -Infinity))); + +assertTrue(Object.is(-0, f(-0, +0))); +assertTrue(Object.is(-0, f(+0, -0))); + +assertEquals(0, f( 0, 0.1)); +assertEquals(0.1, f( 0.1, 0.5)); +assertEquals(-0.1, f( 0.5, -0.1)); +assertEquals(-0.5, f(-0.1, -0.5)); +assertEquals(-0.5, f(-0.5, 1)); +assertEquals(1, f( 1, 1.1)); +assertEquals(-1, f( 1.1, -1)); +assertEquals(-1.1, f(-1, -1.1)); +assertEquals(-1.1, f(-1.1, 0)); + +assertEquals( 0, g( 0, 1)); +assertEquals( 1, g( 1, 5)); +assertEquals(-1, g( 5, -1)); +assertEquals(-5, g(-1, -5)); +assertEquals(-5, g(-5, 1)); +assertEquals(-1, g( 1, -1)); +assertEquals(-1, g(-1, 0)); + +assertEquals(Math.fround(0), h( 0, 0.1)); +assertEquals(Math.fround(0.1), h( 0.1, 0.5)); +assertEquals(Math.fround(-0.1), h( 0.5, -0.1)); +assertEquals(Math.fround(-0.5), h(-0.1, -0.5)); +assertEquals(Math.fround(-0.5), h(-0.5, 1)); +assertEquals(Math.fround(1), h( 1, 1.1)); +assertEquals(Math.fround(-1), h( 1.1, -1)); +assertEquals(Math.fround(-1.1), h(-1, -1.1)); +assertEquals(Math.fround(-1.1), h(-1.1, 0)); + +assertEquals(0, g(0, Number.MIN_SAFE_INTEGER)); +assertEquals(-1, g(0, Number.MAX_SAFE_INTEGER)); + +assertEquals(Number.MIN_VALUE, f(Number.MIN_VALUE, Number.MAX_VALUE)); +assertEquals(Number.MIN_VALUE, f(Number.MAX_VALUE, Number.MIN_VALUE)); +assertEquals(0, f(Number.POSITIVE_INFINITY, 0)); +assertEquals(Number.NEGATIVE_INFINITY, f(Number.NEGATIVE_INFINITY, 0)); diff --git a/test/mjsunit/regress/regress-6838-1.js b/test/mjsunit/regress/regress-6838-1.js new file mode 100644 index 0000000000..bab6a194d3 --- /dev/null +++ b/test/mjsunit/regress/regress-6838-1.js @@ -0,0 +1,33 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + +(function TestMathMaxOnLargeInt() { + function Module(stdlib) { + "use asm"; + var max = stdlib.Math.max; + function f() { + return max(42,0xffffffff); + } + return f; + } + var f = Module(this); + assertEquals(0xffffffff, f()); + assertFalse(%IsAsmWasmCode(Module)); +})(); + +(function TestMathMinOnLargeInt() { + function Module(stdlib) { + "use asm"; + var min = stdlib.Math.min; + function f() { + return min(42,0xffffffff); + } + return f; + } + var f = Module(this); + assertEquals(42, f()); + assertFalse(%IsAsmWasmCode(Module)); +})();