Skip to content

Commit 4e027fa

Browse files
mralephcommit-bot@chromium.org
authored andcommitted
[vm/compiler] Force unboxing of int phis on 64-bit platforms
More specifically on 64-bit platforms, when other heuristic fail: - in AOT mode unbox all int typed phis. - in JIT mode unbox all non-Smi int typed phis i.e. mixed Mint/Smi phis. This brings treatment of int phis closer to how we treat other boxed phis. This considerably improves AOT code quality for loops which our current conservative heuristic does not currently hit, for example: void foo(int start, int end) { for (var i = start; i < end; i++) { } } Code like this would use a boxed Phi previously which does not make sense. In JIT mode to really benefit from aggressive unboxing we might need to shift from over-reliance on Smi operations, e.g. when phi is unboxed we want to cascade that through and unbox operations which are performed on it transitively. This requires much more sophisticated algorithm and is outside of scope for this change. In AOT we in general treat all arithmetic operations as operations over 64-bit int values so unboxing phis makes more sense. As a result of this change we see small improvement in code size: flutter_gallery_app_so_gzip_size -0.2392% TEST=ci Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-release-x64-try Change-Id: Iae9a16f353e53f649da4e665d2812ad47099a674 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/187860 Commit-Queue: Vyacheslav Egorov <[email protected]> Reviewed-by: Alexander Markov <[email protected]>
1 parent fa0f468 commit 4e027fa

8 files changed

+156
-20
lines changed

runtime/vm/compiler/backend/constant_propagator_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ ISOLATE_UNIT_TEST_CASE(ConstantPropagation_PhiUnwrappingAndConvergence) {
5151

5252
{
5353
BlockBuilder builder(H.flow_graph(), b2);
54-
v1 = H.Phi(b2, {{b1, v0}, {b3, FlowGraphBuilderHelper::kPhiSelfReference}});
54+
v1 = H.Phi(b2, {{b1, v0}, {b3, &v1}});
5555
builder.AddPhi(v1);
5656
auto v2 = builder.AddDefinition(
5757
new EqualityCompareInstr(InstructionSource(), Token::kEQ, new Value(v1),

runtime/vm/compiler/backend/flow_graph.cc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,7 +1901,7 @@ void FlowGraph::InsertConversionsFor(Definition* def) {
19011901
}
19021902
}
19031903

1904-
static void UnboxPhi(PhiInstr* phi) {
1904+
static void UnboxPhi(PhiInstr* phi, bool is_aot) {
19051905
Representation unboxed = phi->representation();
19061906

19071907
switch (phi->Type()->ToCid()) {
@@ -2004,10 +2004,22 @@ static void UnboxPhi(PhiInstr* phi) {
20042004
}
20052005
}
20062006

2007+
#if defined(TARGET_ARCH_IS_64_BIT)
2008+
// In AOT mode on 64-bit platforms always unbox integer typed phis (similar
2009+
// to how we treat doubles and other boxed numeric types).
2010+
// In JIT mode only unbox phis which are not fully known to be Smi.
2011+
if ((unboxed == kTagged) && phi->Type()->IsInt() &&
2012+
(is_aot || phi->Type()->ToCid() != kSmiCid)) {
2013+
unboxed = kUnboxedInt64;
2014+
}
2015+
#endif
2016+
20072017
phi->set_representation(unboxed);
20082018
}
20092019

20102020
void FlowGraph::SelectRepresentations() {
2021+
const auto is_aot = CompilerState::Current().is_aot();
2022+
20112023
// First we decide for each phi if it is beneficial to unbox it. If so, we
20122024
// change it's `phi->representation()`
20132025
for (BlockIterator block_it = reverse_postorder_iterator(); !block_it.Done();
@@ -2016,7 +2028,7 @@ void FlowGraph::SelectRepresentations() {
20162028
if (join_entry != NULL) {
20172029
for (PhiIterator it(join_entry); !it.Done(); it.Advance()) {
20182030
PhiInstr* phi = it.Current();
2019-
UnboxPhi(phi);
2031+
UnboxPhi(phi, is_aot);
20202032
}
20212033
}
20222034
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) 2012, 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+
#include "vm/compiler/backend/flow_graph.h"
6+
7+
#include <vector>
8+
9+
#include "platform/utils.h"
10+
#include "vm/compiler/backend/block_builder.h"
11+
#include "vm/compiler/backend/il_printer.h"
12+
#include "vm/compiler/backend/il_test_helper.h"
13+
#include "vm/compiler/backend/type_propagator.h"
14+
#include "vm/unit_test.h"
15+
16+
namespace dart {
17+
18+
#if defined(TARGET_ARCH_IS_64_BIT)
19+
ISOLATE_UNIT_TEST_CASE(FlowGraph_UnboxInt64Phi) {
20+
using compiler::BlockBuilder;
21+
22+
CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
23+
24+
FlowGraphBuilderHelper H;
25+
26+
// Add a variable into the scope which would provide static type for the
27+
// parameter.
28+
LocalVariable* v0_var =
29+
new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
30+
String::Handle(Symbols::New(thread, "v0")),
31+
AbstractType::ZoneHandle(Type::IntType()),
32+
new CompileType(CompileType::Int()));
33+
v0_var->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
34+
H.flow_graph()->parsed_function().scope()->AddVariable(v0_var);
35+
36+
auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
37+
auto loop_header = H.JoinEntry();
38+
auto loop_body = H.TargetEntry();
39+
auto loop_exit = H.TargetEntry();
40+
41+
Definition* v0;
42+
PhiInstr* loop_var;
43+
Definition* add1;
44+
ReturnInstr* ret;
45+
46+
{
47+
BlockBuilder builder(H.flow_graph(), normal_entry);
48+
v0 = builder.AddParameter(0, 0, /*with_frame=*/true, kTagged);
49+
builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
50+
}
51+
52+
{
53+
BlockBuilder builder(H.flow_graph(), loop_header);
54+
loop_var = H.Phi(loop_header, {{normal_entry, v0}, {loop_body, &add1}});
55+
builder.AddPhi(loop_var);
56+
builder.AddBranch(new RelationalOpInstr(
57+
InstructionSource(), Token::kLT, new Value(loop_var),
58+
new Value(H.IntConstant(50)), kMintCid,
59+
S.GetNextDeoptId(), Instruction::kNotSpeculative),
60+
loop_body, loop_exit);
61+
}
62+
63+
{
64+
BlockBuilder builder(H.flow_graph(), loop_body);
65+
add1 = builder.AddDefinition(new BinaryInt64OpInstr(
66+
Token::kADD, new Value(loop_var), new Value(H.IntConstant(1)),
67+
S.GetNextDeoptId(), Instruction::kNotSpeculative));
68+
builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
69+
}
70+
71+
{
72+
BlockBuilder builder(H.flow_graph(), loop_exit);
73+
ret = builder.AddReturn(new Value(loop_var));
74+
}
75+
76+
H.FinishGraph();
77+
78+
FlowGraphTypePropagator::Propagate(H.flow_graph());
79+
H.flow_graph()->SelectRepresentations();
80+
81+
EXPECT_PROPERTY(loop_var, it.representation() == kUnboxedInt64);
82+
}
83+
#endif // defined(TARGET_ARCH_IS_64_BIT)
84+
85+
} // namespace dart

runtime/vm/compiler/backend/il_test_helper.cc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121

2222
namespace dart {
2323

24-
Definition* const FlowGraphBuilderHelper::kPhiSelfReference = nullptr;
25-
2624
LibraryPtr LoadTestScript(const char* script,
2725
Dart_NativeEntryResolver resolver,
2826
const char* lib_uri) {

runtime/vm/compiler/backend/il_test_helper.h

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
66
#define RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
77

8+
#include <type_traits>
89
#include <utility>
910
#include <vector>
1011

@@ -277,20 +278,48 @@ class FlowGraphBuilderHelper {
277278
return flow_graph_.GetConstant(Double::Handle(Double::NewCanonical(value)));
278279
}
279280

280-
static Definition* const kPhiSelfReference;
281+
enum class IncomingDefKind {
282+
kImmediate,
283+
kDelayed,
284+
};
285+
286+
class IncomingDef {
287+
public:
288+
IncomingDef(BlockEntryInstr* from, Definition* defn)
289+
: kind_(IncomingDefKind::kImmediate), from_(from), defn_(defn) {}
290+
291+
template <typename T,
292+
typename = typename std::enable_if<
293+
std::is_base_of<Definition, T>::value>::type>
294+
IncomingDef(BlockEntryInstr* from, T** defn_source)
295+
: kind_(IncomingDefKind::kDelayed),
296+
from_(from),
297+
defn_source_(reinterpret_cast<Definition**>(defn_source)) {}
298+
299+
BlockEntryInstr* from() const { return from_; }
300+
Definition* defn() const {
301+
return kind_ == IncomingDefKind::kImmediate ? defn_ : *defn_source_;
302+
}
303+
304+
private:
305+
IncomingDefKind kind_;
306+
BlockEntryInstr* from_;
307+
union {
308+
Definition* defn_;
309+
Definition** defn_source_;
310+
};
311+
};
281312

282313
PhiInstr* Phi(JoinEntryInstr* join,
283-
std::initializer_list<std::pair<BlockEntryInstr*, Definition*>>
284-
incoming) {
314+
std::initializer_list<IncomingDef> incoming) {
285315
auto phi = new PhiInstr(join, incoming.size());
286316
for (size_t i = 0; i < incoming.size(); i++) {
287317
auto input = new Value(flow_graph_.constant_dead());
288318
phi->SetInputAt(i, input);
289319
input->definition()->AddInputUse(input);
290320
}
291-
for (auto pair : incoming) {
292-
pending_phis_.Add({phi, pair.first,
293-
pair.second == kPhiSelfReference ? phi : pair.second});
321+
for (auto def : incoming) {
322+
pending_phis_.Add({phi, def});
294323
}
295324
return phi;
296325
}
@@ -303,9 +332,9 @@ class FlowGraphBuilderHelper {
303332
for (auto& pending : pending_phis_) {
304333
auto join = pending.phi->block();
305334
EXPECT(pending.phi->InputCount() == join->PredecessorCount());
306-
auto pred_index = join->IndexOfPredecessor(pending.pred);
335+
auto pred_index = join->IndexOfPredecessor(pending.incoming.from());
307336
EXPECT(pred_index != -1);
308-
pending.phi->InputAt(pred_index)->BindTo(pending.defn);
337+
pending.phi->InputAt(pred_index)->BindTo(pending.incoming.defn());
309338
}
310339
}
311340

@@ -347,8 +376,7 @@ class FlowGraphBuilderHelper {
347376

348377
struct PendingPhiInput {
349378
PhiInstr* phi;
350-
BlockEntryInstr* pred;
351-
Definition* defn;
379+
IncomingDef incoming;
352380
};
353381
GrowableArray<PendingPhiInput> pending_phis_;
354382
};

runtime/vm/compiler/backend/inliner_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@ ISOLATE_UNIT_TEST_CASE(Inliner_List_generate) {
222222
RELEASE_ASSERT(cursor.TryMatch({
223223
kMoveGlob,
224224
kMatchAndMoveCreateArray,
225-
kMatchAndMoveUnboxInt64,
226225
{kMoveAny, &unbox1},
226+
kMatchAndMoveUnboxInt64,
227227
{kMoveAny, &unbox2},
228228
kMatchAndMoveGoto,
229229

runtime/vm/compiler/backend/loops_test.cc

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,13 @@ static const char* ComputeInduction(Thread* thread, const char* script_chars) {
6868
Invoke(root_library, "main");
6969

7070
std::initializer_list<CompilerPass::Id> passes = {
71-
CompilerPass::kComputeSSA, CompilerPass::kTypePropagation,
72-
CompilerPass::kApplyICData, CompilerPass::kSelectRepresentations,
73-
CompilerPass::kTypePropagation, CompilerPass::kCanonicalize,
71+
CompilerPass::kComputeSSA,
72+
CompilerPass::kTypePropagation,
73+
CompilerPass::kApplyICData,
74+
CompilerPass::kTypePropagation,
75+
CompilerPass::kSelectRepresentations,
76+
CompilerPass::kTypePropagation,
77+
CompilerPass::kCanonicalize,
7478
};
7579
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
7680
TestPipeline pipeline(function, CompilerPass::kJIT);
@@ -300,7 +304,7 @@ ISOLATE_UNIT_TEST_CASE(WrapAroundAndDerived) {
300304
const char* expected =
301305
" [0\n"
302306
" WRAP(99, LIN(0 + 1 * i))\n" // phi (w)
303-
" LIN(0 + 1 * i) 100\n" // phi
307+
" LIN(0 + 1 * i) 100\n" // phi (i)
304308
" WRAP(102, LIN(3 + 1 * i))\n" // a
305309
" WRAP(94, LIN(-5 + 1 * i))\n" // b
306310
" WRAP(693, LIN(0 + 7 * i))\n" // c
@@ -383,11 +387,15 @@ ISOLATE_UNIT_TEST_CASE(NonStrictConditionUpWrap) {
383387
const char* expected =
384388
" [0\n"
385389
" LIN(9223372036854775806 + 1 * i)\n" // phi
390+
#if !defined(TARGET_ARCH_IS_64_BIT)
386391
" LIN(9223372036854775806 + 1 * i)\n" // (un)boxing
387392
" LIN(9223372036854775806 + 1 * i)\n"
388393
" LIN(9223372036854775806 + 1 * i)\n"
394+
#endif // !defined(TARGET_ARCH_IS_64_BIT)
389395
" LIN(9223372036854775807 + 1 * i)\n" // add
396+
#if !defined(TARGET_ARCH_IS_64_BIT)
390397
" LIN(9223372036854775807 + 1 * i)\n" // unbox
398+
#endif // !defined(TARGET_ARCH_IS_64_BIT)
391399
" ]\n";
392400
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
393401
}
@@ -426,11 +434,15 @@ ISOLATE_UNIT_TEST_CASE(NonStrictConditionDownWrap) {
426434
const char* expected =
427435
" [0\n"
428436
" LIN(-9223372036854775807 + -1 * i)\n" // phi
437+
#if !defined(TARGET_ARCH_IS_64_BIT)
429438
" LIN(-9223372036854775807 + -1 * i)\n" // (un)boxing
430439
" LIN(-9223372036854775807 + -1 * i)\n"
431440
" LIN(-9223372036854775807 + -1 * i)\n"
441+
#endif // !defined(TARGET_ARCH_IS_64_BIT)
432442
" LIN(-9223372036854775808 + -1 * i)\n" // sub
443+
#if !defined(TARGET_ARCH_IS_64_BIT)
433444
" LIN(-9223372036854775808 + -1 * i)\n" // unbox
445+
#endif // !defined(TARGET_ARCH_IS_64_BIT)
434446
" ]\n";
435447
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
436448
}

runtime/vm/compiler/compiler_sources.gni

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ compiler_sources_tests = [
160160
"assembler/disassembler_test.cc",
161161
"backend/bce_test.cc",
162162
"backend/constant_propagator_test.cc",
163+
"backend/flow_graph_test.cc",
163164
"backend/il_test.cc",
164165
"backend/il_test_helper.h",
165166
"backend/il_test_helper.cc",

0 commit comments

Comments
 (0)