[vm/sim/simd] Fix vmin/vmax implementation on simulator.

minss/maxss used by simulator 0.0==-0.0, which is inconsistent with arm native instructions.

Fixes https://github.com/dart-lang/sdk/issues/48988
Addresses https://github.com/dart-lang/sdk/issues/40426
TEST=ci

Change-Id: I9d88d89e342bb543b1e90fdbe3c7aa8303353dab
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/244320
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
Alexander Aprelev 2022-05-11 16:59:24 +00:00 committed by Commit Bot
parent 95e7890d02
commit 1e5d063b21
6 changed files with 176 additions and 9 deletions

View file

@ -195,7 +195,8 @@ DEFINE_NATIVE_ENTRY(Float32x4_clamp, 0, 3) {
float _w;
// ARM semantics are different from X86/X64 at an instruction level. Ensure
// that we match the semantics of the architecture in the C version.
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) || \
defined(USING_SIMULATOR)
_x = self.x() < hi.x() ? self.x() : hi.x();
_y = self.y() < hi.y() ? self.y() : hi.y();
_z = self.z() < hi.z() ? self.z() : hi.z();
@ -741,7 +742,8 @@ DEFINE_NATIVE_ENTRY(Float64x2_clamp, 0, 3) {
// ARM semantics are different from X86/X64 at an instruction level. Ensure
// that we match the semantics of the architecture in the C version.
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) || \
defined(USING_SIMULATOR)
_x = self.x() < hi.x() ? self.x() : hi.x();
_y = self.y() < hi.y() ? self.y() : hi.y();
_x = lo.x() < _x ? _x : lo.x();

View file

@ -6732,6 +6732,25 @@ ASSEMBLER_TEST_RUN(Vcged, test) {
"ret\n");
}
// Verify that vmaxs(-0.0, 0.0) = 0.0
ASSEMBLER_TEST_GENERATE(Vmaxs_zero, assembler) {
__ veor(V1, V1, V1);
__ vnegd(V2, V1);
__ vmaxs(V0, V2, V1);
__ ret();
}
ASSEMBLER_TEST_RUN(Vmaxs_zero, test) {
typedef double (*DoubleReturn)() DART_UNUSED;
double d = EXECUTE_TEST_CODE_DOUBLE(DoubleReturn, test->entry());
EXPECT_EQ(true, !signbit(d) && (d == 0.0));
EXPECT_DISASSEMBLY(
"veor v1, v1, v1\n"
"vnegd v2, v1\n"
"vmaxs v0, v2, v1\n"
"ret\n");
}
ASSEMBLER_TEST_GENERATE(Vmaxs, assembler) {
__ LoadDImmediate(V0, 10.5);
__ LoadDImmediate(V1, 10.0);
@ -6791,6 +6810,25 @@ ASSEMBLER_TEST_RUN(Vmaxs, test) {
"ret\n");
}
// Verify that vmaxd(-0.0, 0.0) = 0.0
ASSEMBLER_TEST_GENERATE(Vmaxd_zero, assembler) {
__ veor(V1, V1, V1);
__ vnegd(V2, V1);
__ vmaxd(V0, V2, V1);
__ ret();
}
ASSEMBLER_TEST_RUN(Vmaxd_zero, test) {
typedef double (*DoubleReturn)() DART_UNUSED;
double d = EXECUTE_TEST_CODE_DOUBLE(DoubleReturn, test->entry());
EXPECT_EQ(true, !signbit(d) && (d == 0.0));
EXPECT_DISASSEMBLY(
"veor v1, v1, v1\n"
"vnegd v2, v1\n"
"vmaxd v0, v2, v1\n"
"ret\n");
}
ASSEMBLER_TEST_GENERATE(Vmaxd, assembler) {
__ LoadDImmediate(V0, 21.0);
__ LoadDImmediate(V1, 20.5);
@ -6826,6 +6864,26 @@ ASSEMBLER_TEST_RUN(Vmaxd, test) {
"ret\n");
}
// Verify that vmins(-0.0, 0.0) = -0.0
ASSEMBLER_TEST_GENERATE(Vmins_zero, assembler) {
__ veor(V1, V1, V1);
__ vnegd(V2, V1);
__ vmins(V0, V1, V2);
__ ret();
}
ASSEMBLER_TEST_RUN(Vmins_zero, test) {
typedef double (*DoubleReturn)() DART_UNUSED;
double d = EXECUTE_TEST_CODE_DOUBLE(DoubleReturn, test->entry());
fprintf(stderr, "d: %f\n", d);
EXPECT_EQ(true, signbit(d) && (d == 0.0));
EXPECT_DISASSEMBLY(
"veor v1, v1, v1\n"
"vnegd v2, v1\n"
"vmins v0, v1, v2\n"
"ret\n");
}
ASSEMBLER_TEST_GENERATE(Vmins, assembler) {
__ LoadDImmediate(V0, 10.5);
__ LoadDImmediate(V1, 11.0);
@ -6885,6 +6943,26 @@ ASSEMBLER_TEST_RUN(Vmins, test) {
"ret\n");
}
// Verify that vmind(-0.0, 0.0) = -0.0
ASSEMBLER_TEST_GENERATE(Vmind_zero, assembler) {
__ veor(V1, V1, V1);
__ vnegd(V2, V1);
__ vmind(V0, V1, V2);
__ ret();
}
ASSEMBLER_TEST_RUN(Vmind_zero, test) {
typedef double (*DoubleReturn)() DART_UNUSED;
double d = EXECUTE_TEST_CODE_DOUBLE(DoubleReturn, test->entry());
fprintf(stderr, "d: %f\n", d);
EXPECT_EQ(true, signbit(d) && (d == 0.0));
EXPECT_DISASSEMBLY(
"veor v1, v1, v1\n"
"vnegd v2, v1\n"
"vmind v0, v1, v2\n"
"ret\n");
}
ASSEMBLER_TEST_GENERATE(Vmind, assembler) {
__ LoadDImmediate(V0, 21.0);
__ LoadDImmediate(V1, 21.5);

View file

@ -3409,6 +3409,25 @@ ASSEMBLER_TEST_RUN(Vcgtqs, test) {
}
}
// Verify that vmins(-0.0, 0.0) = -0.0
ASSEMBLER_TEST_GENERATE(Vminqs_zero, assembler) {
if (TargetCPUFeatures::neon_supported()) {
__ veorq(Q1, Q1, Q1);
__ vnegqs(Q2, Q1);
__ vminqs(Q0, Q1, Q2);
}
__ Ret();
}
ASSEMBLER_TEST_RUN(Vminqs_zero, test) {
EXPECT(test != NULL);
if (TargetCPUFeatures::neon_supported()) {
typedef int (*Tst)() DART_UNUSED;
float res = EXECUTE_TEST_CODE_FLOAT(Tst, test->entry());
EXPECT_EQ(true, signbit(res) && (res == 0.0));
}
}
ASSEMBLER_TEST_GENERATE(Vminqs, assembler) {
if (TargetCPUFeatures::neon_supported()) {
__ LoadSImmediate(S0, 1.0);
@ -3441,6 +3460,25 @@ ASSEMBLER_TEST_RUN(Vminqs, test) {
}
}
ASSEMBLER_TEST_GENERATE(Vmaxqs_zero, assembler) {
if (TargetCPUFeatures::neon_supported()) {
__ veorq(Q1, Q1, Q1);
__ vnegqs(Q2, Q1);
__ vmaxqs(Q0, Q2, Q1);
}
__ Ret();
}
// Verify that vmaxqs(-0.0, 0.0) = 0.0
ASSEMBLER_TEST_RUN(Vmaxqs_zero, test) {
EXPECT(test != NULL);
if (TargetCPUFeatures::neon_supported()) {
typedef int (*Tst)() DART_UNUSED;
float res = EXECUTE_TEST_CODE_FLOAT(Tst, test->entry());
EXPECT_EQ(true, !signbit(res) && (res == 0.0));
}
}
ASSEMBLER_TEST_GENERATE(Vmaxqs, assembler) {
if (TargetCPUFeatures::neon_supported()) {
__ LoadSImmediate(S0, 1.0);

View file

@ -2893,6 +2893,22 @@ static void simd_value_swap(simd_value_t* s1,
s2->data_[i2].u = tmp;
}
static float vminf(float f1, float f2) {
if (f1 == f2) {
// take care of (-0.0) < 0.0, (they are equal according to minss)
return signbit(f1) ? f1 : f2;
}
return f1 > f2 ? f2 : f1;
}
static float vmaxf(float f1, float f2) {
if (f1 == f2) {
// take care of (-0.0) < 0.0, (they are equal according to minss)
return signbit(f1) ? f2 : f1;
}
return f1 < f2 ? f2 : f1;
}
void Simulator::DecodeSIMDDataProcessing(Instr* instr) {
ASSERT(instr->ConditionField() == kSpecialCondition);
@ -3118,13 +3134,13 @@ void Simulator::DecodeSIMDDataProcessing(Instr* instr) {
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) {
// Format(instr, "vminqs 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = fminf(s8n.data_[i].f, s8m.data_[i].f);
s8d.data_[i].f = vminf(s8n.data_[i].f, s8m.data_[i].f);
}
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
// Format(instr, "vmaxqs 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = fmaxf(s8n.data_[i].f, s8m.data_[i].f);
s8d.data_[i].f = vmaxf(s8n.data_[i].f, s8m.data_[i].f);
}
} else if ((instr->Bits(8, 4) == 7) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&

View file

@ -3003,6 +3003,38 @@ void Simulator::DecodeSIMDCopy(Instr* instr) {
}
}
static float vminf(float f1, float f2) {
if (f1 == f2) {
// take care of (-0.0) < 0.0, (they are equal according to minss)
return signbit(f1) ? f1 : f2;
}
return f1 > f2 ? f2 : f1;
}
static float vmaxf(float f1, float f2) {
if (f1 == f2) {
// take care of (-0.0) < 0.0, (they are equal according to minss)
return signbit(f1) ? f2 : f1;
}
return f1 < f2 ? f2 : f1;
}
static double vmind(double f1, double f2) {
if (f1 == f2) {
// take care of (-0.0) < 0.0, (they are equal according to minss)
return signbit(f1) ? f1 : f2;
}
return f1 > f2 ? f2 : f1;
}
static double vmaxd(double f1, double f2) {
if (f1 == f2) {
// take care of (-0.0) < 0.0, (they are equal according to minss)
return signbit(f1) ? f2 : f1;
}
return f1 < f2 ? f2 : f1;
}
void Simulator::DecodeSIMDThreeSame(Instr* instr) {
const int Q = instr->Bit(30);
const int U = instr->Bit(29);
@ -3069,11 +3101,11 @@ void Simulator::DecodeSIMDThreeSame(Instr* instr) {
} else if ((U == 0) && (opcode == 0x1e)) {
if (instr->Bit(23) == 1) {
// Format(instr, "vmin'vsz 'vd, 'vn, 'vm");
const float m = fminf(vn_flt, vm_flt);
const float m = vminf(vn_flt, vm_flt);
res = bit_cast<int32_t, float>(m);
} else {
// Format(instr, "vmax'vsz 'vd, 'vn, 'vm");
const float m = fmaxf(vn_flt, vm_flt);
const float m = vmaxf(vn_flt, vm_flt);
res = bit_cast<int32_t, float>(m);
}
} else if ((U == 0) && (opcode == 0x1f)) {
@ -3143,11 +3175,11 @@ void Simulator::DecodeSIMDThreeSame(Instr* instr) {
} else if ((U == 0) && (opcode == 0x1e)) {
if (instr->Bit(23) == 1) {
// Format(instr, "vmin'vsz 'vd, 'vn, 'vm");
const double m = fmin(vn_dbl, vm_dbl);
const double m = vmind(vn_dbl, vm_dbl);
res = bit_cast<int64_t, double>(m);
} else {
// Format(instr, "vmax'vsz 'vd, 'vn, 'vm");
const double m = fmax(vn_dbl, vm_dbl);
const double m = vmaxd(vn_dbl, vm_dbl);
res = bit_cast<int64_t, double>(m);
}
} else {

View file

@ -1,7 +1,8 @@
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// VMOptions=--optimization-counter-threshold=10 --no-background-compilation
// VMOptions=--intrinsify --optimization-counter-threshold=10 --no-background-compilation
// VMOptions=--no-intrinsify --optimization-counter-threshold=10 --no-background-compilation
// @dart = 2.9