Skip to content

Commit cadc4c3

Browse files
aartbikcommit-bot@chromium.org
authored andcommitted
[vm/compiler] Improved integer power specialized inlining.
Rationale: "Inlines" special cases of int pow() in strong mode (viz. x^y for either small constant x or y). #33861 Change-Id: I2e52784f82e39bb0ce33733aef70cbc56f27e451 Reviewed-on: https://dart-review.googlesource.com/65576 Commit-Queue: Aart Bik <[email protected]> Reviewed-by: Alexander Markov <[email protected]>
1 parent b322779 commit cadc4c3

File tree

3 files changed

+297
-25
lines changed

3 files changed

+297
-25
lines changed

runtime/vm/compiler/backend/inliner.cc

Lines changed: 105 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ static bool CanUseStrongModeTypes(FlowGraph* flow_graph) {
121121
return FLAG_use_strong_mode_types && flow_graph->isolate()->strong();
122122
}
123123

124+
// Test and obtain Smi value.
125+
static bool IsSmiValue(Value* val, intptr_t* int_val) {
126+
if (val->BindsToConstant() && val->BoundConstant().IsSmi()) {
127+
*int_val = Smi::Cast(val->BoundConstant()).Value();
128+
return true;
129+
}
130+
return false;
131+
}
132+
124133
// Test if a call is recursive by looking in the deoptimization environment.
125134
static bool IsCallRecursive(const Function& function, Definition* call) {
126135
Environment* env = call->env();
@@ -3186,29 +3195,31 @@ bool FlowGraphInliner::TryReplaceStaticCallWithInline(
31863195
}
31873196
// Finally insert the sequence other definition in place of this one in the
31883197
// graph.
3189-
if (entry->next() != NULL) {
3190-
call->previous()->LinkTo(entry->next());
3191-
}
3192-
entry->UnuseAllInputs(); // Entry block is not in the graph.
3193-
if (last != NULL) {
3194-
BlockEntryInstr* link = call->GetBlock();
3195-
Instruction* exit = last->GetBlock();
3196-
if (link != exit) {
3197-
// Dominance relation and SSA are updated incrementally when
3198-
// conditionals are inserted. But succ/pred and ordering needs
3199-
// to be redone. TODO(ajcbik): do this incrementally too.
3200-
link->ClearDominatedBlocks();
3201-
for (intptr_t i = 0, n = entry->dominated_blocks().length(); i < n;
3202-
++i) {
3203-
link->AddDominatedBlock(entry->dominated_blocks()[i]);
3204-
}
3205-
while (exit->next()) {
3206-
exit = exit->next();
3198+
if (entry != nullptr) {
3199+
if (entry->next() != nullptr) {
3200+
call->previous()->LinkTo(entry->next());
3201+
}
3202+
entry->UnuseAllInputs(); // Entry block is not in the graph.
3203+
if (last != NULL) {
3204+
BlockEntryInstr* link = call->GetBlock();
3205+
Instruction* exit = last->GetBlock();
3206+
if (link != exit) {
3207+
// Dominance relation and SSA are updated incrementally when
3208+
// conditionals are inserted. But succ/pred and ordering needs
3209+
// to be redone. TODO(ajcbik): do this incrementally too.
3210+
link->ClearDominatedBlocks();
3211+
for (intptr_t i = 0, n = entry->dominated_blocks().length(); i < n;
3212+
++i) {
3213+
link->AddDominatedBlock(entry->dominated_blocks()[i]);
3214+
}
3215+
while (exit->next()) {
3216+
exit = exit->next();
3217+
}
3218+
exit->LinkTo(call);
3219+
flow_graph->DiscoverBlocks();
3220+
} else {
3221+
last->LinkTo(call);
32073222
}
3208-
exit->LinkTo(call);
3209-
flow_graph->DiscoverBlocks();
3210-
} else {
3211-
last->LinkTo(call);
32123223
}
32133224
}
32143225
// Remove through the iterator.
@@ -3346,6 +3357,73 @@ static bool InlineMathCFunction(FlowGraph* flow_graph,
33463357
return true;
33473358
}
33483359

3360+
static Instruction* InlineMul(FlowGraph* flow_graph,
3361+
Instruction* cursor,
3362+
Definition* x,
3363+
Definition* y) {
3364+
BinaryInt64OpInstr* mul = new (Z)
3365+
BinaryInt64OpInstr(Token::kMUL, new (Z) Value(x), new (Z) Value(y),
3366+
Thread::kNoDeoptId, Instruction::kNotSpeculative);
3367+
return flow_graph->AppendTo(cursor, mul, nullptr, FlowGraph::kValue);
3368+
}
3369+
3370+
static bool InlineMathIntPow(FlowGraph* flow_graph,
3371+
Instruction* call,
3372+
TargetEntryInstr** entry,
3373+
Instruction** last) {
3374+
// Invoking the _intPow(x, y) implies that both:
3375+
// (1) x, y are int
3376+
// (2) y >= 0.
3377+
// Thus, try to inline some very obvious cases.
3378+
// TODO(ajcbik): useful to generalize?
3379+
intptr_t val = 0;
3380+
Value* x = call->PushArgumentAt(0)->value();
3381+
Value* y = call->PushArgumentAt(1)->value();
3382+
// Use x^0 == 1, x^1 == x, and x^c == x * .. * x for small c.
3383+
const intptr_t small_exponent = 5;
3384+
if (IsSmiValue(y, &val)) {
3385+
if (val == 0) {
3386+
*last = flow_graph->GetConstant(Smi::ZoneHandle(Smi::New(1)));
3387+
return true;
3388+
} else if (val == 1) {
3389+
*last = x->definition();
3390+
return true;
3391+
} else if (1 < val && val <= small_exponent) {
3392+
// Lazily construct entry only in this case.
3393+
*entry = new (Z)
3394+
TargetEntryInstr(flow_graph->allocate_block_id(),
3395+
call->GetBlock()->try_index(), Thread::kNoDeoptId);
3396+
(*entry)->InheritDeoptTarget(Z, call);
3397+
Definition* x_def = x->definition();
3398+
Definition* square =
3399+
InlineMul(flow_graph, *entry, x_def, x_def)->AsDefinition();
3400+
*last = square;
3401+
switch (val) {
3402+
case 2:
3403+
return true;
3404+
case 3:
3405+
*last = InlineMul(flow_graph, *last, x_def, square);
3406+
return true;
3407+
case 4:
3408+
*last = InlineMul(flow_graph, *last, square, square);
3409+
return true;
3410+
case 5:
3411+
*last = InlineMul(flow_graph, *last, square, square);
3412+
*last = InlineMul(flow_graph, *last, x_def, (*last)->AsDefinition());
3413+
return true;
3414+
}
3415+
}
3416+
}
3417+
// Use 0^y == 0 (only for y != 0) and 1^y == 1.
3418+
if (IsSmiValue(x, &val)) {
3419+
if (val == 1) {
3420+
*last = x->definition();
3421+
return true;
3422+
}
3423+
}
3424+
return false;
3425+
}
3426+
33493427
bool FlowGraphInliner::TryInlineRecognizedMethod(
33503428
FlowGraph* flow_graph,
33513429
intptr_t receiver_cid,
@@ -3718,6 +3796,9 @@ bool FlowGraphInliner::TryInlineRecognizedMethod(
37183796
case MethodRecognizer::kMathAtan2:
37193797
return InlineMathCFunction(flow_graph, call, kind, entry, last);
37203798

3799+
case MethodRecognizer::kMathIntPow:
3800+
return InlineMathIntPow(flow_graph, call, entry, last);
3801+
37213802
case MethodRecognizer::kObjectConstructor: {
37223803
*entry = new (Z)
37233804
TargetEntryInstr(flow_graph->allocate_block_id(),
@@ -3752,9 +3833,8 @@ bool FlowGraphInliner::TryInlineRecognizedMethod(
37523833

37533834
case MethodRecognizer::kObjectArrayAllocate: {
37543835
Value* num_elements = new (Z) Value(call->ArgumentAt(1));
3755-
if (num_elements->BindsToConstant() &&
3756-
num_elements->BoundConstant().IsSmi()) {
3757-
intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value();
3836+
intptr_t length = 0;
3837+
if (IsSmiValue(num_elements, &length)) {
37583838
if (length >= 0 && length <= Array::kMaxElements) {
37593839
Value* type = new (Z) Value(call->ArgumentAt(0));
37603840
*entry = new (Z) TargetEntryInstr(flow_graph->allocate_block_id(),

runtime/vm/compiler/method_recognizer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ namespace dart {
5656
V(::, min, MathMin, Dynamic, 0x32ebc57d) \
5757
V(::, max, MathMax, Dynamic, 0x377e8889) \
5858
V(::, _doublePow, MathDoublePow, Double, 0x5add0ec1) \
59+
V(::, _intPow, MathIntPow, Dynamic, 0x11b45569) \
5960
V(Float32x4, Float32x4., Float32x4Constructor, Float32x4, 0x26ea459b) \
6061
V(Float32x4, Float32x4.zero, Float32x4Zero, Float32x4, 0x16eca604) \
6162
V(Float32x4, Float32x4.splat, Float32x4Splat, Float32x4, 0x694e83e3) \
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// Copyright (c) 2018, 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+
import "package:expect/expect.dart";
6+
import 'dart:math';
7+
8+
final int maxInt32 = 2147483647;
9+
final int minInt32 = -2147483648;
10+
11+
doZeros() {
12+
Expect.equals(1, pow(0, 0));
13+
Expect.equals(0, pow(0, 1));
14+
Expect.equals(0, pow(0, 2));
15+
Expect.equals(0, pow(0, 3));
16+
Expect.equals(0, pow(0, 4));
17+
Expect.equals(0, pow(0, 5));
18+
Expect.equals(0, pow(0, 45));
19+
Expect.equals(0, pow(0, maxInt32 - 1));
20+
Expect.equals(0, pow(0, maxInt32));
21+
22+
Expect.equals(double.infinity, pow(0, -1));
23+
Expect.equals(double.infinity, pow(0, -2));
24+
Expect.equals(double.infinity, pow(0, minInt32));
25+
}
26+
27+
doOnes() {
28+
Expect.equals(1, pow(1, 0));
29+
Expect.equals(1, pow(1, 1));
30+
Expect.equals(1, pow(1, 2));
31+
Expect.equals(1, pow(1, 3));
32+
Expect.equals(1, pow(1, 4));
33+
Expect.equals(1, pow(1, 5));
34+
Expect.equals(1, pow(1, 45));
35+
Expect.equals(1, pow(1, maxInt32 - 1));
36+
Expect.equals(1, pow(1, maxInt32));
37+
38+
Expect.equals(1.0, pow(1, -1));
39+
Expect.equals(1.0, pow(1, -2));
40+
Expect.equals(1.0, pow(1, minInt32));
41+
}
42+
43+
doMinOnes() {
44+
Expect.equals(1, pow(-1, 0));
45+
Expect.equals(-1, pow(-1, 1));
46+
Expect.equals(1, pow(-1, 2));
47+
Expect.equals(-1, pow(-1, 3));
48+
Expect.equals(1, pow(-1, 4));
49+
Expect.equals(-1, pow(-1, 5));
50+
Expect.equals(-1, pow(-1, 45));
51+
Expect.equals(1, pow(-1, maxInt32 - 1));
52+
Expect.equals(-1, pow(-1, maxInt32));
53+
54+
Expect.equals(-1.0, pow(-1, -1));
55+
Expect.equals(1.0, pow(-1, -2));
56+
Expect.equals(1.0, pow(-1, minInt32));
57+
}
58+
59+
doTwos() {
60+
Expect.equals(1, pow(2, 0));
61+
Expect.equals(2, pow(2, 1));
62+
Expect.equals(4, pow(2, 2));
63+
Expect.equals(8, pow(2, 3));
64+
Expect.equals(16, pow(2, 4));
65+
Expect.equals(32, pow(2, 5));
66+
Expect.equals(32768, pow(2, 15));
67+
Expect.equals(65536, pow(2, 16));
68+
Expect.equals(35184372088832, pow(2, 45));
69+
Expect.equals(0, pow(2, maxInt32 - 1));
70+
Expect.equals(0, pow(2, maxInt32));
71+
72+
Expect.equals(0.5, pow(2, -1));
73+
Expect.equals(0.25, pow(2, -2));
74+
Expect.equals(0.0, pow(2, minInt32));
75+
}
76+
77+
doMinTwos() {
78+
Expect.equals(1, pow(-2, 0));
79+
Expect.equals(-2, pow(-2, 1));
80+
Expect.equals(4, pow(-2, 2));
81+
Expect.equals(-8, pow(-2, 3));
82+
Expect.equals(16, pow(-2, 4));
83+
Expect.equals(-32, pow(-2, 5));
84+
Expect.equals(-32768, pow(-2, 15));
85+
Expect.equals(65536, pow(-2, 16));
86+
Expect.equals(-35184372088832, pow(-2, 45));
87+
Expect.equals(0, pow(-2, maxInt32 - 1));
88+
Expect.equals(0, pow(-2, maxInt32));
89+
90+
Expect.equals(-0.5, pow(-2, -1));
91+
Expect.equals(0.25, pow(-2, -2));
92+
Expect.equals(0.0, pow(-2, minInt32));
93+
}
94+
95+
doVar0() {
96+
int d = 0;
97+
for (int i = -10; i < 10; i++) {
98+
d += pow(i, 0);
99+
}
100+
Expect.equals(20, d);
101+
}
102+
103+
doVar1() {
104+
int d = 0;
105+
for (int i = -10; i < 10; i++) {
106+
d += pow(i, 1);
107+
}
108+
Expect.equals(-10, d);
109+
}
110+
111+
doVar2() {
112+
int d = 0;
113+
for (int i = -10; i < 10; i++) {
114+
d += pow(i, 2);
115+
}
116+
Expect.equals(670, d);
117+
}
118+
119+
doVar3() {
120+
int d = 0;
121+
for (int i = -10; i < 10; i++) {
122+
d += pow(i, 3);
123+
}
124+
Expect.equals(-1000, d);
125+
}
126+
127+
doVar4() {
128+
int d = 0;
129+
for (int i = -10; i < 10; i++) {
130+
d += pow(i, 4);
131+
}
132+
Expect.equals(40666, d);
133+
}
134+
135+
doVar5() {
136+
int d = 0;
137+
for (int i = -10; i < 10; i++) {
138+
d += pow(i, 5);
139+
}
140+
Expect.equals(-100000, d);
141+
}
142+
143+
doVarMax() {
144+
int d = 0;
145+
for (int i = -5; i < 10; i++) {
146+
d += pow(i, maxInt32);
147+
}
148+
Expect.equals(1786231423019973616, d);
149+
}
150+
151+
doVarZeroes() {
152+
int d = 0;
153+
for (int i = 0; i < 10; i++) {
154+
d += pow(0, i);
155+
}
156+
Expect.equals(1, d);
157+
}
158+
159+
doVarOnes() {
160+
int d = 0;
161+
for (int i = 0; i < 10; i++) {
162+
d += pow(1, i);
163+
}
164+
Expect.equals(10, d);
165+
}
166+
167+
doVarTwos() {
168+
int d = 0;
169+
for (int i = 0; i < 10; i++) {
170+
d += pow(2, i);
171+
}
172+
Expect.equals(1023, d);
173+
}
174+
175+
main() {
176+
doZeros();
177+
doOnes();
178+
doMinOnes();
179+
doTwos();
180+
doMinTwos();
181+
doVar0();
182+
doVar1();
183+
doVar2();
184+
doVar3();
185+
doVar4();
186+
doVar5();
187+
doVarMax();
188+
doVarZeroes();
189+
doVarOnes();
190+
doVarTwos();
191+
}

0 commit comments

Comments
 (0)