Skip to content

Commit 651bde5

Browse files
rmacnak-googleCommit Queue
authored andcommitted
[vm, compiler] Implement MathMinMaxInstr with min/max when available.
TEST=vm/dart/min_max_test Change-Id: I727a421d8c38261effdf3b09dbd4fd9330693f82 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394800 Reviewed-by: Alexander Markov <[email protected]> Commit-Queue: Ryan Macnak <[email protected]>
1 parent 5ec81d0 commit 651bde5

File tree

11 files changed

+398
-177
lines changed

11 files changed

+398
-177
lines changed
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// Copyright (c) 2024, 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+
// Dart test for testing Math.min and Math.max.
5+
6+
// VMOptions=--optimization-counter-threshold=50 --no-background-compilation
7+
8+
import "package:expect/expect.dart";
9+
import "dart:math" as math;
10+
11+
var inf = double.infinity;
12+
var nan = double.nan;
13+
14+
@pragma("vm:never-inline")
15+
int smi_min(int a, int b) => math.min(a, b);
16+
17+
@pragma("vm:never-inline")
18+
int smi_max(int a, int b) => math.max(a, b);
19+
20+
@pragma("vm:never-inline")
21+
int mint_min(int a, int b) => math.min(a, b);
22+
23+
@pragma("vm:never-inline")
24+
int mint_max(int a, int b) => math.max(a, b);
25+
26+
@pragma("vm:never-inline")
27+
double double_min(double a, double b) => math.min(a, b);
28+
29+
@pragma("vm:never-inline")
30+
double double_max(double a, double b) => math.max(a, b);
31+
32+
testSmiMin() {
33+
Expect.equals(-2, smi_min(-2, -2));
34+
Expect.equals(-2, smi_min(-1, -2));
35+
Expect.equals(-2, smi_min(0, -2));
36+
Expect.equals(-2, smi_min(1, -2));
37+
Expect.equals(-2, smi_min(2, -2));
38+
39+
Expect.equals(-2, smi_min(-2, -1));
40+
Expect.equals(-1, smi_min(-1, -1));
41+
Expect.equals(-1, smi_min(0, -1));
42+
Expect.equals(-1, smi_min(1, -1));
43+
Expect.equals(-1, smi_min(2, -1));
44+
45+
Expect.equals(-2, smi_min(-2, 0));
46+
Expect.equals(-1, smi_min(-1, 0));
47+
Expect.equals(0, smi_min(0, 0));
48+
Expect.equals(0, smi_min(1, 0));
49+
Expect.equals(0, smi_min(2, 0));
50+
51+
Expect.equals(-2, smi_min(-2, 1));
52+
Expect.equals(-1, smi_min(-1, 1));
53+
Expect.equals(0, smi_min(0, 1));
54+
Expect.equals(1, smi_min(1, 1));
55+
Expect.equals(1, smi_min(2, 1));
56+
57+
Expect.equals(-2, smi_min(-2, 2));
58+
Expect.equals(-1, smi_min(-1, 2));
59+
Expect.equals(0, smi_min(0, 2));
60+
Expect.equals(1, smi_min(1, 2));
61+
Expect.equals(2, smi_min(2, 2));
62+
}
63+
64+
testSmiMax() {
65+
Expect.equals(-2, smi_max(-2, -2));
66+
Expect.equals(-1, smi_max(-1, -2));
67+
Expect.equals(0, smi_max(0, -2));
68+
Expect.equals(1, smi_max(1, -2));
69+
Expect.equals(2, smi_max(2, -2));
70+
71+
Expect.equals(-1, smi_max(-2, -1));
72+
Expect.equals(-1, smi_max(-1, -1));
73+
Expect.equals(0, smi_max(0, -1));
74+
Expect.equals(1, smi_max(1, -1));
75+
Expect.equals(2, smi_max(2, -1));
76+
77+
Expect.equals(0, smi_max(-2, 0));
78+
Expect.equals(0, smi_max(-1, 0));
79+
Expect.equals(0, smi_max(0, 0));
80+
Expect.equals(1, smi_max(1, 0));
81+
Expect.equals(2, smi_max(2, 0));
82+
83+
Expect.equals(1, smi_max(-2, 1));
84+
Expect.equals(1, smi_max(-1, 1));
85+
Expect.equals(1, smi_max(0, 1));
86+
Expect.equals(1, smi_max(1, 1));
87+
Expect.equals(2, smi_max(2, 1));
88+
89+
Expect.equals(2, smi_max(-2, 2));
90+
Expect.equals(2, smi_max(-1, 2));
91+
Expect.equals(2, smi_max(0, 2));
92+
Expect.equals(2, smi_max(1, 2));
93+
Expect.equals(2, smi_max(2, 2));
94+
}
95+
96+
testMintMin() {
97+
Expect.equals(0x8000000000000000, mint_min(0x8000000000000000, 0x8000000000000000));
98+
Expect.equals(0x8000000000000000, mint_min(-1, 0x8000000000000000));
99+
Expect.equals(0x8000000000000000, mint_min(0, 0x8000000000000000));
100+
Expect.equals(0x8000000000000000, mint_min(1, 0x8000000000000000));
101+
Expect.equals(0x8000000000000000, mint_min(0x7FFFFFFFFFFFFFFF, 0x8000000000000000));
102+
103+
Expect.equals(0x8000000000000000, mint_min(0x8000000000000000, -1));
104+
Expect.equals(-1, mint_min(-1, -1));
105+
Expect.equals(-1, mint_min(0, -1));
106+
Expect.equals(-1, mint_min(1, -1));
107+
Expect.equals(-1, mint_min(0x7FFFFFFFFFFFFFFF, -1));
108+
109+
Expect.equals(0x8000000000000000, mint_min(0x8000000000000000, 0));
110+
Expect.equals(-1, mint_min(-1, 0));
111+
Expect.equals(0, mint_min(0, 0));
112+
Expect.equals(0, mint_min(1, 0));
113+
Expect.equals(0, mint_min(0x7FFFFFFFFFFFFFFF, 0));
114+
115+
Expect.equals(0x8000000000000000, mint_min(0x8000000000000000, 1));
116+
Expect.equals(-1, mint_min(-1, 1));
117+
Expect.equals(0, mint_min(0, 1));
118+
Expect.equals(1, mint_min(1, 1));
119+
Expect.equals(1, mint_min(0x7FFFFFFFFFFFFFFF, 1));
120+
121+
Expect.equals(0x8000000000000000, mint_min(0x8000000000000000, 0x7FFFFFFFFFFFFFFF));
122+
Expect.equals(-1, mint_min(-1, 0x7FFFFFFFFFFFFFFF));
123+
Expect.equals(0, mint_min(0, 0x7FFFFFFFFFFFFFFF));
124+
Expect.equals(1, mint_min(1, 0x7FFFFFFFFFFFFFFF));
125+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_min(0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF));
126+
}
127+
128+
testMintMax() {
129+
Expect.equals(0x8000000000000000, mint_max(0x8000000000000000, 0x8000000000000000));
130+
Expect.equals(-1, mint_max(-1, 0x8000000000000000));
131+
Expect.equals(0, mint_max(0, 0x8000000000000000));
132+
Expect.equals(1, mint_max(1, 0x8000000000000000));
133+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_max(0x7FFFFFFFFFFFFFFF, 0x8000000000000000));
134+
135+
Expect.equals(-1, mint_max(0x8000000000000000, -1));
136+
Expect.equals(-1, mint_max(-1, -1));
137+
Expect.equals(0, mint_max(0, -1));
138+
Expect.equals(1, mint_max(1, -1));
139+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_max(0x7FFFFFFFFFFFFFFF, -1));
140+
141+
Expect.equals(0, mint_max(0x8000000000000000, 0));
142+
Expect.equals(0, mint_max(-1, 0));
143+
Expect.equals(0, mint_max(0, 0));
144+
Expect.equals(1, mint_max(1, 0));
145+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_max(0x7FFFFFFFFFFFFFFF, 0));
146+
147+
Expect.equals(1, mint_max(0x8000000000000000, 1));
148+
Expect.equals(1, mint_max(-1, 1));
149+
Expect.equals(1, mint_max(0, 1));
150+
Expect.equals(1, mint_max(1, 1));
151+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_max(0x7FFFFFFFFFFFFFFF, 1));
152+
153+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_max(0x8000000000000000, 0x7FFFFFFFFFFFFFFF));
154+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_max(-1, 0x7FFFFFFFFFFFFFFF));
155+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_max(0, 0x7FFFFFFFFFFFFFFF));
156+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_max(1, 0x7FFFFFFFFFFFFFFF));
157+
Expect.equals(0x7FFFFFFFFFFFFFFF, mint_max(0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF));
158+
}
159+
160+
testDoubleMin() {
161+
Expect.equals(-inf, double_min(-inf, -inf));
162+
Expect.equals(-inf, double_min(-1.0, -inf));
163+
Expect.equals(-inf, double_min(0.0, -inf));
164+
Expect.equals(-inf, double_min(1.0, -inf));
165+
Expect.equals(-inf, double_min(inf, -inf));
166+
167+
Expect.equals(-inf, double_min(-inf, -1.0));
168+
Expect.equals(-1.0, double_min(-1.0, -1.0));
169+
Expect.equals(-1.0, double_min(0.0, -1.0));
170+
Expect.equals(-1.0, double_min(1.0, -1.0));
171+
Expect.equals(-1.0, double_min(inf, -1.0));
172+
173+
Expect.equals(-inf, double_min(-inf, 0.0));
174+
Expect.equals(-1.0, double_min(-1.0, 0.0));
175+
Expect.equals(0.0, double_min(0.0, 0.0));
176+
Expect.equals(0.0, double_min(1.0, 0.0));
177+
Expect.equals(0.0, double_min(inf, 0.0));
178+
179+
Expect.equals(-inf, double_min(-inf, 1.0));
180+
Expect.equals(-1.0, double_min(-1.0, 1.0));
181+
Expect.equals(0.0, double_min(0.0, 1.0));
182+
Expect.equals(1.0, double_min(1.0, 1.0));
183+
Expect.equals(1.0, double_min(inf, 1.0));
184+
185+
Expect.equals(-inf, double_min(-inf, inf));
186+
Expect.equals(-1.0, double_min(-1.0, inf));
187+
Expect.equals(0.0, double_min(0.0, inf));
188+
Expect.equals(1.0, double_min(1.0, inf));
189+
Expect.equals(inf, double_min(inf, inf));
190+
191+
Expect.identical(-0.0, double_min(0.0, -0.0));
192+
Expect.identical(-0.0, double_min(-0.0, 0.0));
193+
194+
Expect.isTrue(double_min(nan, nan).isNaN);
195+
Expect.isTrue(double_min(nan, -inf).isNaN);
196+
Expect.isTrue(double_min(nan, 1.0).isNaN);
197+
Expect.isTrue(double_min(nan, inf).isNaN);
198+
Expect.isTrue(double_min(nan, nan).isNaN);
199+
Expect.isTrue(double_min(-inf, nan).isNaN);
200+
Expect.isTrue(double_min(1.0, nan).isNaN);
201+
Expect.isTrue(double_min(inf, nan).isNaN);
202+
}
203+
204+
testDoubleMax() {
205+
Expect.equals(-inf, double_max(-inf, -inf));
206+
Expect.equals(-1.0, double_max(-1.0, -inf));
207+
Expect.equals(0.0, double_max(0.0, -inf));
208+
Expect.equals(1.0, double_max(1.0, -inf));
209+
Expect.equals(inf, double_max(inf, -inf));
210+
211+
Expect.equals(-1.0, double_max(-inf, -1.0));
212+
Expect.equals(-1.0, double_max(-1.0, -1.0));
213+
Expect.equals(0.0, double_max(0.0, -1.0));
214+
Expect.equals(1.0, double_max(1.0, -1.0));
215+
Expect.equals(inf, double_max(inf, -1.0));
216+
217+
Expect.equals(0.0, double_max(-inf, 0.0));
218+
Expect.equals(0.0, double_max(-1.0, 0.0));
219+
Expect.equals(0.0, double_max(0.0, 0.0));
220+
Expect.equals(1.0, double_max(1.0, 0.0));
221+
Expect.equals(inf, double_max(inf, 0.0));
222+
223+
Expect.equals(1.0, double_max(-inf, 1.0));
224+
Expect.equals(1.0, double_max(-1.0, 1.0));
225+
Expect.equals(1.0, double_max(0.0, 1.0));
226+
Expect.equals(1.0, double_max(1.0, 1.0));
227+
Expect.equals(inf, double_max(inf, 1.0));
228+
229+
Expect.equals(inf, double_max(-inf, inf));
230+
Expect.equals(inf, double_max(-1.0, inf));
231+
Expect.equals(inf, double_max(0.0, inf));
232+
Expect.equals(inf, double_max(1.0, inf));
233+
Expect.equals(inf, double_max(inf, inf));
234+
235+
Expect.identical(0.0, double_max(0.0, -0.0));
236+
Expect.identical(0.0, double_max(-0.0, 0.0));
237+
238+
Expect.isTrue(double_max(nan, nan).isNaN);
239+
Expect.isTrue(double_max(nan, -inf).isNaN);
240+
Expect.isTrue(double_max(nan, 1.0).isNaN);
241+
Expect.isTrue(double_max(nan, inf).isNaN);
242+
Expect.isTrue(double_max(-inf, nan).isNaN);
243+
Expect.isTrue(double_max(1.0, nan).isNaN);
244+
Expect.isTrue(double_max(inf, nan).isNaN);
245+
}
246+
247+
main() {
248+
for (int i = 0; i < 100; i++) {
249+
testSmiMin();
250+
testSmiMax();
251+
testMintMin();
252+
testMintMax();
253+
testDoubleMin();
254+
testDoubleMax();
255+
}
256+
}

runtime/vm/compiler/backend/il.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,16 @@ bool MathMinMaxInstr::AttributesEqual(const Instruction& other) const {
11031103
auto const other_op = other.AsMathMinMax();
11041104
ASSERT(other_op != nullptr);
11051105
return (op_kind() == other_op->op_kind()) &&
1106-
(result_cid() == other_op->result_cid());
1106+
(representation() == other_op->representation());
1107+
}
1108+
1109+
Definition* MathMinMaxInstr::Canonicalize(FlowGraph* flow_graph) {
1110+
if (!HasUses()) return nullptr;
1111+
if (left()->definition()->OriginalDefinition() ==
1112+
right()->definition()->OriginalDefinition()) {
1113+
return left()->definition();
1114+
}
1115+
return this;
11071116
}
11081117

11091118
bool BinaryIntegerOpInstr::AttributesEqual(const Instruction& other) const {

runtime/vm/compiler/backend/il.h

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8805,11 +8805,12 @@ class MathMinMaxInstr : public TemplateDefinition<2, NoThrow, Pure> {
88058805
Value* left_value,
88068806
Value* right_value,
88078807
intptr_t deopt_id,
8808-
intptr_t result_cid)
8808+
Representation representation)
88098809
: TemplateDefinition(deopt_id),
88108810
op_kind_(op_kind),
8811-
result_cid_(result_cid) {
8812-
ASSERT((result_cid == kSmiCid) || (result_cid == kDoubleCid));
8811+
representation_(representation) {
8812+
ASSERT((representation == kUnboxedInt64) ||
8813+
(representation == kUnboxedDouble));
88138814
SetInputAt(0, left_value);
88148815
SetInputAt(1, right_value);
88158816
}
@@ -8819,24 +8820,11 @@ class MathMinMaxInstr : public TemplateDefinition<2, NoThrow, Pure> {
88198820
Value* left() const { return inputs_[0]; }
88208821
Value* right() const { return inputs_[1]; }
88218822

8822-
intptr_t result_cid() const { return result_cid_; }
8823-
88248823
virtual bool ComputeCanDeoptimize() const { return false; }
88258824

8826-
virtual Representation representation() const {
8827-
if (result_cid() == kSmiCid) {
8828-
return kTagged;
8829-
}
8830-
ASSERT(result_cid() == kDoubleCid);
8831-
return kUnboxedDouble;
8832-
}
8833-
8825+
virtual Representation representation() const { return representation_; }
88348826
virtual Representation RequiredInputRepresentation(intptr_t idx) const {
8835-
if (result_cid() == kSmiCid) {
8836-
return kTagged;
8837-
}
8838-
ASSERT(result_cid() == kDoubleCid);
8839-
return kUnboxedDouble;
8827+
return representation_;
88408828
}
88418829

88428830
virtual intptr_t DeoptimizationTarget() const {
@@ -8848,10 +8836,11 @@ class MathMinMaxInstr : public TemplateDefinition<2, NoThrow, Pure> {
88488836
DECLARE_INSTRUCTION(MathMinMax)
88498837
virtual CompileType ComputeType() const;
88508838
virtual bool AttributesEqual(const Instruction& other) const;
8839+
virtual Definition* Canonicalize(FlowGraph* flow_graph);
88518840

88528841
#define FIELD_LIST(F) \
88538842
F(const MethodRecognizer::Kind, op_kind_) \
8854-
F(const intptr_t, result_cid_)
8843+
F(const Representation, representation_)
88558844

88568845
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(MathMinMaxInstr,
88578846
TemplateDefinition,

runtime/vm/compiler/backend/il_arm.cc

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5279,7 +5279,7 @@ void CaseInsensitiveCompareInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
52795279

52805280
LocationSummary* MathMinMaxInstr::MakeLocationSummary(Zone* zone,
52815281
bool opt) const {
5282-
if (result_cid() == kDoubleCid) {
5282+
if (representation() == kUnboxedDouble) {
52835283
const intptr_t kNumInputs = 2;
52845284
const intptr_t kNumTemps = 1;
52855285
LocationSummary* summary = new (zone)
@@ -5291,23 +5291,15 @@ LocationSummary* MathMinMaxInstr::MakeLocationSummary(Zone* zone,
52915291
summary->set_temp(0, Location::RequiresRegister());
52925292
return summary;
52935293
}
5294-
ASSERT(result_cid() == kSmiCid);
5295-
const intptr_t kNumInputs = 2;
5296-
const intptr_t kNumTemps = 0;
5297-
LocationSummary* summary = new (zone)
5298-
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
5299-
summary->set_in(0, Location::RequiresRegister());
5300-
summary->set_in(1, Location::RequiresRegister());
5301-
// Reuse the left register so that code can be made shorter.
5302-
summary->set_out(0, Location::SameAsFirstInput());
5303-
return summary;
5294+
UNREACHABLE();
5295+
return nullptr;
53045296
}
53055297

53065298
void MathMinMaxInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
53075299
ASSERT((op_kind() == MethodRecognizer::kMathMin) ||
53085300
(op_kind() == MethodRecognizer::kMathMax));
53095301
const bool is_min = (op_kind() == MethodRecognizer::kMathMin);
5310-
if (result_cid() == kDoubleCid) {
5302+
if (representation() == kUnboxedDouble) {
53115303
compiler::Label done, returns_nan, are_equal;
53125304
const DRegister left = EvenDRegisterOf(locs()->in(0).fpu_reg());
53135305
const DRegister right = EvenDRegisterOf(locs()->in(1).fpu_reg());
@@ -5348,17 +5340,7 @@ void MathMinMaxInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
53485340
return;
53495341
}
53505342

5351-
ASSERT(result_cid() == kSmiCid);
5352-
const Register left = locs()->in(0).reg();
5353-
const Register right = locs()->in(1).reg();
5354-
const Register result = locs()->out(0).reg();
5355-
__ cmp(left, compiler::Operand(right));
5356-
ASSERT(result == left);
5357-
if (is_min) {
5358-
__ mov(result, compiler::Operand(right), GT);
5359-
} else {
5360-
__ mov(result, compiler::Operand(right), LT);
5361-
}
5343+
UNREACHABLE();
53625344
}
53635345

53645346
LocationSummary* UnarySmiOpInstr::MakeLocationSummary(Zone* zone,

0 commit comments

Comments
 (0)