mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[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:
parent
95e7890d02
commit
1e5d063b21
|
@ -195,7 +195,8 @@ DEFINE_NATIVE_ENTRY(Float32x4_clamp, 0, 3) {
|
||||||
float _w;
|
float _w;
|
||||||
// ARM semantics are different from X86/X64 at an instruction level. Ensure
|
// ARM semantics are different from X86/X64 at an instruction level. Ensure
|
||||||
// that we match the semantics of the architecture in the C version.
|
// 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();
|
_x = self.x() < hi.x() ? self.x() : hi.x();
|
||||||
_y = self.y() < hi.y() ? self.y() : hi.y();
|
_y = self.y() < hi.y() ? self.y() : hi.y();
|
||||||
_z = self.z() < hi.z() ? self.z() : hi.z();
|
_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
|
// ARM semantics are different from X86/X64 at an instruction level. Ensure
|
||||||
// that we match the semantics of the architecture in the C version.
|
// 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();
|
_x = self.x() < hi.x() ? self.x() : hi.x();
|
||||||
_y = self.y() < hi.y() ? self.y() : hi.y();
|
_y = self.y() < hi.y() ? self.y() : hi.y();
|
||||||
_x = lo.x() < _x ? _x : lo.x();
|
_x = lo.x() < _x ? _x : lo.x();
|
||||||
|
|
|
@ -6732,6 +6732,25 @@ ASSEMBLER_TEST_RUN(Vcged, test) {
|
||||||
"ret\n");
|
"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) {
|
ASSEMBLER_TEST_GENERATE(Vmaxs, assembler) {
|
||||||
__ LoadDImmediate(V0, 10.5);
|
__ LoadDImmediate(V0, 10.5);
|
||||||
__ LoadDImmediate(V1, 10.0);
|
__ LoadDImmediate(V1, 10.0);
|
||||||
|
@ -6791,6 +6810,25 @@ ASSEMBLER_TEST_RUN(Vmaxs, test) {
|
||||||
"ret\n");
|
"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) {
|
ASSEMBLER_TEST_GENERATE(Vmaxd, assembler) {
|
||||||
__ LoadDImmediate(V0, 21.0);
|
__ LoadDImmediate(V0, 21.0);
|
||||||
__ LoadDImmediate(V1, 20.5);
|
__ LoadDImmediate(V1, 20.5);
|
||||||
|
@ -6826,6 +6864,26 @@ ASSEMBLER_TEST_RUN(Vmaxd, test) {
|
||||||
"ret\n");
|
"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) {
|
ASSEMBLER_TEST_GENERATE(Vmins, assembler) {
|
||||||
__ LoadDImmediate(V0, 10.5);
|
__ LoadDImmediate(V0, 10.5);
|
||||||
__ LoadDImmediate(V1, 11.0);
|
__ LoadDImmediate(V1, 11.0);
|
||||||
|
@ -6885,6 +6943,26 @@ ASSEMBLER_TEST_RUN(Vmins, test) {
|
||||||
"ret\n");
|
"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) {
|
ASSEMBLER_TEST_GENERATE(Vmind, assembler) {
|
||||||
__ LoadDImmediate(V0, 21.0);
|
__ LoadDImmediate(V0, 21.0);
|
||||||
__ LoadDImmediate(V1, 21.5);
|
__ LoadDImmediate(V1, 21.5);
|
||||||
|
|
|
@ -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) {
|
ASSEMBLER_TEST_GENERATE(Vminqs, assembler) {
|
||||||
if (TargetCPUFeatures::neon_supported()) {
|
if (TargetCPUFeatures::neon_supported()) {
|
||||||
__ LoadSImmediate(S0, 1.0);
|
__ 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) {
|
ASSEMBLER_TEST_GENERATE(Vmaxqs, assembler) {
|
||||||
if (TargetCPUFeatures::neon_supported()) {
|
if (TargetCPUFeatures::neon_supported()) {
|
||||||
__ LoadSImmediate(S0, 1.0);
|
__ LoadSImmediate(S0, 1.0);
|
||||||
|
|
|
@ -2893,6 +2893,22 @@ static void simd_value_swap(simd_value_t* s1,
|
||||||
s2->data_[i2].u = tmp;
|
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) {
|
void Simulator::DecodeSIMDDataProcessing(Instr* instr) {
|
||||||
ASSERT(instr->ConditionField() == kSpecialCondition);
|
ASSERT(instr->ConditionField() == kSpecialCondition);
|
||||||
|
|
||||||
|
@ -3118,13 +3134,13 @@ void Simulator::DecodeSIMDDataProcessing(Instr* instr) {
|
||||||
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) {
|
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) {
|
||||||
// Format(instr, "vminqs 'qd, 'qn, 'qm");
|
// Format(instr, "vminqs 'qd, 'qn, 'qm");
|
||||||
for (int i = 0; i < 4; i++) {
|
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) &&
|
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 0) &&
|
||||||
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
|
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
|
||||||
// Format(instr, "vmaxqs 'qd, 'qn, 'qm");
|
// Format(instr, "vmaxqs 'qd, 'qn, 'qm");
|
||||||
for (int i = 0; i < 4; i++) {
|
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) &&
|
} else if ((instr->Bits(8, 4) == 7) && (instr->Bit(4) == 0) &&
|
||||||
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
|
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
|
||||||
|
|
|
@ -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) {
|
void Simulator::DecodeSIMDThreeSame(Instr* instr) {
|
||||||
const int Q = instr->Bit(30);
|
const int Q = instr->Bit(30);
|
||||||
const int U = instr->Bit(29);
|
const int U = instr->Bit(29);
|
||||||
|
@ -3069,11 +3101,11 @@ void Simulator::DecodeSIMDThreeSame(Instr* instr) {
|
||||||
} else if ((U == 0) && (opcode == 0x1e)) {
|
} else if ((U == 0) && (opcode == 0x1e)) {
|
||||||
if (instr->Bit(23) == 1) {
|
if (instr->Bit(23) == 1) {
|
||||||
// Format(instr, "vmin'vsz 'vd, 'vn, 'vm");
|
// 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);
|
res = bit_cast<int32_t, float>(m);
|
||||||
} else {
|
} else {
|
||||||
// Format(instr, "vmax'vsz 'vd, 'vn, 'vm");
|
// 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);
|
res = bit_cast<int32_t, float>(m);
|
||||||
}
|
}
|
||||||
} else if ((U == 0) && (opcode == 0x1f)) {
|
} else if ((U == 0) && (opcode == 0x1f)) {
|
||||||
|
@ -3143,11 +3175,11 @@ void Simulator::DecodeSIMDThreeSame(Instr* instr) {
|
||||||
} else if ((U == 0) && (opcode == 0x1e)) {
|
} else if ((U == 0) && (opcode == 0x1e)) {
|
||||||
if (instr->Bit(23) == 1) {
|
if (instr->Bit(23) == 1) {
|
||||||
// Format(instr, "vmin'vsz 'vd, 'vn, 'vm");
|
// 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);
|
res = bit_cast<int64_t, double>(m);
|
||||||
} else {
|
} else {
|
||||||
// Format(instr, "vmax'vsz 'vd, 'vn, 'vm");
|
// 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);
|
res = bit_cast<int64_t, double>(m);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
// 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
|
// 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.
|
// 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
|
// @dart = 2.9
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue