Skip to content

Commit 019cd35

Browse files
modulovaluedart-scoped@luci-project-accounts.iam.gserviceaccount.com
authored andcommitted
[vm] Use an SSHR-based sign mask on arm64
- adds missing float64x2 tests - modernizes existing tests and tests for more edge cases TEST=lib/typed_data/int32x4_sign_mask_test lib/typed_data/float32x4_sign_mask_test lib/typed_data/float64x2_sign_mask_test Fixed: #41950 Issue: #64170 Change-Id: I5166bf31d1a4005c427b02c0d9d1c4bbaa9f63ec Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/542362 Auto-Submit: Modestas Valauskas <valauskasmodestas@gmail.com> Reviewed-by: Lasse Nielsen <lrn@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
1 parent 8f1c524 commit 019cd35

4 files changed

Lines changed: 125 additions & 86 deletions

File tree

‎runtime/vm/compiler/backend/il_arm64.cc‎

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4113,22 +4113,17 @@ DEFINE_EMIT(SimdUnaryOp, (VRegister result, VRegister value)) {
41134113
}
41144114

41154115
DEFINE_EMIT(Simd32x4GetSignMask,
4116-
(Register out, VRegister value, Temp<Register> temp)) {
4117-
// X lane.
4118-
__ vmovrs(out, value, 0);
4119-
__ LsrImmediate(out, out, 31);
4120-
// Y lane.
4121-
__ vmovrs(temp, value, 1);
4122-
__ LsrImmediate(temp, temp, 31);
4123-
__ orr(out, out, compiler::Operand(temp, LSL, 1));
4124-
// Z lane.
4125-
__ vmovrs(temp, value, 2);
4126-
__ LsrImmediate(temp, temp, 31);
4127-
__ orr(out, out, compiler::Operand(temp, LSL, 2));
4128-
// W lane.
4129-
__ vmovrs(temp, value, 3);
4130-
__ LsrImmediate(temp, temp, 31);
4131-
__ orr(out, out, compiler::Operand(temp, LSL, 3));
4116+
(Register out, VRegister value, Temp<VRegister> temp)) {
4117+
simd128_value_t weights;
4118+
weights.int_storage[0] = 1;
4119+
weights.int_storage[1] = 2;
4120+
weights.int_storage[2] = 4;
4121+
weights.int_storage[3] = 8;
4122+
__ vsshr_4s(VTMP, value, 31);
4123+
__ LoadQImmediate(temp, weights);
4124+
__ vand(VTMP, VTMP, temp);
4125+
__ vuaddlv_4s(VTMP, VTMP);
4126+
__ fmovrs(out, VTMP);
41324127
}
41334128

41344129
DEFINE_EMIT(

‎tests/lib/typed_data/float32x4_sign_mask_test.dart‎

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,53 +12,54 @@ import 'dart:typed_data';
1212
import 'package:expect/expect.dart';
1313

1414
void testImmediates() {
15-
var f = Float32x4(1.0, 2.0, 3.0, 4.0);
16-
var m = f.signMask;
17-
Expect.equals(0x0, m);
18-
f = Float32x4(-1.0, -2.0, -3.0, -0.0);
19-
m = f.signMask;
20-
Expect.equals(0xf, m);
21-
f = Float32x4(-1.0, 2.0, 3.0, 4.0);
22-
m = f.signMask;
23-
Expect.equals(0x1, m);
24-
f = Float32x4(1.0, -2.0, 3.0, 4.0);
25-
m = f.signMask;
26-
Expect.equals(0x2, m);
27-
f = Float32x4(1.0, 2.0, -3.0, 4.0);
28-
m = f.signMask;
29-
Expect.equals(0x4, m);
30-
f = Float32x4(1.0, 2.0, 3.0, -4.0);
31-
m = f.signMask;
32-
Expect.equals(0x8, m);
15+
Expect.equals(0x0, Float32x4(1.0, 2.0, 3.0, 4.0).signMask);
16+
Expect.equals(0xf, Float32x4(-1.0, -2.0, -3.0, -0.0).signMask);
17+
Expect.equals(0x1, Float32x4(-1.0, 2.0, 3.0, 4.0).signMask);
18+
Expect.equals(0x2, Float32x4(1.0, -2.0, 3.0, 4.0).signMask);
19+
Expect.equals(0x4, Float32x4(1.0, 2.0, -3.0, 4.0).signMask);
20+
Expect.equals(0x8, Float32x4(1.0, 2.0, 3.0, -4.0).signMask);
3321
}
3422

3523
void testZero() {
36-
var f = Float32x4(0.0, 0.0, 0.0, 0.0);
37-
var m = f.signMask;
38-
Expect.equals(0x0, m);
39-
f = Float32x4(-0.0, -0.0, -0.0, -0.0);
40-
m = f.signMask;
41-
Expect.equals(0xf, m);
24+
Expect.equals(0x0, Float32x4(0.0, 0.0, 0.0, 0.0).signMask);
25+
Expect.equals(0xf, Float32x4(-0.0, -0.0, -0.0, -0.0).signMask);
4226
}
4327

4428
void testArithmetic() {
45-
var a = Float32x4(1.0, 1.0, 1.0, 1.0);
46-
var b = Float32x4(2.0, 2.0, 2.0, 2.0);
47-
var c = Float32x4(-1.0, -1.0, -1.0, -1.0);
48-
var m1 = (a - b).signMask;
49-
Expect.equals(0xf, m1);
50-
var m2 = (b - a).signMask;
51-
Expect.equals(0x0, m2);
52-
var m3 = (c * c).signMask;
53-
Expect.equals(0x0, m3);
54-
var m4 = (a * c).signMask;
55-
Expect.equals(0xf, m4);
29+
final a = Float32x4(1.0, 1.0, 1.0, 1.0);
30+
final b = Float32x4(2.0, 2.0, 2.0, 2.0);
31+
final c = Float32x4(-1.0, -1.0, -1.0, -1.0);
32+
Expect.equals(0xf, (a - b).signMask);
33+
Expect.equals(0x0, (b - a).signMask);
34+
Expect.equals(0x0, (c * c).signMask);
35+
Expect.equals(0xf, (a * c).signMask);
36+
}
37+
38+
// Check every one of the 16 sign-bit patterns: lane i is negative when bit i of
39+
// `mask` is set, so signMask should return `mask`.
40+
void testExhaustive() {
41+
for (int mask = 0; mask < 16; mask++) {
42+
double lane(int i) => (mask & (1 << i)) != 0 ? -1.0 : 1.0;
43+
Expect.equals(mask, Float32x4(lane(0), lane(1), lane(2), lane(3)).signMask);
44+
}
45+
}
46+
47+
void testSpecialValues() {
48+
const inf = double.infinity;
49+
Expect.equals(0x0, Float32x4(inf, inf, inf, inf).signMask);
50+
Expect.equals(0xf, Float32x4(-inf, -inf, -inf, -inf).signMask);
51+
Expect.equals(0x5, Float32x4(-inf, 1.0, -inf, 1.0).signMask);
52+
// NaN is intentionally not tested. The sign bit of a NaN is not portable
53+
// across architectures or the web backends.
54+
Expect.equals(0xf, Float32x4(-1e-40, -1e-40, -1e-40, -1e-40).signMask);
5655
}
5756

5857
main() {
5958
for (int i = 0; i < 2000; i++) {
6059
testImmediates();
6160
testZero();
6261
testArithmetic();
62+
testExhaustive();
63+
testSpecialValues();
6364
}
6465
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// VMOptions=--optimization-counter-threshold=10 --no-background-compilation
6+
7+
// Library tag to be able to run in html test framework.
8+
library float64x2_sign_mask;
9+
10+
import 'dart:typed_data';
11+
12+
import 'package:expect/expect.dart';
13+
14+
void testImmediates() {
15+
Expect.equals(0x0, Float64x2(1.0, 2.0).signMask);
16+
Expect.equals(0x3, Float64x2(-1.0, -2.0).signMask);
17+
Expect.equals(0x1, Float64x2(-1.0, 2.0).signMask);
18+
Expect.equals(0x2, Float64x2(1.0, -2.0).signMask);
19+
}
20+
21+
void testZero() {
22+
Expect.equals(0x0, Float64x2(0.0, 0.0).signMask);
23+
Expect.equals(0x3, Float64x2(-0.0, -0.0).signMask);
24+
}
25+
26+
// Check every one of the 4 sign-bit patterns: lane i is negative when bit i of
27+
// `mask` is set, so signMask should return `mask`.
28+
void testExhaustive() {
29+
for (int mask = 0; mask < 4; mask++) {
30+
double lane(int i) => (mask & (1 << i)) != 0 ? -1.0 : 1.0;
31+
Expect.equals(mask, Float64x2(lane(0), lane(1)).signMask);
32+
}
33+
}
34+
35+
void testSpecialValues() {
36+
const inf = double.infinity;
37+
Expect.equals(0x0, Float64x2(inf, inf).signMask);
38+
Expect.equals(0x3, Float64x2(-inf, -inf).signMask);
39+
Expect.equals(0x1, Float64x2(-inf, inf).signMask);
40+
// NaN is intentionally not tested. The sign bit of a NaN is not portable
41+
// across architectures or the web backends.
42+
Expect.equals(0x3, Float64x2(-5e-324, -5e-324).signMask);
43+
}
44+
45+
main() {
46+
for (int i = 0; i < 2000; i++) {
47+
testImmediates();
48+
testZero();
49+
testExhaustive();
50+
testSpecialValues();
51+
}
52+
}

‎tests/lib/typed_data/int32x4_sign_mask_test.dart‎

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,53 +12,44 @@ import 'dart:typed_data';
1212
import 'package:expect/expect.dart';
1313

1414
void testImmediates() {
15-
var f = Int32x4(1, 2, 3, 4);
16-
var m = f.signMask;
17-
Expect.equals(0x0, m);
18-
f = Int32x4(-1, -2, -3, -4);
19-
m = f.signMask;
20-
Expect.equals(0xf, m);
21-
f = Int32x4.bool(true, false, false, false);
22-
m = f.signMask;
23-
Expect.equals(0x1, m);
24-
f = Int32x4.bool(false, true, false, false);
25-
m = f.signMask;
26-
Expect.equals(0x2, m);
27-
f = Int32x4.bool(false, false, true, false);
28-
m = f.signMask;
29-
Expect.equals(0x4, m);
30-
f = Int32x4.bool(false, false, false, true);
31-
m = f.signMask;
32-
Expect.equals(0x8, m);
15+
Expect.equals(0x0, Int32x4(1, 2, 3, 4).signMask);
16+
Expect.equals(0xf, Int32x4(-1, -2, -3, -4).signMask);
17+
Expect.equals(0x1, Int32x4.bool(true, false, false, false).signMask);
18+
Expect.equals(0x2, Int32x4.bool(false, true, false, false).signMask);
19+
Expect.equals(0x4, Int32x4.bool(false, false, true, false).signMask);
20+
Expect.equals(0x8, Int32x4.bool(false, false, false, true).signMask);
3321
}
3422

3523
void testZero() {
36-
var f = Int32x4(0, 0, 0, 0);
37-
var m = f.signMask;
38-
Expect.equals(0x0, m);
39-
f = Int32x4(-0, -0, -0, -0);
40-
m = f.signMask;
41-
Expect.equals(0x0, m);
24+
Expect.equals(0x0, Int32x4(0, 0, 0, 0).signMask);
25+
Expect.equals(0x0, Int32x4(-0, -0, -0, -0).signMask);
4226
}
4327

4428
void testLogic() {
45-
var a = Int32x4(0x80000000, 0x80000000, 0x80000000, 0x80000000);
46-
var b = Int32x4(0x70000000, 0x70000000, 0x70000000, 0x70000000);
47-
var c = Int32x4(0xf0000000, 0xf0000000, 0xf0000000, 0xf0000000);
48-
var m1 = (a & c).signMask;
49-
Expect.equals(0xf, m1);
50-
var m2 = (a & b).signMask;
51-
Expect.equals(0x0, m2);
52-
var m3 = (b ^ a).signMask;
53-
Expect.equals(0xf, m3);
54-
var m4 = (b | c).signMask;
55-
Expect.equals(0xf, m4);
29+
final a = Int32x4(0x80000000, 0x80000000, 0x80000000, 0x80000000);
30+
final b = Int32x4(0x70000000, 0x70000000, 0x70000000, 0x70000000);
31+
final c = Int32x4(0xf0000000, 0xf0000000, 0xf0000000, 0xf0000000);
32+
Expect.equals(0xf, (a & c).signMask);
33+
Expect.equals(0x0, (a & b).signMask);
34+
Expect.equals(0xf, (b ^ a).signMask);
35+
Expect.equals(0xf, (b | c).signMask);
36+
}
37+
38+
// Check every one of the 16 sign-bit patterns: lane i is negative when bit i of
39+
// `mask` is set, so signMask should return `mask`. Positive lanes use 0x7fffffff
40+
// (all bits but the sign) to confirm only the sign bit is read.
41+
void testExhaustive() {
42+
for (int mask = 0; mask < 16; mask++) {
43+
int lane(int i) => (mask & (1 << i)) != 0 ? 0x80000000 : 0x7fffffff;
44+
Expect.equals(mask, Int32x4(lane(0), lane(1), lane(2), lane(3)).signMask);
45+
}
5646
}
5747

5848
main() {
5949
for (int i = 0; i < 2000; i++) {
6050
testImmediates();
6151
testZero();
6252
testLogic();
53+
testExhaustive();
6354
}
6455
}

0 commit comments

Comments
 (0)