1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <functional>
18
19#include "arch/x86/instruction_set_features_x86.h"
20#include "code_generator_x86.h"
21#include "constant_folding.h"
22#include "dead_code_elimination.h"
23#include "driver/compiler_options.h"
24#include "graph_checker.h"
25#include "optimizing_unit_test.h"
26#include "pretty_printer.h"
27
28#include "gtest/gtest.h"
29
30namespace art {
31
32/**
33 * Fixture class for the constant folding and dce tests.
34 */
35class ConstantFoldingTest : public OptimizingUnitTest {
36 public:
37  ConstantFoldingTest() : graph_(nullptr) { }
38
39  void TestCode(const std::vector<uint16_t>& data,
40                const std::string& expected_before,
41                const std::string& expected_after_cf,
42                const std::string& expected_after_dce,
43                const std::function<void(HGraph*)>& check_after_cf,
44                DataType::Type return_type = DataType::Type::kInt32) {
45    graph_ = CreateCFG(data, return_type);
46    TestCodeOnReadyGraph(expected_before,
47                         expected_after_cf,
48                         expected_after_dce,
49                         check_after_cf);
50  }
51
52  void TestCodeOnReadyGraph(const std::string& expected_before,
53                            const std::string& expected_after_cf,
54                            const std::string& expected_after_dce,
55                            const std::function<void(HGraph*)>& check_after_cf) {
56    ASSERT_NE(graph_, nullptr);
57
58    StringPrettyPrinter printer_before(graph_);
59    printer_before.VisitInsertionOrder();
60    std::string actual_before = printer_before.str();
61    EXPECT_EQ(expected_before, actual_before);
62
63    std::unique_ptr<const X86InstructionSetFeatures> features_x86(
64        X86InstructionSetFeatures::FromCppDefines());
65    x86::CodeGeneratorX86 codegenX86(graph_, *features_x86.get(), CompilerOptions());
66    HConstantFolding(graph_, "constant_folding").Run();
67    GraphChecker graph_checker_cf(graph_);
68    graph_checker_cf.Run();
69    ASSERT_TRUE(graph_checker_cf.IsValid());
70
71    StringPrettyPrinter printer_after_cf(graph_);
72    printer_after_cf.VisitInsertionOrder();
73    std::string actual_after_cf = printer_after_cf.str();
74    EXPECT_EQ(expected_after_cf, actual_after_cf);
75
76    check_after_cf(graph_);
77
78    HDeadCodeElimination(graph_, nullptr /* stats */, "dead_code_elimination").Run();
79    GraphChecker graph_checker_dce(graph_);
80    graph_checker_dce.Run();
81    ASSERT_TRUE(graph_checker_dce.IsValid());
82
83    StringPrettyPrinter printer_after_dce(graph_);
84    printer_after_dce.VisitInsertionOrder();
85    std::string actual_after_dce = printer_after_dce.str();
86    EXPECT_EQ(expected_after_dce, actual_after_dce);
87  }
88
89  HGraph* graph_;
90};
91
92/**
93 * Tiny three-register program exercising int constant folding on negation.
94 *
95 *                              16-bit
96 *                              offset
97 *                              ------
98 *     v0 <- 1                  0.      const/4 v0, #+1
99 *     v1 <- -v0                1.      neg-int v1, v0
100 *     return v1                2.      return v1
101 */
102TEST_F(ConstantFoldingTest, IntConstantFoldingNegation) {
103  const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
104    Instruction::CONST_4 | 0 << 8 | 1 << 12,
105    Instruction::NEG_INT | 1 << 8 | 0 << 12,
106    Instruction::RETURN | 1 << 8);
107
108  std::string expected_before =
109      "BasicBlock 0, succ: 1\n"
110      "  2: IntConstant [3]\n"
111      "  0: SuspendCheck\n"
112      "  1: Goto 1\n"
113      "BasicBlock 1, pred: 0, succ: 2\n"
114      "  3: Neg(2) [4]\n"
115      "  4: Return(3)\n"
116      "BasicBlock 2, pred: 1\n"
117      "  5: Exit\n";
118
119  // Expected difference after constant folding.
120  diff_t expected_cf_diff = {
121    { "  2: IntConstant [3]\n", "  2: IntConstant\n"
122                                "  6: IntConstant [4]\n" },
123    { "  3: Neg(2) [4]\n",      removed },
124    { "  4: Return(3)\n",       "  4: Return(6)\n" }
125  };
126  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
127
128  // Check the value of the computed constant.
129  auto check_after_cf = [](HGraph* graph) {
130    HInstruction* inst = graph->GetBlocks()[1]->GetFirstInstruction()->InputAt(0);
131    ASSERT_TRUE(inst->IsIntConstant());
132    ASSERT_EQ(inst->AsIntConstant()->GetValue(), -1);
133  };
134
135  // Expected difference after dead code elimination.
136  diff_t expected_dce_diff = {
137    { "  2: IntConstant\n", removed },
138  };
139  std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
140
141  TestCode(data,
142           expected_before,
143           expected_after_cf,
144           expected_after_dce,
145           check_after_cf);
146}
147
148/**
149 * Tiny three-register program exercising long constant folding on negation.
150 *
151 *                              16-bit
152 *                              offset
153 *                              ------
154 *     (v0, v1) <- 4294967296   0.      const-wide v0 #+4294967296
155 *     (v2, v3) <- -(v0, v1)    1.      neg-long v2, v0
156 *     return (v2, v3)          2.      return-wide v2
157 */
158TEST_F(ConstantFoldingTest, LongConstantFoldingNegation) {
159  const int64_t input = INT64_C(4294967296);             // 2^32
160  const uint16_t word0 = Low16Bits(Low32Bits(input));    // LSW.
161  const uint16_t word1 = High16Bits(Low32Bits(input));
162  const uint16_t word2 = Low16Bits(High32Bits(input));
163  const uint16_t word3 = High16Bits(High32Bits(input));  // MSW.
164  const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM(
165    Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3,
166    Instruction::NEG_LONG | 2 << 8 | 0 << 12,
167    Instruction::RETURN_WIDE | 2 << 8);
168
169  std::string expected_before =
170      "BasicBlock 0, succ: 1\n"
171      "  2: LongConstant [3]\n"
172      "  0: SuspendCheck\n"
173      "  1: Goto 1\n"
174      "BasicBlock 1, pred: 0, succ: 2\n"
175      "  3: Neg(2) [4]\n"
176      "  4: Return(3)\n"
177      "BasicBlock 2, pred: 1\n"
178      "  5: Exit\n";
179
180  // Expected difference after constant folding.
181  diff_t expected_cf_diff = {
182    { "  2: LongConstant [3]\n", "  2: LongConstant\n"
183                                 "  6: LongConstant [4]\n" },
184    { "  3: Neg(2) [4]\n",       removed },
185    { "  4: Return(3)\n",        "  4: Return(6)\n" }
186  };
187  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
188
189  // Check the value of the computed constant.
190  auto check_after_cf = [](HGraph* graph) {
191    HInstruction* inst = graph->GetBlocks()[1]->GetFirstInstruction()->InputAt(0);
192    ASSERT_TRUE(inst->IsLongConstant());
193    ASSERT_EQ(inst->AsLongConstant()->GetValue(), INT64_C(-4294967296));
194  };
195
196  // Expected difference after dead code elimination.
197  diff_t expected_dce_diff = {
198    { "  2: LongConstant\n", removed },
199  };
200  std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
201
202  TestCode(data,
203           expected_before,
204           expected_after_cf,
205           expected_after_dce,
206           check_after_cf,
207           DataType::Type::kInt64);
208}
209
210/**
211 * Tiny three-register program exercising int constant folding on addition.
212 *
213 *                              16-bit
214 *                              offset
215 *                              ------
216 *     v0 <- 1                  0.      const/4 v0, #+1
217 *     v1 <- 2                  1.      const/4 v1, #+2
218 *     v2 <- v0 + v1            2.      add-int v2, v0, v1
219 *     return v2                4.      return v2
220 */
221TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition1) {
222  const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM(
223    Instruction::CONST_4 | 0 << 8 | 1 << 12,
224    Instruction::CONST_4 | 1 << 8 | 2 << 12,
225    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
226    Instruction::RETURN | 2 << 8);
227
228  std::string expected_before =
229      "BasicBlock 0, succ: 1\n"
230      "  2: IntConstant [4]\n"
231      "  3: IntConstant [4]\n"
232      "  0: SuspendCheck\n"
233      "  1: Goto 1\n"
234      "BasicBlock 1, pred: 0, succ: 2\n"
235      "  4: Add(2, 3) [5]\n"
236      "  5: Return(4)\n"
237      "BasicBlock 2, pred: 1\n"
238      "  6: Exit\n";
239
240  // Expected difference after constant folding.
241  diff_t expected_cf_diff = {
242    { "  2: IntConstant [4]\n", "  2: IntConstant\n" },
243    { "  3: IntConstant [4]\n", "  3: IntConstant\n"
244                                "  7: IntConstant [5]\n" },
245    { "  4: Add(2, 3) [5]\n",   removed },
246    { "  5: Return(4)\n",       "  5: Return(7)\n" }
247  };
248  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
249
250  // Check the value of the computed constant.
251  auto check_after_cf = [](HGraph* graph) {
252    HInstruction* inst = graph->GetBlocks()[1]->GetFirstInstruction()->InputAt(0);
253    ASSERT_TRUE(inst->IsIntConstant());
254    ASSERT_EQ(inst->AsIntConstant()->GetValue(), 3);
255  };
256
257  // Expected difference after dead code elimination.
258  diff_t expected_dce_diff = {
259    { "  2: IntConstant\n", removed },
260    { "  3: IntConstant\n", removed }
261  };
262  std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
263
264  TestCode(data,
265           expected_before,
266           expected_after_cf,
267           expected_after_dce,
268           check_after_cf);
269}
270
271/**
272 * Small three-register program exercising int constant folding on addition.
273 *
274 *                              16-bit
275 *                              offset
276 *                              ------
277 *     v0 <- 1                  0.      const/4 v0, #+1
278 *     v1 <- 2                  1.      const/4 v1, #+2
279 *     v0 <- v0 + v1            2.      add-int/2addr v0, v1
280 *     v1 <- 4                  3.      const/4 v1, #+4
281 *     v2 <- 5                  4.      const/4 v2, #+5
282 *     v1 <- v1 + v2            5.      add-int/2addr v1, v2
283 *     v2 <- v0 + v1            6.      add-int v2, v0, v1
284 *     return v2                8.      return v2
285 */
286TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition2) {
287  const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM(
288    Instruction::CONST_4 | 0 << 8 | 1 << 12,
289    Instruction::CONST_4 | 1 << 8 | 2 << 12,
290    Instruction::ADD_INT_2ADDR | 0 << 8 | 1 << 12,
291    Instruction::CONST_4 | 1 << 8 | 4 << 12,
292    Instruction::CONST_4 | 2 << 8 | 5 << 12,
293    Instruction::ADD_INT_2ADDR | 1 << 8 | 2 << 12,
294    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
295    Instruction::RETURN | 2 << 8);
296
297  std::string expected_before =
298      "BasicBlock 0, succ: 1\n"
299      "  2: IntConstant [4]\n"
300      "  3: IntConstant [4]\n"
301      "  5: IntConstant [7]\n"
302      "  6: IntConstant [7]\n"
303      "  0: SuspendCheck\n"
304      "  1: Goto 1\n"
305      "BasicBlock 1, pred: 0, succ: 2\n"
306      "  4: Add(2, 3) [8]\n"
307      "  7: Add(5, 6) [8]\n"
308      "  8: Add(4, 7) [9]\n"
309      "  9: Return(8)\n"
310      "BasicBlock 2, pred: 1\n"
311      "  10: Exit\n";
312
313  // Expected difference after constant folding.
314  diff_t expected_cf_diff = {
315    { "  2: IntConstant [4]\n",  "  2: IntConstant\n" },
316    { "  3: IntConstant [4]\n",  "  3: IntConstant\n" },
317    { "  5: IntConstant [7]\n",  "  5: IntConstant\n" },
318    { "  6: IntConstant [7]\n",  "  6: IntConstant\n"
319                                 "  11: IntConstant\n"
320                                 "  12: IntConstant\n"
321                                 "  13: IntConstant [9]\n" },
322    { "  4: Add(2, 3) [8]\n",    removed },
323    { "  7: Add(5, 6) [8]\n",    removed },
324    { "  8: Add(4, 7) [9]\n",    removed  },
325    { "  9: Return(8)\n",        "  9: Return(13)\n" }
326  };
327  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
328
329  // Check the values of the computed constants.
330  auto check_after_cf = [](HGraph* graph) {
331    HInstruction* inst1 = graph->GetBlocks()[1]->GetFirstInstruction()->InputAt(0);
332    ASSERT_TRUE(inst1->IsIntConstant());
333    ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 12);
334    HInstruction* inst2 = inst1->GetPrevious();
335    ASSERT_TRUE(inst2->IsIntConstant());
336    ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 9);
337    HInstruction* inst3 = inst2->GetPrevious();
338    ASSERT_TRUE(inst3->IsIntConstant());
339    ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 3);
340  };
341
342  // Expected difference after dead code elimination.
343  diff_t expected_dce_diff = {
344    { "  2: IntConstant\n",  removed },
345    { "  3: IntConstant\n",  removed },
346    { "  5: IntConstant\n",  removed },
347    { "  6: IntConstant\n",  removed },
348    { "  11: IntConstant\n", removed },
349    { "  12: IntConstant\n", removed }
350  };
351  std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
352
353  TestCode(data,
354           expected_before,
355           expected_after_cf,
356           expected_after_dce,
357           check_after_cf);
358}
359
360/**
361 * Tiny three-register program exercising int constant folding on subtraction.
362 *
363 *                              16-bit
364 *                              offset
365 *                              ------
366 *     v0 <- 3                  0.      const/4 v0, #+3
367 *     v1 <- 2                  1.      const/4 v1, #+2
368 *     v2 <- v0 - v1            2.      sub-int v2, v0, v1
369 *     return v2                4.      return v2
370 */
371TEST_F(ConstantFoldingTest, IntConstantFoldingOnSubtraction) {
372  const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM(
373    Instruction::CONST_4 | 0 << 8 | 3 << 12,
374    Instruction::CONST_4 | 1 << 8 | 2 << 12,
375    Instruction::SUB_INT | 2 << 8, 0 | 1 << 8,
376    Instruction::RETURN | 2 << 8);
377
378  std::string expected_before =
379      "BasicBlock 0, succ: 1\n"
380      "  2: IntConstant [4]\n"
381      "  3: IntConstant [4]\n"
382      "  0: SuspendCheck\n"
383      "  1: Goto 1\n"
384      "BasicBlock 1, pred: 0, succ: 2\n"
385      "  4: Sub(2, 3) [5]\n"
386      "  5: Return(4)\n"
387      "BasicBlock 2, pred: 1\n"
388      "  6: Exit\n";
389
390  // Expected difference after constant folding.
391  diff_t expected_cf_diff = {
392    { "  2: IntConstant [4]\n",  "  2: IntConstant\n" },
393    { "  3: IntConstant [4]\n",  "  3: IntConstant\n"
394                                 "  7: IntConstant [5]\n" },
395    { "  4: Sub(2, 3) [5]\n",    removed },
396    { "  5: Return(4)\n",        "  5: Return(7)\n" }
397  };
398  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
399
400  // Check the value of the computed constant.
401  auto check_after_cf = [](HGraph* graph) {
402    HInstruction* inst = graph->GetBlocks()[1]->GetFirstInstruction()->InputAt(0);
403    ASSERT_TRUE(inst->IsIntConstant());
404    ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1);
405  };
406
407  // Expected difference after dead code elimination.
408  diff_t expected_dce_diff = {
409    { "  2: IntConstant\n", removed },
410    { "  3: IntConstant\n", removed }
411  };
412  std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
413
414  TestCode(data,
415           expected_before,
416           expected_after_cf,
417           expected_after_dce,
418           check_after_cf);
419}
420
421/**
422 * Tiny three-register-pair program exercising long constant folding
423 * on addition.
424 *
425 *                              16-bit
426 *                              offset
427 *                              ------
428 *     (v0, v1) <- 1            0.      const-wide/16 v0, #+1
429 *     (v2, v3) <- 2            2.      const-wide/16 v2, #+2
430 *     (v4, v5) <-
431 *       (v0, v1) + (v1, v2)    4.      add-long v4, v0, v2
432 *     return (v4, v5)          6.      return-wide v4
433 */
434TEST_F(ConstantFoldingTest, LongConstantFoldingOnAddition) {
435  const std::vector<uint16_t> data = SIX_REGISTERS_CODE_ITEM(
436    Instruction::CONST_WIDE_16 | 0 << 8, 1,
437    Instruction::CONST_WIDE_16 | 2 << 8, 2,
438    Instruction::ADD_LONG | 4 << 8, 0 | 2 << 8,
439    Instruction::RETURN_WIDE | 4 << 8);
440
441  std::string expected_before =
442      "BasicBlock 0, succ: 1\n"
443      "  2: LongConstant [4]\n"
444      "  3: LongConstant [4]\n"
445      "  0: SuspendCheck\n"
446      "  1: Goto 1\n"
447      "BasicBlock 1, pred: 0, succ: 2\n"
448      "  4: Add(2, 3) [5]\n"
449      "  5: Return(4)\n"
450      "BasicBlock 2, pred: 1\n"
451      "  6: Exit\n";
452
453  // Expected difference after constant folding.
454  diff_t expected_cf_diff = {
455    { "  2: LongConstant [4]\n",  "  2: LongConstant\n" },
456    { "  3: LongConstant [4]\n",  "  3: LongConstant\n"
457                                  "  7: LongConstant [5]\n" },
458    { "  4: Add(2, 3) [5]\n",     removed },
459    { "  5: Return(4)\n",         "  5: Return(7)\n" }
460  };
461  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
462
463  // Check the value of the computed constant.
464  auto check_after_cf = [](HGraph* graph) {
465    HInstruction* inst = graph->GetBlocks()[1]->GetFirstInstruction()->InputAt(0);
466    ASSERT_TRUE(inst->IsLongConstant());
467    ASSERT_EQ(inst->AsLongConstant()->GetValue(), 3);
468  };
469
470  // Expected difference after dead code elimination.
471  diff_t expected_dce_diff = {
472    { "  2: LongConstant\n", removed },
473    { "  3: LongConstant\n", removed }
474  };
475  std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
476
477  TestCode(data,
478           expected_before,
479           expected_after_cf,
480           expected_after_dce,
481           check_after_cf,
482           DataType::Type::kInt64);
483}
484
485/**
486 * Tiny three-register-pair program exercising long constant folding
487 * on subtraction.
488 *
489 *                              16-bit
490 *                              offset
491 *                              ------
492 *     (v0, v1) <- 3            0.      const-wide/16 v0, #+3
493 *     (v2, v3) <- 2            2.      const-wide/16 v2, #+2
494 *     (v4, v5) <-
495 *       (v0, v1) - (v1, v2)    4.      sub-long v4, v0, v2
496 *     return (v4, v5)          6.      return-wide v4
497 */
498TEST_F(ConstantFoldingTest, LongConstantFoldingOnSubtraction) {
499  const std::vector<uint16_t> data = SIX_REGISTERS_CODE_ITEM(
500    Instruction::CONST_WIDE_16 | 0 << 8, 3,
501    Instruction::CONST_WIDE_16 | 2 << 8, 2,
502    Instruction::SUB_LONG | 4 << 8, 0 | 2 << 8,
503    Instruction::RETURN_WIDE | 4 << 8);
504
505  std::string expected_before =
506      "BasicBlock 0, succ: 1\n"
507      "  2: LongConstant [4]\n"
508      "  3: LongConstant [4]\n"
509      "  0: SuspendCheck\n"
510      "  1: Goto 1\n"
511      "BasicBlock 1, pred: 0, succ: 2\n"
512      "  4: Sub(2, 3) [5]\n"
513      "  5: Return(4)\n"
514      "BasicBlock 2, pred: 1\n"
515      "  6: Exit\n";
516
517  // Expected difference after constant folding.
518  diff_t expected_cf_diff = {
519    { "  2: LongConstant [4]\n",  "  2: LongConstant\n" },
520    { "  3: LongConstant [4]\n",  "  3: LongConstant\n"
521                                  "  7: LongConstant [5]\n" },
522    { "  4: Sub(2, 3) [5]\n",     removed },
523    { "  5: Return(4)\n",         "  5: Return(7)\n" }
524  };
525  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
526
527  // Check the value of the computed constant.
528  auto check_after_cf = [](HGraph* graph) {
529    HInstruction* inst = graph->GetBlocks()[1]->GetFirstInstruction()->InputAt(0);
530    ASSERT_TRUE(inst->IsLongConstant());
531    ASSERT_EQ(inst->AsLongConstant()->GetValue(), 1);
532  };
533
534  // Expected difference after dead code elimination.
535  diff_t expected_dce_diff = {
536    { "  2: LongConstant\n", removed },
537    { "  3: LongConstant\n", removed }
538  };
539  std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
540
541  TestCode(data,
542           expected_before,
543           expected_after_cf,
544           expected_after_dce,
545           check_after_cf,
546           DataType::Type::kInt64);
547}
548
549/**
550 * Three-register program with jumps leading to the creation of many
551 * blocks.
552 *
553 * The intent of this test is to ensure that all constant expressions
554 * are actually evaluated at compile-time, thanks to the reverse
555 * (forward) post-order traversal of the the dominator tree.
556 *
557 *                              16-bit
558 *                              offset
559 *                              ------
560 *     v0 <- 1                   0.     const/4 v0, #+1
561 *     v1 <- 2                   1.     const/4 v1, #+2
562 *     v2 <- v0 + v1             2.     add-int v2, v0, v1
563 *     goto L2                   4.     goto +4
564 * L1: v1 <- v0 + 5              5.     add-int/lit16 v1, v0, #+5
565 *     goto L3                   7.     goto +4
566 * L2: v0 <- v2 + 4              8.     add-int/lit16 v0, v2, #+4
567 *     goto L1                  10.     goto +(-5)
568 * L3: v2 <- v1 + 8             11.     add-int/lit16 v2, v1, #+8
569 *     return v2                13.     return v2
570 */
571TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) {
572  const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM(
573    Instruction::CONST_4 | 0 << 8 | 1 << 12,
574    Instruction::CONST_4 | 1 << 8 | 2 << 12,
575    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
576    Instruction::GOTO | 4 << 8,
577    Instruction::ADD_INT_LIT16 | 1 << 8 | 0 << 12, 5,
578    Instruction::GOTO | 4 << 8,
579    Instruction::ADD_INT_LIT16 | 0 << 8 | 2 << 12, 4,
580    static_cast<uint16_t>(Instruction::GOTO | 0xFFFFFFFB << 8),
581    Instruction::ADD_INT_LIT16 | 2 << 8 | 1 << 12, 8,
582    Instruction::RETURN | 2 << 8);
583
584  std::string expected_before =
585      "BasicBlock 0, succ: 1\n"
586      "  2: IntConstant [4]\n"             // v0 <- 1
587      "  3: IntConstant [4]\n"             // v1 <- 2
588      "  6: IntConstant [7]\n"             // const 5
589      "  9: IntConstant [10]\n"            // const 4
590      "  12: IntConstant [13]\n"           // const 8
591      "  0: SuspendCheck\n"
592      "  1: Goto 1\n"
593      "BasicBlock 1, pred: 0, succ: 3\n"
594      "  4: Add(2, 3) [7]\n"               // v2 <- v0 + v1 = 1 + 2 = 3
595      "  5: Goto 3\n"                      // goto L2
596      "BasicBlock 2, pred: 3, succ: 4\n"   // L1:
597      "  10: Add(7, 9) [13]\n"             // v1 <- v0 + 3 = 7 + 5 = 12
598      "  11: Goto 4\n"                     // goto L3
599      "BasicBlock 3, pred: 1, succ: 2\n"   // L2:
600      "  7: Add(4, 6) [10]\n"              // v0 <- v2 + 2 = 3 + 4 = 7
601      "  8: Goto 2\n"                      // goto L1
602      "BasicBlock 4, pred: 2, succ: 5\n"   // L3:
603      "  13: Add(10, 12) [14]\n"           // v2 <- v1 + 4 = 12 + 8 = 20
604      "  14: Return(13)\n"                 // return v2
605      "BasicBlock 5, pred: 4\n"
606      "  15: Exit\n";
607
608  // Expected difference after constant folding.
609  diff_t expected_cf_diff = {
610    { "  2: IntConstant [4]\n",   "  2: IntConstant\n" },
611    { "  3: IntConstant [4]\n",   "  3: IntConstant\n" },
612    { "  6: IntConstant [7]\n",   "  6: IntConstant\n" },
613    { "  9: IntConstant [10]\n",  "  9: IntConstant\n" },
614    { "  12: IntConstant [13]\n", "  12: IntConstant\n"
615                                  "  16: IntConstant\n"
616                                  "  17: IntConstant\n"
617                                  "  18: IntConstant\n"
618                                  "  19: IntConstant [14]\n" },
619    { "  4: Add(2, 3) [7]\n",     removed },
620    { "  10: Add(7, 9) [13]\n",   removed },
621    { "  7: Add(4, 6) [10]\n",    removed },
622    { "  13: Add(10, 12) [14]\n", removed },
623    { "  14: Return(13)\n",       "  14: Return(19)\n"}
624  };
625  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
626
627  // Check the values of the computed constants.
628  auto check_after_cf = [](HGraph* graph) {
629    HInstruction* inst1 = graph->GetBlocks()[4]->GetFirstInstruction()->InputAt(0);
630    ASSERT_TRUE(inst1->IsIntConstant());
631    ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 20);
632    HInstruction* inst2 = inst1->GetPrevious();
633    ASSERT_TRUE(inst2->IsIntConstant());
634    ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 12);
635    HInstruction* inst3 = inst2->GetPrevious();
636    ASSERT_TRUE(inst3->IsIntConstant());
637    ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 7);
638    HInstruction* inst4 = inst3->GetPrevious();
639    ASSERT_TRUE(inst4->IsIntConstant());
640    ASSERT_EQ(inst4->AsIntConstant()->GetValue(), 3);
641  };
642
643  // Expected difference after dead code elimination.
644  std::string expected_after_dce =
645      "BasicBlock 0, succ: 1\n"
646      "  19: IntConstant [14]\n"
647      "  0: SuspendCheck\n"
648      "  1: Goto 1\n"
649      "BasicBlock 1, pred: 0, succ: 5\n"
650      "  14: Return(19)\n"
651      "BasicBlock 5, pred: 1\n"
652      "  15: Exit\n";
653
654  TestCode(data,
655           expected_before,
656           expected_after_cf,
657           expected_after_dce,
658           check_after_cf);
659}
660
661/**
662 * Three-register program with a constant (static) condition.
663 *
664 *                              16-bit
665 *                              offset
666 *                              ------
667 *     v1 <- 1                  0.      const/4 v1, #+1
668 *     v0 <- 0                  1.      const/4 v0, #+0
669 *     if v1 >= 0 goto L1       2.      if-gez v1, +3
670 *     v0 <- v1                 4.      move v0, v1
671 * L1: v2 <- v0 + v1            5.      add-int v2, v0, v1
672 *     return-void              7.      return
673 */
674TEST_F(ConstantFoldingTest, ConstantCondition) {
675  const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM(
676    Instruction::CONST_4 | 1 << 8 | 1 << 12,
677    Instruction::CONST_4 | 0 << 8 | 0 << 12,
678    Instruction::IF_GEZ | 1 << 8, 3,
679    Instruction::MOVE | 0 << 8 | 1 << 12,
680    Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
681    Instruction::RETURN_VOID);
682
683  std::string expected_before =
684      "BasicBlock 0, succ: 1\n"
685      "  3: IntConstant [9, 8, 5]\n"
686      "  4: IntConstant [8, 5]\n"
687      "  1: SuspendCheck\n"
688      "  2: Goto 1\n"
689      "BasicBlock 1, pred: 0, succ: 5, 2\n"
690      "  5: GreaterThanOrEqual(3, 4) [6]\n"
691      "  6: If(5)\n"
692      "BasicBlock 2, pred: 1, succ: 3\n"
693      "  7: Goto 3\n"
694      "BasicBlock 3, pred: 5, 2, succ: 4\n"
695      "  8: Phi(4, 3) [9]\n"
696      "  9: Add(8, 3)\n"
697      "  10: ReturnVoid\n"
698      "BasicBlock 4, pred: 3\n"
699      "  11: Exit\n"
700      "BasicBlock 5, pred: 1, succ: 3\n"
701      "  0: Goto 3\n";
702
703  // Expected difference after constant folding.
704  diff_t expected_cf_diff = {
705    { "  3: IntConstant [9, 8, 5]\n",        "  3: IntConstant [6, 9, 8]\n" },
706    { "  4: IntConstant [8, 5]\n",           "  4: IntConstant [8]\n" },
707    { "  5: GreaterThanOrEqual(3, 4) [6]\n", removed },
708    { "  6: If(5)\n",                        "  6: If(3)\n" }
709  };
710  std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
711
712  // Check the values of the computed constants.
713  auto check_after_cf = [](HGraph* graph) {
714    HInstruction* inst = graph->GetBlocks()[1]->GetFirstInstruction()->InputAt(0);
715    ASSERT_TRUE(inst->IsIntConstant());
716    ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1);
717  };
718
719  // Expected graph after dead code elimination.
720  std::string expected_after_dce =
721      "BasicBlock 0, succ: 1\n"
722      "  1: SuspendCheck\n"
723      "  2: Goto 1\n"
724      "BasicBlock 1, pred: 0, succ: 4\n"
725      "  10: ReturnVoid\n"
726      "BasicBlock 4, pred: 1\n"
727      "  11: Exit\n";
728
729  TestCode(data,
730           expected_before,
731           expected_after_cf,
732           expected_after_dce,
733           check_after_cf);
734}
735
736/**
737 * Unsigned comparisons with zero. Since these instructions are not present
738 * in the bytecode, we need to set up the graph explicitly.
739 */
740TEST_F(ConstantFoldingTest, UnsignedComparisonsWithZero) {
741  graph_ = CreateGraph();
742  HBasicBlock* entry_block = new (GetAllocator()) HBasicBlock(graph_);
743  graph_->AddBlock(entry_block);
744  graph_->SetEntryBlock(entry_block);
745  HBasicBlock* block = new (GetAllocator()) HBasicBlock(graph_);
746  graph_->AddBlock(block);
747  HBasicBlock* exit_block = new (GetAllocator()) HBasicBlock(graph_);
748  graph_->AddBlock(exit_block);
749  graph_->SetExitBlock(exit_block);
750  entry_block->AddSuccessor(block);
751  block->AddSuccessor(exit_block);
752
753  // Make various unsigned comparisons with zero against a parameter.
754  HInstruction* parameter = new (GetAllocator()) HParameterValue(
755      graph_->GetDexFile(), dex::TypeIndex(0), 0, DataType::Type::kInt32, true);
756  entry_block->AddInstruction(parameter);
757  entry_block->AddInstruction(new (GetAllocator()) HGoto());
758
759  HInstruction* zero = graph_->GetIntConstant(0);
760
761  HInstruction* last;
762  block->AddInstruction(last = new (GetAllocator()) HAbove(zero, parameter));
763  block->AddInstruction(new (GetAllocator()) HSelect(last, parameter, parameter, 0));
764  block->AddInstruction(last = new (GetAllocator()) HAbove(parameter, zero));
765  block->AddInstruction(new (GetAllocator()) HSelect(last, parameter, parameter, 0));
766  block->AddInstruction(last = new (GetAllocator()) HAboveOrEqual(zero, parameter));
767  block->AddInstruction(new (GetAllocator()) HSelect(last, parameter, parameter, 0));
768  block->AddInstruction(last = new (GetAllocator()) HAboveOrEqual(parameter, zero));
769  block->AddInstruction(new (GetAllocator()) HSelect(last, parameter, parameter, 0));
770  block->AddInstruction(last = new (GetAllocator()) HBelow(zero, parameter));
771  block->AddInstruction(new (GetAllocator()) HSelect(last, parameter, parameter, 0));
772  block->AddInstruction(last = new (GetAllocator()) HBelow(parameter, zero));
773  block->AddInstruction(new (GetAllocator()) HSelect(last, parameter, parameter, 0));
774  block->AddInstruction(last = new (GetAllocator()) HBelowOrEqual(zero, parameter));
775  block->AddInstruction(new (GetAllocator()) HSelect(last, parameter, parameter, 0));
776  block->AddInstruction(last = new (GetAllocator()) HBelowOrEqual(parameter, zero));
777  block->AddInstruction(new (GetAllocator()) HSelect(last, parameter, parameter, 0));
778  block->AddInstruction(new (GetAllocator()) HReturn(zero));
779
780  exit_block->AddInstruction(new (GetAllocator()) HExit());
781
782  graph_->BuildDominatorTree();
783
784  const std::string expected_before =
785      "BasicBlock 0, succ: 1\n"
786      "  0: ParameterValue [18, 18, 17, 16, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10, 9, "
787                            "8, 8, 7, 6, 6, 5, 4, 4, 3]\n"
788      "  2: IntConstant [19, 17, 15, 13, 11, 9, 7, 5, 3]\n"
789      "  1: Goto 1\n"
790      "BasicBlock 1, pred: 0, succ: 2\n"
791      "  3: Above(2, 0) [4]\n"
792      "  4: Select(0, 0, 3)\n"
793      "  5: Above(0, 2) [6]\n"
794      "  6: Select(0, 0, 5)\n"
795      "  7: AboveOrEqual(2, 0) [8]\n"
796      "  8: Select(0, 0, 7)\n"
797      "  9: AboveOrEqual(0, 2) [10]\n"
798      "  10: Select(0, 0, 9)\n"
799      "  11: Below(2, 0) [12]\n"
800      "  12: Select(0, 0, 11)\n"
801      "  13: Below(0, 2) [14]\n"
802      "  14: Select(0, 0, 13)\n"
803      "  15: BelowOrEqual(2, 0) [16]\n"
804      "  16: Select(0, 0, 15)\n"
805      "  17: BelowOrEqual(0, 2) [18]\n"
806      "  18: Select(0, 0, 17)\n"
807      "  19: Return(2)\n"
808      "BasicBlock 2, pred: 1\n"
809      "  20: Exit\n";
810
811  const std::string expected_after_cf =
812      "BasicBlock 0, succ: 1\n"
813      "  0: ParameterValue [18, 18, 17, 16, 16, 14, 14, 12, 12, 11, 10, 10, "
814                            "8, 8, 7, 6, 6, 5, 4, 4]\n"
815      "  2: IntConstant [14, 4, 19, 17, 11, 7, 5]\n"
816      "  21: IntConstant [16, 10]\n"
817      "  1: Goto 1\n"
818      "BasicBlock 1, pred: 0, succ: 2\n"
819      "  4: Select(0, 0, 2)\n"
820      "  5: Above(0, 2) [6]\n"
821      "  6: Select(0, 0, 5)\n"
822      "  7: AboveOrEqual(2, 0) [8]\n"
823      "  8: Select(0, 0, 7)\n"
824      "  10: Select(0, 0, 21)\n"
825      "  11: Below(2, 0) [12]\n"
826      "  12: Select(0, 0, 11)\n"
827      "  14: Select(0, 0, 2)\n"
828      "  16: Select(0, 0, 21)\n"
829      "  17: BelowOrEqual(0, 2) [18]\n"
830      "  18: Select(0, 0, 17)\n"
831      "  19: Return(2)\n"
832      "BasicBlock 2, pred: 1\n"
833      "  20: Exit\n";
834
835  const std::string expected_after_dce =
836      "BasicBlock 0, succ: 1\n"
837      "  0: ParameterValue\n"
838      "  2: IntConstant [19]\n"
839      "  1: Goto 1\n"
840      "BasicBlock 1, pred: 0, succ: 2\n"
841      "  19: Return(2)\n"
842      "BasicBlock 2, pred: 1\n"
843      "  20: Exit\n";
844
845  auto check_after_cf = [](HGraph* graph) {
846    CHECK(graph != nullptr);
847  };
848
849  TestCodeOnReadyGraph(expected_before,
850                       expected_after_cf,
851                       expected_after_dce,
852                       check_after_cf);
853}
854
855}  // namespace art
856