diff options
248 files changed, 7902 insertions, 2947 deletions
diff --git a/benchmark/const-class/info.txt b/benchmark/const-class/info.txt new file mode 100644 index 0000000000..ed0b827ab2 --- /dev/null +++ b/benchmark/const-class/info.txt @@ -0,0 +1 @@ +Benchmarks for repeating const-class instructions in a loop. diff --git a/benchmark/const-class/src/ConstClassBenchmark.java b/benchmark/const-class/src/ConstClassBenchmark.java new file mode 100644 index 0000000000..d45b49f272 --- /dev/null +++ b/benchmark/const-class/src/ConstClassBenchmark.java @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class ConstClassBenchmark { + // Define 1025 classes with consecutive type indexes in the dex file. + // The tests below rely on the knowledge that ART uses the low 10 bits + // of the type index as the hash into DexCache types array. + // Note: n == n + 1024 (mod 2^10), n + 1 != n + 1023 (mod 2^10). + public static class TestClass_0000 {} + public static class TestClass_0001 {} + public static class TestClass_0002 {} + public static class TestClass_0003 {} + public static class TestClass_0004 {} + public static class TestClass_0005 {} + public static class TestClass_0006 {} + public static class TestClass_0007 {} + public static class TestClass_0008 {} + public static class TestClass_0009 {} + public static class TestClass_0010 {} + public static class TestClass_0011 {} + public static class TestClass_0012 {} + public static class TestClass_0013 {} + public static class TestClass_0014 {} + public static class TestClass_0015 {} + public static class TestClass_0016 {} + public static class TestClass_0017 {} + public static class TestClass_0018 {} + public static class TestClass_0019 {} + public static class TestClass_0020 {} + public static class TestClass_0021 {} + public static class TestClass_0022 {} + public static class TestClass_0023 {} + public static class TestClass_0024 {} + public static class TestClass_0025 {} + public static class TestClass_0026 {} + public static class TestClass_0027 {} + public static class TestClass_0028 {} + public static class TestClass_0029 {} + public static class TestClass_0030 {} + public static class TestClass_0031 {} + public static class TestClass_0032 {} + public static class TestClass_0033 {} + public static class TestClass_0034 {} + public static class TestClass_0035 {} + public static class TestClass_0036 {} + public static class TestClass_0037 {} + public static class TestClass_0038 {} + public static class TestClass_0039 {} + public static class TestClass_0040 {} + public static class TestClass_0041 {} + public static class TestClass_0042 {} + public static class TestClass_0043 {} + public static class TestClass_0044 {} + public static class TestClass_0045 {} + public static class TestClass_0046 {} + public static class TestClass_0047 {} + public static class TestClass_0048 {} + public static class TestClass_0049 {} + public static class TestClass_0050 {} + public static class TestClass_0051 {} + public static class TestClass_0052 {} + public static class TestClass_0053 {} + public static class TestClass_0054 {} + public static class TestClass_0055 {} + public static class TestClass_0056 {} + public static class TestClass_0057 {} + public static class TestClass_0058 {} + public static class TestClass_0059 {} + public static class TestClass_0060 {} + public static class TestClass_0061 {} + public static class TestClass_0062 {} + public static class TestClass_0063 {} + public static class TestClass_0064 {} + public static class TestClass_0065 {} + public static class TestClass_0066 {} + public static class TestClass_0067 {} + public static class TestClass_0068 {} + public static class TestClass_0069 {} + public static class TestClass_0070 {} + public static class TestClass_0071 {} + public static class TestClass_0072 {} + public static class TestClass_0073 {} + public static class TestClass_0074 {} + public static class TestClass_0075 {} + public static class TestClass_0076 {} + public static class TestClass_0077 {} + public static class TestClass_0078 {} + public static class TestClass_0079 {} + public static class TestClass_0080 {} + public static class TestClass_0081 {} + public static class TestClass_0082 {} + public static class TestClass_0083 {} + public static class TestClass_0084 {} + public static class TestClass_0085 {} + public static class TestClass_0086 {} + public static class TestClass_0087 {} + public static class TestClass_0088 {} + public static class TestClass_0089 {} + public static class TestClass_0090 {} + public static class TestClass_0091 {} + public static class TestClass_0092 {} + public static class TestClass_0093 {} + public static class TestClass_0094 {} + public static class TestClass_0095 {} + public static class TestClass_0096 {} + public static class TestClass_0097 {} + public static class TestClass_0098 {} + public static class TestClass_0099 {} + public static class TestClass_0100 {} + public static class TestClass_0101 {} + public static class TestClass_0102 {} + public static class TestClass_0103 {} + public static class TestClass_0104 {} + public static class TestClass_0105 {} + public static class TestClass_0106 {} + public static class TestClass_0107 {} + public static class TestClass_0108 {} + public static class TestClass_0109 {} + public static class TestClass_0110 {} + public static class TestClass_0111 {} + public static class TestClass_0112 {} + public static class TestClass_0113 {} + public static class TestClass_0114 {} + public static class TestClass_0115 {} + public static class TestClass_0116 {} + public static class TestClass_0117 {} + public static class TestClass_0118 {} + public static class TestClass_0119 {} + public static class TestClass_0120 {} + public static class TestClass_0121 {} + public static class TestClass_0122 {} + public static class TestClass_0123 {} + public static class TestClass_0124 {} + public static class TestClass_0125 {} + public static class TestClass_0126 {} + public static class TestClass_0127 {} + public static class TestClass_0128 {} + public static class TestClass_0129 {} + public static class TestClass_0130 {} + public static class TestClass_0131 {} + public static class TestClass_0132 {} + public static class TestClass_0133 {} + public static class TestClass_0134 {} + public static class TestClass_0135 {} + public static class TestClass_0136 {} + public static class TestClass_0137 {} + public static class TestClass_0138 {} + public static class TestClass_0139 {} + public static class TestClass_0140 {} + public static class TestClass_0141 {} + public static class TestClass_0142 {} + public static class TestClass_0143 {} + public static class TestClass_0144 {} + public static class TestClass_0145 {} + public static class TestClass_0146 {} + public static class TestClass_0147 {} + public static class TestClass_0148 {} + public static class TestClass_0149 {} + public static class TestClass_0150 {} + public static class TestClass_0151 {} + public static class TestClass_0152 {} + public static class TestClass_0153 {} + public static class TestClass_0154 {} + public static class TestClass_0155 {} + public static class TestClass_0156 {} + public static class TestClass_0157 {} + public static class TestClass_0158 {} + public static class TestClass_0159 {} + public static class TestClass_0160 {} + public static class TestClass_0161 {} + public static class TestClass_0162 {} + public static class TestClass_0163 {} + public static class TestClass_0164 {} + public static class TestClass_0165 {} + public static class TestClass_0166 {} + public static class TestClass_0167 {} + public static class TestClass_0168 {} + public static class TestClass_0169 {} + public static class TestClass_0170 {} + public static class TestClass_0171 {} + public static class TestClass_0172 {} + public static class TestClass_0173 {} + public static class TestClass_0174 {} + public static class TestClass_0175 {} + public static class TestClass_0176 {} + public static class TestClass_0177 {} + public static class TestClass_0178 {} + public static class TestClass_0179 {} + public static class TestClass_0180 {} + public static class TestClass_0181 {} + public static class TestClass_0182 {} + public static class TestClass_0183 {} + public static class TestClass_0184 {} + public static class TestClass_0185 {} + public static class TestClass_0186 {} + public static class TestClass_0187 {} + public static class TestClass_0188 {} + public static class TestClass_0189 {} + public static class TestClass_0190 {} + public static class TestClass_0191 {} + public static class TestClass_0192 {} + public static class TestClass_0193 {} + public static class TestClass_0194 {} + public static class TestClass_0195 {} + public static class TestClass_0196 {} + public static class TestClass_0197 {} + public static class TestClass_0198 {} + public static class TestClass_0199 {} + public static class TestClass_0200 {} + public static class TestClass_0201 {} + public static class TestClass_0202 {} + public static class TestClass_0203 {} + public static class TestClass_0204 {} + public static class TestClass_0205 {} + public static class TestClass_0206 {} + public static class TestClass_0207 {} + public static class TestClass_0208 {} + public static class TestClass_0209 {} + public static class TestClass_0210 {} + public static class TestClass_0211 {} + public static class TestClass_0212 {} + public static class TestClass_0213 {} + public static class TestClass_0214 {} + public static class TestClass_0215 {} + public static class TestClass_0216 {} + public static class TestClass_0217 {} + public static class TestClass_0218 {} + public static class TestClass_0219 {} + public static class TestClass_0220 {} + public static class TestClass_0221 {} + public static class TestClass_0222 {} + public static class TestClass_0223 {} + public static class TestClass_0224 {} + public static class TestClass_0225 {} + public static class TestClass_0226 {} + public static class TestClass_0227 {} + public static class TestClass_0228 {} + public static class TestClass_0229 {} + public static class TestClass_0230 {} + public static class TestClass_0231 {} + public static class TestClass_0232 {} + public static class TestClass_0233 {} + public static class TestClass_0234 {} + public static class TestClass_0235 {} + public static class TestClass_0236 {} + public static class TestClass_0237 {} + public static class TestClass_0238 {} + public static class TestClass_0239 {} + public static class TestClass_0240 {} + public static class TestClass_0241 {} + public static class TestClass_0242 {} + public static class TestClass_0243 {} + public static class TestClass_0244 {} + public static class TestClass_0245 {} + public static class TestClass_0246 {} + public static class TestClass_0247 {} + public static class TestClass_0248 {} + public static class TestClass_0249 {} + public static class TestClass_0250 {} + public static class TestClass_0251 {} + public static class TestClass_0252 {} + public static class TestClass_0253 {} + public static class TestClass_0254 {} + public static class TestClass_0255 {} + public static class TestClass_0256 {} + public static class TestClass_0257 {} + public static class TestClass_0258 {} + public static class TestClass_0259 {} + public static class TestClass_0260 {} + public static class TestClass_0261 {} + public static class TestClass_0262 {} + public static class TestClass_0263 {} + public static class TestClass_0264 {} + public static class TestClass_0265 {} + public static class TestClass_0266 {} + public static class TestClass_0267 {} + public static class TestClass_0268 {} + public static class TestClass_0269 {} + public static class TestClass_0270 {} + public static class TestClass_0271 {} + public static class TestClass_0272 {} + public static class TestClass_0273 {} + public static class TestClass_0274 {} + public static class TestClass_0275 {} + public static class TestClass_0276 {} + public static class TestClass_0277 {} + public static class TestClass_0278 {} + public static class TestClass_0279 {} + public static class TestClass_0280 {} + public static class TestClass_0281 {} + public static class TestClass_0282 {} + public static class TestClass_0283 {} + public static class TestClass_0284 {} + public static class TestClass_0285 {} + public static class TestClass_0286 {} + public static class TestClass_0287 {} + public static class TestClass_0288 {} + public static class TestClass_0289 {} + public static class TestClass_0290 {} + public static class TestClass_0291 {} + public static class TestClass_0292 {} + public static class TestClass_0293 {} + public static class TestClass_0294 {} + public static class TestClass_0295 {} + public static class TestClass_0296 {} + public static class TestClass_0297 {} + public static class TestClass_0298 {} + public static class TestClass_0299 {} + public static class TestClass_0300 {} + public static class TestClass_0301 {} + public static class TestClass_0302 {} + public static class TestClass_0303 {} + public static class TestClass_0304 {} + public static class TestClass_0305 {} + public static class TestClass_0306 {} + public static class TestClass_0307 {} + public static class TestClass_0308 {} + public static class TestClass_0309 {} + public static class TestClass_0310 {} + public static class TestClass_0311 {} + public static class TestClass_0312 {} + public static class TestClass_0313 {} + public static class TestClass_0314 {} + public static class TestClass_0315 {} + public static class TestClass_0316 {} + public static class TestClass_0317 {} + public static class TestClass_0318 {} + public static class TestClass_0319 {} + public static class TestClass_0320 {} + public static class TestClass_0321 {} + public static class TestClass_0322 {} + public static class TestClass_0323 {} + public static class TestClass_0324 {} + public static class TestClass_0325 {} + public static class TestClass_0326 {} + public static class TestClass_0327 {} + public static class TestClass_0328 {} + public static class TestClass_0329 {} + public static class TestClass_0330 {} + public static class TestClass_0331 {} + public static class TestClass_0332 {} + public static class TestClass_0333 {} + public static class TestClass_0334 {} + public static class TestClass_0335 {} + public static class TestClass_0336 {} + public static class TestClass_0337 {} + public static class TestClass_0338 {} + public static class TestClass_0339 {} + public static class TestClass_0340 {} + public static class TestClass_0341 {} + public static class TestClass_0342 {} + public static class TestClass_0343 {} + public static class TestClass_0344 {} + public static class TestClass_0345 {} + public static class TestClass_0346 {} + public static class TestClass_0347 {} + public static class TestClass_0348 {} + public static class TestClass_0349 {} + public static class TestClass_0350 {} + public static class TestClass_0351 {} + public static class TestClass_0352 {} + public static class TestClass_0353 {} + public static class TestClass_0354 {} + public static class TestClass_0355 {} + public static class TestClass_0356 {} + public static class TestClass_0357 {} + public static class TestClass_0358 {} + public static class TestClass_0359 {} + public static class TestClass_0360 {} + public static class TestClass_0361 {} + public static class TestClass_0362 {} + public static class TestClass_0363 {} + public static class TestClass_0364 {} + public static class TestClass_0365 {} + public static class TestClass_0366 {} + public static class TestClass_0367 {} + public static class TestClass_0368 {} + public static class TestClass_0369 {} + public static class TestClass_0370 {} + public static class TestClass_0371 {} + public static class TestClass_0372 {} + public static class TestClass_0373 {} + public static class TestClass_0374 {} + public static class TestClass_0375 {} + public static class TestClass_0376 {} + public static class TestClass_0377 {} + public static class TestClass_0378 {} + public static class TestClass_0379 {} + public static class TestClass_0380 {} + public static class TestClass_0381 {} + public static class TestClass_0382 {} + public static class TestClass_0383 {} + public static class TestClass_0384 {} + public static class TestClass_0385 {} + public static class TestClass_0386 {} + public static class TestClass_0387 {} + public static class TestClass_0388 {} + public static class TestClass_0389 {} + public static class TestClass_0390 {} + public static class TestClass_0391 {} + public static class TestClass_0392 {} + public static class TestClass_0393 {} + public static class TestClass_0394 {} + public static class TestClass_0395 {} + public static class TestClass_0396 {} + public static class TestClass_0397 {} + public static class TestClass_0398 {} + public static class TestClass_0399 {} + public static class TestClass_0400 {} + public static class TestClass_0401 {} + public static class TestClass_0402 {} + public static class TestClass_0403 {} + public static class TestClass_0404 {} + public static class TestClass_0405 {} + public static class TestClass_0406 {} + public static class TestClass_0407 {} + public static class TestClass_0408 {} + public static class TestClass_0409 {} + public static class TestClass_0410 {} + public static class TestClass_0411 {} + public static class TestClass_0412 {} + public static class TestClass_0413 {} + public static class TestClass_0414 {} + public static class TestClass_0415 {} + public static class TestClass_0416 {} + public static class TestClass_0417 {} + public static class TestClass_0418 {} + public static class TestClass_0419 {} + public static class TestClass_0420 {} + public static class TestClass_0421 {} + public static class TestClass_0422 {} + public static class TestClass_0423 {} + public static class TestClass_0424 {} + public static class TestClass_0425 {} + public static class TestClass_0426 {} + public static class TestClass_0427 {} + public static class TestClass_0428 {} + public static class TestClass_0429 {} + public static class TestClass_0430 {} + public static class TestClass_0431 {} + public static class TestClass_0432 {} + public static class TestClass_0433 {} + public static class TestClass_0434 {} + public static class TestClass_0435 {} + public static class TestClass_0436 {} + public static class TestClass_0437 {} + public static class TestClass_0438 {} + public static class TestClass_0439 {} + public static class TestClass_0440 {} + public static class TestClass_0441 {} + public static class TestClass_0442 {} + public static class TestClass_0443 {} + public static class TestClass_0444 {} + public static class TestClass_0445 {} + public static class TestClass_0446 {} + public static class TestClass_0447 {} + public static class TestClass_0448 {} + public static class TestClass_0449 {} + public static class TestClass_0450 {} + public static class TestClass_0451 {} + public static class TestClass_0452 {} + public static class TestClass_0453 {} + public static class TestClass_0454 {} + public static class TestClass_0455 {} + public static class TestClass_0456 {} + public static class TestClass_0457 {} + public static class TestClass_0458 {} + public static class TestClass_0459 {} + public static class TestClass_0460 {} + public static class TestClass_0461 {} + public static class TestClass_0462 {} + public static class TestClass_0463 {} + public static class TestClass_0464 {} + public static class TestClass_0465 {} + public static class TestClass_0466 {} + public static class TestClass_0467 {} + public static class TestClass_0468 {} + public static class TestClass_0469 {} + public static class TestClass_0470 {} + public static class TestClass_0471 {} + public static class TestClass_0472 {} + public static class TestClass_0473 {} + public static class TestClass_0474 {} + public static class TestClass_0475 {} + public static class TestClass_0476 {} + public static class TestClass_0477 {} + public static class TestClass_0478 {} + public static class TestClass_0479 {} + public static class TestClass_0480 {} + public static class TestClass_0481 {} + public static class TestClass_0482 {} + public static class TestClass_0483 {} + public static class TestClass_0484 {} + public static class TestClass_0485 {} + public static class TestClass_0486 {} + public static class TestClass_0487 {} + public static class TestClass_0488 {} + public static class TestClass_0489 {} + public static class TestClass_0490 {} + public static class TestClass_0491 {} + public static class TestClass_0492 {} + public static class TestClass_0493 {} + public static class TestClass_0494 {} + public static class TestClass_0495 {} + public static class TestClass_0496 {} + public static class TestClass_0497 {} + public static class TestClass_0498 {} + public static class TestClass_0499 {} + public static class TestClass_0500 {} + public static class TestClass_0501 {} + public static class TestClass_0502 {} + public static class TestClass_0503 {} + public static class TestClass_0504 {} + public static class TestClass_0505 {} + public static class TestClass_0506 {} + public static class TestClass_0507 {} + public static class TestClass_0508 {} + public static class TestClass_0509 {} + public static class TestClass_0510 {} + public static class TestClass_0511 {} + public static class TestClass_0512 {} + public static class TestClass_0513 {} + public static class TestClass_0514 {} + public static class TestClass_0515 {} + public static class TestClass_0516 {} + public static class TestClass_0517 {} + public static class TestClass_0518 {} + public static class TestClass_0519 {} + public static class TestClass_0520 {} + public static class TestClass_0521 {} + public static class TestClass_0522 {} + public static class TestClass_0523 {} + public static class TestClass_0524 {} + public static class TestClass_0525 {} + public static class TestClass_0526 {} + public static class TestClass_0527 {} + public static class TestClass_0528 {} + public static class TestClass_0529 {} + public static class TestClass_0530 {} + public static class TestClass_0531 {} + public static class TestClass_0532 {} + public static class TestClass_0533 {} + public static class TestClass_0534 {} + public static class TestClass_0535 {} + public static class TestClass_0536 {} + public static class TestClass_0537 {} + public static class TestClass_0538 {} + public static class TestClass_0539 {} + public static class TestClass_0540 {} + public static class TestClass_0541 {} + public static class TestClass_0542 {} + public static class TestClass_0543 {} + public static class TestClass_0544 {} + public static class TestClass_0545 {} + public static class TestClass_0546 {} + public static class TestClass_0547 {} + public static class TestClass_0548 {} + public static class TestClass_0549 {} + public static class TestClass_0550 {} + public static class TestClass_0551 {} + public static class TestClass_0552 {} + public static class TestClass_0553 {} + public static class TestClass_0554 {} + public static class TestClass_0555 {} + public static class TestClass_0556 {} + public static class TestClass_0557 {} + public static class TestClass_0558 {} + public static class TestClass_0559 {} + public static class TestClass_0560 {} + public static class TestClass_0561 {} + public static class TestClass_0562 {} + public static class TestClass_0563 {} + public static class TestClass_0564 {} + public static class TestClass_0565 {} + public static class TestClass_0566 {} + public static class TestClass_0567 {} + public static class TestClass_0568 {} + public static class TestClass_0569 {} + public static class TestClass_0570 {} + public static class TestClass_0571 {} + public static class TestClass_0572 {} + public static class TestClass_0573 {} + public static class TestClass_0574 {} + public static class TestClass_0575 {} + public static class TestClass_0576 {} + public static class TestClass_0577 {} + public static class TestClass_0578 {} + public static class TestClass_0579 {} + public static class TestClass_0580 {} + public static class TestClass_0581 {} + public static class TestClass_0582 {} + public static class TestClass_0583 {} + public static class TestClass_0584 {} + public static class TestClass_0585 {} + public static class TestClass_0586 {} + public static class TestClass_0587 {} + public static class TestClass_0588 {} + public static class TestClass_0589 {} + public static class TestClass_0590 {} + public static class TestClass_0591 {} + public static class TestClass_0592 {} + public static class TestClass_0593 {} + public static class TestClass_0594 {} + public static class TestClass_0595 {} + public static class TestClass_0596 {} + public static class TestClass_0597 {} + public static class TestClass_0598 {} + public static class TestClass_0599 {} + public static class TestClass_0600 {} + public static class TestClass_0601 {} + public static class TestClass_0602 {} + public static class TestClass_0603 {} + public static class TestClass_0604 {} + public static class TestClass_0605 {} + public static class TestClass_0606 {} + public static class TestClass_0607 {} + public static class TestClass_0608 {} + public static class TestClass_0609 {} + public static class TestClass_0610 {} + public static class TestClass_0611 {} + public static class TestClass_0612 {} + public static class TestClass_0613 {} + public static class TestClass_0614 {} + public static class TestClass_0615 {} + public static class TestClass_0616 {} + public static class TestClass_0617 {} + public static class TestClass_0618 {} + public static class TestClass_0619 {} + public static class TestClass_0620 {} + public static class TestClass_0621 {} + public static class TestClass_0622 {} + public static class TestClass_0623 {} + public static class TestClass_0624 {} + public static class TestClass_0625 {} + public static class TestClass_0626 {} + public static class TestClass_0627 {} + public static class TestClass_0628 {} + public static class TestClass_0629 {} + public static class TestClass_0630 {} + public static class TestClass_0631 {} + public static class TestClass_0632 {} + public static class TestClass_0633 {} + public static class TestClass_0634 {} + public static class TestClass_0635 {} + public static class TestClass_0636 {} + public static class TestClass_0637 {} + public static class TestClass_0638 {} + public static class TestClass_0639 {} + public static class TestClass_0640 {} + public static class TestClass_0641 {} + public static class TestClass_0642 {} + public static class TestClass_0643 {} + public static class TestClass_0644 {} + public static class TestClass_0645 {} + public static class TestClass_0646 {} + public static class TestClass_0647 {} + public static class TestClass_0648 {} + public static class TestClass_0649 {} + public static class TestClass_0650 {} + public static class TestClass_0651 {} + public static class TestClass_0652 {} + public static class TestClass_0653 {} + public static class TestClass_0654 {} + public static class TestClass_0655 {} + public static class TestClass_0656 {} + public static class TestClass_0657 {} + public static class TestClass_0658 {} + public static class TestClass_0659 {} + public static class TestClass_0660 {} + public static class TestClass_0661 {} + public static class TestClass_0662 {} + public static class TestClass_0663 {} + public static class TestClass_0664 {} + public static class TestClass_0665 {} + public static class TestClass_0666 {} + public static class TestClass_0667 {} + public static class TestClass_0668 {} + public static class TestClass_0669 {} + public static class TestClass_0670 {} + public static class TestClass_0671 {} + public static class TestClass_0672 {} + public static class TestClass_0673 {} + public static class TestClass_0674 {} + public static class TestClass_0675 {} + public static class TestClass_0676 {} + public static class TestClass_0677 {} + public static class TestClass_0678 {} + public static class TestClass_0679 {} + public static class TestClass_0680 {} + public static class TestClass_0681 {} + public static class TestClass_0682 {} + public static class TestClass_0683 {} + public static class TestClass_0684 {} + public static class TestClass_0685 {} + public static class TestClass_0686 {} + public static class TestClass_0687 {} + public static class TestClass_0688 {} + public static class TestClass_0689 {} + public static class TestClass_0690 {} + public static class TestClass_0691 {} + public static class TestClass_0692 {} + public static class TestClass_0693 {} + public static class TestClass_0694 {} + public static class TestClass_0695 {} + public static class TestClass_0696 {} + public static class TestClass_0697 {} + public static class TestClass_0698 {} + public static class TestClass_0699 {} + public static class TestClass_0700 {} + public static class TestClass_0701 {} + public static class TestClass_0702 {} + public static class TestClass_0703 {} + public static class TestClass_0704 {} + public static class TestClass_0705 {} + public static class TestClass_0706 {} + public static class TestClass_0707 {} + public static class TestClass_0708 {} + public static class TestClass_0709 {} + public static class TestClass_0710 {} + public static class TestClass_0711 {} + public static class TestClass_0712 {} + public static class TestClass_0713 {} + public static class TestClass_0714 {} + public static class TestClass_0715 {} + public static class TestClass_0716 {} + public static class TestClass_0717 {} + public static class TestClass_0718 {} + public static class TestClass_0719 {} + public static class TestClass_0720 {} + public static class TestClass_0721 {} + public static class TestClass_0722 {} + public static class TestClass_0723 {} + public static class TestClass_0724 {} + public static class TestClass_0725 {} + public static class TestClass_0726 {} + public static class TestClass_0727 {} + public static class TestClass_0728 {} + public static class TestClass_0729 {} + public static class TestClass_0730 {} + public static class TestClass_0731 {} + public static class TestClass_0732 {} + public static class TestClass_0733 {} + public static class TestClass_0734 {} + public static class TestClass_0735 {} + public static class TestClass_0736 {} + public static class TestClass_0737 {} + public static class TestClass_0738 {} + public static class TestClass_0739 {} + public static class TestClass_0740 {} + public static class TestClass_0741 {} + public static class TestClass_0742 {} + public static class TestClass_0743 {} + public static class TestClass_0744 {} + public static class TestClass_0745 {} + public static class TestClass_0746 {} + public static class TestClass_0747 {} + public static class TestClass_0748 {} + public static class TestClass_0749 {} + public static class TestClass_0750 {} + public static class TestClass_0751 {} + public static class TestClass_0752 {} + public static class TestClass_0753 {} + public static class TestClass_0754 {} + public static class TestClass_0755 {} + public static class TestClass_0756 {} + public static class TestClass_0757 {} + public static class TestClass_0758 {} + public static class TestClass_0759 {} + public static class TestClass_0760 {} + public static class TestClass_0761 {} + public static class TestClass_0762 {} + public static class TestClass_0763 {} + public static class TestClass_0764 {} + public static class TestClass_0765 {} + public static class TestClass_0766 {} + public static class TestClass_0767 {} + public static class TestClass_0768 {} + public static class TestClass_0769 {} + public static class TestClass_0770 {} + public static class TestClass_0771 {} + public static class TestClass_0772 {} + public static class TestClass_0773 {} + public static class TestClass_0774 {} + public static class TestClass_0775 {} + public static class TestClass_0776 {} + public static class TestClass_0777 {} + public static class TestClass_0778 {} + public static class TestClass_0779 {} + public static class TestClass_0780 {} + public static class TestClass_0781 {} + public static class TestClass_0782 {} + public static class TestClass_0783 {} + public static class TestClass_0784 {} + public static class TestClass_0785 {} + public static class TestClass_0786 {} + public static class TestClass_0787 {} + public static class TestClass_0788 {} + public static class TestClass_0789 {} + public static class TestClass_0790 {} + public static class TestClass_0791 {} + public static class TestClass_0792 {} + public static class TestClass_0793 {} + public static class TestClass_0794 {} + public static class TestClass_0795 {} + public static class TestClass_0796 {} + public static class TestClass_0797 {} + public static class TestClass_0798 {} + public static class TestClass_0799 {} + public static class TestClass_0800 {} + public static class TestClass_0801 {} + public static class TestClass_0802 {} + public static class TestClass_0803 {} + public static class TestClass_0804 {} + public static class TestClass_0805 {} + public static class TestClass_0806 {} + public static class TestClass_0807 {} + public static class TestClass_0808 {} + public static class TestClass_0809 {} + public static class TestClass_0810 {} + public static class TestClass_0811 {} + public static class TestClass_0812 {} + public static class TestClass_0813 {} + public static class TestClass_0814 {} + public static class TestClass_0815 {} + public static class TestClass_0816 {} + public static class TestClass_0817 {} + public static class TestClass_0818 {} + public static class TestClass_0819 {} + public static class TestClass_0820 {} + public static class TestClass_0821 {} + public static class TestClass_0822 {} + public static class TestClass_0823 {} + public static class TestClass_0824 {} + public static class TestClass_0825 {} + public static class TestClass_0826 {} + public static class TestClass_0827 {} + public static class TestClass_0828 {} + public static class TestClass_0829 {} + public static class TestClass_0830 {} + public static class TestClass_0831 {} + public static class TestClass_0832 {} + public static class TestClass_0833 {} + public static class TestClass_0834 {} + public static class TestClass_0835 {} + public static class TestClass_0836 {} + public static class TestClass_0837 {} + public static class TestClass_0838 {} + public static class TestClass_0839 {} + public static class TestClass_0840 {} + public static class TestClass_0841 {} + public static class TestClass_0842 {} + public static class TestClass_0843 {} + public static class TestClass_0844 {} + public static class TestClass_0845 {} + public static class TestClass_0846 {} + public static class TestClass_0847 {} + public static class TestClass_0848 {} + public static class TestClass_0849 {} + public static class TestClass_0850 {} + public static class TestClass_0851 {} + public static class TestClass_0852 {} + public static class TestClass_0853 {} + public static class TestClass_0854 {} + public static class TestClass_0855 {} + public static class TestClass_0856 {} + public static class TestClass_0857 {} + public static class TestClass_0858 {} + public static class TestClass_0859 {} + public static class TestClass_0860 {} + public static class TestClass_0861 {} + public static class TestClass_0862 {} + public static class TestClass_0863 {} + public static class TestClass_0864 {} + public static class TestClass_0865 {} + public static class TestClass_0866 {} + public static class TestClass_0867 {} + public static class TestClass_0868 {} + public static class TestClass_0869 {} + public static class TestClass_0870 {} + public static class TestClass_0871 {} + public static class TestClass_0872 {} + public static class TestClass_0873 {} + public static class TestClass_0874 {} + public static class TestClass_0875 {} + public static class TestClass_0876 {} + public static class TestClass_0877 {} + public static class TestClass_0878 {} + public static class TestClass_0879 {} + public static class TestClass_0880 {} + public static class TestClass_0881 {} + public static class TestClass_0882 {} + public static class TestClass_0883 {} + public static class TestClass_0884 {} + public static class TestClass_0885 {} + public static class TestClass_0886 {} + public static class TestClass_0887 {} + public static class TestClass_0888 {} + public static class TestClass_0889 {} + public static class TestClass_0890 {} + public static class TestClass_0891 {} + public static class TestClass_0892 {} + public static class TestClass_0893 {} + public static class TestClass_0894 {} + public static class TestClass_0895 {} + public static class TestClass_0896 {} + public static class TestClass_0897 {} + public static class TestClass_0898 {} + public static class TestClass_0899 {} + public static class TestClass_0900 {} + public static class TestClass_0901 {} + public static class TestClass_0902 {} + public static class TestClass_0903 {} + public static class TestClass_0904 {} + public static class TestClass_0905 {} + public static class TestClass_0906 {} + public static class TestClass_0907 {} + public static class TestClass_0908 {} + public static class TestClass_0909 {} + public static class TestClass_0910 {} + public static class TestClass_0911 {} + public static class TestClass_0912 {} + public static class TestClass_0913 {} + public static class TestClass_0914 {} + public static class TestClass_0915 {} + public static class TestClass_0916 {} + public static class TestClass_0917 {} + public static class TestClass_0918 {} + public static class TestClass_0919 {} + public static class TestClass_0920 {} + public static class TestClass_0921 {} + public static class TestClass_0922 {} + public static class TestClass_0923 {} + public static class TestClass_0924 {} + public static class TestClass_0925 {} + public static class TestClass_0926 {} + public static class TestClass_0927 {} + public static class TestClass_0928 {} + public static class TestClass_0929 {} + public static class TestClass_0930 {} + public static class TestClass_0931 {} + public static class TestClass_0932 {} + public static class TestClass_0933 {} + public static class TestClass_0934 {} + public static class TestClass_0935 {} + public static class TestClass_0936 {} + public static class TestClass_0937 {} + public static class TestClass_0938 {} + public static class TestClass_0939 {} + public static class TestClass_0940 {} + public static class TestClass_0941 {} + public static class TestClass_0942 {} + public static class TestClass_0943 {} + public static class TestClass_0944 {} + public static class TestClass_0945 {} + public static class TestClass_0946 {} + public static class TestClass_0947 {} + public static class TestClass_0948 {} + public static class TestClass_0949 {} + public static class TestClass_0950 {} + public static class TestClass_0951 {} + public static class TestClass_0952 {} + public static class TestClass_0953 {} + public static class TestClass_0954 {} + public static class TestClass_0955 {} + public static class TestClass_0956 {} + public static class TestClass_0957 {} + public static class TestClass_0958 {} + public static class TestClass_0959 {} + public static class TestClass_0960 {} + public static class TestClass_0961 {} + public static class TestClass_0962 {} + public static class TestClass_0963 {} + public static class TestClass_0964 {} + public static class TestClass_0965 {} + public static class TestClass_0966 {} + public static class TestClass_0967 {} + public static class TestClass_0968 {} + public static class TestClass_0969 {} + public static class TestClass_0970 {} + public static class TestClass_0971 {} + public static class TestClass_0972 {} + public static class TestClass_0973 {} + public static class TestClass_0974 {} + public static class TestClass_0975 {} + public static class TestClass_0976 {} + public static class TestClass_0977 {} + public static class TestClass_0978 {} + public static class TestClass_0979 {} + public static class TestClass_0980 {} + public static class TestClass_0981 {} + public static class TestClass_0982 {} + public static class TestClass_0983 {} + public static class TestClass_0984 {} + public static class TestClass_0985 {} + public static class TestClass_0986 {} + public static class TestClass_0987 {} + public static class TestClass_0988 {} + public static class TestClass_0989 {} + public static class TestClass_0990 {} + public static class TestClass_0991 {} + public static class TestClass_0992 {} + public static class TestClass_0993 {} + public static class TestClass_0994 {} + public static class TestClass_0995 {} + public static class TestClass_0996 {} + public static class TestClass_0997 {} + public static class TestClass_0998 {} + public static class TestClass_0999 {} + public static class TestClass_1000 {} + public static class TestClass_1001 {} + public static class TestClass_1002 {} + public static class TestClass_1003 {} + public static class TestClass_1004 {} + public static class TestClass_1005 {} + public static class TestClass_1006 {} + public static class TestClass_1007 {} + public static class TestClass_1008 {} + public static class TestClass_1009 {} + public static class TestClass_1010 {} + public static class TestClass_1011 {} + public static class TestClass_1012 {} + public static class TestClass_1013 {} + public static class TestClass_1014 {} + public static class TestClass_1015 {} + public static class TestClass_1016 {} + public static class TestClass_1017 {} + public static class TestClass_1018 {} + public static class TestClass_1019 {} + public static class TestClass_1020 {} + public static class TestClass_1021 {} + public static class TestClass_1022 {} + public static class TestClass_1023 {} + public static class TestClass_1024 {} + + public void timeConstClassWithConflict(int count) { + Class<?> class0001 = TestClass_0001.class; + for (int i = 0; i < count; ++i) { + $noinline$foo(class0001); // Prevent LICM on the TestClass_xxxx.class below. + $noinline$foo(TestClass_0000.class); + $noinline$foo(TestClass_1024.class); + } + } + + public void timeConstClassWithoutConflict(int count) { + Class<?> class0000 = TestClass_0000.class; + for (int i = 0; i < count; ++i) { + $noinline$foo(class0000); // Prevent LICM on the TestClass_xxxx.class below. + $noinline$foo(TestClass_0001.class); + $noinline$foo(TestClass_1023.class); + } + } + + static void $noinline$foo(Class<?> s) { + if (doThrow) { throw new Error(); } + } + + public static boolean doThrow = false; +} diff --git a/benchmark/const-string/src/ConstStringBenchmark.java b/benchmark/const-string/src/ConstStringBenchmark.java index 2beb0a4c60..2359a5f1fb 100644 --- a/benchmark/const-string/src/ConstStringBenchmark.java +++ b/benchmark/const-string/src/ConstStringBenchmark.java @@ -18,6 +18,7 @@ public class ConstStringBenchmark { // Initialize 1025 strings with consecutive string indexes in the dex file. // The tests below rely on the knowledge that ART uses the low 10 bits // of the string index as the hash into DexCache strings array. + // Note: n == n + 1024 (mod 2^10), n + 1 != n + 1023 (mod 2^10). public static final String string_0000 = "TestString_0000"; public static final String string_0001 = "TestString_0001"; public static final String string_0002 = "TestString_0002"; @@ -1045,21 +1046,21 @@ public class ConstStringBenchmark { public static final String string_1024 = "TestString_1024"; public void timeConstStringsWithConflict(int count) { - for (int i = 0; i < count; ++i) { - $noinline$foo("TestString_0000"); - $noinline$foo("TestString_1024"); - } + for (int i = 0; i < count; ++i) { + $noinline$foo("TestString_0000"); + $noinline$foo("TestString_1024"); + } } public void timeConstStringsWithoutConflict(int count) { - for (int i = 0; i < count; ++i) { - $noinline$foo("TestString_0001"); - $noinline$foo("TestString_1023"); - } + for (int i = 0; i < count; ++i) { + $noinline$foo("TestString_0001"); + $noinline$foo("TestString_1023"); + } } static void $noinline$foo(String s) { - if (doThrow) { throw new Error(); } + if (doThrow) { throw new Error(); } } public static boolean doThrow = false; diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index c785befda0..990262844c 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -25,6 +25,7 @@ include art/build/Android.common_build.mk GTEST_DEX_DIRECTORIES := \ AbstractMethod \ AllFields \ + DexToDexDecompiler \ ExceptionHandle \ GetMethodSignature \ ImageLayoutA \ @@ -106,6 +107,7 @@ ART_GTEST_stub_test_DEX_DEPS := AllFields ART_GTEST_transaction_test_DEX_DEPS := Transaction ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps MultiDex +ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler # The elf writer test has dependencies on core.oat. ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_optimizing_no-pic_64) $(HOST_CORE_IMAGE_optimizing_no-pic_32) @@ -114,10 +116,14 @@ ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_optimizing_no-pic_6 ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \ $(HOST_CORE_IMAGE_optimizing_pic_64) \ $(HOST_CORE_IMAGE_optimizing_pic_32) \ + $(HOST_CORE_IMAGE_optimizing_no-pic_64) \ + $(HOST_CORE_IMAGE_optimizing_no-pic_32) \ $(HOST_OUT_EXECUTABLES)/patchoatd ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_optimizing_pic_64) \ $(TARGET_CORE_IMAGE_optimizing_pic_32) \ + $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \ + $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \ $(TARGET_OUT_EXECUTABLES)/patchoatd ART_GTEST_oat_file_assistant_test_HOST_DEPS := \ diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 3f55eefa0e..156ca9ef3e 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -496,11 +496,7 @@ static gc::CollectorType ParseCollectorType(const std::string& option) { struct XGcOption { // These defaults are used when the command line arguments for -Xgc: // are either omitted completely or partially. - gc::CollectorType collector_type_ = kUseReadBarrier ? - // If RB is enabled (currently a build-time decision), - // use CC as the default GC. - gc::kCollectorTypeCC : - gc::kCollectorTypeDefault; + gc::CollectorType collector_type_ = gc::kCollectorTypeDefault; bool verify_pre_gc_heap_ = false; bool verify_pre_sweeping_heap_ = kIsDebugBuild; bool verify_post_gc_heap_ = false; @@ -580,10 +576,6 @@ struct BackgroundGcOption { : background_collector_type_(background_collector_type) {} BackgroundGcOption() : background_collector_type_(gc::kCollectorTypeNone) { - - if (kUseReadBarrier) { - background_collector_type_ = gc::kCollectorTypeCCBackground; // Background compaction for CC. - } } operator gc::CollectorType() const { return background_collector_type_; } diff --git a/compiler/Android.bp b/compiler/Android.bp index 6edb639a1a..b883e0881a 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -28,6 +28,7 @@ art_cc_defaults { "compiled_method.cc", "debug/elf_debug_writer.cc", "dex/dex_to_dex_compiler.cc", + "dex/dex_to_dex_decompiler.cc", "dex/verified_method.cc", "dex/verification_results.cc", "dex/quick_compiler_callbacks.cc", @@ -52,6 +53,7 @@ art_cc_defaults { "optimizing/code_generator_utils.cc", "optimizing/constant_folding.cc", "optimizing/dead_code_elimination.cc", + "optimizing/escape.cc", "optimizing/graph_checker.cc", "optimizing/graph_visualizer.cc", "optimizing/gvn.cc", @@ -253,7 +255,10 @@ art_cc_library { }, }, }, - shared_libs: ["libart"], + shared_libs: [ + "libart", + "libart-dexlayout", + ], } art_cc_library { @@ -290,7 +295,10 @@ art_cc_library { }, }, }, - shared_libs: ["libartd"], + shared_libs: [ + "libartd", + "libartd-dexlayout" + ], } art_cc_library { @@ -312,6 +320,7 @@ art_cc_test { srcs: [ "compiled_method_test.cc", "debug/dwarf/dwarf_test.cc", + "dex/dex_to_dex_decompiler_test.cc", "driver/compiled_method_storage_test.cc", "driver/compiler_driver_test.cc", "elf_writer_test.cc", diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 99b0ac10d1..174e85e1bf 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -26,6 +26,7 @@ #include "base/array_ref.h" #include "base/bit_utils.h" #include "base/length_prefixed_array.h" +#include "dex_file_types.h" #include "method_reference.h" namespace art { @@ -302,9 +303,9 @@ class LinkerPatch { return target_dex_file_; } - uint32_t TargetTypeIndex() const { + dex::TypeIndex TargetTypeIndex() const { DCHECK(patch_type_ == Type::kType || patch_type_ == Type::kTypeRelative); - return type_idx_; + return dex::TypeIndex(type_idx_); } const DexFile* TargetStringDexFile() const { diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 9c1d72b9be..cf69f469a0 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -233,6 +233,8 @@ Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) { << " by replacing it with 2 NOPs at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " << GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true); + quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegA_21c())); + quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegB_21c())); // We are modifying 4 consecutive bytes. inst->SetOpcode(Instruction::NOP); inst->SetVRegA_10x(0u); // keep compliant with verifier. diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h index 3fad6d4c95..0a00d45297 100644 --- a/compiler/dex/dex_to_dex_compiler.h +++ b/compiler/dex/dex_to_dex_compiler.h @@ -17,8 +17,6 @@ #ifndef ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_ #define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_ -#include "jni.h" - #include "dex_file.h" #include "invoke_type.h" diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc new file mode 100644 index 0000000000..051125eeaa --- /dev/null +++ b/compiler/dex/dex_to_dex_decompiler.cc @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dex_to_dex_decompiler.h" + +#include "base/logging.h" +#include "base/mutex.h" +#include "dex_file-inl.h" +#include "dex_instruction-inl.h" +#include "optimizing/bytecode_utils.h" + +namespace art { +namespace optimizer { + +class DexDecompiler { + public: + DexDecompiler(const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info) + : code_item_(code_item), + quickened_info_ptr_(quickened_info.data()), + quickened_info_end_(quickened_info.data() + quickened_info.size()) {} + + bool Decompile(); + + private: + void DecompileInstanceFieldAccess(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode) { + uint16_t index = GetIndexAt(dex_pc); + inst->SetOpcode(new_opcode); + inst->SetVRegC_22c(index); + } + + void DecompileInvokeVirtual(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_range) { + uint16_t index = GetIndexAt(dex_pc); + inst->SetOpcode(new_opcode); + if (is_range) { + inst->SetVRegB_3rc(index); + } else { + inst->SetVRegB_35c(index); + } + } + + void DecompileNop(Instruction* inst, uint32_t dex_pc) { + if (quickened_info_ptr_ == quickened_info_end_) { + return; + } + const uint8_t* temporary_pointer = quickened_info_ptr_; + uint32_t quickened_pc = DecodeUnsignedLeb128(&temporary_pointer); + if (quickened_pc != dex_pc) { + return; + } + uint16_t reference_index = GetIndexAt(dex_pc); + uint16_t type_index = GetIndexAt(dex_pc); + inst->SetOpcode(Instruction::CHECK_CAST); + inst->SetVRegA_21c(reference_index); + inst->SetVRegB_21c(type_index); + } + + uint16_t GetIndexAt(uint32_t dex_pc) { + // Note that as a side effect, DecodeUnsignedLeb128 update the given pointer + // to the new position in the buffer. + DCHECK_LT(quickened_info_ptr_, quickened_info_end_); + uint32_t quickened_pc = DecodeUnsignedLeb128(&quickened_info_ptr_); + DCHECK_LT(quickened_info_ptr_, quickened_info_end_); + uint16_t index = DecodeUnsignedLeb128(&quickened_info_ptr_); + DCHECK_LE(quickened_info_ptr_, quickened_info_end_); + DCHECK_EQ(quickened_pc, dex_pc); + return index; + } + + const DexFile::CodeItem& code_item_; + const uint8_t* quickened_info_ptr_; + const uint8_t* const quickened_info_end_; + + DISALLOW_COPY_AND_ASSIGN(DexDecompiler); +}; + +bool DexDecompiler::Decompile() { + // We need to iterate over the code item, and not over the quickening data, + // because the RETURN_VOID quickening is not encoded in the quickening data. Because + // unquickening is a rare need and not performance sensitive, it is not worth the + // added storage to also add the RETURN_VOID quickening in the quickened data. + for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) { + uint32_t dex_pc = it.CurrentDexPc(); + Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction()); + + switch (inst->Opcode()) { + case Instruction::RETURN_VOID_NO_BARRIER: + inst->SetOpcode(Instruction::RETURN_VOID); + break; + + case Instruction::NOP: + DecompileNop(inst, dex_pc); + break; + + case Instruction::IGET_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET); + break; + + case Instruction::IGET_WIDE_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE); + break; + + case Instruction::IGET_OBJECT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT); + break; + + case Instruction::IGET_BOOLEAN_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN); + break; + + case Instruction::IGET_BYTE_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE); + break; + + case Instruction::IGET_CHAR_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR); + break; + + case Instruction::IGET_SHORT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT); + break; + + case Instruction::IPUT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT); + break; + + case Instruction::IPUT_BOOLEAN_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN); + break; + + case Instruction::IPUT_BYTE_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE); + break; + + case Instruction::IPUT_CHAR_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR); + break; + + case Instruction::IPUT_SHORT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT); + break; + + case Instruction::IPUT_WIDE_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE); + break; + + case Instruction::IPUT_OBJECT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT); + break; + + case Instruction::INVOKE_VIRTUAL_QUICK: + DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL, false); + break; + + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: + DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE, true); + break; + + default: + break; + } + } + + if (quickened_info_ptr_ != quickened_info_end_) { + LOG(ERROR) << "Failed to use all values in quickening info." + << " Actual: " << std::hex << quickened_info_ptr_ + << " Expected: " << quickened_info_end_; + return false; + } + + return true; +} + +bool ArtDecompileDEX(const DexFile::CodeItem& code_item, + const ArrayRef<const uint8_t>& quickened_info) { + DexDecompiler decompiler(code_item, quickened_info); + return decompiler.Decompile(); +} + +} // namespace optimizer +} // namespace art diff --git a/compiler/dex/dex_to_dex_decompiler.h b/compiler/dex/dex_to_dex_decompiler.h new file mode 100644 index 0000000000..5502ca2d92 --- /dev/null +++ b/compiler/dex/dex_to_dex_decompiler.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_ +#define ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_ + +#include "base/array_ref.h" +#include "dex_file.h" + +namespace art { +namespace optimizer { + +// "Decompile", that is unquicken, the code item provided, given the +// associated quickening data. +// TODO: code_item isn't really a const element, but changing it +// to non-const has too many repercussions on the code base. We make it +// consistent with DexToDexCompiler, but we should really change it to +// DexFile::CodeItem*. +bool ArtDecompileDEX(const DexFile::CodeItem& code_item, + const ArrayRef<const uint8_t>& quickened_data); + +} // namespace optimizer +} // namespace art + +#endif // ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_ diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc new file mode 100644 index 0000000000..ea6c7a2e09 --- /dev/null +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dex/dex_to_dex_decompiler.h" + +#include "class_linker.h" +#include "compiler/common_compiler_test.h" +#include "compiler/compiled_method.h" +#include "compiler/driver/compiler_options.h" +#include "compiler/driver/compiler_driver.h" +#include "compiler_callbacks.h" +#include "dex_file.h" +#include "handle_scope-inl.h" +#include "verifier/method_verifier-inl.h" +#include "mirror/class_loader.h" +#include "runtime.h" +#include "thread.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +class DexToDexDecompilerTest : public CommonCompilerTest { + public: + void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) { + TimingLogger timings("CompilerDriverTest::CompileAll", false, false); + TimingLogger::ScopedTiming t(__FUNCTION__, &timings); + compiler_options_->boot_image_ = false; + compiler_options_->SetCompilerFilter(CompilerFilter::kInterpretOnly); + compiler_driver_->CompileAll(class_loader, + GetDexFiles(class_loader), + /* verifier_deps */ nullptr, + &timings); + } + + void RunTest(const char* dex_name) { + Thread* self = Thread::Current(); + // First load the original dex file. + jobject original_class_loader; + { + ScopedObjectAccess soa(self); + original_class_loader = LoadDex(dex_name); + } + const DexFile* original_dex_file = GetDexFiles(original_class_loader)[0]; + + // Load the dex file again and make it writable to quicken them. + jobject class_loader; + const DexFile* updated_dex_file = nullptr; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex(dex_name); + updated_dex_file = GetDexFiles(class_loader)[0]; + Runtime::Current()->GetClassLinker()->RegisterDexFile( + *updated_dex_file, soa.Decode<mirror::ClassLoader>(class_loader).Ptr()); + } + // The dex files should be identical. + int cmp = memcmp(original_dex_file->Begin(), + updated_dex_file->Begin(), + updated_dex_file->Size()); + ASSERT_EQ(0, cmp); + + updated_dex_file->EnableWrite(); + CompileAll(class_loader); + // The dex files should be different after quickening. + cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size()); + ASSERT_NE(0, cmp); + + // Unquicken the dex file. + for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = updated_dex_file->GetClassDef(i); + const uint8_t* class_data = updated_dex_file->GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + ClassDataItemIterator it(*updated_dex_file, class_data); + // Skip fields + while (it.HasNextStaticField()) { + it.Next(); + } + while (it.HasNextInstanceField()) { + it.Next(); + } + + // Unquicken each method. + while (it.HasNextDirectMethod()) { + uint32_t method_idx = it.GetMemberIndex(); + CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); + ArrayRef<const uint8_t> table; + if (compiled_method != nullptr) { + table = compiled_method->GetVmapTable(); + } + optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table); + it.Next(); + } + while (it.HasNextVirtualMethod()) { + uint32_t method_idx = it.GetMemberIndex(); + CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); + ArrayRef<const uint8_t> table; + if (compiled_method != nullptr) { + table = compiled_method->GetVmapTable(); + } + optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table); + it.Next(); + } + DCHECK(!it.HasNext()); + } + + // Make sure after unquickening we go back to the same contents as the original dex file. + cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size()); + ASSERT_EQ(0, cmp); + } +}; + +TEST_F(DexToDexDecompilerTest, VerifierDeps) { + RunTest("VerifierDeps"); +} + +TEST_F(DexToDexDecompilerTest, DexToDexDecompiler) { + RunTest("DexToDexDecompiler"); +} + +} // namespace art diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 511a787878..3fb10d89dd 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -31,49 +31,68 @@ namespace art { VerificationResults::VerificationResults(const CompilerOptions* compiler_options) : compiler_options_(compiler_options), verified_methods_lock_("compiler verified methods lock"), - verified_methods_(), - rejected_classes_lock_("compiler rejected classes lock"), - rejected_classes_() { -} + rejected_classes_lock_("compiler rejected classes lock") {} VerificationResults::~VerificationResults() { - Thread* self = Thread::Current(); - { - WriterMutexLock mu(self, verified_methods_lock_); - STLDeleteValues(&verified_methods_); - } + WriterMutexLock mu(Thread::Current(), verified_methods_lock_); + DeleteResults(preregistered_dex_files_); + STLDeleteValues(&verified_methods_); } void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) { DCHECK(method_verifier != nullptr); MethodReference ref = method_verifier->GetMethodReference(); bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags()); - const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile); + std::unique_ptr<const VerifiedMethod> verified_method( + VerifiedMethod::Create(method_verifier, compile)); if (verified_method == nullptr) { // We'll punt this later. return; } - - WriterMutexLock mu(Thread::Current(), verified_methods_lock_); - auto it = verified_methods_.find(ref); - if (it != verified_methods_.end()) { + bool inserted; + DexFileMethodArray* const array = GetMethodArray(ref.dex_file); + const VerifiedMethod* existing = nullptr; + if (array != nullptr) { + DCHECK(array != nullptr); + Atomic<const VerifiedMethod*>* slot = &(*array)[ref.dex_method_index]; + inserted = slot->CompareExchangeStrongSequentiallyConsistent(nullptr, verified_method.get()); + if (!inserted) { + existing = slot->LoadSequentiallyConsistent(); + DCHECK_NE(verified_method.get(), existing); + } + } else { + WriterMutexLock mu(Thread::Current(), verified_methods_lock_); + auto it = verified_methods_.find(ref); + inserted = it == verified_methods_.end(); + if (inserted) { + verified_methods_.Put(ref, verified_method.get()); + DCHECK(verified_methods_.find(ref) != verified_methods_.end()); + } else { + existing = it->second; + } + } + if (inserted) { + // Successfully added, release the unique_ptr since we no longer have ownership. + DCHECK_EQ(GetVerifiedMethod(ref), verified_method.get()); + verified_method.release(); + } else { // TODO: Investigate why are we doing the work again for this method and try to avoid it. LOG(WARNING) << "Method processed more than once: " << ref.PrettyMethod(); if (!Runtime::Current()->UseJitCompilation()) { - DCHECK_EQ(it->second->GetDevirtMap().size(), verified_method->GetDevirtMap().size()); - DCHECK_EQ(it->second->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size()); + DCHECK_EQ(existing->GetDevirtMap().size(), verified_method->GetDevirtMap().size()); + DCHECK_EQ(existing->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size()); } - // Delete the new verified method since there was already an existing one registered. It - // is unsafe to replace the existing one since the JIT may be using it to generate a - // native GC map. - delete verified_method; - return; + // Let the unique_ptr delete the new verified method since there was already an existing one + // registered. It is unsafe to replace the existing one since the JIT may be using it to + // generate a native GC map. } - verified_methods_.Put(ref, verified_method); - DCHECK(verified_methods_.find(ref) != verified_methods_.end()); } const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) { + DexFileMethodArray* array = GetMethodArray(ref.dex_file); + if (array != nullptr) { + return (*array)[ref.dex_method_index].LoadRelaxed(); + } ReaderMutexLock mu(Thread::Current(), verified_methods_lock_); auto it = verified_methods_.find(ref); return (it != verified_methods_.end()) ? it->second : nullptr; @@ -105,4 +124,42 @@ bool VerificationResults::IsCandidateForCompilation(MethodReference&, return true; } +void VerificationResults::PreRegisterDexFile(const DexFile* dex_file) { + CHECK(preregistered_dex_files_.find(dex_file) == preregistered_dex_files_.end()) + << dex_file->GetLocation(); + DexFileMethodArray array(dex_file->NumMethodIds()); + WriterMutexLock mu(Thread::Current(), verified_methods_lock_); + // There can be some verified methods that are already registered for the dex_file since we set + // up well known classes earlier. Remove these and put them in the array so that we don't + // accidentally miss seeing them. + for (auto it = verified_methods_.begin(); it != verified_methods_.end(); ) { + MethodReference ref = it->first; + if (ref.dex_file == dex_file) { + array[ref.dex_method_index].StoreSequentiallyConsistent(it->second); + it = verified_methods_.erase(it); + } else { + ++it; + } + } + preregistered_dex_files_.emplace(dex_file, std::move(array)); +} + +void VerificationResults::DeleteResults(DexFileResults& array) { + for (auto& pair : array) { + for (Atomic<const VerifiedMethod*>& method : pair.second) { + delete method.LoadSequentiallyConsistent(); + } + } + array.clear(); +} + +VerificationResults::DexFileMethodArray* VerificationResults::GetMethodArray( + const DexFile* dex_file) { + auto it = preregistered_dex_files_.find(dex_file); + if (it != preregistered_dex_files_.end()) { + return &it->second; + } + return nullptr; +} + } // namespace art diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h index 6afd1ab626..b3356e0e10 100644 --- a/compiler/dex/verification_results.h +++ b/compiler/dex/verification_results.h @@ -19,8 +19,8 @@ #include <stdint.h> #include <set> -#include <vector> +#include "base/dchecked_vector.h" #include "base/macros.h" #include "base/mutex.h" #include "class_reference.h" @@ -38,35 +38,48 @@ class VerifiedMethod; // Used by CompilerCallbacks to track verification information from the Runtime. class VerificationResults { - public: - explicit VerificationResults(const CompilerOptions* compiler_options); - ~VerificationResults(); + public: + explicit VerificationResults(const CompilerOptions* compiler_options); + ~VerificationResults(); - void ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!verified_methods_lock_); + void ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!verified_methods_lock_); - const VerifiedMethod* GetVerifiedMethod(MethodReference ref) - REQUIRES(!verified_methods_lock_); + const VerifiedMethod* GetVerifiedMethod(MethodReference ref) + REQUIRES(!verified_methods_lock_); - void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_); - bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_); + void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_); + bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_); - bool IsCandidateForCompilation(MethodReference& method_ref, - const uint32_t access_flags); + bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags); - private: - const CompilerOptions* const compiler_options_; + // Add a dex file array to the preregistered_dex_files_ array. These dex files require no locks to + // access. It is not safe to call if other callers are calling GetVerifiedMethod concurrently. + void PreRegisterDexFile(const DexFile* dex_file) REQUIRES(!verified_methods_lock_); - // Verified methods. - typedef SafeMap<MethodReference, const VerifiedMethod*, - MethodReferenceComparator> VerifiedMethodMap; - ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_); + private: + // Verified methods. The method array is fixed to avoid needing a lock to extend it. + using DexFileMethodArray = dchecked_vector<Atomic<const VerifiedMethod*>>; + using DexFileResults = std::map<const DexFile*, DexFileMethodArray>; + using VerifiedMethodMap = SafeMap<MethodReference, + const VerifiedMethod*, + MethodReferenceComparator>; - // Rejected classes. - ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_); + static void DeleteResults(DexFileResults& array); + + DexFileMethodArray* GetMethodArray(const DexFile* dex_file) REQUIRES(!verified_methods_lock_); + VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_); + const CompilerOptions* const compiler_options_; + + // Dex2oat can preregister dex files to avoid locking when calling GetVerifiedMethod. + DexFileResults preregistered_dex_files_; + + ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + + // Rejected classes. + ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_); }; } // namespace art diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc index e19fb7b300..1bdace9284 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -230,7 +230,7 @@ void VerifiedMethod::GenerateSafeCastSet(verifier::MethodVerifier* method_verifi const verifier::RegType& reg_type(line->GetRegisterType(method_verifier, inst->VRegA_21c())); const verifier::RegType& cast_type = - method_verifier->ResolveCheckedClass(inst->VRegB_21c()); + method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c())); is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type, method_verifier); } else { const verifier::RegType& array_type(line->GetRegisterType(method_verifier, diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 971151665a..f056dd3c00 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -38,7 +38,7 @@ inline mirror::ClassLoader* CompilerDriver::GetClassLoader(const ScopedObjectAcc inline mirror::Class* CompilerDriver::ResolveClass( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, uint16_t cls_index, + Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit)); @@ -141,7 +141,7 @@ inline std::pair<bool, bool> CompilerDriver::IsClassOfStaticMemberAvailableToRef mirror::Class* referrer_class, ArtMember* resolved_member, uint16_t member_idx, - uint32_t* storage_index) { + dex::TypeIndex* storage_index) { DCHECK(resolved_member->IsStatic()); if (LIKELY(referrer_class != nullptr)) { ObjPtr<mirror::Class> members_class = resolved_member->GetDeclaringClass(); @@ -156,7 +156,7 @@ inline std::pair<bool, bool> CompilerDriver::IsClassOfStaticMemberAvailableToRef // TODO: for images we can elide the static storage base null check // if we know there's a non-null entry in the image const DexFile* dex_file = dex_cache->GetDexFile(); - uint32_t storage_idx = DexFile::kDexNoIndex; + dex::TypeIndex storage_idx(DexFile::kDexNoIndex16); if (LIKELY(members_class->GetDexCache() == dex_cache)) { // common case where the dex cache of both the referrer and the member are the same, // no need to search the dex file @@ -166,27 +166,27 @@ inline std::pair<bool, bool> CompilerDriver::IsClassOfStaticMemberAvailableToRef // of the class mentioned in the dex file and there is no dex cache entry. storage_idx = resolved_member->GetDeclaringClass()->FindTypeIndexInOtherDexFile(*dex_file); } - if (storage_idx != DexFile::kDexNoIndex) { + if (storage_idx.IsValid()) { *storage_index = storage_idx; return std::make_pair(true, !resolved_member->IsFinal()); } } } // Conservative defaults. - *storage_index = DexFile::kDexNoIndex; + *storage_index = dex::TypeIndex(DexFile::kDexNoIndex16); return std::make_pair(false, false); } inline std::pair<bool, bool> CompilerDriver::IsFastStaticField( mirror::DexCache* dex_cache, mirror::Class* referrer_class, - ArtField* resolved_field, uint16_t field_idx, uint32_t* storage_index) { + ArtField* resolved_field, uint16_t field_idx, dex::TypeIndex* storage_index) { return IsClassOfStaticMemberAvailableToReferrer( dex_cache, referrer_class, resolved_field, field_idx, storage_index); } inline bool CompilerDriver::IsClassOfStaticMethodAvailableToReferrer( mirror::DexCache* dex_cache, mirror::Class* referrer_class, - ArtMethod* resolved_method, uint16_t method_idx, uint32_t* storage_index) { + ArtMethod* resolved_method, uint16_t method_idx, dex::TypeIndex* storage_index) { std::pair<bool, bool> result = IsClassOfStaticMemberAvailableToReferrer( dex_cache, referrer_class, resolved_method, method_idx, storage_index); // Only the first member of `result` is meaningful, as there is no diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 223be884b0..ad75ec4604 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -45,6 +45,7 @@ #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex/dex_to_dex_compiler.h" +#include "dex/dex_to_dex_decompiler.h" #include "dex/verification_results.h" #include "dex/verified_method.h" #include "driver/compiler_options.h" @@ -72,6 +73,7 @@ #include "transaction.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "utils/swap_space.h" +#include "vdex_file.h" #include "verifier/method_verifier.h" #include "verifier/method_verifier-inl.h" #include "verifier/verifier_log_mode.h" @@ -394,7 +396,6 @@ static void SetupIntrinsic(Thread* self, void CompilerDriver::CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings) { DCHECK(!Runtime::Current()->IsStarted()); @@ -406,7 +407,7 @@ void CompilerDriver::CompileAll(jobject class_loader, // 2) Resolve all classes // 3) Attempt to verify all classes // 4) Attempt to initialize image classes, and trivially initialized classes - PreCompile(class_loader, dex_files, verifier_deps, timings); + PreCompile(class_loader, dex_files, timings); if (GetCompilerOptions().IsBootImage()) { // We don't need to setup the intrinsics for non boot image compilation, as // those compilations will pick up a boot image that have the ArtMethod already @@ -433,6 +434,72 @@ INTRINSICS_LIST(SETUP_INTRINSICS) FreeThreadPools(); } +// In-place unquicken the given `dex_files` based on `quickening_info`. +static void Unquicken(const std::vector<const DexFile*>& dex_files, + const ArrayRef<const uint8_t>& quickening_info) { + const uint8_t* quickening_info_ptr = quickening_info.data(); + const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size(); + for (const DexFile* dex_file : dex_files) { + for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + ClassDataItemIterator it(*dex_file, class_data); + // Skip fields + while (it.HasNextStaticField()) { + it.Next(); + } + while (it.HasNextInstanceField()) { + it.Next(); + } + + // Unquicken each method. + while (it.HasNextDirectMethod()) { + const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + if (code_item != nullptr) { + uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr); + quickening_info_ptr += sizeof(uint32_t); + optimizer::ArtDecompileDEX( + *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size)); + quickening_info_ptr += quickening_size; + } + it.Next(); + } + + while (it.HasNextVirtualMethod()) { + const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + if (code_item != nullptr) { + uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr); + quickening_info_ptr += sizeof(uint32_t); + optimizer::ArtDecompileDEX( + *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size)); + quickening_info_ptr += quickening_size; + } + it.Next(); + } + DCHECK(!it.HasNext()); + } + } + DCHECK_EQ(quickening_info_ptr, quickening_info_end) << "Failed to use all quickening info"; +} + +void CompilerDriver::CompileAll(jobject class_loader, + const std::vector<const DexFile*>& dex_files, + VdexFile* vdex_file, + TimingLogger* timings) { + if (vdex_file != nullptr) { + // TODO: we unquicken unconditionnally, as we don't know + // if the boot image has changed. How exactly we'll know is under + // experimentation. + Unquicken(dex_files, vdex_file->GetQuickeningInfo()); + Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps( + new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData())); + } + CompileAll(class_loader, dex_files, timings); +} + static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( Thread* self, const CompilerDriver& driver, Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, const DexFile::ClassDef& class_def) @@ -538,14 +605,9 @@ static void CompileMethod(Thread* self, : optimizer::DexToDexCompilationLevel::kRequired); } } else if ((access_flags & kAccNative) != 0) { - const InstructionSet instruction_set = driver->GetInstructionSet(); - const bool use_generic_jni = - // Are we extracting only and have support for generic JNI down calls? - (!driver->GetCompilerOptions().IsJniCompilationEnabled() && - InstructionSetHasGenericJniStub(instruction_set)) || - // Always punt to generic JNI for MIPS because of no support for @CriticalNative. b/31743474 - (instruction_set == kMips || instruction_set == kMips64); - if (use_generic_jni) { + // Are we extracting only and have support for generic JNI down calls? + if (!driver->GetCompilerOptions().IsJniCompilationEnabled() && + InstructionSetHasGenericJniStub(driver->GetInstructionSet())) { // Leaving this empty will trigger the generic JNI version } else { // Look-up the ArtMethod associated with this code_item (if any) @@ -678,7 +740,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t InitializeThreadPools(); - PreCompile(jclass_loader, dex_files, /* verifier_deps */ nullptr, timings); + PreCompile(jclass_loader, dex_files, timings); // Can we run DEX-to-DEX compiler on this class ? optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level = @@ -875,7 +937,6 @@ inline void CompilerDriver::CheckThreadPools() { void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings) { CheckThreadPools(); @@ -909,7 +970,7 @@ void CompilerDriver::PreCompile(jobject class_loader, VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false); } - Verify(class_loader, dex_files, verifier_deps, timings); + Verify(class_loader, dex_files, timings); VLOG(compiler) << "Verify: " << GetMemoryUsageString(false); if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) { @@ -973,11 +1034,12 @@ bool CompilerDriver::ShouldVerifyClassBasedOnProfile(const DexFile& dex_file, return true; } DCHECK(profile_compilation_info_ != nullptr); - bool result = profile_compilation_info_->ContainsClass(dex_file, class_idx); + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_idx); + dex::TypeIndex type_idx = class_def.class_idx_; + bool result = profile_compilation_info_->ContainsClass(dex_file, type_idx); if (kDebugProfileGuidedCompilation) { - LOG(INFO) << "[ProfileGuidedCompilation] " - << (result ? "Verified" : "Skipped") << " method:" - << dex_file.GetClassDescriptor(dex_file.GetClassDef(class_idx)); + LOG(INFO) << "[ProfileGuidedCompilation] " << (result ? "Verified" : "Skipped") << " method:" + << dex_file.GetClassDescriptor(class_def); } return result; } @@ -985,7 +1047,7 @@ bool CompilerDriver::ShouldVerifyClassBasedOnProfile(const DexFile& dex_file, class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { public: explicit ResolveCatchBlockExceptionsClassVisitor( - std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve) + std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve) : exceptions_to_resolve_(exceptions_to_resolve) {} virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1016,8 +1078,8 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { has_catch_all = true; } for (int32_t j = 0; j < encoded_catch_handler_size; j++) { - uint16_t encoded_catch_handler_handlers_type_idx = - DecodeUnsignedLeb128(&encoded_catch_handler_list); + dex::TypeIndex encoded_catch_handler_handlers_type_idx = + dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list)); // Add to set of types to resolve if not already in the dex cache resolved types if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx, pointer_size)) { @@ -1034,7 +1096,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { } } - std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve_; + std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve_; }; class RecordImageClassesVisitor : public ClassVisitor { @@ -1082,7 +1144,7 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { // Resolve exception classes referenced by the loaded classes. The catch logic assumes // exceptions are resolved by the verifier when there is a catch block in an interested method. // Do this here so that exception classes appear to have been specified image classes. - std::set<std::pair<uint16_t, const DexFile*>> unresolved_exception_types; + std::set<std::pair<dex::TypeIndex, const DexFile*>> unresolved_exception_types; StackHandleScope<1> hs(self); Handle<mirror::Class> java_lang_Throwable( hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;"))); @@ -1090,8 +1152,8 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { unresolved_exception_types.clear(); ResolveCatchBlockExceptionsClassVisitor visitor(unresolved_exception_types); class_linker->VisitClasses(&visitor); - for (const std::pair<uint16_t, const DexFile*>& exception_type : unresolved_exception_types) { - uint16_t exception_type_idx = exception_type.first; + for (const auto& exception_type : unresolved_exception_types) { + dex::TypeIndex exception_type_idx = exception_type.first; const DexFile* dex_file = exception_type.second; StackHandleScope<2> hs2(self); Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file, @@ -1342,7 +1404,7 @@ void CompilerDriver::MarkForDexToDexCompilation(Thread* self, const MethodRefere bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, Handle<mirror::DexCache> dex_cache, - uint32_t type_idx) { + dex::TypeIndex type_idx) { // Get type from dex cache assuming it was populated by the verifier mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == nullptr) { @@ -1371,7 +1433,7 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, Handle<mirror::DexCache> dex_cache, - uint32_t type_idx, + dex::TypeIndex type_idx, bool* finalizable) { // Get type from dex cache assuming it was populated by the verifier. mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); @@ -1865,7 +1927,7 @@ class ResolveTypeVisitor : public CompilationVisitor { public: explicit ResolveTypeVisitor(const ParallelCompilationManager* manager) : manager_(manager) { } - virtual void Visit(size_t type_idx) OVERRIDE REQUIRES(!Locks::mutator_lock_) { + void Visit(size_t type_idx) OVERRIDE REQUIRES(!Locks::mutator_lock_) { // Class derived values are more complicated, they require the linker and loader. ScopedObjectAccess soa(Thread::Current()); ClassLinker* class_linker = manager_->GetClassLinker(); @@ -1876,7 +1938,10 @@ class ResolveTypeVisitor : public CompilationVisitor { Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile( dex_file, class_loader.Get()))); - mirror::Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader); + mirror::Class* klass = class_linker->ResolveType(dex_file, + dex::TypeIndex(type_idx), + dex_cache, + class_loader); if (klass == nullptr) { soa.Self()->AssertPendingException(); @@ -1937,8 +2002,10 @@ void CompilerDriver::SetVerified(jobject class_loader, void CompilerDriver::Verify(jobject jclass_loader, const std::vector<const DexFile*>& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings) { + verifier::VerifierDeps* verifier_deps = + Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps(); + // If there is an existing `VerifierDeps`, try to use it for fast verification. if (verifier_deps != nullptr) { TimingLogger::ScopedTiming t("Fast Verify", timings); ScopedObjectAccess soa(Thread::Current()); @@ -1956,9 +2023,9 @@ void CompilerDriver::Verify(jobject jclass_loader, for (const DexFile* dex_file : dex_files) { // Fetch the list of unverified classes and turn it into a set for faster // lookups. - const std::vector<uint16_t>& unverified_classes = + const std::vector<dex::TypeIndex>& unverified_classes = verifier_deps->GetUnverifiedClasses(*dex_file); - std::set<uint16_t> set(unverified_classes.begin(), unverified_classes.end()); + std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end()); for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); const char* descriptor = dex_file->GetClassDescriptor(class_def); @@ -1976,13 +2043,20 @@ void CompilerDriver::Verify(jobject jclass_loader, } } - // If there is no passed `verifier_deps` (because of non-existing vdex), or - // the passed `verifier_deps` is not valid anymore, create a new one for + // If there is no existing `verifier_deps` (because of non-existing vdex), or + // the existing `verifier_deps` is not valid anymore, create a new one for // non boot image compilation. The verifier will need it to record the new dependencies. // Then dex2oat can update the vdex file with these new dependencies. if (!GetCompilerOptions().IsBootImage()) { - Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps( - new verifier::VerifierDeps(dex_files)); + // Create the main VerifierDeps, and set it to this thread. + verifier_deps = new verifier::VerifierDeps(dex_files); + Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps); + Thread::Current()->SetVerifierDeps(verifier_deps); + // Create per-thread VerifierDeps to avoid contention on the main one. + // We will merge them after verification. + for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { + worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files)); + } } // Note: verification should not be pulling in classes anymore when compiling the boot image, // as all should have been resolved before. As such, doing this in parallel should still @@ -1996,6 +2070,17 @@ void CompilerDriver::Verify(jobject jclass_loader, parallel_thread_count_, timings); } + + if (!GetCompilerOptions().IsBootImage()) { + // Merge all VerifierDeps into the main one. + for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { + verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps(); + worker->GetThread()->SetVerifierDeps(nullptr); + verifier_deps->MergeWith(*thread_deps, dex_files);; + delete thread_deps; + } + Thread::Current()->SetVerifierDeps(nullptr); + } } class VerifyClassVisitor : public CompilationVisitor { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index c8d6cb0d4d..7418b006ef 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -31,6 +31,7 @@ #include "class_reference.h" #include "compiler.h" #include "dex_file.h" +#include "dex_file_types.h" #include "driver/compiled_method_storage.h" #include "jit/offline_profiling_info.h" #include "invoke_type.h" @@ -50,7 +51,6 @@ class DexCache; namespace verifier { class MethodVerifier; -class VerifierDeps; class VerifierDepsTest; } // namespace verifier @@ -68,6 +68,7 @@ class SrcMapElem; using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>; template<class T> class Handle; class TimingLogger; +class VdexFile; class VerificationResults; class VerifiedMethod; @@ -118,7 +119,12 @@ class CompilerDriver { void CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files, - verifier::VerifierDeps* verifier_deps, + TimingLogger* timings) + REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_); + + void CompileAll(jobject class_loader, + const std::vector<const DexFile*>& dex_files, + VdexFile* vdex_file, TimingLogger* timings) REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_); @@ -188,14 +194,14 @@ class CompilerDriver { // Are runtime access checks necessary in the compiled code? bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, Handle<mirror::DexCache> dex_cache, - uint32_t type_idx) + dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); // Are runtime access and instantiable checks necessary in the code? // out_is_finalizable is set to whether the type is finalizable. bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, Handle<mirror::DexCache> dex_cache, - uint32_t type_idx, + dex::TypeIndex type_idx, bool* out_is_finalizable) REQUIRES_SHARED(Locks::mutator_lock_); @@ -207,7 +213,7 @@ class CompilerDriver { mirror::Class* ResolveClass( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, uint16_t type_index, + Handle<mirror::ClassLoader> class_loader, dex::TypeIndex type_index, const DexCompilationUnit* mUnit) REQUIRES_SHARED(Locks::mutator_lock_); @@ -234,9 +240,11 @@ class CompilerDriver { // Can we fast-path an SGET/SPUT access to a static field? If yes, compute the type index // of the declaring class in the referrer's dex file. - std::pair<bool, bool> IsFastStaticField( - mirror::DexCache* dex_cache, mirror::Class* referrer_class, - ArtField* resolved_field, uint16_t field_idx, uint32_t* storage_index) + std::pair<bool, bool> IsFastStaticField(mirror::DexCache* dex_cache, + mirror::Class* referrer_class, + ArtField* resolved_field, + uint16_t field_idx, + dex::TypeIndex* storage_index) REQUIRES_SHARED(Locks::mutator_lock_); // Return whether the declaring class of `resolved_method` is @@ -248,7 +256,7 @@ class CompilerDriver { mirror::Class* referrer_class, ArtMethod* resolved_method, uint16_t method_idx, - uint32_t* storage_index) + dex::TypeIndex* storage_index) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a method. Returns null on failure, including incompatible class change. @@ -395,7 +403,7 @@ class CompilerDriver { mirror::Class* referrer_class, ArtMember* resolved_member, uint16_t member_idx, - uint32_t* storage_index) + dex::TypeIndex* storage_index) REQUIRES_SHARED(Locks::mutator_lock_); // Can `referrer_class` access the resolved `member`? @@ -417,7 +425,6 @@ class CompilerDriver { private: void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings) REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_); @@ -440,7 +447,6 @@ class CompilerDriver { void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings); void VerifyDexFile(jobject class_loader, @@ -584,6 +590,7 @@ class CompilerDriver { const BitVector* current_dex_to_dex_methods_; friend class CompileClassVisitor; + friend class DexToDexDecompilerTest; friend class verifier::VerifierDepsTest; DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 9679a796cb..f40c71283b 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -24,6 +24,7 @@ #include "class_linker-inl.h" #include "common_compiler_test.h" #include "dex_file.h" +#include "dex_file_types.h" #include "gc/heap.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" @@ -115,9 +116,9 @@ TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { } EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes()); for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { - mirror::Class* type = dex_cache->GetResolvedType(i); + mirror::Class* type = dex_cache->GetResolvedType(dex::TypeIndex(i)); EXPECT_TRUE(type != nullptr) << "type_idx=" << i - << " " << dex.GetTypeDescriptor(dex.GetTypeId(i)); + << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i))); } EXPECT_EQ(dex.NumMethodIds(), dex_cache->NumResolvedMethods()); auto* cl = Runtime::Current()->GetClassLinker(); diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 56b632d6b4..9c62f80339 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -341,6 +341,7 @@ class CompilerOptions FINAL { const std::vector<std::string>* passes_to_run_; friend class Dex2Oat; + friend class DexToDexDecompilerTest; friend class CommonCompilerTest; friend class verifier::VerifierDepsTest; diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 31a75294bc..7c02384ff2 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -879,7 +879,7 @@ class ElfBuilder FINAL { elf_header.e_ident[EI_MAG2] = ELFMAG2; elf_header.e_ident[EI_MAG3] = ELFMAG3; elf_header.e_ident[EI_CLASS] = (sizeof(Elf_Addr) == sizeof(Elf32_Addr)) - ? ELFCLASS32 : ELFCLASS64;; + ? ELFCLASS32 : ELFCLASS64; elf_header.e_ident[EI_DATA] = ELFDATA2LSB; elf_header.e_ident[EI_VERSION] = EV_CURRENT; elf_header.e_ident[EI_OSABI] = ELFOSABI_LINUX; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index fcb8979459..5629dffce5 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -211,7 +211,9 @@ void CompilationHelper::Compile(CompilerDriver* driver, &driver->GetCompilerOptions(), oat_file.GetFile())); elf_writers.back()->Start(); - oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true, &timings)); + oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true, + &timings, + /*profile_compilation_info*/nullptr)); } std::vector<OutputStream*> rodata; diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 51ef440c7f..a706697496 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -32,6 +32,7 @@ #include "class_linker-inl.h" #include "compiled_method.h" #include "dex_file-inl.h" +#include "dex_file_types.h" #include "driver/compiler_driver.h" #include "elf_file.h" #include "elf_utils.h" @@ -697,7 +698,7 @@ bool ImageWriter::AllocMemory() { return true; } -class ComputeLazyFieldsForClassesVisitor : public ClassVisitor { +class ImageWriter::ComputeLazyFieldsForClassesVisitor : public ClassVisitor { public: bool operator()(ObjPtr<Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(Thread::Current()); @@ -837,7 +838,7 @@ bool ImageWriter::KeepClass(Class* klass) { return true; } -class NonImageClassesVisitor : public ClassVisitor { +class ImageWriter::NonImageClassesVisitor : public ClassVisitor { public: explicit NonImageClassesVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {} @@ -890,9 +891,9 @@ void ImageWriter::PruneNonImageClasses() { } ObjPtr<mirror::DexCache> dex_cache = self->DecodeJObject(data.weak_root)->AsDexCache(); for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { - Class* klass = dex_cache->GetResolvedType(i); + Class* klass = dex_cache->GetResolvedType(dex::TypeIndex(i)); if (klass != nullptr && !KeepClass(klass)) { - dex_cache->SetResolvedType(i, nullptr); + dex_cache->SetResolvedType(dex::TypeIndex(i), nullptr); } } ArtMethod** resolved_methods = dex_cache->GetResolvedMethods(); @@ -1701,7 +1702,7 @@ ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) { return reinterpret_cast<ArtMethod*>(image_info.image_begin_ + it->second.offset); } -class FixupRootVisitor : public RootVisitor { +class ImageWriter::FixupRootVisitor : public RootVisitor { public: explicit FixupRootVisitor(ImageWriter* image_writer) : image_writer_(image_writer) { } @@ -1944,7 +1945,7 @@ void ImageWriter::CopyAndFixupObject(Object* obj) { } // Rewrite all the references in the copied object to point to their image address equivalent -class FixupVisitor { +class ImageWriter::FixupVisitor { public: FixupVisitor(ImageWriter* image_writer, Object* copy) : image_writer_(image_writer), copy_(copy) { } @@ -1980,7 +1981,7 @@ class FixupVisitor { mirror::Object* const copy_; }; -class FixupClassVisitor FINAL : public FixupVisitor { +class ImageWriter::FixupClassVisitor FINAL : public FixupVisitor { public: FixupClassVisitor(ImageWriter* image_writer, Object* copy) : FixupVisitor(image_writer, copy) { } @@ -2045,7 +2046,7 @@ T* ImageWriter::NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) { } } -class NativeLocationVisitor { +class ImageWriter::NativeLocationVisitor { public: explicit NativeLocationVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {} diff --git a/compiler/image_writer.h b/compiler/image_writer.h index c9cf4cbc1b..24fad466e4 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -582,14 +582,15 @@ class ImageWriter FINAL { // Map of dex files to the indexes of oat files that they were compiled into. const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_; - friend class ContainsBootClassLoaderNonImageClassVisitor; - friend class FixupClassVisitor; - friend class FixupRootVisitor; - friend class FixupVisitor; + class ComputeLazyFieldsForClassesVisitor; + class FixupClassVisitor; + class FixupRootVisitor; + class FixupVisitor; class GetRootsVisitor; - friend class NativeLocationVisitor; - friend class NonImageClassesVisitor; + class NativeLocationVisitor; + class NonImageClassesVisitor; class VisitReferencesVisitor; + DISALLOW_COPY_AND_ASSIGN(ImageWriter); }; diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index a9044a2047..21042a337d 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -20,6 +20,7 @@ #include <math.h> #include "art_method-inl.h" +#include "base/bit_utils.h" #include "class_linker.h" #include "common_compiler_test.h" #include "compiler.h" @@ -366,7 +367,9 @@ class JniCompilerTest : public CommonCompilerTest { void StackArgsIntsFirstImpl(); void StackArgsFloatsFirstImpl(); void StackArgsMixedImpl(); +#if defined(__mips__) && defined(__LP64__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) void StackArgsSignExtendedMips64Impl(); +#endif void NormalNativeImpl(); void FastNativeImpl(); @@ -2145,50 +2148,43 @@ void JniCompilerTest::StackArgsMixedImpl() { JNI_TEST_CRITICAL(StackArgsMixed) -void Java_MyClassNatives_stackArgsSignExtendedMips64(JNIEnv*, jclass, jint i1, jint i2, jint i3, - jint i4, jint i5, jint i6, jint i7, jint i8) { - EXPECT_EQ(i1, 1); - EXPECT_EQ(i2, 2); - EXPECT_EQ(i3, 3); - EXPECT_EQ(i4, 4); - EXPECT_EQ(i5, 5); - EXPECT_EQ(i6, 6); - EXPECT_EQ(i7, 7); - EXPECT_EQ(i8, -8); - #if defined(__mips__) && defined(__LP64__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - // Mips64 ABI requires that arguments passed through stack be sign-extended 8B slots. - // First 8 arguments are passed through registers, check i7 and i8. - uint32_t stack1_high = *(&i7 + 1); - uint32_t stack2_high = *(&i8 + 1); - - EXPECT_EQ(stack1_high, static_cast<uint32_t>(0)); - EXPECT_EQ(stack2_high, static_cast<uint32_t>(0xffffffff)); -#else - LOG(INFO) << "Skipping stackArgsSignExtendedMips64 as there is nothing to be done on " - << kRuntimeISA; - // Force-print to std::cout so it's also outside the logcat. - std::cout << "Skipping stackArgsSignExtendedMips64 as there is nothing to be done on " - << kRuntimeISA << std::endl; -#endif +// Function will fetch the last argument passed from caller that is now on top of the stack and +// return it as a 8B long. That way we can test if the caller has properly sign-extended the +// value when placing it on the stack. +__attribute__((naked)) +jlong Java_MyClassNatives_getStackArgSignExtendedMips64( + JNIEnv*, jclass, // Arguments passed from caller + jint, jint, jint, jint, jint, jint, // through regs a0 to a7. + jint) { // The last argument will be passed on the stack. + __asm__( + ".set noreorder\n\t" // Just return and store 8 bytes from the top of the stack + "jr $ra\n\t" // in v0 (in branch delay slot). This should be the last + "ld $v0, 0($sp)\n\t"); // argument. It is a 32-bit int, but it should be sign + // extended and it occupies 64-bit location. } void JniCompilerTest::StackArgsSignExtendedMips64Impl() { - SetUpForTest(true, "stackArgsSignExtendedMips64", "(IIIIIIII)V", - CURRENT_JNI_WRAPPER(Java_MyClassNatives_stackArgsSignExtendedMips64)); - jint i1 = 1; - jint i2 = 2; - jint i3 = 3; - jint i4 = 4; - jint i5 = 5; - jint i6 = 6; - jint i7 = 7; - jint i8 = -8; + uint64_t ret; + SetUpForTest(true, + "getStackArgSignExtendedMips64", + "(IIIIIII)J", + // Don't use wrapper because this is raw assembly function. + reinterpret_cast<void*>(&Java_MyClassNatives_getStackArgSignExtendedMips64)); - env_->CallStaticVoidMethod(jklass_, jmethod_, i1, i2, i3, i4, i5, i6, i7, i8); + // Mips64 ABI requires that arguments passed through stack be sign-extended 8B slots. + // First 8 arguments are passed through registers. + // Final argument's value is 7. When sign-extended, higher stack bits should be 0. + ret = env_->CallStaticLongMethod(jklass_, jmethod_, 1, 2, 3, 4, 5, 6, 7); + EXPECT_EQ(High32Bits(ret), static_cast<uint32_t>(0)); + + // Final argument's value is -8. When sign-extended, higher stack bits should be 0xffffffff. + ret = env_->CallStaticLongMethod(jklass_, jmethod_, 1, 2, 3, 4, 5, 6, -8); + EXPECT_EQ(High32Bits(ret), static_cast<uint32_t>(0xffffffff)); } -JNI_TEST_CRITICAL(StackArgsSignExtendedMips64) +JNI_TEST(StackArgsSignExtendedMips64) +#endif void Java_MyClassNatives_normalNative(JNIEnv*, jclass) { // Intentionally left empty. diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc index 3fb7b56284..33f4d77fc2 100644 --- a/compiler/jni/quick/arm64/calling_convention_arm64.cc +++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc @@ -222,7 +222,11 @@ Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static, bool is_synchronized, bool is_critical_native, const char* shorty) - : JniCallingConvention(is_static, is_synchronized, is_critical_native, shorty, kArm64PointerSize) { + : JniCallingConvention(is_static, + is_synchronized, + is_critical_native, + shorty, + kArm64PointerSize) { } uint32_t Arm64JniCallingConvention::CoreSpillMask() const { diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc index 9859b5da30..36a87a89db 100644 --- a/compiler/jni/quick/calling_convention.cc +++ b/compiler/jni/quick/calling_convention.cc @@ -152,24 +152,6 @@ std::unique_ptr<JniCallingConvention> JniCallingConvention::Create(ArenaAllocato bool is_critical_native, const char* shorty, InstructionSet instruction_set) { - if (UNLIKELY(is_critical_native)) { - // Sanity check that the requested JNI instruction set - // is supported for critical natives. Not every one is. - switch (instruction_set) { - case kX86_64: - case kX86: - case kArm64: - case kArm: - case kThumb2: - break; - default: - is_critical_native = false; - LOG(WARNING) << "@CriticalNative support not implemented for " << instruction_set - << "; will crash at runtime if trying to invoke such a method."; - // TODO: implement for MIPS/MIPS64 - } - } - switch (instruction_set) { #ifdef ART_ENABLE_CODEGEN_arm case kArm: @@ -191,12 +173,18 @@ std::unique_ptr<JniCallingConvention> JniCallingConvention::Create(ArenaAllocato #ifdef ART_ENABLE_CODEGEN_mips case kMips: return std::unique_ptr<JniCallingConvention>( - new (arena) mips::MipsJniCallingConvention(is_static, is_synchronized, shorty)); + new (arena) mips::MipsJniCallingConvention(is_static, + is_synchronized, + is_critical_native, + shorty)); #endif #ifdef ART_ENABLE_CODEGEN_mips64 case kMips64: return std::unique_ptr<JniCallingConvention>( - new (arena) mips64::Mips64JniCallingConvention(is_static, is_synchronized, shorty)); + new (arena) mips64::Mips64JniCallingConvention(is_static, + is_synchronized, + is_critical_native, + shorty)); #endif #ifdef ART_ENABLE_CODEGEN_x86 case kX86: diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h index f541d8fa19..335a2dfa3c 100644 --- a/compiler/jni/quick/calling_convention.h +++ b/compiler/jni/quick/calling_convention.h @@ -370,14 +370,6 @@ class JniCallingConvention : public CallingConvention { kObjectOrClass = 1 }; - // TODO: remove this constructor once all are changed to the below one. - JniCallingConvention(bool is_static, - bool is_synchronized, - const char* shorty, - PointerSize frame_pointer_size) - : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size), - is_critical_native_(false) {} - JniCallingConvention(bool is_static, bool is_synchronized, bool is_critical_native, diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc index f5ab5f7659..e6948ec198 100644 --- a/compiler/jni/quick/mips/calling_convention_mips.cc +++ b/compiler/jni/quick/mips/calling_convention_mips.cc @@ -23,6 +23,13 @@ namespace art { namespace mips { +// Up to how many float-like (float, double) args can be enregistered in floating-point registers. +// The rest of the args must go in integer registers or on the stack. +constexpr size_t kMaxFloatOrDoubleRegisterArguments = 2u; +// Up to how many integer-like (pointers, objects, longs, int, short, bool, etc) args can be +// enregistered. The rest of the args must go on the stack. +constexpr size_t kMaxIntLikeRegisterArguments = 4u; + static const Register kCoreArgumentRegisters[] = { A0, A1, A2, A3 }; static const FRegister kFArgumentRegisters[] = { F12, F14 }; static const DRegister kDArgumentRegisters[] = { D6, D7 }; @@ -170,23 +177,134 @@ const ManagedRegisterEntrySpills& MipsManagedRuntimeCallingConvention::EntrySpil } // JNI calling convention -MipsJniCallingConvention::MipsJniCallingConvention(bool is_static, bool is_synchronized, +MipsJniCallingConvention::MipsJniCallingConvention(bool is_static, + bool is_synchronized, + bool is_critical_native, const char* shorty) - : JniCallingConvention(is_static, is_synchronized, shorty, kMipsPointerSize) { - // Compute padding to ensure longs and doubles are not split in AAPCS. Ignore the 'this' jobject - // or jclass for static methods and the JNIEnv. We start at the aligned register A2. + : JniCallingConvention(is_static, + is_synchronized, + is_critical_native, + shorty, + kMipsPointerSize) { + // SYSTEM V - Application Binary Interface (MIPS RISC Processor): + // Data Representation - Fundamental Types (3-4) specifies fundamental alignments for each type. + // "Each member is assigned to the lowest available offset with the appropriate alignment. This + // may require internal padding, depending on the previous member." + // + // All of our stack arguments are usually 4-byte aligned, however longs and doubles must be 8 + // bytes aligned. Add padding to maintain 8-byte alignment invariant. + // + // Compute padding to ensure longs and doubles are not split in o32. size_t padding = 0; - for (size_t cur_arg = IsStatic() ? 0 : 1, cur_reg = 2; cur_arg < NumArgs(); cur_arg++) { + size_t cur_arg, cur_reg; + if (LIKELY(HasExtraArgumentsForJni())) { + // Ignore the 'this' jobject or jclass for static methods and the JNIEnv. + // We start at the aligned register A2. + // + // Ignore the first 2 parameters because they are guaranteed to be aligned. + cur_arg = NumImplicitArgs(); // Skip the "this" argument. + cur_reg = 2; // Skip {A0=JNIEnv, A1=jobject} / {A0=JNIEnv, A1=jclass} parameters (start at A2). + } else { + // Check every parameter. + cur_arg = 0; + cur_reg = 0; + } + + // Shift across a logical register mapping that looks like: + // + // | A0 | A1 | A2 | A3 | SP+16 | SP+20 | SP+24 | ... | SP+n | SP+n+4 | + // + // or some of variants with floating-point registers (F12 and F14), for example + // + // | F12 | F14 | A3 | SP+16 | SP+20 | SP+24 | ... | SP+n | SP+n+4 | + // + // (where SP is the stack pointer at the start of called function). + // + // Any time there would normally be a long/double in an odd logical register, + // we have to push out the rest of the mappings by 4 bytes to maintain an 8-byte alignment. + // + // This works for both physical register pairs {A0, A1}, {A2, A3}, + // floating-point registers F12, F14 and for when the value is on the stack. + // + // For example: + // (a) long would normally go into A1, but we shift it into A2 + // | INT | (PAD) | LONG | + // | A0 | A1 | A2 | A3 | + // + // (b) long would normally go into A3, but we shift it into SP + // | INT | INT | INT | (PAD) | LONG | + // | A0 | A1 | A2 | A3 | SP+16 SP+20 | + // + // where INT is any <=4 byte arg, and LONG is any 8-byte arg. + for (; cur_arg < NumArgs(); cur_arg++) { if (IsParamALongOrDouble(cur_arg)) { if ((cur_reg & 1) != 0) { padding += 4; - cur_reg++; // additional bump to ensure alignment + cur_reg++; // Additional bump to ensure alignment. + } + cur_reg += 2; // Bump the iterator twice for every long argument. + } else { + cur_reg++; // Bump the iterator for every argument. + } + } + if (cur_reg < kMaxIntLikeRegisterArguments) { + // As a special case when, as a result of shifting (or not) there are no arguments on the stack, + // we actually have 0 stack padding. + // + // For example with @CriticalNative and: + // (int, long) -> shifts the long but doesn't need to pad the stack + // + // shift + // \/ + // | INT | (PAD) | LONG | (EMPTY) ... + // | r0 | r1 | r2 | r3 | SP ... + // /\ + // no stack padding + padding_ = 0; + } else { + padding_ = padding; + } + + // Argument Passing (3-17): + // "When the first argument is integral, the remaining arguments are passed in the integer + // registers." + // + // "The rules that determine which arguments go into registers and which ones must be passed on + // the stack are most easily explained by considering the list of arguments as a structure, + // aligned according to normal structure rules. Mapping of this structure into the combination of + // stack and registers is as follows: up to two leading floating-point arguments can be passed in + // $f12 and $f14; everything else with a structure offset greater than or equal to 16 is passed on + // the stack. The remainder of the arguments are passed in $4..$7 based on their structure offset. + // Holes left in the structure for alignment are unused, whether in registers or in the stack." + // + // For example with @CriticalNative and: + // (a) first argument is not floating-point, so all go into integer registers + // | INT | FLOAT | DOUBLE | + // | A0 | A1 | A2 | A3 | + // (b) first argument is floating-point, but 2nd is integer + // | FLOAT | INT | DOUBLE | + // | F12 | A1 | A2 | A3 | + // (c) first two arguments are floating-point (float, double) + // | FLAOT | (PAD) | DOUBLE | INT | + // | F12 | | F14 | SP+16 | + // (d) first two arguments are floating-point (double, float) + // | DOUBLE | FLOAT | INT | + // | F12 | F14 | A3 | + // (e) first three arguments are floating-point, but just first two will go into fp registers + // | DOUBLE | FLOAT | FLOAT | + // | F12 | F14 | A3 | + // + // Find out if the first argument is a floating-point. In that case, floating-point registers will + // be used for up to two leading floating-point arguments. Otherwise, all arguments will be passed + // using integer registers. + use_fp_arg_registers_ = false; + if (is_critical_native) { + if (NumArgs() > 0) { + if (IsParamAFloatOrDouble(0)) { + use_fp_arg_registers_ = true; } - cur_reg++; // additional bump to skip extra long word } - cur_reg++; // bump the iterator for every argument } - padding_ = padding; } uint32_t MipsJniCallingConvention::CoreSpillMask() const { @@ -202,74 +320,127 @@ ManagedRegister MipsJniCallingConvention::ReturnScratchRegister() const { } size_t MipsJniCallingConvention::FrameSize() { - // ArtMethod*, RA and callee save area size, local reference segment state - size_t frame_data_size = static_cast<size_t>(kMipsPointerSize) + - (2 + CalleeSaveRegisters().size()) * kFramePointerSize; - // References plus 2 words for HandleScope header - size_t handle_scope_size = HandleScope::SizeOf(kMipsPointerSize, ReferenceCount()); - // Plus return value spill area size - return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment); + // ArtMethod*, RA and callee save area size, local reference segment state. + const size_t method_ptr_size = static_cast<size_t>(kMipsPointerSize); + const size_t ra_return_addr_size = kFramePointerSize; + const size_t callee_save_area_size = CalleeSaveRegisters().size() * kFramePointerSize; + + size_t frame_data_size = method_ptr_size + ra_return_addr_size + callee_save_area_size; + + if (LIKELY(HasLocalReferenceSegmentState())) { + // Local reference segment state. + frame_data_size += kFramePointerSize; + } + + // References plus 2 words for HandleScope header. + const size_t handle_scope_size = HandleScope::SizeOf(kMipsPointerSize, ReferenceCount()); + + size_t total_size = frame_data_size; + if (LIKELY(HasHandleScope())) { + // HandleScope is sometimes excluded. + total_size += handle_scope_size; // Handle scope size. + } + + // Plus return value spill area size. + total_size += SizeOfReturnValue(); + + return RoundUp(total_size, kStackAlignment); } size_t MipsJniCallingConvention::OutArgSize() { - return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize + padding_, kStackAlignment); + // Argument Passing (3-17): + // "Despite the fact that some or all of the arguments to a function are passed in registers, + // always allocate space on the stack for all arguments. This stack space should be a structure + // large enough to contain all the arguments, aligned according to normal structure rules (after + // promotion and structure return pointer insertion). The locations within the stack frame used + // for arguments are called the home locations." + // + // Allocate 16 bytes for home locations + space needed for stack arguments. + return RoundUp( + (kMaxIntLikeRegisterArguments + NumberOfOutgoingStackArgs()) * kFramePointerSize + padding_, + kStackAlignment); } ArrayRef<const ManagedRegister> MipsJniCallingConvention::CalleeSaveRegisters() const { return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters); } -// JniCallingConvention ABI follows AAPCS where longs and doubles must occur -// in even register numbers and stack slots +// JniCallingConvention ABI follows o32 where longs and doubles must occur +// in even register numbers and stack slots. void MipsJniCallingConvention::Next() { JniCallingConvention::Next(); - size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(); - if ((itr_args_ >= 2) && - (arg_pos < NumArgs()) && - IsParamALongOrDouble(arg_pos)) { - // itr_slots_ needs to be an even number, according to AAPCS. - if ((itr_slots_ & 0x1u) != 0) { + + if (LIKELY(HasNext())) { // Avoid CHECK failure for IsCurrentParam + // Ensure slot is 8-byte aligned for longs/doubles (o32). + if (IsCurrentParamALongOrDouble() && ((itr_slots_ & 0x1u) != 0)) { + // itr_slots_ needs to be an even number, according to o32. itr_slots_++; } } } bool MipsJniCallingConvention::IsCurrentParamInRegister() { - return itr_slots_ < 4; + // Argument Passing (3-17): + // "The rules that determine which arguments go into registers and which ones must be passed on + // the stack are most easily explained by considering the list of arguments as a structure, + // aligned according to normal structure rules. Mapping of this structure into the combination of + // stack and registers is as follows: up to two leading floating-point arguments can be passed in + // $f12 and $f14; everything else with a structure offset greater than or equal to 16 is passed on + // the stack. The remainder of the arguments are passed in $4..$7 based on their structure offset. + // Holes left in the structure for alignment are unused, whether in registers or in the stack." + // + // Even when floating-point registers are used, there can be up to 4 arguments passed in + // registers. + return itr_slots_ < kMaxIntLikeRegisterArguments; } bool MipsJniCallingConvention::IsCurrentParamOnStack() { return !IsCurrentParamInRegister(); } -static const Register kJniArgumentRegisters[] = { - A0, A1, A2, A3 -}; ManagedRegister MipsJniCallingConvention::CurrentParamRegister() { - CHECK_LT(itr_slots_, 4u); - int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(); - if ((itr_args_ >= 2) && IsParamALongOrDouble(arg_pos)) { - CHECK_EQ(itr_slots_, 2u); - return MipsManagedRegister::FromRegisterPair(A2_A3); + CHECK_LT(itr_slots_, kMaxIntLikeRegisterArguments); + // Up to two leading floating-point arguments can be passed in floating-point registers. + if (use_fp_arg_registers_ && (itr_args_ < kMaxFloatOrDoubleRegisterArguments)) { + if (IsCurrentParamAFloatOrDouble()) { + if (IsCurrentParamADouble()) { + return MipsManagedRegister::FromDRegister(kDArgumentRegisters[itr_args_]); + } else { + return MipsManagedRegister::FromFRegister(kFArgumentRegisters[itr_args_]); + } + } + } + // All other arguments (including other floating-point arguments) will be passed in integer + // registers. + if (IsCurrentParamALongOrDouble()) { + if (itr_slots_ == 0u) { + return MipsManagedRegister::FromRegisterPair(A0_A1); + } else { + CHECK_EQ(itr_slots_, 2u); + return MipsManagedRegister::FromRegisterPair(A2_A3); + } } else { - return - MipsManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]); + return MipsManagedRegister::FromCoreRegister(kCoreArgumentRegisters[itr_slots_]); } } FrameOffset MipsJniCallingConvention::CurrentParamStackOffset() { - CHECK_GE(itr_slots_, 4u); + CHECK_GE(itr_slots_, kMaxIntLikeRegisterArguments); size_t offset = displacement_.Int32Value() - OutArgSize() + (itr_slots_ * kFramePointerSize); CHECK_LT(offset, OutArgSize()); return FrameOffset(offset); } size_t MipsJniCallingConvention::NumberOfOutgoingStackArgs() { - size_t static_args = IsStatic() ? 1 : 0; // count jclass - // regular argument parameters and this - size_t param_args = NumArgs() + NumLongOrDoubleArgs(); - // count JNIEnv* - return static_args + param_args + 1; + size_t static_args = HasSelfClass() ? 1 : 0; // Count jclass. + // Regular argument parameters and this. + size_t param_args = NumArgs() + NumLongOrDoubleArgs(); // Twice count 8-byte args. + // Count JNIEnv* less arguments in registers. + size_t internal_args = (HasJniEnv() ? 1 : 0); + size_t total_args = static_args + param_args + internal_args; + + return total_args - std::min(kMaxIntLikeRegisterArguments, static_cast<size_t>(total_args)); } + } // namespace mips } // namespace art diff --git a/compiler/jni/quick/mips/calling_convention_mips.h b/compiler/jni/quick/mips/calling_convention_mips.h index e95a738f2f..ad3f118bad 100644 --- a/compiler/jni/quick/mips/calling_convention_mips.h +++ b/compiler/jni/quick/mips/calling_convention_mips.h @@ -54,14 +54,17 @@ class MipsManagedRuntimeCallingConvention FINAL : public ManagedRuntimeCallingCo class MipsJniCallingConvention FINAL : public JniCallingConvention { public: - MipsJniCallingConvention(bool is_static, bool is_synchronized, const char* shorty); + MipsJniCallingConvention(bool is_static, + bool is_synchronized, + bool is_critical_native, + const char* shorty); ~MipsJniCallingConvention() OVERRIDE {} // Calling convention ManagedRegister ReturnRegister() OVERRIDE; ManagedRegister IntReturnRegister() OVERRIDE; ManagedRegister InterproceduralScratchRegister() OVERRIDE; // JNI calling convention - void Next() OVERRIDE; // Override default behavior for AAPCS + void Next() OVERRIDE; // Override default behavior for o32. size_t FrameSize() OVERRIDE; size_t OutArgSize() OVERRIDE; ArrayRef<const ManagedRegister> CalleeSaveRegisters() const OVERRIDE; @@ -82,8 +85,9 @@ class MipsJniCallingConvention FINAL : public JniCallingConvention { size_t NumberOfOutgoingStackArgs() OVERRIDE; private: - // Padding to ensure longs and doubles are not split in AAPCS + // Padding to ensure longs and doubles are not split in o32. size_t padding_; + size_t use_fp_arg_registers_; DISALLOW_COPY_AND_ASSIGN(MipsJniCallingConvention); }; diff --git a/compiler/jni/quick/mips64/calling_convention_mips64.cc b/compiler/jni/quick/mips64/calling_convention_mips64.cc index 8341e8e5b4..afe6a762eb 100644 --- a/compiler/jni/quick/mips64/calling_convention_mips64.cc +++ b/compiler/jni/quick/mips64/calling_convention_mips64.cc @@ -23,6 +23,9 @@ namespace art { namespace mips64 { +// Up to kow many args can be enregistered. The rest of the args must go on the stack. +constexpr size_t kMaxRegisterArguments = 8u; + static const GpuRegister kGpuArgumentRegisters[] = { A0, A1, A2, A3, A4, A5, A6, A7 }; @@ -150,9 +153,15 @@ const ManagedRegisterEntrySpills& Mips64ManagedRuntimeCallingConvention::EntrySp // JNI calling convention -Mips64JniCallingConvention::Mips64JniCallingConvention(bool is_static, bool is_synchronized, +Mips64JniCallingConvention::Mips64JniCallingConvention(bool is_static, + bool is_synchronized, + bool is_critical_native, const char* shorty) - : JniCallingConvention(is_static, is_synchronized, shorty, kMips64PointerSize) { + : JniCallingConvention(is_static, + is_synchronized, + is_critical_native, + shorty, + kMips64PointerSize) { } uint32_t Mips64JniCallingConvention::CoreSpillMask() const { @@ -168,13 +177,28 @@ ManagedRegister Mips64JniCallingConvention::ReturnScratchRegister() const { } size_t Mips64JniCallingConvention::FrameSize() { - // ArtMethod*, RA and callee save area size, local reference segment state - size_t frame_data_size = kFramePointerSize + - (CalleeSaveRegisters().size() + 1) * kFramePointerSize + sizeof(uint32_t); - // References plus 2 words for HandleScope header + // ArtMethod*, RA and callee save area size, local reference segment state. + size_t method_ptr_size = static_cast<size_t>(kFramePointerSize); + size_t ra_and_callee_save_area_size = (CalleeSaveRegisters().size() + 1) * kFramePointerSize; + + size_t frame_data_size = method_ptr_size + ra_and_callee_save_area_size; + if (LIKELY(HasLocalReferenceSegmentState())) { // Local ref. segment state. + // Local reference segment state is sometimes excluded. + frame_data_size += sizeof(uint32_t); + } + // References plus 2 words for HandleScope header. size_t handle_scope_size = HandleScope::SizeOf(kMips64PointerSize, ReferenceCount()); - // Plus return value spill area size - return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment); + + size_t total_size = frame_data_size; + if (LIKELY(HasHandleScope())) { + // HandleScope is sometimes excluded. + total_size += handle_scope_size; // Handle scope size. + } + + // Plus return value spill area size. + total_size += SizeOfReturnValue(); + + return RoundUp(total_size, kStackAlignment); } size_t Mips64JniCallingConvention::OutArgSize() { @@ -186,7 +210,7 @@ ArrayRef<const ManagedRegister> Mips64JniCallingConvention::CalleeSaveRegisters( } bool Mips64JniCallingConvention::IsCurrentParamInRegister() { - return itr_args_ < 8; + return itr_args_ < kMaxRegisterArguments; } bool Mips64JniCallingConvention::IsCurrentParamOnStack() { @@ -204,7 +228,8 @@ ManagedRegister Mips64JniCallingConvention::CurrentParamRegister() { FrameOffset Mips64JniCallingConvention::CurrentParamStackOffset() { CHECK(IsCurrentParamOnStack()); - size_t offset = displacement_.Int32Value() - OutArgSize() + ((itr_args_ - 8) * kFramePointerSize); + size_t args_on_stack = itr_args_ - kMaxRegisterArguments; + size_t offset = displacement_.Int32Value() - OutArgSize() + (args_on_stack * kFramePointerSize); CHECK_LT(offset, OutArgSize()); return FrameOffset(offset); } @@ -214,7 +239,7 @@ size_t Mips64JniCallingConvention::NumberOfOutgoingStackArgs() { size_t all_args = NumArgs() + NumberOfExtraArgumentsForJni(); // Nothing on the stack unless there are more than 8 arguments - return (all_args > 8) ? all_args - 8 : 0; + return (all_args > kMaxRegisterArguments) ? all_args - kMaxRegisterArguments : 0; } } // namespace mips64 } // namespace art diff --git a/compiler/jni/quick/mips64/calling_convention_mips64.h b/compiler/jni/quick/mips64/calling_convention_mips64.h index a5fd111c88..faedaeff6c 100644 --- a/compiler/jni/quick/mips64/calling_convention_mips64.h +++ b/compiler/jni/quick/mips64/calling_convention_mips64.h @@ -54,7 +54,10 @@ class Mips64ManagedRuntimeCallingConvention FINAL : public ManagedRuntimeCalling class Mips64JniCallingConvention FINAL : public JniCallingConvention { public: - Mips64JniCallingConvention(bool is_static, bool is_synchronized, const char* shorty); + Mips64JniCallingConvention(bool is_static, + bool is_synchronized, + bool is_critical_native, + const char* shorty); ~Mips64JniCallingConvention() OVERRIDE {} // Calling convention ManagedRegister ReturnRegister() OVERRIDE; diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 102637f01b..94585769b4 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -125,7 +125,9 @@ class OatTest : public CommonCompilerTest { SafeMap<std::string, std::string>& key_value_store, bool verify) { TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(/*compiling_boot_image*/false, &timings); + OatWriter oat_writer(/*compiling_boot_image*/false, + &timings, + /*profile_compilation_info*/nullptr); for (const DexFile* dex_file : dex_files) { ArrayRef<const uint8_t> raw_dex_file( reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()), @@ -145,7 +147,9 @@ class OatTest : public CommonCompilerTest { SafeMap<std::string, std::string>& key_value_store, bool verify) { TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(/*compiling_boot_image*/false, &timings); + OatWriter oat_writer(/*compiling_boot_image*/false, + &timings, + /*profile_compilation_info*/nullptr); for (const char* dex_filename : dex_filenames) { if (!oat_writer.AddDexFileSource(dex_filename, dex_filename)) { return false; @@ -161,7 +165,9 @@ class OatTest : public CommonCompilerTest { SafeMap<std::string, std::string>& key_value_store, bool verify) { TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(/*compiling_boot_image*/false, &timings); + OatWriter oat_writer(/*compiling_boot_image*/false, + &timings, + /*profile_compilation_info*/nullptr); if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) { return false; } diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index f9173f50a6..153aff40dc 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -33,6 +33,7 @@ #include "debug/method_debug_info.h" #include "dex/verification_results.h" #include "dex_file-inl.h" +#include "dexlayout.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "gc/space/image_space.h" @@ -260,16 +261,7 @@ class OatWriter::OatDexFile { // Data to write to a separate section. dchecked_vector<uint32_t> class_offsets_; - void InitTypeLookupTable(const DexFile& dex_file, uint8_t* storage) const { - lookup_table_.reset(TypeLookupTable::Create(dex_file, storage)); - } - - TypeLookupTable* GetTypeLookupTable() const { - return lookup_table_.get(); - } - private: - mutable std::unique_ptr<TypeLookupTable> lookup_table_; size_t GetClassOffsetsRawSize() const { return class_offsets_.size() * sizeof(class_offsets_[0]); } @@ -285,7 +277,7 @@ class OatWriter::OatDexFile { DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \ << "file_offset=" << file_offset << " offset_=" << offset_ -OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings) +OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCompilationInfo* info) : write_state_(WriteState::kAddingDexFileSources), timings_(timings), raw_dex_files_(), @@ -346,7 +338,8 @@ OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings) size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0), relative_patcher_(nullptr), - absolute_patch_locations_() { + absolute_patch_locations_(), + profile_compilation_info_(info) { } bool OatWriter::AddDexFileSource(const char* filename, @@ -410,6 +403,35 @@ bool OatWriter::AddZippedDexFilesSource(File&& zip_fd, return true; } +// Add dex file source(s) from a vdex file specified by a file handle. +bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, + const char* location, + CreateTypeLookupTable create_type_lookup_table) { + DCHECK(write_state_ == WriteState::kAddingDexFileSources); + const uint8_t* current_dex_data = nullptr; + for (size_t i = 0; ; ++i) { + current_dex_data = vdex_file.GetNextDexFileData(current_dex_data); + if (current_dex_data == nullptr) { + break; + } + if (!DexFile::IsMagicValid(current_dex_data)) { + LOG(ERROR) << "Invalid magic in vdex file created from " << location; + return false; + } + // We used `zipped_dex_file_locations_` to keep the strings in memory. + zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location)); + const char* full_location = zipped_dex_file_locations_.back().c_str(); + oat_dex_files_.emplace_back(full_location, + DexFileSource(current_dex_data), + create_type_lookup_table); + } + if (oat_dex_files_.empty()) { + LOG(ERROR) << "No dex files in vdex file created from " << location; + return false; + } + return true; +} + // Add dex file source from raw memory. bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data, const char* location, @@ -2090,7 +2112,11 @@ bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_ if (!SeekToDexFile(out, file, oat_dex_file)) { return false; } - if (oat_dex_file->source_.IsZipEntry()) { + if (profile_compilation_info_ != nullptr) { + if (!LayoutAndWriteDexFile(out, oat_dex_file)) { + return false; + } + } else if (oat_dex_file->source_.IsZipEntry()) { if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) { return false; } @@ -2155,6 +2181,39 @@ bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex return true; } +bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file) { + TimingLogger::ScopedTiming split("Dex Layout", timings_); + std::string error_msg; + std::string location(oat_dex_file->GetLocation()); + std::unique_ptr<const DexFile> dex_file; + if (oat_dex_file->source_.IsZipEntry()) { + ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry(); + std::unique_ptr<MemMap> mem_map( + zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg)); + dex_file = DexFile::Open(location, + zip_entry->GetCrc32(), + std::move(mem_map), + /* verify */ true, + /* verify_checksum */ true, + &error_msg); + } else { + DCHECK(oat_dex_file->source_.IsRawFile()); + File* raw_file = oat_dex_file->source_.GetRawFile(); + dex_file = DexFile::OpenDex(raw_file->Fd(), location, /* verify_checksum */ true, &error_msg); + } + Options options; + options.output_to_memmap_ = true; + DexLayout dex_layout(options, profile_compilation_info_, nullptr); + dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0); + std::unique_ptr<MemMap> mem_map(dex_layout.GetAndReleaseMemMap()); + if (!WriteDexFile(out, oat_dex_file, mem_map->Begin())) { + return false; + } + // Set the checksum of the new oat dex file to be the original file's checksum. + oat_dex_file->dex_file_location_checksum_ = dex_file->GetLocationChecksum(); + return true; +} + bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file, @@ -2481,8 +2540,15 @@ bool OatWriter::WriteTypeLookupTables( // Create the lookup table. When `nullptr` is given as the storage buffer, // TypeLookupTable allocates its own and OatDexFile takes ownership. - oat_dex_file->InitTypeLookupTable(*opened_dex_files[i], /* storage */ nullptr); - TypeLookupTable* table = oat_dex_file->GetTypeLookupTable(); + const DexFile& dex_file = *opened_dex_files[i]; + { + std::unique_ptr<TypeLookupTable> type_lookup_table = + TypeLookupTable::Create(dex_file, /* storage */ nullptr); + type_lookup_table_oat_dex_files_.push_back( + std::make_unique<art::OatDexFile>(std::move(type_lookup_table))); + dex_file.SetOatDexFile(type_lookup_table_oat_dex_files_.back().get()); + } + TypeLookupTable* const table = type_lookup_table_oat_dex_files_.back()->GetTypeLookupTable(); // Type tables are required to be 4 byte aligned. size_t initial_offset = oat_size_; diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 3d08ad3173..0dcf79e54e 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -38,9 +38,11 @@ class BitVector; class CompiledMethod; class CompilerDriver; class ImageWriter; +class ProfileCompilationInfo; class OutputStream; class TimingLogger; class TypeLookupTable; +class VdexFile; class ZipEntry; namespace debug { @@ -110,12 +112,13 @@ class OatWriter { kDefault = kCreate }; - OatWriter(bool compiling_boot_image, TimingLogger* timings); + OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCompilationInfo* info); // To produce a valid oat file, the user must first add sources with any combination of // - AddDexFileSource(), // - AddZippedDexFilesSource(), - // - AddRawDexFileSource(). + // - AddRawDexFileSource(), + // - AddVdexDexFilesSource(). // Then the user must call in order // - WriteAndOpenDexFiles() // - Initialize() @@ -144,6 +147,11 @@ class OatWriter { const char* location, uint32_t location_checksum, CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); + // Add dex file source(s) from a vdex file. + bool AddVdexDexFilesSource( + const VdexFile& vdex_file, + const char* location, + CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); dchecked_vector<const char*> GetSourceLocations() const; // Write raw dex files to the vdex file, mmap the file and open the dex files from it. @@ -258,6 +266,7 @@ class OatWriter { bool WriteDexFiles(OutputStream* out, File* file); bool WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file); bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file); + bool LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file); bool WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file, @@ -363,6 +372,9 @@ class OatWriter { // Offset of the oat data from the start of the mmapped region of the elf file. size_t oat_data_offset_; + // Fake OatDexFiles to hold type lookup tables for the compiler. + std::vector<std::unique_ptr<art::OatDexFile>> type_lookup_table_oat_dex_files_; + // data to write std::unique_ptr<OatHeader> oat_header_; dchecked_vector<OatDexFile> oat_dex_files_; @@ -419,6 +431,9 @@ class OatWriter { // The locations of absolute patches relative to the start of the executable section. dchecked_vector<uintptr_t> absolute_patch_locations_; + // Profile info used to generate new layout of files. + ProfileCompilationInfo* profile_compilation_info_; + DISALLOW_COPY_AND_ASSIGN(OatWriter); }; diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index b7c24ff207..dfa150406d 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -70,9 +70,9 @@ TEST_F(BoundsCheckEliminationTest, NarrowingRangeArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); // array + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); // array HInstruction* parameter2 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); // i + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); // i entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); @@ -167,9 +167,9 @@ TEST_F(BoundsCheckEliminationTest, OverflowArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); // array + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); // array HInstruction* parameter2 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); // i + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); // i entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); @@ -231,9 +231,9 @@ TEST_F(BoundsCheckEliminationTest, UnderflowArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); // array + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); // array HInstruction* parameter2 = new (&allocator_) - HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); // i + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); // i entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); @@ -295,7 +295,7 @@ TEST_F(BoundsCheckEliminationTest, ConstantArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter = new (&allocator_) HParameterValue( - graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_5 = graph_->GetIntConstant(5); @@ -364,7 +364,7 @@ static HInstruction* BuildSSAGraph1(HGraph* graph, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_initial = graph->GetIntConstant(initial); @@ -479,7 +479,7 @@ static HInstruction* BuildSSAGraph2(HGraph *graph, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_initial = graph->GetIntConstant(initial); @@ -600,7 +600,7 @@ static HInstruction* BuildSSAGraph3(HGraph* graph, constant_10, graph->GetCurrentMethod(), 0, - Primitive::kPrimInt, + dex::TypeIndex(static_cast<uint16_t>(Primitive::kPrimInt)), graph->GetDexFile(), kQuickAllocArray); block->AddInstruction(new_array); @@ -692,7 +692,7 @@ static HInstruction* BuildSSAGraph4(HGraph* graph, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_initial = graph->GetIntConstant(initial); @@ -795,7 +795,7 @@ TEST_F(BoundsCheckEliminationTest, BubbleSortArrayBoundsElimination) { graph_->AddBlock(entry); graph_->SetEntryBlock(entry); HInstruction* parameter = new (&allocator_) HParameterValue( - graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HInstruction* constant_0 = graph_->GetIntConstant(0); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 8b450e11dc..9f6b78a82c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1375,4 +1375,33 @@ uint32_t CodeGenerator::GetReferenceDisableFlagOffset() const { return klass->GetDisableIntrinsicFlagOffset().Uint32Value(); } +void CodeGenerator::EmitJitRoots(uint8_t* code, + Handle<mirror::ObjectArray<mirror::Object>> roots, + const uint8_t* roots_data, + Handle<mirror::DexCache> outer_dex_cache) { + DCHECK_EQ(static_cast<size_t>(roots->GetLength()), GetNumberOfJitRoots()); + StackHandleScope<1> hs(Thread::Current()); + MutableHandle<mirror::DexCache> h_dex_cache(hs.NewHandle<mirror::DexCache>(nullptr)); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + size_t index = 0; + for (auto& entry : jit_string_roots_) { + const DexFile& entry_dex_file = *entry.first.dex_file; + // Avoid the expensive FindDexCache call by checking if the string is + // in the compiled method's dex file. + h_dex_cache.Assign(IsSameDexFile(*outer_dex_cache->GetDexFile(), entry_dex_file) + ? outer_dex_cache.Get() + : class_linker->FindDexCache(hs.Self(), entry_dex_file)); + mirror::String* string = class_linker->LookupString( + entry_dex_file, entry.first.string_index, h_dex_cache); + DCHECK(string != nullptr) << "JIT roots require strings to have been loaded"; + // Ensure the string is strongly interned. This is a requirement on how the JIT + // handles strings. b/32995596 + class_linker->GetInternTable()->InternStrong(string); + roots->Set(index, string); + entry.second = index; + ++index; + } + EmitJitRootPatches(code, roots_data); +} + } // namespace art diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index bf246ad309..a5d19abe92 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -32,6 +32,7 @@ #include "optimizing_compiler_stats.h" #include "read_barrier_option.h" #include "stack_map_stream.h" +#include "string_reference.h" #include "utils/label.h" namespace art { @@ -335,6 +336,17 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { void BuildStackMaps(MemoryRegion region, const DexFile::CodeItem& code_item); size_t ComputeStackMapsSize(); + size_t GetNumberOfJitRoots() const { + return jit_string_roots_.size(); + } + + // Fills the `literals` array with literals collected during code generation. + // Also emits literal patches. + void EmitJitRoots(uint8_t* code, + Handle<mirror::ObjectArray<mirror::Object>> roots, + const uint8_t* roots_data, + Handle<mirror::DexCache> outer_dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); bool IsLeafMethod() const { return is_leaf_; @@ -515,6 +527,26 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { virtual HLoadClass::LoadKind GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) = 0; + static LocationSummary::CallKind GetLoadStringCallKind(HLoadString* load) { + switch (load->GetLoadKind()) { + case HLoadString::LoadKind::kBssEntry: + DCHECK(load->NeedsEnvironment()); + return LocationSummary::kCallOnSlowPath; + case HLoadString::LoadKind::kDexCacheViaMethod: + DCHECK(load->NeedsEnvironment()); + return LocationSummary::kCallOnMainOnly; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(!load->NeedsEnvironment()); + return kEmitCompilerReadBarrier + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + break; + default: + DCHECK(!load->NeedsEnvironment()); + return LocationSummary::kNoCall; + } + } + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch( @@ -571,6 +603,8 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { fpu_callee_save_mask_(fpu_callee_save_mask), stack_map_stream_(graph->GetArena()), block_order_(nullptr), + jit_string_roots_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), disasm_info_(nullptr), stats_(stats), graph_(graph), @@ -637,6 +671,12 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { return current_slow_path_; } + // Emit the patches assocatied with JIT roots. Only applies to JIT compiled code. + virtual void EmitJitRootPatches(uint8_t* code ATTRIBUTE_UNUSED, + const uint8_t* roots_data ATTRIBUTE_UNUSED) { + DCHECK_EQ(jit_string_roots_.size(), 0u); + } + // Frame size required for this method. uint32_t frame_size_; uint32_t core_spill_mask_; @@ -662,6 +702,11 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { // The order to use for code generation. const ArenaVector<HBasicBlock*>* block_order_; + // Maps a StringReference (dex_file, string_index) to the index in the literal table. + // Entries are intially added with a 0 index, and `EmitJitRoots` will compute all the + // indices. + ArenaSafeMap<StringReference, size_t, StringReferenceValueComparator> jit_string_roots_; + DisassemblyInformation* disasm_info_; private: diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index c6363d1708..8a6b94e0ea 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -383,7 +383,7 @@ class LoadClassSlowPathARM : public SlowPathCodeARM { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); + __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); @@ -1214,7 +1214,9 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_string_patches_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Always save the LR register to mimic Quick. AddAllocatedRegister(Location::RegisterLocation(LR)); } @@ -3951,7 +3953,7 @@ void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; - __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -5741,7 +5743,7 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -5828,7 +5830,7 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { current_method, ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); + size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option); generate_null_check = !cls->IsInDexCache(); } @@ -5893,6 +5895,9 @@ HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind( case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; case HLoadString::LoadKind::kDexCacheViaMethod: break; } @@ -5900,13 +5905,8 @@ HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind( } void LocationsBuilderARM::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = load->NeedsEnvironment() - ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) - ? LocationSummary::kCallOnMainOnly - : LocationSummary::kCallOnSlowPath) - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); - HLoadString::LoadKind load_kind = load->GetLoadKind(); if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) { locations->SetOut(Location::RegisterLocation(R0)); @@ -5979,6 +5979,13 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { __ Bind(slow_path->GetExitLabel()); return; } + case HLoadString::LoadKind::kJitTableAddress: { + __ LoadLiteral(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(), + load->GetStringIndex())); + // /* GcRoot<mirror::String> */ out = *out + GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + return; + } default: break; } @@ -6490,12 +6497,9 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { iftable_offset, maybe_temp2_loc, kWithoutReadBarrier); - // Null iftable means it is empty and will always fail the check. - __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel()); - - // Loop through the iftable and check if any class matches. + // Iftable is never null. __ ldr(maybe_temp2_loc.AsRegister<Register>(), Address(temp, array_length_offset)); - + // Loop through the iftable and check if any class matches. Label start_loop; __ Bind(&start_loop); __ CompareAndBranchIfZero(maybe_temp2_loc.AsRegister<Register>(), @@ -7320,8 +7324,8 @@ CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatc } CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch( - const DexFile& dex_file, uint32_t type_index) { - return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_); + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_); } CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch( @@ -7343,7 +7347,7 @@ Literal* CodeGeneratorARM::DeduplicateBootImageStringLiteral(const DexFile& dex_ } Literal* CodeGeneratorARM::DeduplicateBootImageTypeLiteral(const DexFile& dex_file, - uint32_t type_index) { + dex::TypeIndex type_index) { return boot_image_type_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); @@ -7359,6 +7363,14 @@ Literal* CodeGeneratorARM::DeduplicateDexCacheAddressLiteral(uint32_t address) { return DeduplicateUint32Literal(address, &uint32_literals_); } +Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file, + uint32_t string_index) { + jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u); + return jit_string_patches_.GetOrCreate( + StringReference(&dex_file, string_index), + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorARM::EmitPcRelativeLinkerPatches( const ArenaDeque<PcRelativePatchInfo>& infos, @@ -7440,7 +7452,7 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche uint32_t literal_offset = literal->GetLabel()->Position(); linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, target_type.dex_file, - target_type.type_index)); + target_type.type_index.index_)); } EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, linker_patches); @@ -7675,6 +7687,21 @@ void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction } } +void CodeGeneratorARM::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { + for (const auto& entry : jit_string_patches_) { + const auto& it = jit_string_roots_.find(entry.first); + DCHECK(it != jit_string_roots_.end()); + size_t index_in_table = it->second; + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = literal->GetLabel()->Position(); + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + uint8_t* data = code + literal_offset; + reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address); + } +} + #undef __ #undef QUICK_ENTRY_POINT diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index f95dd573cb..a4ccb57c1f 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -19,6 +19,7 @@ #include "base/enums.h" #include "code_generator.h" +#include "dex_file_types.h" #include "driver/compiler_options.h" #include "nodes.h" #include "string_reference.h" @@ -481,16 +482,19 @@ class CodeGeneratorARM : public CodeGenerator { }; PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); - PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, uint32_t type_index); + PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); - Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, uint32_t type_index); + Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); Literal* DeduplicateBootImageAddressLiteral(uint32_t address); Literal* DeduplicateDexCacheAddressLiteral(uint32_t address); + Literal* DeduplicateJitStringLiteral(const DexFile& dex_file, uint32_t string_index); void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -591,9 +595,9 @@ class CodeGeneratorARM : public CodeGenerator { using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>; using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>; - using BootStringToLiteralMap = ArenaSafeMap<StringReference, - Literal*, - StringReferenceValueComparator>; + using StringToLiteralMap = ArenaSafeMap<StringReference, + Literal*, + StringReferenceValueComparator>; using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, Literal*, TypeReferenceValueComparator>; @@ -605,7 +609,6 @@ class CodeGeneratorARM : public CodeGenerator { PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches); - template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> static void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos, ArenaVector<LinkerPatch>* linker_patches); @@ -630,7 +633,7 @@ class CodeGeneratorARM : public CodeGenerator { // PC-relative patch info for each HArmDexCacheArraysBase. ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; // Deduplication map for boot string literals for kBootImageLinkTimeAddress. - BootStringToLiteralMap boot_image_string_patches_; + StringToLiteralMap boot_image_string_patches_; // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC). ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. @@ -640,6 +643,9 @@ class CodeGeneratorARM : public CodeGenerator { // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; + // Patches for string literals in JIT compiled code. + StringToLiteralMap jit_string_patches_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM); }; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 4ab6065819..a78b3da455 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -288,7 +288,7 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex()); + __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; arm64_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); @@ -572,8 +572,10 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) { // We are about to use the assembler to place literals directly. Make sure we have enough // underlying code buffer and we have generated the jump table with right size. - CodeBufferCheckScope scope(codegen->GetVIXLAssembler(), num_entries * sizeof(int32_t), - CodeBufferCheckScope::kCheck, CodeBufferCheckScope::kExactSize); + vixl::CodeBufferCheckScope scope(codegen->GetVIXLAssembler(), + num_entries * sizeof(int32_t), + vixl::CodeBufferCheckScope::kReserveBufferSpace, + vixl::CodeBufferCheckScope::kExactSize); __ Bind(&table_start_); const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors(); @@ -1158,7 +1160,9 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_string_patches_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Save the link register (containing the return address) to mimic Quick. AddAllocatedRegister(LocationFrom(lr)); } @@ -2258,10 +2262,10 @@ void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* masm->GetCursorAddress<vixl::aarch64::Instruction*>() - kInstructionSize; if (prev->IsLoadOrStore()) { // Make sure we emit only exactly one nop. - vixl::aarch64::CodeBufferCheckScope scope(masm, - kInstructionSize, - vixl::aarch64::CodeBufferCheckScope::kCheck, - vixl::aarch64::CodeBufferCheckScope::kExactSize); + vixl::CodeBufferCheckScope scope(masm, + kInstructionSize, + vixl::CodeBufferCheckScope::kReserveBufferSpace, + vixl::CodeBufferCheckScope::kExactSize); __ nop(); } } @@ -3802,12 +3806,9 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { iftable_offset, maybe_temp2_loc, kWithoutReadBarrier); - // Null iftable means it is empty and will always fail the check. - __ Cbz(temp, type_check_slow_path->GetEntryLabel()); - - // Loop through the iftable and check if any class matches. + // Iftable is never null. __ Ldr(WRegisterFrom(maybe_temp2_loc), HeapOperand(temp.W(), array_length_offset)); - + // Loop through the iftable and check if any class matches. vixl::aarch64::Label start_loop; __ Bind(&start_loop); __ Cbz(WRegisterFrom(maybe_temp2_loc), type_check_slow_path->GetEntryLabel()); @@ -4037,7 +4038,8 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok vixl::aarch64::Label* label = &relative_call_patches_.back().label; SingleEmissionCheckScope guard(GetVIXLAssembler()); __ Bind(label); - __ bl(0); // Branch and link to itself. This will be overriden at link time. + // Branch and link to itself. This will be overriden at link time. + __ bl(static_cast<int64_t>(0)); break; } case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: @@ -4103,9 +4105,9 @@ vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeStringPatch( vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeTypePatch( const DexFile& dex_file, - uint32_t type_index, + dex::TypeIndex type_index, vixl::aarch64::Label* adrp_label) { - return NewPcRelativePatch(dex_file, type_index, adrp_label, &pc_relative_type_patches_); + return NewPcRelativePatch(dex_file, type_index.index_, adrp_label, &pc_relative_type_patches_); } vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeDexCacheArrayPatch( @@ -4137,7 +4139,7 @@ vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageString } vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageTypeLiteral( - const DexFile& dex_file, uint32_t type_index) { + const DexFile& dex_file, dex::TypeIndex type_index) { return boot_image_type_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); @@ -4155,12 +4157,20 @@ vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateDexCacheAddress return DeduplicateUint64Literal(address); } +vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLiteral( + const DexFile& dex_file, uint32_t string_index) { + jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u); + return jit_string_patches_.GetOrCreate( + StringReference(&dex_file, string_index), + [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); +} + void CodeGeneratorARM64::EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg) { DCHECK(reg.IsX()); SingleEmissionCheckScope guard(GetVIXLAssembler()); __ Bind(fixup_label); - __ adrp(reg, /* offset placeholder */ 0); + __ adrp(reg, /* offset placeholder */ static_cast<int64_t>(0)); } void CodeGeneratorARM64::EmitAddPlaceholder(vixl::aarch64::Label* fixup_label, @@ -4250,7 +4260,7 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc vixl::aarch64::Literal<uint32_t>* literal = entry.second; linker_patches->push_back(LinkerPatch::TypePatch(literal->GetOffset(), target_type.dex_file, - target_type.type_index)); + target_type.type_index.index_)); } EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, linker_patches); @@ -4374,7 +4384,7 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -4410,7 +4420,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); // Add ADRP with its PC-relative type patch. const DexFile& dex_file = cls->GetDexFile(); - uint32_t type_index = cls->GetTypeIndex(); + dex::TypeIndex type_index = cls->GetTypeIndex(); vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeTypePatch(dex_file, type_index); codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); // Add ADD with its PC-relative type patch. @@ -4478,7 +4488,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { GenerateGcRootFieldLoad(cls, out_loc, out.X(), - CodeGenerator::GetCacheOffset(cls->GetTypeIndex()), + CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_), /* fixup_label */ nullptr, read_barrier_option); generate_null_check = !cls->IsInDexCache(); @@ -4540,16 +4550,15 @@ HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind( break; case HLoadString::LoadKind::kDexCacheViaMethod: break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; } return desired_string_load_kind; } void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = load->NeedsEnvironment() - ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) - ? LocationSummary::kCallOnMainOnly - : LocationSummary::kCallOnSlowPath) - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) { InvokeRuntimeCallingConvention calling_convention; @@ -4575,6 +4584,7 @@ void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { Register out = OutputRegister(load); + Location out_loc = load->GetLocations()->Out(); switch (load->GetLoadKind()) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: @@ -4611,9 +4621,9 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { // Add LDR with its PC-relative String patch. vixl::aarch64::Label* ldr_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index, adrp_label); - // /* GcRoot<mirror::Class> */ out = *(base_address + offset) /* PC-relative */ + // /* GcRoot<mirror::String> */ out = *(base_address + offset) /* PC-relative */ GenerateGcRootFieldLoad(load, - load->GetLocations()->Out(), + out_loc, temp, /* offset placeholder */ 0u, ldr_label, @@ -4625,6 +4635,17 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { __ Bind(slow_path->GetExitLabel()); return; } + case HLoadString::LoadKind::kJitTableAddress: { + __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(), + load->GetStringIndex())); + GenerateGcRootFieldLoad(load, + out_loc, + out.X(), + /* offset */ 0, + /* fixup_label */ nullptr, + kCompilerReadBarrierOption); + return; + } default: break; } @@ -4757,7 +4778,7 @@ void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt); DCHECK(type_index.Is(w0)); - __ Mov(type_index, instruction->GetTypeIndex()); + __ Mov(type_index, instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -5744,7 +5765,19 @@ void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instructi } } - +void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { + for (const auto& entry : jit_string_patches_) { + const auto& it = jit_string_roots_.find(entry.first); + DCHECK(it != jit_string_roots_.end()); + size_t index_in_table = it->second; + vixl::aarch64::Literal<uint32_t>* literal = entry.second; + uint32_t literal_offset = literal->GetOffset(); + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + uint8_t* data = code + literal_offset; + reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address); + } +} #undef __ #undef QUICK_ENTRY_POINT diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 0e8d4fd549..1545fd3860 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -20,6 +20,7 @@ #include "arch/arm64/quick_method_frame_info_arm64.h" #include "code_generator.h" #include "common_arm64.h" +#include "dex_file_types.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" @@ -547,7 +548,7 @@ class CodeGeneratorARM64 : public CodeGenerator { // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing // to the associated ADRP patch label). vixl::aarch64::Label* NewPcRelativeTypePatch(const DexFile& dex_file, - uint32_t type_index, + dex::TypeIndex type_index, vixl::aarch64::Label* adrp_label = nullptr); // Add a new PC-relative dex cache array patch for an instruction and return @@ -562,9 +563,11 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, - uint32_t type_index); + dex::TypeIndex type_index); vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address); vixl::aarch64::Literal<uint64_t>* DeduplicateDexCacheAddressLiteral(uint64_t address); + vixl::aarch64::Literal<uint32_t>* DeduplicateJitStringLiteral(const DexFile& dex_file, + uint32_t string_index); void EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg); void EmitAddPlaceholder(vixl::aarch64::Label* fixup_label, @@ -576,6 +579,8 @@ class CodeGeneratorARM64 : public CodeGenerator { void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -673,9 +678,9 @@ class CodeGeneratorARM64 : public CodeGenerator { using MethodToLiteralMap = ArenaSafeMap<MethodReference, vixl::aarch64::Literal<uint64_t>*, MethodReferenceComparator>; - using BootStringToLiteralMap = ArenaSafeMap<StringReference, - vixl::aarch64::Literal<uint32_t>*, - StringReferenceValueComparator>; + using StringToLiteralMap = ArenaSafeMap<StringReference, + vixl::aarch64::Literal<uint32_t>*, + StringReferenceValueComparator>; using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, vixl::aarch64::Literal<uint32_t>*, TypeReferenceValueComparator>; @@ -739,7 +744,7 @@ class CodeGeneratorARM64 : public CodeGenerator { // PC-relative DexCache access info. ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; // Deduplication map for boot string literals for kBootImageLinkTimeAddress. - BootStringToLiteralMap boot_image_string_patches_; + StringToLiteralMap boot_image_string_patches_; // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC). ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. @@ -749,6 +754,9 @@ class CodeGeneratorARM64 : public CodeGenerator { // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; + // Patches for string literals in JIT compiled code. + StringToLiteralMap jit_string_patches_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64); }; diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index e9827e8620..e399f3228e 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -47,6 +47,7 @@ using helpers::InputRegister; using helpers::InputRegisterAt; using helpers::InputSRegisterAt; using helpers::InputVRegisterAt; +using helpers::Int32ConstantFrom; using helpers::LocationFrom; using helpers::LowRegisterFrom; using helpers::LowSRegisterFrom; @@ -132,7 +133,7 @@ static size_t SaveContiguousSRegisterList(size_t first, vixl32::Register base = sp; if (stack_offset != 0) { base = temps.Acquire(); - __ Add(base, sp, stack_offset); + __ Add(base, sp, Operand::From(stack_offset)); } __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs)); } @@ -180,7 +181,7 @@ static size_t RestoreContiguousSRegisterList(size_t first, vixl32::Register base = sp; if (stack_offset != 0) { base = temps.Acquire(); - __ Add(base, sp, stack_offset); + __ Add(base, sp, Operand::From(stack_offset)); } __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs)); } @@ -398,7 +399,7 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConventionARMVIXL calling_convention; - __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); + __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); @@ -673,13 +674,22 @@ void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) { DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold); // We are about to use the assembler to place literals directly. Make sure we have enough - // underlying code buffer and we have generated the jump table with right size. - codegen->GetVIXLAssembler()->GetBuffer().Align(); + // underlying code buffer and we have generated a jump table of the right size, using + // codegen->GetVIXLAssembler()->GetBuffer().Align(); AssemblerAccurateScope aas(codegen->GetVIXLAssembler(), num_entries * sizeof(int32_t), CodeBufferCheckScope::kMaximumSize); // TODO(VIXL): Check that using lower case bind is fine here. codegen->GetVIXLAssembler()->bind(&table_start_); + for (uint32_t i = 0; i < num_entries; i++) { + codegen->GetVIXLAssembler()->place(bb_addresses_[i].get()); + } +} + +void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) { + uint32_t num_entries = switch_instr_->GetNumEntries(); + DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold); + const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors(); for (uint32_t i = 0; i < num_entries; i++) { vixl32::Label* target_label = codegen->GetLabelOf(successors[i]); @@ -691,21 +701,21 @@ void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) { } DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min()); DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max()); - vixl32::Literal<int32_t> literal(jump_offset); - codegen->GetVIXLAssembler()->place(&literal); + + bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer()); } } -void CodeGeneratorARMVIXL::EmitJumpTables() { +void CodeGeneratorARMVIXL::FixJumpTables() { for (auto&& jump_table : jump_tables_) { - jump_table->EmitTable(this); + jump_table->FixTable(this); } } #define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) { - EmitJumpTables(); + FixJumpTables(); GetAssembler()->FinalizeCode(); CodeGenerator::Finalize(allocator); } @@ -1143,7 +1153,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* c void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, vixl32::Label* true_target, - vixl32::Label* false_target) { + vixl32::Label* false_target, + bool far_target) { HInstruction* cond = instruction->InputAt(condition_input_index); if (true_target == nullptr && false_target == nullptr) { @@ -1179,9 +1190,13 @@ void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instru DCHECK(cond_val.IsRegister()); } if (true_target == nullptr) { - __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target); + __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index), + false_target, + far_target); } else { - __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target); + __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index), + true_target, + far_target); } } else { // Condition has not been materialized. Use its inputs as the comparison and @@ -1276,7 +1291,8 @@ void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) { GenerateTestAndBranch(select, /* condition_input_index */ 2, /* true_target */ nullptr, - &false_target); + &false_target, + /* far_target */ false); codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); __ Bind(&false_target); } @@ -1652,7 +1668,20 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* inv // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other // instruction from clobbering it as they might use r12 as a scratch register. DCHECK(hidden_reg.Is(r12)); - __ Mov(hidden_reg, invoke->GetDexMethodIndex()); + + { + // The VIXL macro assembler may clobber any of the scratch registers that are available to it, + // so it checks if the application is using them (by passing them to the macro assembler + // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of + // what is available, and is the opposite of the standard usage: Instead of requesting a + // temporary location, it imposes an external constraint (i.e. a specific register is reserved + // for the hidden argument). Note that this works even if VIXL needs a scratch register itself + // (to materialize the constant), since the destination register becomes available for such use + // internally for the duration of the macro instruction. + UseScratchRegisterScope temps(GetVIXLAssembler()); + temps.Exclude(hidden_reg); + __ Mov(hidden_reg, invoke->GetDexMethodIndex()); + } { AssemblerAccurateScope aas(GetVIXLAssembler(), @@ -2016,7 +2045,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimFloat: { // Processing a Dex `float-to-int' instruction. vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0)); - __ Vcvt(I32, F32, temp, InputSRegisterAt(conversion, 0)); + __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0)); __ Vmov(OutputRegister(conversion), temp); break; } @@ -2024,7 +2053,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimDouble: { // Processing a Dex `double-to-int' instruction. vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0)); - __ Vcvt(I32, F64, temp_s, DRegisterFrom(in)); + __ Vcvt(S32, F64, temp_s, DRegisterFrom(in)); __ Vmov(OutputRegister(conversion), temp_s); break; } @@ -2100,7 +2129,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimChar: { // Processing a Dex `int-to-float' instruction. __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0)); - __ Vcvt(F32, I32, OutputSRegister(conversion), OutputSRegister(conversion)); + __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion)); break; } @@ -2131,7 +2160,7 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve case Primitive::kPrimChar: { // Processing a Dex `int-to-double' instruction. __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0)); - __ Vcvt(F64, I32, DRegisterFrom(out), LowSRegisterFrom(out)); + __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out)); break; } @@ -2139,18 +2168,15 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve // Processing a Dex `long-to-double' instruction. vixl32::Register low = LowRegisterFrom(in); vixl32::Register high = HighRegisterFrom(in); - vixl32::SRegister out_s = LowSRegisterFrom(out); vixl32::DRegister out_d = DRegisterFrom(out); - vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0)); vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0)); - - vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(0)); + vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1)); // temp_d = int-to-double(high) __ Vmov(temp_s, high); - __ Vcvt(F64, I32, temp_d, temp_s); + __ Vcvt(F64, S32, temp_d, temp_s); // constant_d = k2Pow32EncodingForDouble __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble)); // out_d = unsigned-to-double(low) @@ -2446,13 +2472,13 @@ void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOpera vixl32::Register dividend = InputRegisterAt(instruction, 0); vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0)); vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1)); - int64_t imm = second.GetConstant()->AsIntConstant()->GetValue(); + int32_t imm = Int32ConstantFrom(second); int64_t magic; int shift; CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift); - __ Mov(temp1, magic); + __ Mov(temp1, Operand::From(magic)); __ Smull(temp2, temp1, dividend, temp1); if (imm > 0 && magic < 0) { @@ -2769,7 +2795,7 @@ void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instructi case Primitive::kPrimShort: case Primitive::kPrimInt: { if (value.IsRegister()) { - __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); } else { DCHECK(value.IsConstant()) << value; if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { @@ -2845,9 +2871,9 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { } // Rotate, or mov to out for zero or word size rotations. if (rot != 0u) { - __ Lsr(out_reg_hi, in_reg_hi, rot); + __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot)); __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot)); - __ Lsr(out_reg_lo, in_reg_lo, rot); + __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot)); __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot)); } else { __ Mov(out_reg_lo, in_reg_lo); @@ -2862,7 +2888,7 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { __ And(shift_right, RegisterFrom(rhs), 0x1F); __ Lsrs(shift_left, RegisterFrom(rhs), 6); // TODO(VIXL): Check that flags are kept after "vixl32::LeaveFlags" enabled. - __ Rsb(shift_left, shift_right, kArmBitsPerWord); + __ Rsb(shift_left, shift_right, Operand::From(kArmBitsPerWord)); __ B(cc, &shift_by_32_plus_shift_right); // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right). @@ -3028,11 +3054,11 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { // Shift the high part __ Lsl(o_h, high, o_l); // Shift the low part and `or` what overflew on the high part - __ Rsb(temp, o_l, kArmBitsPerWord); + __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord)); __ Lsr(temp, low, temp); __ Orr(o_h, o_h, temp); // If the shift is > 32 bits, override the high part - __ Subs(temp, o_l, kArmBitsPerWord); + __ Subs(temp, o_l, Operand::From(kArmBitsPerWord)); { AssemblerAccurateScope guard(GetVIXLAssembler(), 3 * kArmInstrMaxSizeInBytes, @@ -3047,11 +3073,11 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { // Shift the low part __ Lsr(o_l, low, o_h); // Shift the high part and `or` what underflew on the low part - __ Rsb(temp, o_h, kArmBitsPerWord); + __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord)); __ Lsl(temp, high, temp); __ Orr(o_l, o_l, temp); // If the shift is > 32 bits, override the low part - __ Subs(temp, o_h, kArmBitsPerWord); + __ Subs(temp, o_h, Operand::From(kArmBitsPerWord)); { AssemblerAccurateScope guard(GetVIXLAssembler(), 3 * kArmInstrMaxSizeInBytes, @@ -3065,10 +3091,10 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { __ And(o_h, second_reg, kMaxLongShiftDistance); // same as Shr except we use `Lsr`s and not `Asr`s __ Lsr(o_l, low, o_h); - __ Rsb(temp, o_h, kArmBitsPerWord); + __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord)); __ Lsl(temp, high, temp); __ Orr(o_l, o_l, temp); - __ Subs(temp, o_h, kArmBitsPerWord); + __ Subs(temp, o_h, Operand::From(kArmBitsPerWord)); { AssemblerAccurateScope guard(GetVIXLAssembler(), 3 * kArmInstrMaxSizeInBytes, @@ -3211,7 +3237,7 @@ void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConventionARMVIXL calling_convention; - __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -3412,7 +3438,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register ad __ Add(temp, addr, offset); addr = temp; } - __ Ldrexd(out_lo, out_hi, addr); + __ Ldrexd(out_lo, out_hi, MemOperand(addr)); } void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr, @@ -3432,10 +3458,10 @@ void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register a __ Bind(&fail); // We need a load followed by store. (The address used in a STREX instruction must // be the same as the address in the most recently executed LDREX instruction.) - __ Ldrexd(temp1, temp2, addr); + __ Ldrexd(temp1, temp2, MemOperand(addr)); codegen_->MaybeRecordImplicitNullCheck(instruction); - __ Strexd(temp1, value_lo, value_hi, addr); - __ Cbnz(temp1, &fail); + __ Strexd(temp1, value_lo, value_hi, MemOperand(addr)); + __ CompareAndBranchIfNonZero(temp1, &fail); } void LocationsBuilderARMVIXL::HandleFieldSet( @@ -3951,7 +3977,7 @@ void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) { NullCheckSlowPathARMVIXL* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction); AddSlowPath(slow_path); - __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); } void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) { @@ -4164,7 +4190,14 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { vixl32::Register temp = temps.Acquire(); if (has_intermediate_address) { - TODO_VIXL32(FATAL); + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); + DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset); + } + temp = obj; } else { __ Add(temp, obj, data_offset); } @@ -4209,7 +4242,14 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { vixl32::Register temp = temps.Acquire(); if (has_intermediate_address) { - TODO_VIXL32(FATAL); + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); + DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset); + } + temp = obj; } else { __ Add(temp, obj, data_offset); } @@ -4337,7 +4377,14 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { vixl32::Register temp = temps.Acquire(); if (has_intermediate_address) { - TODO_VIXL32(FATAL); + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); + DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset); + } + temp = array; } else { __ Add(temp, array, data_offset); } @@ -4386,7 +4433,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { codegen_->AddSlowPath(slow_path); if (instruction->GetValueCanBeNull()) { vixl32::Label non_zero; - __ Cbnz(value, &non_zero); + __ CompareAndBranchIfNonZero(value, &non_zero); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; @@ -4434,7 +4481,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset); // If heap poisoning is enabled, no need to unpoison // `temp1`, as we are comparing against null below. - __ Cbnz(temp1, slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); __ Bind(&do_put); } else { __ B(ne, slow_path->GetEntryLabel()); @@ -4556,6 +4603,32 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction } } +void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) { + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!kEmitCompilerReadBarrier); + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset())); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) { + vixl32::Register out = OutputRegister(instruction); + vixl32::Register first = InputRegisterAt(instruction, 0); + Location second = instruction->GetLocations()->InAt(1); + + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!kEmitCompilerReadBarrier); + + if (second.IsRegister()) { + __ Add(out, first, RegisterFrom(second)); + } else { + __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue()); + } +} + void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) { RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConventionARMVIXL calling_convention; @@ -4585,11 +4658,11 @@ void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp, bool can_be_null) { vixl32::Label is_null; if (can_be_null) { - __ Cbz(value, &is_null); + __ CompareAndBranchIfZero(value, &is_null); } GetAssembler()->LoadFromOffset( kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value()); - __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); + __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift)); __ Strb(card, MemOperand(card, temp)); if (can_be_null) { __ Bind(&is_null); @@ -4644,10 +4717,10 @@ void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instru GetAssembler()->LoadFromOffset( kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value()); if (successor == nullptr) { - __ Cbnz(temp, slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel()); __ Bind(slow_path->GetReturnLabel()); } else { - __ Cbz(temp, codegen_->GetLabelOf(successor)); + __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor)); __ B(slow_path->GetEntryLabel()); } } @@ -4915,7 +4988,7 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -4947,7 +5020,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value(); GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset); // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); + size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); GenerateGcRootFieldLoad(cls, out_loc, out, offset, kEmitCompilerReadBarrier); generate_null_check = !cls->IsInDexCache(); break; @@ -4962,7 +5035,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); codegen_->AddSlowPath(slow_path); if (generate_null_check) { - __ Cbz(out, slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); } if (cls->MustGenerateClinitCheck()) { GenerateClassInitializationCheck(slow_path, out); @@ -5153,14 +5226,17 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) // Return 0 if `obj` is null. // avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ Cbz(obj, &zero); + __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false); } - // /* HeapReference<Class> */ out = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); - switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); __ Cmp(out, cls); // Classes must be equal for the instanceof to succeed. __ B(ne, &zero); @@ -5170,6 +5246,12 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kAbstractClassCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. vixl32::Label loop; @@ -5177,7 +5259,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) // /* HeapReference<Class> */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); // If `out` is null, we use it for the result, and jump to `done`. - __ Cbz(out, &done); + __ CompareAndBranchIfZero(out, &done, /* far_target */ false); __ Cmp(out, cls); __ B(ne, &loop); __ Mov(out, 1); @@ -5188,6 +5270,12 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kClassHierarchyCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); // Walk over the class hierarchy to find a match. vixl32::Label loop, success; __ Bind(&loop); @@ -5195,7 +5283,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) __ B(eq, &success); // /* HeapReference<Class> */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); - __ Cbnz(out, &loop); + __ CompareAndBranchIfNonZero(out, &loop); // If `out` is null, we use it for the result, and jump to `done`. __ B(&done); __ Bind(&success); @@ -5207,6 +5295,12 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kArrayObjectCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); // Do an exact check. vixl32::Label exact_check; __ Cmp(out, cls); @@ -5215,10 +5309,10 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) // /* HeapReference<Class> */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); // If `out` is null, we use it for the result, and jump to `done`. - __ Cbz(out, &done); + __ CompareAndBranchIfZero(out, &done, /* far_target */ false); GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(out, &zero); + __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false); __ Bind(&exact_check); __ Mov(out, 1); __ B(&done); @@ -5226,6 +5320,12 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kArrayCheck: { + // /* HeapReference<Class> */ out = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + out_loc, + obj_loc, + class_offset, + maybe_temp_loc); __ Cmp(out, cls); DCHECK(locations->OnlyCallsOnSlowPath()); slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction, @@ -5348,7 +5448,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { vixl32::Label done; // Avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ Cbz(obj, &done); + __ CompareAndBranchIfZero(obj, &done, /* far_target */ false); } // /* HeapReference<Class> */ temp = obj->klass_ @@ -5374,7 +5474,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // If the class reference currently in `temp` is null, jump to the slow path to throw the // exception. - __ Cbz(temp, type_check_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel()); // Otherwise, compare the classes. __ Cmp(temp, cls); @@ -5394,7 +5494,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // If the class reference currently in `temp` is null, jump to the slow path to throw the // exception. - __ Cbz(temp, type_check_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel()); // Otherwise, jump to the beginning of the loop. __ B(&loop); break; @@ -5409,12 +5509,12 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // /* HeapReference<Class> */ temp = temp->component_type_ GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); // If the component type is null, jump to the slow path to throw the exception. - __ Cbz(temp, type_check_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel()); // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type` // to further check that this component type is not a primitive type. GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset); static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot"); - __ Cbnz(temp, type_check_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel()); break; } @@ -5491,6 +5591,70 @@ void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); } +void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + DCHECK(instruction->GetResultType() == Primitive::kPrimInt + || instruction->GetResultType() == Primitive::kPrimLong); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + Location out = locations->Out(); + + if (instruction->GetResultType() == Primitive::kPrimInt) { + vixl32::Register first_reg = RegisterFrom(first); + vixl32::Register second_reg = RegisterFrom(second); + vixl32::Register out_reg = RegisterFrom(out); + + switch (instruction->GetOpKind()) { + case HInstruction::kAnd: + __ Bic(out_reg, first_reg, second_reg); + break; + case HInstruction::kOr: + __ Orn(out_reg, first_reg, second_reg); + break; + // There is no EON on arm. + case HInstruction::kXor: + default: + LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); + UNREACHABLE(); + } + return; + + } else { + DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); + vixl32::Register first_low = LowRegisterFrom(first); + vixl32::Register first_high = HighRegisterFrom(first); + vixl32::Register second_low = LowRegisterFrom(second); + vixl32::Register second_high = HighRegisterFrom(second); + vixl32::Register out_low = LowRegisterFrom(out); + vixl32::Register out_high = HighRegisterFrom(out); + + switch (instruction->GetOpKind()) { + case HInstruction::kAnd: + __ Bic(out_low, first_low, second_low); + __ Bic(out_high, first_high, second_high); + break; + case HInstruction::kOr: + __ Orn(out_low, first_low, second_low); + __ Orn(out_high, first_high, second_high); + break; + // There is no EON on arm. + case HInstruction::kXor: + default: + LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); + UNREACHABLE(); + } + } +} + // TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl. void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out, vixl32::Register first, @@ -5858,6 +6022,32 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location __ Blx(lr); } +void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + vixl32::Register res = OutputRegister(instr); + vixl32::Register accumulator = + InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); + vixl32::Register mul_left = + InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex); + vixl32::Register mul_right = + InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex); + + if (instr->GetOpKind() == HInstruction::kAdd) { + __ Mla(res, mul_left, mul_right, accumulator); + } else { + __ Mls(res, mul_left, mul_right, accumulator); + } +} + void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. LOG(FATAL) << "Unreachable"; @@ -5952,6 +6142,8 @@ void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_in vixl32::Register target_address = table_base; __ Add(target_address, table_base, jump_offset); __ Bx(target_address); + + jump_table->EmitTable(codegen_); } } diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index ccd866c367..38c756fb0f 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -112,6 +112,7 @@ class LoadClassSlowPathARMVIXL; M(ArraySet) \ M(Below) \ M(BelowOrEqual) \ + M(BitwiseNegatedRight) \ M(BooleanNot) \ M(BoundsCheck) \ M(BoundType) \ @@ -136,6 +137,7 @@ class LoadClassSlowPathARMVIXL; M(InstanceFieldSet) \ M(InstanceOf) \ M(IntConstant) \ + M(IntermediateAddress) \ M(InvokeInterface) \ M(InvokeStaticOrDirect) \ M(InvokeUnresolved) \ @@ -149,6 +151,7 @@ class LoadClassSlowPathARMVIXL; M(MemoryBarrier) \ M(MonitorOperation) \ M(Mul) \ + M(MultiplyAccumulate) \ M(NativeDebugInfo) \ M(Neg) \ M(NewArray) \ @@ -186,24 +189,33 @@ class LoadClassSlowPathARMVIXL; // TODO: Remove once the VIXL32 backend is implemented completely. #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ M(ArmDexCacheArraysBase) \ - M(BitwiseNegatedRight) \ - M(IntermediateAddress) \ - M(MultiplyAccumulate) \ class CodeGeneratorARMVIXL; class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> { public: + typedef vixl::aarch32::Literal<int32_t> IntLiteral; + explicit JumpTableARMVIXL(HPackedSwitch* switch_instr) - : switch_instr_(switch_instr), table_start_() {} + : switch_instr_(switch_instr), + table_start_(), + bb_addresses_(switch_instr->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + uint32_t num_entries = switch_instr_->GetNumEntries(); + for (uint32_t i = 0; i < num_entries; i++) { + IntLiteral *lit = new IntLiteral(0); + bb_addresses_.emplace_back(lit); + } + } vixl::aarch32::Label* GetTableStartLabel() { return &table_start_; } void EmitTable(CodeGeneratorARMVIXL* codegen); + void FixTable(CodeGeneratorARMVIXL* codegen); private: HPackedSwitch* const switch_instr_; vixl::aarch32::Label table_start_; + ArenaVector<std::unique_ptr<IntLiteral>> bb_addresses_; DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL); }; @@ -357,7 +369,7 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR) ArmVIXLAssembler* GetAssembler() const { return assembler_; } - vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); } + ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); } private: void VisitUnimplemementedInstruction(HInstruction* instruction) { @@ -439,7 +451,8 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, vixl::aarch32::Label* true_target, - vixl::aarch32::Label* false_target); + vixl::aarch32::Label* false_target, + bool far_target = true); void GenerateCompareTestAndBranch(HCondition* condition, vixl::aarch32::Label* true_target, vixl::aarch32::Label* false_target); @@ -492,7 +505,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { const ArmVIXLAssembler& GetAssembler() const OVERRIDE { return assembler_; } - vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); } + ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); } size_t GetWordSize() const OVERRIDE { return kArmWordSize; } @@ -513,7 +526,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; } - void EmitJumpTables(); + void FixJumpTables(); void GenerateMemoryBarrier(MemBarrierKind kind); void Finalize(CodeAllocator* allocator) OVERRIDE; void SetupBlockedRegisters() const OVERRIDE; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 573bb507f2..8f94834333 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -224,7 +224,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); + __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; @@ -1056,7 +1056,7 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, target_type.dex_file, - target_type.type_index)); + target_type.type_index.index_)); } for (const auto& entry : boot_image_address_patches_) { DCHECK(GetCompilerOptions().GetIncludePatchInformation()); @@ -1073,8 +1073,8 @@ CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeStringPa } CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeTypePatch( - const DexFile& dex_file, uint32_t type_index) { - return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_); + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_); } CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeDexCacheArrayPatch( @@ -1117,7 +1117,7 @@ Literal* CodeGeneratorMIPS::DeduplicateBootImageStringLiteral(const DexFile& dex } Literal* CodeGeneratorMIPS::DeduplicateBootImageTypeLiteral(const DexFile& dex_file, - uint32_t type_index) { + dex::TypeIndex type_index) { return boot_image_type_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); @@ -5207,6 +5207,11 @@ HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind( case HLoadString::LoadKind::kDexCacheViaMethod: fallback_load = false; break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + // TODO: implement. + fallback_load = true; + break; } if (fallback_load) { desired_string_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; @@ -5534,7 +5539,7 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -5628,7 +5633,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { base_or_current_method_reg, ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); + size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); GenerateGcRootFieldLoad(cls, out_loc, out, offset); generate_null_check = !cls->IsInDexCache(); } @@ -5970,7 +5975,7 @@ void InstructionCodeGeneratorMIPS::VisitNewArray(HNewArray* instruction) { Register current_method_register = calling_convention.GetRegisterAt(2); __ Lw(current_method_register, SP, kCurrentMethodStackOffset); // Move an uint16_t value to a register. - __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_); codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index e132819c24..e225d20094 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -449,11 +449,11 @@ class CodeGeneratorMIPS : public CodeGenerator { }; PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); - PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, uint32_t type_index); + PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); - Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, uint32_t type_index); + Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); Literal* DeduplicateBootImageAddressLiteral(uint32_t address); void EmitPcRelativeAddressPlaceholder(PcRelativePatchInfo* info, Register out, Register base); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 1a54935a25..02b01c85e5 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -180,7 +180,7 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); + __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; mips64_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); @@ -3157,7 +3157,7 @@ void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -3174,7 +3174,7 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { __ LoadFromOffset(kLoadDoubleword, out, current_method, ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value()); __ LoadFromOffset( - kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); + kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)); // TODO: We will need a read barrier here. if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { DCHECK(cls->CanCallRuntime()); @@ -3382,7 +3382,8 @@ void LocationsBuilderMIPS64::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) { LocationSummary* locations = instruction->GetLocations(); // Move an uint16_t value to a register. - __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), instruction->GetTypeIndex()); + __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), + instruction->GetTypeIndex().index_); codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 2451b8d247..51e902a824 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -265,7 +265,7 @@ class LoadClassSlowPathX86 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ movl(calling_convention.GetRegisterAt(0), Immediate(cls_->GetTypeIndex())); + __ movl(calling_convention.GetRegisterAt(0), Immediate(cls_->GetTypeIndex().index_)); x86_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType, at_, dex_pc_, this); @@ -1012,6 +1012,7 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), constant_area_start_(-1), fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), method_address_offset_(-1) { @@ -4167,7 +4168,7 @@ void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; - __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex())); + __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex().index_)); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -4611,7 +4612,7 @@ void CodeGeneratorX86::RecordBootStringPatch(HLoadString* load_string) { } void CodeGeneratorX86::RecordTypePatch(HLoadClass* load_class) { - type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex()); + type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_); __ Bind(&type_patches_.back().label); } @@ -6059,7 +6060,7 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -6141,7 +6142,8 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { // /* GcRoot<mirror::Class> */ out = out[type_index] GenerateGcRootFieldLoad(cls, out_loc, - Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())), + Address(out, + CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)), /* fixup_label */ nullptr, read_barrier_option); generate_null_check = !cls->IsInDexCache(); @@ -6218,16 +6220,15 @@ HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( break; case HLoadString::LoadKind::kDexCacheViaMethod: break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; } return desired_string_load_kind; } void LocationsBuilderX86::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier) - ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) - ? LocationSummary::kCallOnMainOnly - : LocationSummary::kCallOnSlowPath) - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); HLoadString::LoadKind load_kind = load->GetLoadKind(); if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative || @@ -6252,6 +6253,14 @@ void LocationsBuilderX86::VisitLoadString(HLoadString* load) { } } +Label* CodeGeneratorX86::NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index) { + jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u); + // Add a patch entry and return the label. + jit_string_patches_.emplace_back(dex_file, dex_index); + PatchInfo<Label>* info = &jit_string_patches_.back(); + return &info->label; +} + void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) { LocationSummary* locations = load->GetLocations(); Location out_loc = locations->Out(); @@ -6280,7 +6289,7 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) { Register method_address = locations->InAt(0).AsRegister<Register>(); Address address = Address(method_address, CodeGeneratorX86::kDummy32BitOffset); Label* fixup_label = codegen_->NewStringBssEntryPatch(load); - // /* GcRoot<mirror::Class> */ out = *address /* PC-relative */ + // /* GcRoot<mirror::String> */ out = *address /* PC-relative */ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption); SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load); codegen_->AddSlowPath(slow_path); @@ -6289,6 +6298,14 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) { __ Bind(slow_path->GetExitLabel()); return; } + case HLoadString::LoadKind::kJitTableAddress: { + Address address = Address::Absolute(CodeGeneratorX86::kDummy32BitOffset); + Label* fixup_label = codegen_->NewJitRootStringPatch( + load->GetDexFile(), load->GetStringIndex()); + // /* GcRoot<mirror::String> */ out = *address + GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption); + return; + } default: break; } @@ -6849,24 +6866,24 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { temp_loc, iftable_offset, kWithoutReadBarrier); - // Null iftable means it is empty. - __ testl(temp, temp); - __ j(kZero, type_check_slow_path->GetEntryLabel()); - - // Loop through the iftable and check if any class matches. + // Iftable is never null. __ movl(maybe_temp2_loc.AsRegister<Register>(), Address(temp, array_length_offset)); - + // Loop through the iftable and check if any class matches. NearLabel start_loop; __ Bind(&start_loop); - __ cmpl(cls.AsRegister<Register>(), Address(temp, object_array_data_offset)); - __ j(kEqual, &done); // Return if same class. - // Go to next interface. - __ addl(temp, Immediate(2 * kHeapReferenceSize)); + // Need to subtract first to handle the empty array case. __ subl(maybe_temp2_loc.AsRegister<Register>(), Immediate(2)); - __ j(kNotZero, &start_loop); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister<Register>(), + CodeGeneratorX86::ArrayAddress(temp, + maybe_temp2_loc, + TIMES_4, + object_array_data_offset)); + __ j(kNotEqual, &start_loop); + } else { + __ jmp(type_check_slow_path->GetEntryLabel()); } - - __ jmp(type_check_slow_path->GetEntryLabel()); break; } } @@ -7562,7 +7579,7 @@ class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenera // The value to patch is the distance from the offset in the constant area // from the address computed by the HX86ComputeBaseMethodAddress instruction. int32_t constant_offset = codegen_->ConstantAreaStart() + offset_into_constant_area_; - int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset();; + int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset(); // Patch in the right value. region.StoreUnaligned<int32_t>(pos - 4, relative_position); @@ -7736,6 +7753,20 @@ void CodeGeneratorX86::MoveFromReturnRegister(Location target, Primitive::Type t } } +void CodeGeneratorX86::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { + for (const PatchInfo<Label>& info : jit_string_patches_) { + const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, info.index)); + DCHECK(it != jit_string_roots_.end()); + size_t index_in_table = it->second; + uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; + reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] = + dchecked_integral_cast<uint32_t>(address); + } +} + #undef __ } // namespace x86 diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 164231b4e5..16ea6b55d6 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -414,12 +414,15 @@ class CodeGeneratorX86 : public CodeGenerator { void RecordTypePatch(HLoadClass* load_class); Label* NewStringBssEntryPatch(HLoadString* load_string); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); + Label* NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; // Emit linker patches. void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Emit a write barrier. void MarkGCCard(Register temp, Register card, @@ -616,6 +619,9 @@ class CodeGeneratorX86 : public CodeGenerator { // Type patch locations. ArenaDeque<PatchInfo<Label>> type_patches_; + // Patches for string root accesses in JIT compiled code. + ArenaDeque<PatchInfo<Label>> jit_string_patches_; + // Offset to the start of the constant area in the assembled code. // Used for fixups to the constant area. int32_t constant_area_start_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 2425a4c3cb..34673138bf 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -246,7 +246,8 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(cls_->GetTypeIndex())); + __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), + Immediate(cls_->GetTypeIndex().index_)); x86_64_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType, at_, dex_pc_, @@ -1110,7 +1111,7 @@ void CodeGeneratorX86_64::RecordBootStringPatch(HLoadString* load_string) { } void CodeGeneratorX86_64::RecordTypePatch(HLoadClass* load_class) { - type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex()); + type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_); __ Bind(&type_patches_.back().label); } @@ -1258,7 +1259,8 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister)); } @@ -4097,7 +4099,7 @@ void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) { void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) { InvokeRuntimeCallingConvention calling_convention; codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)), - instruction->GetTypeIndex()); + instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); @@ -5484,7 +5486,7 @@ void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); + codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); return; @@ -5567,7 +5569,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { GenerateGcRootFieldLoad( cls, out_loc, - Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())), + Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)), /* fixup_label */ nullptr, read_barrier_option); generate_null_check = !cls->IsInDexCache(); @@ -5630,16 +5632,15 @@ HLoadString::LoadKind CodeGeneratorX86_64::GetSupportedLoadStringKind( break; case HLoadString::LoadKind::kDexCacheViaMethod: break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; } return desired_string_load_kind; } void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = load->NeedsEnvironment() - ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) - ? LocationSummary::kCallOnMainOnly - : LocationSummary::kCallOnSlowPath) - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) { locations->SetOut(Location::RegisterLocation(RAX)); @@ -5659,6 +5660,14 @@ void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) { } } +Label* CodeGeneratorX86_64::NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index) { + jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u); + // Add a patch entry and return the label. + jit_string_patches_.emplace_back(dex_file, dex_index); + PatchInfo<Label>* info = &jit_string_patches_.back(); + return &info->label; +} + void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) { LocationSummary* locations = load->GetLocations(); Location out_loc = locations->Out(); @@ -5690,6 +5699,15 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) { __ Bind(slow_path->GetExitLabel()); return; } + case HLoadString::LoadKind::kJitTableAddress: { + Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, + /* no_rip */ true); + Label* fixup_label = + codegen_->NewJitRootStringPatch(load->GetDexFile(), load->GetStringIndex()); + // /* GcRoot<mirror::String> */ out = *address + GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption); + return; + } default: break; } @@ -6259,23 +6277,24 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { temp_loc, iftable_offset, kWithoutReadBarrier); - // Null iftable means it is empty. - __ testl(temp, temp); - __ j(kZero, type_check_slow_path->GetEntryLabel()); - - // Loop through the iftable and check if any class matches. + // Iftable is never null. __ movl(maybe_temp2_loc.AsRegister<CpuRegister>(), Address(temp, array_length_offset)); - + // Loop through the iftable and check if any class matches. NearLabel start_loop; __ Bind(&start_loop); - __ cmpl(cls.AsRegister<CpuRegister>(), Address(temp, object_array_data_offset)); - __ j(kEqual, &done); // Return if same class. - // Go to next interface. - __ addl(temp, Immediate(2 * kHeapReferenceSize)); + // Need to subtract first to handle the empty array case. __ subl(maybe_temp2_loc.AsRegister<CpuRegister>(), Immediate(2)); - __ j(kNotZero, &start_loop); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister<CpuRegister>(), + CodeGeneratorX86_64::ArrayAddress(temp, + maybe_temp2_loc, + TIMES_4, + object_array_data_offset)); + __ j(kNotEqual, &start_loop); // Return if same class. + } else { + __ jmp(type_check_slow_path->GetEntryLabel()); } - __ jmp(type_check_slow_path->GetEntryLabel()); break; } @@ -7090,6 +7109,20 @@ void CodeGeneratorX86_64::MoveInt64ToAddress(const Address& addr_low, } } +void CodeGeneratorX86_64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { + for (const PatchInfo<Label>& info : jit_string_patches_) { + const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, info.index)); + DCHECK(it != jit_string_roots_.end()); + size_t index_in_table = it->second; + uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; + reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] = + dchecked_integral_cast<uint32_t>(address); + } +} + #undef __ } // namespace x86_64 diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index e5a4152517..0f70b15787 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -412,11 +412,14 @@ class CodeGeneratorX86_64 : public CodeGenerator { void RecordTypePatch(HLoadClass* load_class); Label* NewStringBssEntryPatch(HLoadString* load_string); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); + Label* NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + const X86_64InstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; } @@ -602,6 +605,9 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Fixups for jump tables need to be handled specially. ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_; + // Patches for string literals in JIT compiled code. + ArenaDeque<PatchInfo<Label>> jit_string_patches_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86_64); }; diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h index 5129dafca1..d3623f17d1 100644 --- a/compiler/optimizing/common_arm.h +++ b/compiler/optimizing/common_arm.h @@ -139,9 +139,14 @@ inline int32_t Int32ConstantFrom(Location location) { HConstant* instr = location.GetConstant(); if (instr->IsIntConstant()) { return instr->AsIntConstant()->GetValue(); - } else { - DCHECK(instr->IsNullConstant()) << instr->DebugName(); + } else if (instr->IsNullConstant()) { return 0; + } else { + DCHECK(instr->IsLongConstant()) << instr->DebugName(); + const int64_t ret = instr->AsLongConstant()->GetValue(); + DCHECK_GE(ret, std::numeric_limits<int32_t>::min()); + DCHECK_LE(ret, std::numeric_limits<int32_t>::max()); + return ret; } } @@ -161,7 +166,7 @@ inline vixl::aarch32::Operand OperandFrom(Location location, Primitive::Type typ if (location.IsRegister()) { return vixl::aarch32::Operand(RegisterFrom(location, type)); } else { - return vixl::aarch32::Operand(Int64ConstantFrom(location)); + return vixl::aarch32::Operand(Int32ConstantFrom(location)); } } diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index 5fac3acb8a..7ef28ed910 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -756,7 +756,7 @@ TEST_F(ConstantFoldingTest, UnsignedComparisonsWithZero) { // Make various unsigned comparisons with zero against a parameter. HInstruction* parameter = new (&allocator_) HParameterValue( - graph_->GetDexFile(), 0, 0, Primitive::kPrimInt, true); + graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt, true); entry_block->AddInstruction(parameter); entry_block->AddInstruction(new (&allocator_) HGoto()); diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc new file mode 100644 index 0000000000..c80e19ef15 --- /dev/null +++ b/compiler/optimizing/escape.cc @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "escape.h" + +#include "nodes.h" + +namespace art { + +void CalculateEscape(HInstruction* reference, + bool (*no_escape)(HInstruction*, HInstruction*), + /*out*/ bool* is_singleton, + /*out*/ bool* is_singleton_and_non_escaping) { + // For references not allocated in the method, don't assume anything. + if (!reference->IsNewInstance() && !reference->IsNewArray()) { + *is_singleton = false; + *is_singleton_and_non_escaping = false; + return; + } + // Assume the best until proven otherwise. + *is_singleton = true; + *is_singleton_and_non_escaping = true; + // Visit all uses to determine if this reference can escape into the heap, + // a method call, an alias, etc. + for (const HUseListNode<HInstruction*>& use : reference->GetUses()) { + HInstruction* user = use.GetUser(); + if (no_escape != nullptr && (*no_escape)(reference, user)) { + // Client supplied analysis says there is no escape. + continue; + } else if (user->IsBoundType() || user->IsNullCheck()) { + // BoundType shouldn't normally be necessary for an allocation. Just be conservative + // for the uncommon cases. Similarly, null checks are eventually eliminated for explicit + // allocations, but if we see one before it is simplified, assume an alias. + *is_singleton = false; + *is_singleton_and_non_escaping = false; + return; + } else if (user->IsPhi() || user->IsSelect() || user->IsInvoke() || + (user->IsInstanceFieldSet() && (reference == user->InputAt(1))) || + (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(1))) || + (user->IsStaticFieldSet() && (reference == user->InputAt(1))) || + (user->IsUnresolvedStaticFieldSet() && (reference == user->InputAt(0))) || + (user->IsArraySet() && (reference == user->InputAt(2)))) { + // The reference is merged to HPhi/HSelect, passed to a callee, or stored to heap. + // Hence, the reference is no longer the only name that can refer to its value. + *is_singleton = false; + *is_singleton_and_non_escaping = false; + return; + } else if ((user->IsUnresolvedInstanceFieldGet() && (reference == user->InputAt(0))) || + (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(0)))) { + // The field is accessed in an unresolved way. We mark the object as a non-singleton. + // Note that we could optimize this case and still perform some optimizations until + // we hit the unresolved access, but the conservative assumption is the simplest. + *is_singleton = false; + *is_singleton_and_non_escaping = false; + return; + } else if (user->IsReturn()) { + *is_singleton_and_non_escaping = false; + } + } + + // Need for further analysis? + if (!*is_singleton_and_non_escaping) { + return; + } + + // Look at the environment uses and if it's for HDeoptimize, it's treated the + // same as a return which escapes at the end of executing the compiled code. + // Other environment uses are fine, as long as all client optimizations that + // rely on this informations are disabled for debuggable. + for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) { + HEnvironment* user = use.GetUser(); + if (user->GetHolder()->IsDeoptimize()) { + *is_singleton_and_non_escaping = false; + break; + } + } +} + +bool IsNonEscapingSingleton(HInstruction* reference, + bool (*no_escape)(HInstruction*, HInstruction*)) { + bool is_singleton = true; + bool is_singleton_and_non_escaping = true; + CalculateEscape(reference, no_escape, &is_singleton, &is_singleton_and_non_escaping); + return is_singleton_and_non_escaping; +} + +} // namespace art diff --git a/compiler/optimizing/escape.h b/compiler/optimizing/escape.h new file mode 100644 index 0000000000..6514843247 --- /dev/null +++ b/compiler/optimizing/escape.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_ESCAPE_H_ +#define ART_COMPILER_OPTIMIZING_ESCAPE_H_ + +namespace art { + +class HInstruction; + +/* + * Methods related to escape analysis, i.e. determining whether an object + * allocation is visible outside ('escapes') its immediate method context. + */ + +/* + * Performs escape analysis on the given instruction, typically a reference to an + * allocation. The method assigns true to parameter 'is_singleton' if the reference + * is the only name that can refer to its value during the lifetime of the method, + * meaning that the reference is not aliased with something else, is not stored to + * heap memory, and not passed to another method. The method assigns true to parameter + * 'is_singleton_and_non_escaping' if the reference is a singleton and is not returned + * to the caller or used as an environment local of an HDeoptimize instruction. + * + * When set, the no_escape function is applied to any use of the allocation instruction + * prior to any built-in escape analysis. This allows clients to define better escape + * analysis in certain case-specific circumstances. If 'no_escape(reference, user)' + * returns true, the user is assumed *not* to cause any escape right away. The return + * value false means the client cannot provide a definite answer and built-in escape + * analysis is applied to the user instead. + */ +void CalculateEscape(HInstruction* reference, + bool (*no_escape)(HInstruction*, HInstruction*), + /*out*/ bool* is_singleton, + /*out*/ bool* is_singleton_and_non_escaping); + +/* + * Convenience method for testing singleton and non-escaping property at once. + * Callers should be aware that this method invokes the full analysis at each call. + */ +bool IsNonEscapingSingleton(HInstruction* reference, + bool (*no_escape)(HInstruction*, HInstruction*)); + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_ESCAPE_H_ diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc index 6abf00e21a..437d35ccb7 100644 --- a/compiler/optimizing/gvn_test.cc +++ b/compiler/optimizing/gvn_test.cc @@ -35,7 +35,7 @@ TEST_F(GVNTest, LocalFieldElimination) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(), - 0, + dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); @@ -120,7 +120,7 @@ TEST_F(GVNTest, GlobalFieldElimination) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(), - 0, + dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); @@ -204,7 +204,7 @@ TEST_F(GVNTest, LoopFieldElimination) { graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(), - 0, + dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); @@ -352,7 +352,7 @@ TEST_F(GVNTest, LoopSideEffects) { inner_loop_exit->AddSuccessor(outer_loop_header); HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(), - 0, + dex::TypeIndex(0), 0, Primitive::kPrimBoolean); entry->AddInstruction(parameter); diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index 031f1d74a8..3425b88260 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -80,7 +80,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest { // Provide entry and exit instructions. parameter_ = new (&allocator_) HParameterValue( - graph_->GetDexFile(), 0, 0, Primitive::kPrimNot, true); + graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot, true); entry_->AddInstruction(parameter_); constant0_ = graph_->GetIntConstant(0); constant1_ = graph_->GetIntConstant(1); diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index 8bbdd4acb7..4c99e3cb6e 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -62,9 +62,15 @@ class InductionVarRangeTest : public CommonCompilerTest { graph_->SetEntryBlock(entry_block_); graph_->SetExitBlock(exit_block_); // Two parameters. - x_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); + x_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + Primitive::kPrimInt); entry_block_->AddInstruction(x_); - y_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); + y_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + Primitive::kPrimInt); entry_block_->AddInstruction(y_); // Set arbitrary range analysis hint while testing private methods. SetHint(x_); @@ -572,7 +578,8 @@ TEST_F(InductionVarRangeTest, ArrayLengthAndHints) { HInstruction* new_array = new (&allocator_) HNewArray(x_, graph_->GetCurrentMethod(), - 0, Primitive::kPrimInt, + 0, + dex::TypeIndex(Primitive::kPrimInt), graph_->GetDexFile(), kQuickAllocArray); entry_block_->AddInstruction(new_array); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 7fe54b9045..16a465a43d 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -197,15 +197,15 @@ static uint32_t FindMethodIndexIn(ArtMethod* method, } } -static uint32_t FindClassIndexIn(mirror::Class* cls, - const DexFile& dex_file, - Handle<mirror::DexCache> dex_cache) +static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, + const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { - uint32_t index = DexFile::kDexNoIndex; + dex::TypeIndex index; if (cls->GetDexCache() == nullptr) { DCHECK(cls->IsArrayClass()) << cls->PrettyClass(); index = cls->FindTypeIndexInOtherDexFile(dex_file); - } else if (cls->GetDexTypeIndex() == DexFile::kDexNoIndex16) { + } else if (!cls->GetDexTypeIndex().IsValid()) { DCHECK(cls->IsProxyClass()) << cls->PrettyClass(); // TODO: deal with proxy classes. } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) { @@ -223,8 +223,8 @@ static uint32_t FindClassIndexIn(mirror::Class* cls, // We cannot guarantee the entry in the dex cache will resolve to the same class, // as there may be different class loaders. So only return the index if it's // the right class in the dex cache already. - if (index != DexFile::kDexNoIndex && dex_cache->GetResolvedType(index) != cls) { - index = DexFile::kDexNoIndex; + if (index.IsValid() && dex_cache->GetResolvedType(index) != cls) { + index = dex::TypeIndex::Invalid(); } } @@ -363,9 +363,9 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, << invoke_instruction->DebugName(); const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); - uint32_t class_index = FindClassIndexIn( + dex::TypeIndex class_index = FindClassIndexIn( ic.GetMonomorphicType(), caller_dex_file, caller_compilation_unit_.GetDexCache()); - if (class_index == DexFile::kDexNoIndex) { + if (!class_index.IsValid()) { VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method) << " from inline cache is not inlined because its class is not" << " accessible to the caller"; @@ -417,7 +417,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, HInstruction* cursor, HBasicBlock* bb_cursor, - uint32_t class_index, + dex::TypeIndex class_index, bool is_referrer, HInstruction* invoke_instruction, bool with_deoptimization) { @@ -489,10 +489,10 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - uint32_t class_index = FindClassIndexIn( + dex::TypeIndex class_index = FindClassIndexIn( ic.GetTypeAt(i), caller_dex_file, caller_compilation_unit_.GetDexCache()); HInstruction* return_replacement = nullptr; - if (class_index == DexFile::kDexNoIndex || + if (!class_index.IsValid() || !TryBuildAndInline(invoke_instruction, method, &return_replacement)) { all_targets_inlined = false; } else { diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index a1dcd58a84..682393e697 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_INLINER_H_ #define ART_COMPILER_OPTIMIZING_INLINER_H_ +#include "dex_file_types.h" #include "invoke_type.h" #include "optimization.h" @@ -150,7 +151,7 @@ class HInliner : public HOptimization { HInstruction* AddTypeGuard(HInstruction* receiver, HInstruction* cursor, HBasicBlock* bb_cursor, - uint32_t class_index, + dex::TypeIndex class_index, bool is_referrer, HInstruction* invoke_instruction, bool with_deoptimization) diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index c8c4ca76fd..40de5ce0cc 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -908,7 +908,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, false /* is_unresolved */); } -bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) { +bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); @@ -1004,7 +1004,7 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass())); // The index at which the method's class is stored in the DexCache's type array. - uint32_t storage_index = DexFile::kDexNoIndex; + dex::TypeIndex storage_index; bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get()); if (is_outer_class) { storage_index = outer_class->GetDexTypeIndex(); @@ -1021,7 +1021,7 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( if (IsInitialized(resolved_method_class)) { *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; - } else if (storage_index != DexFile::kDexNoIndex) { + } else if (storage_index.IsValid()) { *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit; HLoadClass* load_class = new (arena_) HLoadClass( graph_->GetCurrentMethod(), @@ -1297,7 +1297,7 @@ mirror::Class* HInstructionBuilder::GetCompilingClass() const { return GetClassFrom(compiler_driver_, *dex_compilation_unit_); } -bool HInstructionBuilder::IsOutermostCompilingClass(uint16_t type_index) const { +bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) const { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<3> hs(soa.Self()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); @@ -1360,7 +1360,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); // The index at which the field's class is stored in the DexCache's type array. - uint32_t storage_index; + dex::TypeIndex storage_index; bool is_outer_class = (outer_class.Get() == resolved_field->GetDeclaringClass()); if (is_outer_class) { storage_index = outer_class->GetDexTypeIndex(); @@ -1497,7 +1497,7 @@ void HInstructionBuilder::BuildArrayAccess(const Instruction& instruction, } void HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc, - uint32_t type_index, + dex::TypeIndex type_index, uint32_t number_of_vreg_arguments, bool is_range, uint32_t* args, @@ -1644,7 +1644,7 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, - uint16_t type_index, + dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); @@ -1684,14 +1684,14 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, } } -bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, +bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, Handle<mirror::DexCache> dex_cache, bool* finalizable) const { return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks( dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable); } -bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const { +bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const { ScopedObjectAccess soa(Thread::Current()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); return NeedsAccessCheck(type_index, dex_cache, finalizable); @@ -1723,7 +1723,10 @@ uint16_t HInstructionBuilder::LookupQuickenedInfo(uint32_t dex_pc) { if (dex_pc_in_map == dex_pc) { return value_in_map; } else { - skipped_interpreter_metadata_.Put(dex_pc_in_map, value_in_map); + // Overwrite and not Put, as quickened CHECK-CAST has two entries with + // the same dex_pc. This is OK, because the compiler does not care about those + // entries. + skipped_interpreter_metadata_.Overwrite(dex_pc_in_map, value_in_map); } } } @@ -2446,7 +2449,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::NEW_INSTANCE: { - if (!BuildNewInstance(instruction.VRegB_21c(), dex_pc)) { + if (!BuildNewInstance(dex::TypeIndex(instruction.VRegB_21c()), dex_pc)) { return false; } UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); @@ -2454,7 +2457,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::NEW_ARRAY: { - uint16_t type_index = instruction.VRegC_22c(); + dex::TypeIndex type_index(instruction.VRegC_22c()); HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt); bool finalizable; QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable) @@ -2472,7 +2475,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::FILLED_NEW_ARRAY: { uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); - uint32_t type_index = instruction.VRegB_35c(); + dex::TypeIndex type_index(instruction.VRegB_35c()); uint32_t args[5]; instruction.GetVarArgs(args); BuildFilledNewArray(dex_pc, type_index, number_of_vreg_arguments, false, args, 0); @@ -2481,7 +2484,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::FILLED_NEW_ARRAY_RANGE: { uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); - uint32_t type_index = instruction.VRegB_3rc(); + dex::TypeIndex type_index(instruction.VRegB_3rc()); uint32_t register_index = instruction.VRegC_3rc(); BuildFilledNewArray( dex_pc, type_index, number_of_vreg_arguments, true, nullptr, register_index); @@ -2638,7 +2641,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::CONST_CLASS: { - uint16_t type_index = instruction.VRegB_21c(); + dex::TypeIndex type_index(instruction.VRegB_21c()); // `CanAccessTypeWithoutChecks` will tell whether the method being // built is trying to access its own class, so that the generated // code can optimize for this case. However, the optimization does not @@ -2679,14 +2682,14 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::INSTANCE_OF: { uint8_t destination = instruction.VRegA_22c(); uint8_t reference = instruction.VRegB_22c(); - uint16_t type_index = instruction.VRegC_22c(); + dex::TypeIndex type_index(instruction.VRegC_22c()); BuildTypeCheck(instruction, destination, reference, type_index, dex_pc); break; } case Instruction::CHECK_CAST: { uint8_t reference = instruction.VRegA_21c(); - uint16_t type_index = instruction.VRegB_21c(); + dex::TypeIndex type_index(instruction.VRegB_21c()); BuildTypeCheck(instruction, -1, reference, type_index, dex_pc); break; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index aa34ddd1d1..f29e522040 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -20,6 +20,7 @@ #include "base/arena_containers.h" #include "base/arena_object.h" #include "block_builder.h" +#include "dex_file_types.h" #include "driver/compiler_driver.h" #include "driver/compiler_driver-inl.h" #include "driver/dex_compilation_unit.h" @@ -100,11 +101,11 @@ class HInstructionBuilder : public ValueObject { // Returns whether the current method needs access check for the type. // Output parameter finalizable is set to whether the type is finalizable. - bool NeedsAccessCheck(uint32_t type_index, + bool NeedsAccessCheck(dex::TypeIndex type_index, Handle<mirror::DexCache> dex_cache, /*out*/bool* finalizable) const REQUIRES_SHARED(Locks::mutator_lock_); - bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const; + bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const; template<typename T> void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc); @@ -176,7 +177,7 @@ class HInstructionBuilder : public ValueObject { // Builds a new array node and the instructions that fill it. void BuildFilledNewArray(uint32_t dex_pc, - uint32_t type_index, + dex::TypeIndex type_index, uint32_t number_of_vreg_arguments, bool is_range, uint32_t* args, @@ -205,7 +206,7 @@ class HInstructionBuilder : public ValueObject { void BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, - uint16_t type_index, + dex::TypeIndex type_index, uint32_t dex_pc); // Builds an instruction sequence for a switch statement. @@ -218,7 +219,7 @@ class HInstructionBuilder : public ValueObject { mirror::Class* GetCompilingClass() const; // Returns whether `type_index` points to the outer-most compiling method's class. - bool IsOutermostCompilingClass(uint16_t type_index) const; + bool IsOutermostCompilingClass(dex::TypeIndex type_index) const; void PotentiallySimplifyFakeString(uint16_t original_dex_register, uint32_t dex_pc, @@ -258,7 +259,7 @@ class HInstructionBuilder : public ValueObject { REQUIRES_SHARED(Locks::mutator_lock_); // Build a HNewInstance instruction. - bool BuildNewInstance(uint16_t type_index, uint32_t dex_pc); + bool BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc); // Return whether the compiler can assume `cls` is initialized. bool IsInitialized(Handle<mirror::Class> cls) const diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 451abc56d3..17a97da6cc 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -2165,11 +2165,11 @@ void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) { __ Cbz(dst, slow_path->GetEntryLabel()); if (!length.IsConstant()) { - // If the length is negative, bail out. - __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel()); - // If the length > 32 then (currently) prefer libcore's native implementation. + // Merge the following two comparisons into one: + // If the length is negative, bail out (delegate to libcore's native implementation). + // If the length > 32 then (currently) prefer libcore's native implementation. __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold); - __ B(slow_path->GetEntryLabel(), gt); + __ B(slow_path->GetEntryLabel(), hi); } else { // We have already checked in the LocationsBuilder for the constant case. DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0); @@ -2379,11 +2379,11 @@ void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) { if (!length.IsConstant() && !optimizations.GetCountIsSourceLength() && !optimizations.GetCountIsDestinationLength()) { - // If the length is negative, bail out. - __ Tbnz(WRegisterFrom(length), kWRegSize - 1, intrinsic_slow_path->GetEntryLabel()); - // If the length >= 128 then (currently) prefer native implementation. + // Merge the following two comparisons into one: + // If the length is negative, bail out (delegate to libcore's native implementation). + // If the length >= 128 then (currently) prefer native implementation. __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold); - __ B(intrinsic_slow_path->GetEntryLabel(), ge); + __ B(intrinsic_slow_path->GetEntryLabel(), hs); } // Validity checks: source. CheckSystemArrayCopyPosition(masm, diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index e4bef3446c..c8e3534164 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -303,7 +303,7 @@ static void GenNumberOfLeadingZeros(LocationSummary* locations, vixl32::Register in_reg_hi = HighRegisterFrom(in); vixl32::Label end; __ Clz(out, in_reg_hi); - __ Cbnz(in_reg_hi, &end); + __ CompareAndBranchIfNonZero(in_reg_hi, &end, /* far_target */ false); __ Clz(out, in_reg_lo); __ Add(out, out, 32); __ Bind(&end); @@ -345,7 +345,7 @@ static void GenNumberOfTrailingZeros(LocationSummary* locations, vixl32::Label end; __ Rbit(out, in_reg_lo); __ Clz(out, out); - __ Cbnz(in_reg_lo, &end); + __ CompareAndBranchIfNonZero(in_reg_lo, &end, /* far_target */ false); __ Rbit(out, in_reg_hi); __ Clz(out, out); __ Add(out, out, 32); @@ -518,7 +518,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) { void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); // Ignore upper 4B of long address. - __ Ldrsb(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Ldrsb(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) { @@ -528,7 +528,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); // Ignore upper 4B of long address. - __ Ldr(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Ldr(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) { @@ -545,9 +545,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) { vixl32::Register hi = HighRegisterFrom(invoke->GetLocations()->Out()); if (addr.Is(lo)) { __ Ldr(hi, MemOperand(addr, 4)); - __ Ldr(lo, addr); + __ Ldr(lo, MemOperand(addr)); } else { - __ Ldr(lo, addr); + __ Ldr(lo, MemOperand(addr)); __ Ldr(hi, MemOperand(addr, 4)); } } @@ -559,7 +559,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekShortNative(HInvoke* invok void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); // Ignore upper 4B of long address. - __ Ldrsh(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Ldrsh(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) { @@ -576,7 +576,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) { void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); - __ Strb(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Strb(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) { @@ -585,7 +585,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); - __ Str(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Str(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) { @@ -598,7 +598,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) { vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0)); // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor // exception. So we can't use ldrd as addr may be unaligned. - __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), addr); + __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr)); __ Str(HighRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr, 4)); } @@ -608,7 +608,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeShortNative(HInvoke* invok void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) { ArmVIXLAssembler* assembler = GetAssembler(); - __ Strh(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0))); + __ Strh(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0)))); } void IntrinsicLocationsBuilderARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) { @@ -842,8 +842,8 @@ static void GenUnsafePut(LocationSummary* locations, __ Add(temp_reg, base, offset); vixl32::Label loop_head; __ Bind(&loop_head); - __ Ldrexd(temp_lo, temp_hi, temp_reg); - __ Strexd(temp_lo, value_lo, value_hi, temp_reg); + __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg)); + __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg)); __ Cmp(temp_lo, 0); __ B(ne, &loop_head); } else { @@ -1042,7 +1042,7 @@ static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARMVIXL* vixl32::Label loop_head; __ Bind(&loop_head); - __ Ldrex(tmp, tmp_ptr); + __ Ldrex(tmp, MemOperand(tmp_ptr)); __ Subs(tmp, tmp, expected); @@ -1052,7 +1052,7 @@ static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARMVIXL* CodeBufferCheckScope::kMaximumSize); __ itt(eq); - __ strex(eq, tmp, value, tmp_ptr); + __ strex(eq, tmp, value, MemOperand(tmp_ptr)); __ cmp(eq, tmp, 1); } @@ -1158,7 +1158,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { if (can_slow_path) { slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke); codegen_->AddSlowPath(slow_path); - __ Cbz(arg, slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel()); } // Reference equality check, return 0 if same reference. @@ -1191,7 +1191,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { } // Shorter string is empty? - __ Cbz(temp0, &end); + // Note that mirror::kUseStringCompression==true introduces lots of instructions, + // which makes &end label far away from this branch and makes it not 'CBZ-encodable'. + __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression); if (mirror::kUseStringCompression) { // Check if both strings using same compression style to use this comparison loop. @@ -1218,7 +1220,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { static_assert(IsAligned<8>(kObjectAlignment), "String data must be 8-byte aligned for unrolled CompareTo loop."); - const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + const unsigned char_size = Primitive::ComponentSize(Primitive::kPrimChar); DCHECK_EQ(char_size, 2u); UseScratchRegisterScope temps(assembler->GetVIXLAssembler()); @@ -1281,20 +1283,20 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`. // The compression flag is now in the highest bit of temp3, so let's play some tricks. - __ orr(temp3, temp3, 0xffu << 23); // uncompressed ? 0xff800000u : 0x7ff80000u - __ bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u) + __ Orr(temp3, temp3, 0xffu << 23); // uncompressed ? 0xff800000u : 0x7ff80000u + __ Bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3)); // &= ~(uncompressed ? 0xfu : 0x7u) __ Asr(temp3, temp3, 7u); // uncompressed ? 0xffff0000u : 0xff0000u. __ Lsr(temp2, temp2, temp1); // Extract second character. __ Lsr(temp3, temp3, 16u); // uncompressed ? 0xffffu : 0xffu __ Lsr(out, temp_reg, temp1); // Extract first character. - __ and_(temp2, temp2, temp3); - __ and_(out, out, temp3); + __ And(temp2, temp2, temp3); + __ And(out, out, temp3); } else { - __ bic(temp1, temp1, 0xf); + __ Bic(temp1, temp1, 0xf); __ Lsr(temp2, temp2, temp1); __ Lsr(out, temp_reg, temp1); - __ movt(temp2, 0); - __ movt(out, 0); + __ Movt(temp2, 0); + __ Movt(out, 0); } __ Sub(out, out, temp2); @@ -1313,10 +1315,10 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { // need to treat as unsigned. Start by freeing the bit with an ADD and continue // further down by a LSRS+SBC which will flip the meaning of the flag but allow // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition. - __ add(temp0, temp0, temp0); // Unlike LSL, this ADD is always 16-bit. + __ Add(temp0, temp0, temp0); // Unlike LSL, this ADD is always 16-bit. // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer. - __ mov(temp1, str); - __ mov(temp2, arg); + __ Mov(temp1, str); + __ Mov(temp2, arg); __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag. { AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), @@ -1326,11 +1328,11 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { __ mov(cs, temp1, arg); // Preserves flags. __ mov(cs, temp2, str); // Preserves flags. } - __ sbc(temp0, temp0, 0); // Complete the move of the compression flag. + __ Sbc(temp0, temp0, 0); // Complete the move of the compression flag. // Adjust temp1 and temp2 from string pointers to data pointers. - __ add(temp1, temp1, value_offset); - __ add(temp2, temp2, value_offset); + __ Add(temp1, temp1, value_offset); + __ Add(temp2, temp2, value_offset); vixl32::Label different_compression_loop; vixl32::Label different_compression_diff; @@ -1340,7 +1342,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { __ Bind(&different_compression_loop); __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex)); __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex)); - __ cmp(temp_reg, temp3); + __ Cmp(temp_reg, temp3); __ B(ne, &different_compression_diff); __ Subs(temp0, temp0, 2); __ B(hi, &different_compression_loop); @@ -1414,7 +1416,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { StringEqualsOptimizations optimizations(invoke); if (!optimizations.GetArgumentNotNull()) { // Check if input is null, return false if it is. - __ Cbz(arg, &return_false); + __ CompareAndBranchIfZero(arg, &return_false, /* far_target */ false); } // Reference equality check, return true if same reference. @@ -1442,7 +1444,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { // Return true if both strings are empty. Even with string compression `count == 0` means empty. static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); - __ Cbz(temp, &return_true); + __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false); // Assertions that must hold in order to compare strings 4 bytes at a time. DCHECK_ALIGNED(value_offset, 4); @@ -1467,7 +1469,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { __ Bind(&loop); __ Ldr(out, MemOperand(str, temp1)); __ Ldr(temp2, MemOperand(arg, temp1)); - __ Add(temp1, temp1, sizeof(uint32_t)); + __ Add(temp1, temp1, Operand::From(sizeof(uint32_t))); __ Cmp(out, temp2); __ B(ne, &return_false); // With string compression, we have compared 4 bytes, otherwise 2 chars. @@ -1718,7 +1720,7 @@ static void CheckPosition(ArmVIXLAssembler* assembler, } else if (length_is_input_length) { // The only way the copy can succeed is if pos is zero. vixl32::Register pos_reg = RegisterFrom(pos); - __ Cbnz(pos_reg, slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel()); } else { // Check that pos >= 0. vixl32::Register pos_reg = RegisterFrom(pos); @@ -1815,12 +1817,12 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { if (!optimizations.GetSourceIsNotNull()) { // Bail out if the source is null. - __ Cbz(src, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel()); } if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) { // Bail out if the destination is null. - __ Cbz(dest, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel()); } // If the length is negative, bail out. @@ -1865,13 +1867,13 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // /* HeapReference<Class> */ temp1 = temp1->component_type_ codegen_->GenerateFieldLoadWithBakerReadBarrier( invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false); - __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel()); // If heap poisoning is enabled, `temp1` has been unpoisoned // by the the previous call to GenerateFieldLoadWithBakerReadBarrier. // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_); __ Ldrh(temp1, MemOperand(temp1, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel()); } // /* HeapReference<Class> */ temp1 = dest->klass_ @@ -1889,13 +1891,13 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // /* HeapReference<Class> */ temp2 = temp1->component_type_ codegen_->GenerateFieldLoadWithBakerReadBarrier( invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false); - __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel()); // If heap poisoning is enabled, `temp2` has been unpoisoned // by the the previous call to GenerateFieldLoadWithBakerReadBarrier. // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_); __ Ldrh(temp2, MemOperand(temp2, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel()); } // For the same reason given earlier, `temp1` is not trashed by the @@ -1918,7 +1920,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // comparison with null below, and this reference is not // kept afterwards. __ Ldr(temp1, MemOperand(temp1, super_offset)); - __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel()); __ Bind(&do_copy); } else { __ B(ne, intrinsic_slow_path->GetEntryLabel()); @@ -1944,24 +1946,24 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // Bail out if the destination is not a non primitive array. // /* HeapReference<Class> */ temp3 = temp1->component_type_ __ Ldr(temp3, MemOperand(temp1, component_offset)); - __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel()); assembler->MaybeUnpoisonHeapReference(temp3); // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_); __ Ldrh(temp3, MemOperand(temp3, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel()); } if (!optimizations.GetSourceIsNonPrimitiveArray()) { // Bail out if the source is not a non primitive array. // /* HeapReference<Class> */ temp3 = temp2->component_type_ __ Ldr(temp3, MemOperand(temp2, component_offset)); - __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel()); assembler->MaybeUnpoisonHeapReference(temp3); // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_); __ Ldrh(temp3, MemOperand(temp3, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel()); } __ Cmp(temp1, temp2); @@ -1978,7 +1980,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // /* HeapReference<Class> */ temp1 = temp1->super_class_ __ Ldr(temp1, MemOperand(temp1, super_offset)); // No need to unpoison the result, we're comparing against null. - __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel()); __ Bind(&do_copy); } else { __ B(ne, intrinsic_slow_path->GetEntryLabel()); @@ -1994,7 +1996,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // /* HeapReference<Class> */ temp3 = temp1->component_type_ codegen_->GenerateFieldLoadWithBakerReadBarrier( invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false); - __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel()); // If heap poisoning is enabled, `temp3` has been unpoisoned // by the the previous call to GenerateFieldLoadWithBakerReadBarrier. } else { @@ -2003,13 +2005,13 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { assembler->MaybeUnpoisonHeapReference(temp1); // /* HeapReference<Class> */ temp3 = temp1->component_type_ __ Ldr(temp3, MemOperand(temp1, component_offset)); - __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel()); assembler->MaybeUnpoisonHeapReference(temp3); } // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_); __ Ldrh(temp3, MemOperand(temp3, primitive_offset)); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel()); + __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel()); } int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot); diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index 2a62643465..8c34dc6a86 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -63,7 +63,10 @@ class LICMTest : public CommonCompilerTest { return_->AddSuccessor(exit_); // Provide boiler-plate instructions. - parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); + parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + Primitive::kPrimNot); entry_->AddInstruction(parameter_); int_constant_ = graph_->GetIntConstant(42); float_constant_ = graph_->GetFloatConstant(42.0f); diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 15e605971e..edecf17f33 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -15,6 +15,8 @@ */ #include "load_store_elimination.h" + +#include "escape.h" #include "side_effects_analysis.h" #include <iostream> @@ -31,70 +33,12 @@ constexpr size_t kMaxNumberOfHeapLocations = 32; // whether it's a singleton, returned, etc. class ReferenceInfo : public ArenaObject<kArenaAllocMisc> { public: - ReferenceInfo(HInstruction* reference, size_t pos) : reference_(reference), position_(pos) { - is_singleton_ = true; - is_singleton_and_non_escaping_ = true; - if (!reference_->IsNewInstance() && !reference_->IsNewArray()) { - // For references not allocated in the method, don't assume anything. - is_singleton_ = false; - is_singleton_and_non_escaping_ = false; - return; - } - - // Visit all uses to determine if this reference can spread into the heap, - // a method call, etc. - for (const HUseListNode<HInstruction*>& use : reference_->GetUses()) { - HInstruction* user = use.GetUser(); - DCHECK(!user->IsNullCheck()) << "NullCheck should have been eliminated"; - if (user->IsBoundType()) { - // BoundType shouldn't normally be necessary for a NewInstance. - // Just be conservative for the uncommon cases. - is_singleton_ = false; - is_singleton_and_non_escaping_ = false; - return; - } - if (user->IsPhi() || user->IsSelect() || user->IsInvoke() || - (user->IsInstanceFieldSet() && (reference_ == user->InputAt(1))) || - (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(1))) || - (user->IsStaticFieldSet() && (reference_ == user->InputAt(1))) || - (user->IsUnresolvedStaticFieldSet() && (reference_ == user->InputAt(0))) || - (user->IsArraySet() && (reference_ == user->InputAt(2)))) { - // reference_ is merged to HPhi/HSelect, passed to a callee, or stored to heap. - // reference_ isn't the only name that can refer to its value anymore. - is_singleton_ = false; - is_singleton_and_non_escaping_ = false; - return; - } - if ((user->IsUnresolvedInstanceFieldGet() && (reference_ == user->InputAt(0))) || - (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(0)))) { - // The field is accessed in an unresolved way. We mark the object as a non-singleton - // to disable load/store optimizations on it. - // Note that we could optimize this case and still perform some optimizations until - // we hit the unresolved access, but disabling is the simplest. - is_singleton_ = false; - is_singleton_and_non_escaping_ = false; - return; - } - if (user->IsReturn()) { - is_singleton_and_non_escaping_ = false; - } - } - - if (!is_singleton_ || !is_singleton_and_non_escaping_) { - return; - } - - // Look at Environment uses and if it's for HDeoptimize, it's treated the same - // as a return which escapes at the end of executing the compiled code. We don't - // do store elimination for singletons that escape through HDeoptimize. - // Other Environment uses are fine since LSE is disabled for debuggable. - for (const HUseListNode<HEnvironment*>& use : reference_->GetEnvUses()) { - HEnvironment* user = use.GetUser(); - if (user->GetHolder()->IsDeoptimize()) { - is_singleton_and_non_escaping_ = false; - break; - } - } + ReferenceInfo(HInstruction* reference, size_t pos) + : reference_(reference), + position_(pos), + is_singleton_(true), + is_singleton_and_non_escaping_(true) { + CalculateEscape(reference_, nullptr, &is_singleton_, &is_singleton_and_non_escaping_); } HInstruction* GetReference() const { diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc index 7805a69a06..9a6b4935b2 100644 --- a/compiler/optimizing/loop_optimization_test.cc +++ b/compiler/optimizing/loop_optimization_test.cc @@ -48,7 +48,10 @@ class LoopOptimizationTest : public CommonCompilerTest { graph_->AddBlock(exit_block_); graph_->SetEntryBlock(entry_block_); graph_->SetExitBlock(exit_block_); - parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt); + parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + Primitive::kPrimInt); entry_block_->AddInstruction(parameter_); return_block_->AddInstruction(new (&allocator_) HReturnVoid()); exit_block_->AddInstruction(new (&allocator_) HExit()); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e0c582a76d..eebc49c991 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -29,6 +29,7 @@ #include "base/stl_util.h" #include "base/transform_array_ref.h" #include "dex_file.h" +#include "dex_file_types.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" @@ -800,7 +801,7 @@ class TryCatchInformation : public ArenaObject<kArenaAllocTryCatchInfo> { } // Catch block information constructor. - TryCatchInformation(uint16_t catch_type_index, const DexFile& dex_file) + TryCatchInformation(dex::TypeIndex catch_type_index, const DexFile& dex_file) : try_entry_(nullptr), catch_dex_file_(&dex_file), catch_type_index_(catch_type_index) {} @@ -816,10 +817,10 @@ class TryCatchInformation : public ArenaObject<kArenaAllocTryCatchInfo> { bool IsCatchAllTypeIndex() const { DCHECK(IsCatchBlock()); - return catch_type_index_ == DexFile::kDexNoIndex16; + return !catch_type_index_.IsValid(); } - uint16_t GetCatchTypeIndex() const { + dex::TypeIndex GetCatchTypeIndex() const { DCHECK(IsCatchBlock()); return catch_type_index_; } @@ -836,7 +837,7 @@ class TryCatchInformation : public ArenaObject<kArenaAllocTryCatchInfo> { // Exception type information. Only set for catch blocks. const DexFile* catch_dex_file_; - const uint16_t catch_type_index_; + const dex::TypeIndex catch_type_index_; }; static constexpr size_t kNoLifetime = -1; @@ -3671,7 +3672,7 @@ class HNewInstance FINAL : public HExpression<2> { HNewInstance(HInstruction* cls, HCurrentMethod* current_method, uint32_t dex_pc, - uint16_t type_index, + dex::TypeIndex type_index, const DexFile& dex_file, bool needs_access_check, bool finalizable, @@ -3686,7 +3687,7 @@ class HNewInstance FINAL : public HExpression<2> { SetRawInputAt(1, current_method); } - uint16_t GetTypeIndex() const { return type_index_; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } // Calls runtime so needs an environment. @@ -3719,7 +3720,7 @@ class HNewInstance FINAL : public HExpression<2> { static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - const uint16_t type_index_; + const dex::TypeIndex type_index_; const DexFile& dex_file_; QuickEntrypointEnum entrypoint_; @@ -4265,7 +4266,7 @@ class HNewArray FINAL : public HExpression<2> { HNewArray(HInstruction* length, HCurrentMethod* current_method, uint32_t dex_pc, - uint16_t type_index, + dex::TypeIndex type_index, const DexFile& dex_file, QuickEntrypointEnum entrypoint) : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), @@ -4276,7 +4277,7 @@ class HNewArray FINAL : public HExpression<2> { SetRawInputAt(1, current_method); } - uint16_t GetTypeIndex() const { return type_index_; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } // Calls runtime so needs an environment. @@ -4292,7 +4293,7 @@ class HNewArray FINAL : public HExpression<2> { DECLARE_INSTRUCTION(NewArray); private: - const uint16_t type_index_; + const dex::TypeIndex type_index_; const DexFile& dex_file_; const QuickEntrypointEnum entrypoint_; @@ -4829,7 +4830,7 @@ class HRor FINAL : public HBinaryOperation { class HParameterValue FINAL : public HExpression<0> { public: HParameterValue(const DexFile& dex_file, - uint16_t type_index, + dex::TypeIndex type_index, uint8_t index, Primitive::Type parameter_type, bool is_this = false) @@ -4842,7 +4843,7 @@ class HParameterValue FINAL : public HExpression<0> { } const DexFile& GetDexFile() const { return dex_file_; } - uint16_t GetTypeIndex() const { return type_index_; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } uint8_t GetIndex() const { return index_; } bool IsThis() const { return GetPackedFlag<kFlagIsThis>(); } @@ -4860,7 +4861,7 @@ class HParameterValue FINAL : public HExpression<0> { "Too many packed fields."); const DexFile& dex_file_; - const uint16_t type_index_; + const dex::TypeIndex type_index_; // The index of this parameter in the parameters list. Must be less // than HGraph::number_of_in_vregs_. const uint8_t index_; @@ -5455,7 +5456,7 @@ class HLoadClass FINAL : public HInstruction { }; HLoadClass(HCurrentMethod* current_method, - uint16_t type_index, + dex::TypeIndex type_index, const DexFile& dex_file, bool is_referrers_class, uint32_t dex_pc, @@ -5487,7 +5488,7 @@ class HLoadClass FINAL : public HInstruction { void SetLoadKindWithTypeReference(LoadKind load_kind, const DexFile& dex_file, - uint32_t type_index) { + dex::TypeIndex type_index) { DCHECK(HasTypeReference(load_kind)); DCHECK(IsSameDexFile(dex_file_, dex_file)); DCHECK_EQ(type_index_, type_index); @@ -5511,7 +5512,7 @@ class HLoadClass FINAL : public HInstruction { bool InstructionDataEquals(const HInstruction* other) const; - size_t ComputeHashCode() const OVERRIDE { return type_index_; } + size_t ComputeHashCode() const OVERRIDE { return type_index_.index_; } bool CanBeNull() const OVERRIDE { return false; } @@ -5547,7 +5548,7 @@ class HLoadClass FINAL : public HInstruction { loaded_class_rti_ = rti; } - uint32_t GetTypeIndex() const { return type_index_; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } uint32_t GetDexCacheElementOffset() const; @@ -5630,7 +5631,7 @@ class HLoadClass FINAL : public HInstruction { // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative. HUserRecord<HInstruction*> special_input_; - const uint16_t type_index_; + const dex::TypeIndex type_index_; const DexFile& dex_file_; union { @@ -5690,7 +5691,10 @@ class HLoadString FINAL : public HInstruction { // all other types are unavailable. kDexCacheViaMethod, - kLast = kDexCacheViaMethod + // Load from the root table associated with the JIT compiled method. + kJitTableAddress, + + kLast = kJitTableAddress, }; HLoadString(HCurrentMethod* current_method, @@ -5748,7 +5752,8 @@ class HLoadString FINAL : public HInstruction { LoadKind load_kind = GetLoadKind(); if (load_kind == LoadKind::kBootImageLinkTimeAddress || load_kind == LoadKind::kBootImageLinkTimePcRelative || - load_kind == LoadKind::kBootImageAddress) { + load_kind == LoadKind::kBootImageAddress || + load_kind == LoadKind::kJitTableAddress) { return false; } return !IsInDexCache(); @@ -5801,7 +5806,8 @@ class HLoadString FINAL : public HInstruction { return load_kind == LoadKind::kBootImageLinkTimeAddress || load_kind == LoadKind::kBootImageLinkTimePcRelative || load_kind == LoadKind::kBssEntry || - load_kind == LoadKind::kDexCacheViaMethod; + load_kind == LoadKind::kDexCacheViaMethod || + load_kind == LoadKind::kJitTableAddress; } static bool HasAddress(LoadKind load_kind) { diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc index d4e2a58103..5d9a6528ca 100644 --- a/compiler/optimizing/nodes_test.cc +++ b/compiler/optimizing/nodes_test.cc @@ -35,7 +35,7 @@ TEST(Node, RemoveInstruction) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); entry->AddInstruction(new (&allocator) HGoto()); @@ -78,9 +78,9 @@ TEST(Node, InsertInstruction) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); HInstruction* parameter2 = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); entry->AddInstruction(new (&allocator) HExit()); @@ -106,7 +106,7 @@ TEST(Node, AddInstruction) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); ASSERT_FALSE(parameter->HasUses()); @@ -127,7 +127,7 @@ TEST(Node, ParentEnvironment) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter1 = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); HInstruction* with_environment = new (&allocator) HNullCheck(parameter1, 0); entry->AddInstruction(parameter1); entry->AddInstruction(with_environment); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 7a930cce71..2382b728df 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -61,6 +61,7 @@ #include "debug/method_debug_info.h" #include "dex/verification_results.h" #include "dex/verified_method.h" +#include "dex_file_types.h" #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" @@ -117,6 +118,7 @@ class CodeVectorAllocator FINAL : public CodeAllocator { size_t GetSize() const { return size_; } const ArenaVector<uint8_t>& GetMemory() const { return memory_; } + uint8_t* GetData() { return memory_.data(); } private: ArenaVector<uint8_t> memory_; @@ -624,17 +626,14 @@ void OptimizingCompiler::RunArchOptimizations(InstructionSet instruction_set, UNUSED(codegen); // To avoid compilation error when compiling for svelte OptimizingCompilerStats* stats = compilation_stats_.get(); ArenaAllocator* arena = graph->GetArena(); -#ifdef ART_USE_VIXL_ARM_BACKEND - UNUSED(arena); - UNUSED(pass_observer); - UNUSED(stats); -#endif switch (instruction_set) { -#if defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_USE_VIXL_ARM_BACKEND) +#if defined(ART_ENABLE_CODEGEN_arm) case kThumb2: case kArm: { +#ifndef ART_USE_VIXL_ARM_BACKEND arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, codegen, stats); +#endif arm::InstructionSimplifierArm* simplifier = new (arena) arm::InstructionSimplifierArm(graph, stats); SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph); @@ -643,7 +642,9 @@ void OptimizingCompiler::RunArchOptimizations(InstructionSet instruction_set, simplifier, side_effects, gvn, +#ifndef ART_USE_VIXL_ARM_BACKEND fixups +#endif }; RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); break; @@ -948,7 +949,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, graph->SetArtMethod(method); ScopedObjectAccess soa(Thread::Current()); interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize()); - uint16_t type_index = method->GetDeclaringClass()->GetDexTypeIndex(); + dex::TypeIndex type_index = method->GetDeclaringClass()->GetDexTypeIndex(); // Update the dex cache if the type is not in it yet. Note that under AOT, // the verifier must have set it, but under JIT, there's no guarantee, as we @@ -1125,7 +1126,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, jit::JitCodeCache* code_cache, ArtMethod* method, bool osr) { - StackHandleScope<2> hs(self); + StackHandleScope<3> hs(self); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( method->GetDeclaringClass()->GetClassLoader())); Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); @@ -1171,25 +1172,46 @@ bool OptimizingCompiler::JitCompile(Thread* self, } size_t stack_map_size = codegen->ComputeStackMapsSize(); - uint8_t* stack_map_data = code_cache->ReserveData(self, stack_map_size, method); - if (stack_map_data == nullptr) { + size_t number_of_roots = codegen->GetNumberOfJitRoots(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + // We allocate an object array to ensure the JIT roots that we will collect in EmitJitRoots + // will be visible by the GC between EmitLiterals and CommitCode. Once CommitCode is + // executed, this array is not needed. + Handle<mirror::ObjectArray<mirror::Object>> roots( + hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc( + self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots))); + if (roots.Get() == nullptr) { + // Out of memory, just clear the exception to avoid any Java exception uncaught problems. + DCHECK(self->IsExceptionPending()); + self->ClearException(); + return false; + } + uint8_t* stack_map_data = nullptr; + uint8_t* roots_data = nullptr; + code_cache->ReserveData( + self, stack_map_size, number_of_roots, method, &stack_map_data, &roots_data); + if (stack_map_data == nullptr || roots_data == nullptr) { return false; } MaybeRecordStat(MethodCompilationStat::kCompiled); codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size), *code_item); + codegen->EmitJitRoots(code_allocator.GetData(), roots, roots_data, dex_cache); + const void* code = code_cache->CommitCode( self, method, stack_map_data, + roots_data, codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(), codegen->GetCoreSpillMask(), codegen->GetFpuSpillMask(), code_allocator.GetMemory().data(), code_allocator.GetSize(), - osr); + osr, + roots); if (code == nullptr) { - code_cache->ClearData(self, stack_map_data); + code_cache->ClearData(self, stack_map_data, roots_data); return false; } diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index 0db60882db..f9ac3a0f72 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -143,7 +143,7 @@ void PrepareForRegisterAllocation::VisitNewInstance(HNewInstance* instruction) { // - or the load class has only one use. if (instruction->IsFinalizable() || has_only_one_use || load_class->NeedsAccessCheck()) { instruction->SetEntrypoint(kQuickAllocObject); - instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex()), 0); + instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex().index_), 0); if (has_only_one_use) { // We've just removed the only use of the HLoadClass. Since we don't run DCE after this pass, // do it manually if possible. diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index d588deaace..c191c6651f 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -96,7 +96,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitBoundType(HBoundType* instr) OVERRIDE; void VisitNullCheck(HNullCheck* instr) OVERRIDE; void UpdateReferenceTypeInfo(HInstruction* instr, - uint16_t type_idx, + dex::TypeIndex type_idx, const DexFile& dex_file, bool is_exact); @@ -463,7 +463,7 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst } void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr, - uint16_t type_idx, + dex::TypeIndex type_idx, const DexFile& dex_file, bool is_exact) { DCHECK_EQ(instr->GetType(), Primitive::kPrimNot); @@ -484,7 +484,7 @@ void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) { static mirror::Class* GetClassFromDexCache(Thread* self, const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, Handle<mirror::DexCache> hint_dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { mirror::DexCache* dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache); diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 55ea99e592..559f40923b 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -20,6 +20,7 @@ #include "code_generator.h" #include "code_generator_x86.h" #include "dex_file.h" +#include "dex_file_types.h" #include "dex_instruction.h" #include "driver/compiler_options.h" #include "nodes.h" @@ -495,7 +496,7 @@ static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HBasicBlock* block = new (allocator) HBasicBlock(graph); @@ -658,7 +659,7 @@ static HGraph* BuildFieldReturn(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimNot); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot); entry->AddInstruction(parameter); HBasicBlock* block = new (allocator) HBasicBlock(graph); @@ -742,7 +743,7 @@ static HGraph* BuildTwoSubs(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); entry->AddInstruction(parameter); HInstruction* constant1 = graph->GetIntConstant(1); @@ -821,9 +822,9 @@ static HGraph* BuildDiv(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* first = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); HInstruction* second = new (allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); entry->AddInstruction(first); entry->AddInstruction(second); @@ -883,13 +884,13 @@ TEST_F(RegisterAllocatorTest, SpillInactive) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* one = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); HInstruction* two = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); HInstruction* three = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); HInstruction* four = new (&allocator) HParameterValue( - graph->GetDexFile(), 0, 0, Primitive::kPrimInt); + graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); entry->AddInstruction(one); entry->AddInstruction(two); entry->AddInstruction(three); diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 63e4ca674e..a127708ab0 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -147,7 +147,7 @@ void HSharpening::ProcessLoadClass(HLoadClass* load_class) { DCHECK(!load_class->IsInBootImage()) << "HLoadClass should not be optimized before sharpening."; const DexFile& dex_file = load_class->GetDexFile(); - uint32_t type_index = load_class->GetTypeIndex(); + dex::TypeIndex type_index = load_class->GetTypeIndex(); bool is_in_dex_cache = false; bool is_in_boot_image = false; @@ -197,7 +197,7 @@ void HSharpening::ProcessLoadClass(HLoadClass* load_class) { // inlined frames are used correctly for OOM stack trace. // TODO: Write a test for this. Bug: 29416588 desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress; - void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index]; + void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index.index_]; address = reinterpret_cast64<uint64_t>(dex_cache_element_address); } // AOT app compilation. Check if the class is in the boot image. @@ -281,7 +281,8 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); if (codegen_->GetCompilerOptions().IsBootImage()) { - // Compiling boot image. Resolve the string and allocate it if needed. + // Compiling boot image. Resolve the string and allocate it if needed, to ensure + // the string will be added to the boot image. DCHECK(!runtime->UseJitCompilation()); mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache); CHECK(string != nullptr); @@ -297,10 +298,14 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { } else if (runtime->UseJitCompilation()) { // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); - mirror::String* string = dex_cache->GetResolvedString(string_index); - if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { - desired_load_kind = HLoadString::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(string); + mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache); + if (string != nullptr) { + if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { + desired_load_kind = HLoadString::LoadKind::kBootImageAddress; + address = reinterpret_cast64<uint64_t>(string); + } else { + desired_load_kind = HLoadString::LoadKind::kJitTableAddress; + } } } else { // AOT app compilation. Try to lookup the string without allocating if not found. @@ -322,6 +327,7 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: case HLoadString::LoadKind::kBssEntry: case HLoadString::LoadKind::kDexCacheViaMethod: + case HLoadString::LoadKind::kJitTableAddress: load_string->SetLoadKindWithStringReference(load_kind, dex_file, string_index); break; case HLoadString::LoadKind::kBootImageAddress: diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc index e3b9fb62c8..c35c39328c 100644 --- a/compiler/utils/arm/assembler_arm_vixl.cc +++ b/compiler/utils/arm/assembler_arm_vixl.cc @@ -43,12 +43,12 @@ size_t ArmVIXLAssembler::CodeSize() const { } const uint8_t* ArmVIXLAssembler::CodeBufferBaseAddress() const { - return vixl_masm_.GetStartAddress<uint8_t*>(); + return vixl_masm_.GetBuffer().GetStartAddress<const uint8_t*>(); } void ArmVIXLAssembler::FinalizeInstructions(const MemoryRegion& region) { // Copy the instructions from the buffer. - MemoryRegion from(vixl_masm_.GetStartAddress<void*>(), CodeSize()); + MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), CodeSize()); region.CopyFrom(0, from); } @@ -365,7 +365,7 @@ void ArmVIXLAssembler::StoreRegisterList(RegList regs, size_t stack_offset) { if (stack_offset != 0) { base = temps.Acquire(); DCHECK_EQ(regs & (1u << base.GetCode()), 0u); - ___ Add(base, sp, stack_offset); + ___ Add(base, sp, Operand::From(stack_offset)); } ___ Stm(base, NO_WRITE_BACK, RegisterList(regs)); } else { @@ -385,7 +385,7 @@ void ArmVIXLAssembler::LoadRegisterList(RegList regs, size_t stack_offset) { vixl32::Register base = sp; if (stack_offset != 0) { base = temps.Acquire(); - ___ Add(base, sp, stack_offset); + ___ Add(base, sp, Operand::From(stack_offset)); } ___ Ldm(base, NO_WRITE_BACK, RegisterList(regs)); } else { @@ -429,5 +429,31 @@ void ArmVIXLAssembler::AddConstantInIt(vixl32::Register rd, } } +void ArmVIXLMacroAssembler::CompareAndBranchIfZero(vixl32::Register rn, + vixl32::Label* label, + bool is_far_target) { + if (!is_far_target && rn.IsLow() && !label->IsBound()) { + // In T32, Cbz/Cbnz instructions have following limitations: + // - There are only 7 bits (i:imm5:0) to encode branch target address (cannot be far target). + // - Only low registers (i.e R0 .. R7) can be encoded. + // - Only forward branches (unbound labels) are supported. + Cbz(rn, label); + return; + } + Cmp(rn, 0); + B(eq, label); +} + +void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn, + vixl32::Label* label, + bool is_far_target) { + if (!is_far_target && rn.IsLow() && !label->IsBound()) { + Cbnz(rn, label); + return; + } + Cmp(rn, 0); + B(ne, label); +} + } // namespace arm } // namespace art diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h index e0206287eb..b4a4abc87e 100644 --- a/compiler/utils/arm/assembler_arm_vixl.h +++ b/compiler/utils/arm/assembler_arm_vixl.h @@ -37,6 +37,25 @@ namespace vixl32 = vixl::aarch32; namespace art { namespace arm { +class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler { + public: + // The following interfaces can generate CMP+Bcc or Cbz/Cbnz. + // CMP+Bcc are generated by default. + // If a hint is given (is_far_target = false) and rn and label can all fit into Cbz/Cbnz, + // then Cbz/Cbnz is generated. + // Prefer following interfaces to using vixl32::MacroAssembler::Cbz/Cbnz. + // In T32, Cbz/Cbnz instructions have following limitations: + // - Far targets, which are over 126 bytes away, are not supported. + // - Only low registers can be encoded. + // - Backward branches are not supported. + void CompareAndBranchIfZero(vixl32::Register rn, + vixl32::Label* label, + bool is_far_target = true); + void CompareAndBranchIfNonZero(vixl32::Register rn, + vixl32::Label* label, + bool is_far_target = true); +}; + class ArmVIXLAssembler FINAL : public Assembler { private: class ArmException; @@ -48,7 +67,7 @@ class ArmVIXLAssembler FINAL : public Assembler { } virtual ~ArmVIXLAssembler() {} - vixl32::MacroAssembler* GetVIXLAssembler() { return &vixl_masm_; } + ArmVIXLMacroAssembler* GetVIXLAssembler() { return &vixl_masm_; } void FinalizeCode() OVERRIDE; // Size of generated code. @@ -117,7 +136,7 @@ class ArmVIXLAssembler FINAL : public Assembler { private: // VIXL assembler. - vixl32::MacroAssembler vixl_masm_; + ArmVIXLMacroAssembler vixl_masm_; }; // Thread register declaration. diff --git a/compiler/utils/arm/jni_macro_assembler_arm.cc b/compiler/utils/arm/jni_macro_assembler_arm.cc index cf7a4d1b72..3f425dfaf5 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm.cc +++ b/compiler/utils/arm/jni_macro_assembler_arm.cc @@ -594,6 +594,41 @@ void ArmJNIMacroAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_ __ b(slow->Entry(), NE); } +std::unique_ptr<JNIMacroLabel> ArmJNIMacroAssembler::CreateLabel() { + return std::unique_ptr<JNIMacroLabel>(new ArmJNIMacroLabel()); +} + +void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label) { + CHECK(label != nullptr); + __ b(ArmJNIMacroLabel::Cast(label)->AsArm()); +} + +void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label, + JNIMacroUnaryCondition condition, + ManagedRegister test) { + CHECK(label != nullptr); + + arm::Condition arm_cond; + switch (condition) { + case JNIMacroUnaryCondition::kZero: + arm_cond = EQ; + break; + case JNIMacroUnaryCondition::kNotZero: + arm_cond = NE; + break; + default: + LOG(FATAL) << "Not implemented condition: " << static_cast<int>(condition); + UNREACHABLE(); + } + __ cmp(test.AsArm().AsCoreRegister(), ShifterOperand(0)); + __ b(ArmJNIMacroLabel::Cast(label)->AsArm(), arm_cond); +} + +void ArmJNIMacroAssembler::Bind(JNIMacroLabel* label) { + CHECK(label != nullptr); + __ Bind(ArmJNIMacroLabel::Cast(label)->AsArm()); +} + #undef __ void ArmExceptionSlowPath::Emit(Assembler* sasm) { diff --git a/compiler/utils/arm/jni_macro_assembler_arm.h b/compiler/utils/arm/jni_macro_assembler_arm.h index 4471906c27..809ac8be94 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm.h +++ b/compiler/utils/arm/jni_macro_assembler_arm.h @@ -25,6 +25,7 @@ #include "base/enums.h" #include "base/macros.h" #include "utils/jni_macro_assembler.h" +#include "utils/label.h" #include "offsets.h" namespace art { @@ -159,10 +160,26 @@ class ArmJNIMacroAssembler : public JNIMacroAssembler<PointerSize::k32> { void MemoryBarrier(ManagedRegister scratch) OVERRIDE; + // Create a new label that can be used with Jump/Bind calls. + std::unique_ptr<JNIMacroLabel> CreateLabel() OVERRIDE; + // Emit an unconditional jump to the label. + void Jump(JNIMacroLabel* label) OVERRIDE; + // Emit a conditional jump to the label by applying a unary condition test to the register. + void Jump(JNIMacroLabel* label, JNIMacroUnaryCondition cond, ManagedRegister test) OVERRIDE; + // Code at this offset will serve as the target for the Jump call. + void Bind(JNIMacroLabel* label) OVERRIDE; + private: std::unique_ptr<ArmAssembler> asm_; }; +class ArmJNIMacroLabel FINAL : public JNIMacroLabelCommon<ArmJNIMacroLabel, art::Label, kArm> { + public: + art::Label* AsArm() { + return AsPlatformLabel(); + } +}; + } // namespace arm } // namespace art diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc index 8a9fd90c32..f20ed0a0d0 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc @@ -49,7 +49,7 @@ static dwarf::Reg DWARFReg(vixl32::SRegister reg) { return dwarf::Reg::ArmFp(static_cast<int>(reg.GetCode())); } -static constexpr size_t kFramePointerSize = static_cast<size_t>(kArmPointerSize);; +static constexpr size_t kFramePointerSize = static_cast<size_t>(kArmPointerSize); void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, @@ -168,6 +168,8 @@ void ArmVIXLJNIMacroAssembler::Store(FrameOffset dest, ManagedRegister m_src, si CHECK_EQ(0u, size); } else if (src.IsCoreRegister()) { CHECK_EQ(4u, size); + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(src.AsVIXLRegister()); asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); } else if (src.IsRegisterPair()) { CHECK_EQ(8u, size); @@ -186,12 +188,16 @@ void ArmVIXLJNIMacroAssembler::Store(FrameOffset dest, ManagedRegister m_src, si void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) { ArmManagedRegister src = msrc.AsArm(); CHECK(src.IsCoreRegister()) << src; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(src.AsVIXLRegister()); asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) { ArmManagedRegister src = msrc.AsArm(); CHECK(src.IsCoreRegister()) << src; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(src.AsVIXLRegister()); asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); } @@ -202,6 +208,8 @@ void ArmVIXLJNIMacroAssembler::StoreSpanning(FrameOffset dest, ArmManagedRegister src = msrc.AsArm(); ArmManagedRegister scratch = mscratch.AsArm(); asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(scratch.AsVIXLRegister()); asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, in_off.Int32Value()); asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value() + 4); } @@ -210,6 +218,8 @@ void ArmVIXLJNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) { ArmManagedRegister scratch = mscratch.AsArm(); + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(scratch.AsVIXLRegister()); asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, src.Int32Value()); asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value()); } @@ -220,6 +230,8 @@ void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister dest, bool unpoison_reference) { ArmManagedRegister dst = dest.AsArm(); CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(dst.AsVIXLRegister(), base.AsArm().AsVIXLRegister()); asm_.LoadFromOffset(kLoadWord, dst.AsVIXLRegister(), base.AsArm().AsVIXLRegister(), @@ -246,6 +258,8 @@ void ArmVIXLJNIMacroAssembler::StoreImmediateToFrame(FrameOffset dest, ManagedRegister scratch) { ArmManagedRegister mscratch = scratch.AsArm(); CHECK(mscratch.IsCoreRegister()) << mscratch; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(mscratch.AsVIXLRegister()); asm_.LoadImmediate(mscratch.AsVIXLRegister(), imm); asm_.StoreToOffset(kStoreWord, mscratch.AsVIXLRegister(), sp, dest.Int32Value()); } @@ -263,6 +277,8 @@ void ArmVIXLJNIMacroAssembler::LoadFromThread(ManagedRegister m_dst, void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) { ArmManagedRegister dst = m_dst.AsArm(); CHECK(dst.IsCoreRegister()) << dst; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(dst.AsVIXLRegister()); asm_.LoadFromOffset(kLoadWord, dst.AsVIXLRegister(), tr, offs.Int32Value()); } @@ -271,6 +287,8 @@ void ArmVIXLJNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs, ManagedRegister mscratch) { ArmManagedRegister scratch = mscratch.AsArm(); CHECK(scratch.IsCoreRegister()) << scratch; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(scratch.AsVIXLRegister()); asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value()); asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, fr_offs.Int32Value()); } @@ -286,6 +304,8 @@ void ArmVIXLJNIMacroAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs, ManagedRegister mscratch) { ArmManagedRegister scratch = mscratch.AsArm(); CHECK(scratch.IsCoreRegister()) << scratch; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(scratch.AsVIXLRegister()); asm_.AddConstant(scratch.AsVIXLRegister(), sp, fr_offs.Int32Value()); asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value()); } @@ -312,6 +332,8 @@ void ArmVIXLJNIMacroAssembler::Move(ManagedRegister m_dst, if (!dst.Equals(src)) { if (dst.IsCoreRegister()) { CHECK(src.IsCoreRegister()) << src; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(dst.AsVIXLRegister()); ___ Mov(dst.AsVIXLRegister(), src.AsVIXLRegister()); } else if (dst.IsDRegister()) { if (src.IsDRegister()) { @@ -351,6 +373,8 @@ void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest, ArmManagedRegister temp = scratch.AsArm(); CHECK(temp.IsCoreRegister()) << temp; CHECK(size == 4 || size == 8) << size; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(temp.AsVIXLRegister()); if (size == 4) { asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value()); asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value()); @@ -414,6 +438,8 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg, ArmManagedRegister in_reg = min_reg.AsArm(); CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg; CHECK(out_reg.IsCoreRegister()) << out_reg; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(out_reg.AsVIXLRegister()); if (null_allowed) { // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. @@ -425,6 +451,8 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg, handle_scope_offset.Int32Value()); in_reg = out_reg; } + + temps.Exclude(in_reg.AsVIXLRegister()); ___ Cmp(in_reg.AsVIXLRegister(), 0); if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) { @@ -457,6 +485,8 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off, bool null_allowed) { ArmManagedRegister scratch = mscratch.AsArm(); CHECK(scratch.IsCoreRegister()) << scratch; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(scratch.AsVIXLRegister()); if (null_allowed) { asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is @@ -503,6 +533,8 @@ void ArmVIXLJNIMacroAssembler::Call(ManagedRegister mbase, ArmManagedRegister scratch = mscratch.AsArm(); CHECK(base.IsCoreRegister()) << base; CHECK(scratch.IsCoreRegister()) << scratch; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(scratch.AsVIXLRegister()); asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), base.AsVIXLRegister(), @@ -514,6 +546,8 @@ void ArmVIXLJNIMacroAssembler::Call(ManagedRegister mbase, void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) { ArmManagedRegister scratch = mscratch.AsArm(); CHECK(scratch.IsCoreRegister()) << scratch; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(scratch.AsVIXLRegister()); // Call *(*(SP + base) + offset) asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, base.Int32Value()); asm_.LoadFromOffset(kLoadWord, @@ -541,6 +575,8 @@ void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset, void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) { CHECK_ALIGNED(stack_adjust, kStackAlignment); ArmManagedRegister scratch = m_scratch.AsArm(); + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(scratch.AsVIXLRegister()); exception_blocks_.emplace_back( new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust)); asm_.LoadFromOffset(kLoadWord, @@ -574,10 +610,12 @@ void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label, switch (condition) { case JNIMacroUnaryCondition::kZero: - ___ Cbz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); + ___ CompareAndBranchIfZero(test.AsArm().AsVIXLRegister(), + ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); break; case JNIMacroUnaryCondition::kNotZero: - ___ Cbnz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); + ___ CompareAndBranchIfNonZero(test.AsArm().AsVIXLRegister(), + ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); break; default: LOG(FATAL) << "Not implemented unary condition: " << static_cast<int>(condition); @@ -596,11 +634,14 @@ void ArmVIXLJNIMacroAssembler::EmitExceptionPoll( if (exception->stack_adjust_ != 0) { // Fix up the frame. DecreaseFrameSize(exception->stack_adjust_); } + + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(exception->scratch_.AsVIXLRegister()); // Pass exception object as argument. // Don't care about preserving r0 as this won't return. ___ Mov(r0, exception->scratch_.AsVIXLRegister()); + temps.Include(exception->scratch_.AsVIXLRegister()); // TODO: check that exception->scratch_ is dead by this point. - UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); ___ Ldr(temp, MemOperand(tr, @@ -622,6 +663,9 @@ void ArmVIXLJNIMacroAssembler::Load(ArmManagedRegister } else if (dest.IsCoreRegister()) { CHECK(!dest.AsVIXLRegister().Is(sp)) << dest; + UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); + temps.Exclude(dest.AsVIXLRegister()); + if (size == 1u) { ___ Ldrb(dest.AsVIXLRegister(), MemOperand(base, offset)); } else { diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index f91bcfa92e..6ed0e9b670 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -40,12 +40,12 @@ size_t Arm64Assembler::CodeSize() const { } const uint8_t* Arm64Assembler::CodeBufferBaseAddress() const { - return vixl_masm_.GetStartAddress<uint8_t*>(); + return vixl_masm_.GetBuffer().GetStartAddress<const uint8_t*>(); } void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) { // Copy the instructions from the buffer. - MemoryRegion from(vixl_masm_.GetStartAddress<void*>(), CodeSize()); + MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), CodeSize()); region.CopyFrom(0, from); } diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 10bed13dad..50a1d9fd98 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -1753,7 +1753,10 @@ TEST_F(ArmVIXLAssemblerTest, VixlLoadFromOffset) { __ LoadFromOffset(kLoadWordPair, R2, R4, 0x40400); __ LoadFromOffset(kLoadWordPair, R4, R4, 0x40400); + vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler()); + temps.Exclude(R12); __ LoadFromOffset(kLoadWord, R0, R12, 12); // 32-bit because of R12. + temps.Include(R12); __ LoadFromOffset(kLoadWord, R2, R4, 0xa4 - 0x100000); __ LoadFromOffset(kLoadSignedByte, R2, R4, 12); @@ -1783,7 +1786,10 @@ TEST_F(ArmVIXLAssemblerTest, VixlStoreToOffset) { __ StoreToOffset(kStoreWordPair, R2, R4, 0x40400); __ StoreToOffset(kStoreWordPair, R4, R4, 0x40400); + vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler()); + temps.Exclude(R12); __ StoreToOffset(kStoreWord, R0, R12, 12); // 32-bit because of R12. + temps.Include(R12); __ StoreToOffset(kStoreWord, R2, R4, 0xa4 - 0x100000); __ StoreToOffset(kStoreByte, R2, R4, 12); diff --git a/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc index 2f154fb862..1b743134ed 100644 --- a/compiler/utils/jni_macro_assembler.cc +++ b/compiler/utils/jni_macro_assembler.cc @@ -20,7 +20,7 @@ #include <vector> #ifdef ART_ENABLE_CODEGEN_arm -#include "arm/jni_macro_assembler_arm_vixl.h" +#include "arm/jni_macro_assembler_arm.h" #endif #ifdef ART_ENABLE_CODEGEN_arm64 #include "arm64/jni_macro_assembler_arm64.h" @@ -58,7 +58,7 @@ MacroAsm32UniquePtr JNIMacroAssembler<PointerSize::k32>::Create( #ifdef ART_ENABLE_CODEGEN_arm case kArm: case kThumb2: - return MacroAsm32UniquePtr(new (arena) arm::ArmVIXLJNIMacroAssembler(arena)); + return MacroAsm32UniquePtr(new (arena) arm::ArmJNIMacroAssembler(arena, instruction_set)); #endif #ifdef ART_ENABLE_CODEGEN_mips case kMips: diff --git a/compiler/utils/test_dex_file_builder_test.cc b/compiler/utils/test_dex_file_builder_test.cc index da4ac798c7..922f8b1dfa 100644 --- a/compiler/utils/test_dex_file_builder_test.cc +++ b/compiler/utils/test_dex_file_builder_test.cc @@ -62,7 +62,8 @@ TEST(TestDexFileBuilderTest, SimpleTest) { }; ASSERT_EQ(arraysize(expected_types), dex_file->NumTypeIds()); for (size_t i = 0; i != arraysize(expected_types); ++i) { - EXPECT_STREQ(expected_types[i], dex_file->GetTypeDescriptor(dex_file->GetTypeId(i))) << i; + EXPECT_STREQ(expected_types[i], + dex_file->GetTypeDescriptor(dex_file->GetTypeId(dex::TypeIndex(i)))) << i; } ASSERT_EQ(1u, dex_file->NumFieldIds()); diff --git a/compiler/utils/type_reference.h b/compiler/utils/type_reference.h index d0c1656836..a0fa1a4a63 100644 --- a/compiler/utils/type_reference.h +++ b/compiler/utils/type_reference.h @@ -20,6 +20,7 @@ #include <stdint.h> #include "base/logging.h" +#include "dex_file_types.h" #include "string_reference.h" namespace art { @@ -28,10 +29,10 @@ class DexFile; // A type is located by its DexFile and the string_ids_ table index into that DexFile. struct TypeReference { - TypeReference(const DexFile* file, uint32_t index) : dex_file(file), type_index(index) { } + TypeReference(const DexFile* file, dex::TypeIndex index) : dex_file(file), type_index(index) { } const DexFile* dex_file; - uint32_t type_index; + dex::TypeIndex type_index; }; // Compare the actual referenced type names. Used for type reference deduplication. diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 6b690aab40..525a2ee293 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -23,6 +23,7 @@ #include "compiler/driver/compiler_driver.h" #include "compiler_callbacks.h" #include "dex_file.h" +#include "dex_file_types.h" #include "handle_scope-inl.h" #include "verifier/method_verifier-inl.h" #include "mirror/class_loader.h" @@ -82,10 +83,13 @@ class VerifierDepsTest : public CommonCompilerTest { // The compiler driver handles the verifier deps in the callbacks, so // remove what this class did for unit testing. verifier_deps_.reset(nullptr); - callbacks_->SetVerifierDeps(nullptr); - compiler_driver_->Verify(class_loader_, dex_files_, deps, &timings); + callbacks_->SetVerifierDeps(deps); + compiler_driver_->Verify(class_loader_, dex_files_, &timings); // The compiler driver may have updated the VerifierDeps in the callback object. - verifier_deps_.reset(callbacks_->GetVerifierDeps()); + if (callbacks_->GetVerifierDeps() != deps) { + verifier_deps_.reset(callbacks_->GetVerifierDeps()); + } + callbacks_->SetVerifierDeps(nullptr); } void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) { @@ -153,6 +157,7 @@ class VerifierDepsTest : public CommonCompilerTest { } CHECK(method != nullptr); + Thread::Current()->SetVerifierDeps(callbacks_->GetVerifierDeps()); MethodVerifier verifier(Thread::Current(), primary_dex_file_, dex_cache_handle, @@ -168,6 +173,7 @@ class VerifierDepsTest : public CommonCompilerTest { false /* verify to dump */, true /* allow_thread_suspension */); verifier.Verify(); + Thread::Current()->SetVerifierDeps(nullptr); return !verifier.HasFailures(); } @@ -207,8 +213,8 @@ class VerifierDepsTest : public CommonCompilerTest { hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_))); MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr)); for (const DexFile* dex_file : dex_files_) { - const std::vector<uint16_t>& unverified_classes = deps.GetUnverifiedClasses(*dex_file); - std::set<uint16_t> set(unverified_classes.begin(), unverified_classes.end()); + const std::vector<dex::TypeIndex>& unverified_classes = deps.GetUnverifiedClasses(*dex_file); + std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end()); for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); const char* descriptor = dex_file->GetClassDescriptor(class_def); @@ -228,10 +234,9 @@ class VerifierDepsTest : public CommonCompilerTest { bool HasUnverifiedClass(const std::string& cls) { const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str()); DCHECK(type_id != nullptr); - uint16_t index = primary_dex_file_->GetIndexForTypeId(*type_id); - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + dex::TypeIndex index = primary_dex_file_->GetIndexForTypeId(*type_id); for (const auto& dex_dep : verifier_deps_->dex_deps_) { - for (uint16_t entry : dex_dep.second->unverified_classes_) { + for (dex::TypeIndex entry : dex_dep.second->unverified_classes_) { if (index == entry) { return true; } @@ -245,7 +250,6 @@ class VerifierDepsTest : public CommonCompilerTest { bool HasAssignable(const std::string& expected_destination, const std::string& expected_source, bool expected_is_assignable) { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (auto& dex_dep : verifier_deps_->dex_deps_) { const DexFile& dex_file = *dex_dep.first; auto& storage = expected_is_assignable ? dex_dep.second->assignable_types_ @@ -267,7 +271,6 @@ class VerifierDepsTest : public CommonCompilerTest { bool HasClass(const std::string& expected_klass, bool expected_resolved, const std::string& expected_access_flags = "") { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (auto& dex_dep : verifier_deps_->dex_deps_) { for (auto& entry : dex_dep.second->classes_) { if (expected_resolved != entry.IsResolved()) { @@ -302,7 +305,6 @@ class VerifierDepsTest : public CommonCompilerTest { bool expected_resolved, const std::string& expected_access_flags = "", const std::string& expected_decl_klass = "") { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (auto& dex_dep : verifier_deps_->dex_deps_) { for (auto& entry : dex_dep.second->fields_) { if (expected_resolved != entry.IsResolved()) { @@ -356,7 +358,6 @@ class VerifierDepsTest : public CommonCompilerTest { bool expected_resolved, const std::string& expected_access_flags = "", const std::string& expected_decl_klass = "") { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (auto& dex_dep : verifier_deps_->dex_deps_) { auto& storage = (expected_kind == "direct") ? dex_dep.second->direct_methods_ : (expected_kind == "virtual") ? dex_dep.second->virtual_methods_ @@ -405,13 +406,10 @@ class VerifierDepsTest : public CommonCompilerTest { } size_t NumberOfCompiledDexFiles() { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); return verifier_deps_->dex_deps_.size(); } size_t HasEachKindOfRecord() { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); - bool has_strings = false; bool has_assignability = false; bool has_classes = false; @@ -462,8 +460,6 @@ TEST_F(VerifierDepsTest, StringToId) { ScopedObjectAccess soa(Thread::Current()); LoadDexFile(&soa); - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); - uint32_t id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;"); ASSERT_LT(id_Main1, primary_dex_file_->NumStringIds()); ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main1)); diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 05a5d0fa2c..0924aec7f1 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -89,6 +89,7 @@ art_cc_binary { ], static_libs: [ "libart-compiler", + "libart-dexlayout", "libart", "libvixl-arm", "libvixl-arm64", @@ -118,6 +119,7 @@ art_cc_binary { ], static_libs: [ "libartd-compiler", + "libartd-dexlayout", "libartd", "libvixld-arm", "libvixld-arm64", diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 65703a23fc..9e6032f83a 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -78,6 +78,7 @@ #include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "utils.h" +#include "vdex_file.h" #include "verifier/verifier_deps.h" #include "well_known_classes.h" #include "zip_archive.h" @@ -270,6 +271,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { "|balanced" "|speed-profile" "|speed" + "|layout-profile" "|everything-profile" "|everything):"); UsageError(" select compiler filter."); @@ -519,6 +521,7 @@ class Dex2Oat FINAL { oat_fd_(-1), input_vdex_fd_(-1), output_vdex_fd_(-1), + input_vdex_file_(nullptr), zip_fd_(-1), image_base_(0U), image_classes_zip_filename_(nullptr), @@ -709,6 +712,10 @@ class Dex2Oat FINAL { Usage("Output must be supplied with either --oat-file or --oat-fd"); } + if (input_vdex_fd_ != -1 && !input_vdex_.empty()) { + Usage("Can't have both --input-vdex-fd and --input-vdex"); + } + if (!oat_filenames_.empty() && oat_fd_ != -1) { Usage("--oat-file should not be used with --oat-fd"); } @@ -1122,6 +1129,8 @@ class Dex2Oat FINAL { zip_location_ = option.substr(strlen("--zip-location=")).data(); } else if (option.starts_with("--input-vdex-fd=")) { ParseInputVdexFd(option); + } else if (option.starts_with("--input-vdex=")) { + input_vdex_ = option.substr(strlen("--input-vdex=")).data(); } else if (option.starts_with("--output-vdex-fd=")) { ParseOutputVdexFd(option); } else if (option.starts_with("--oat-file=")) { @@ -1265,6 +1274,17 @@ class Dex2Oat FINAL { return false; } oat_files_.push_back(std::move(oat_file)); + DCHECK_EQ(input_vdex_fd_, -1); + if (!input_vdex_.empty()) { + std::string error_msg; + input_vdex_file_.reset(VdexFile::Open(input_vdex_, + /* writable */ false, + /* low_4gb */ false, + &error_msg)); + if (input_vdex_file_ != nullptr && !input_vdex_file_->IsValid()) { + input_vdex_file_.reset(nullptr); + } + } DCHECK_EQ(output_vdex_fd_, -1); std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex"); @@ -1292,6 +1312,31 @@ class Dex2Oat FINAL { } oat_files_.push_back(std::move(oat_file)); + DCHECK_NE(input_vdex_fd_, output_vdex_fd_); + if (input_vdex_fd_ != -1) { + struct stat s; + int rc = TEMP_FAILURE_RETRY(fstat(input_vdex_fd_, &s)); + if (rc == -1) { + PLOG(WARNING) << "Failed getting length of vdex file"; + } else { + std::string error_msg; + input_vdex_file_.reset(VdexFile::Open(input_vdex_fd_, + s.st_size, + "vdex", + /* writable */ false, + /* low_4gb */ false, + &error_msg)); + // If there's any problem with the passed vdex, just warn and proceed + // without it. + if (input_vdex_file_ == nullptr) { + PLOG(WARNING) << "Failed opening vdex file " << error_msg; + } else if (!input_vdex_file_->IsValid()) { + PLOG(WARNING) << "Existing vdex file is invalid"; + input_vdex_file_.reset(nullptr); + } + } + } + DCHECK_NE(output_vdex_fd_, -1); std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex"); std::unique_ptr<File> vdex_file(new File(output_vdex_fd_, vdex_location, /* check_usage */ true)); @@ -1387,7 +1432,6 @@ class Dex2Oat FINAL { // boot class path. bool Setup() { TimingLogger::ScopedTiming t("dex2oat Setup", timings_); - art::MemMap::Init(); // For ZipEntry::ExtractToMemMap. if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) { return false; @@ -1479,13 +1523,18 @@ class Dex2Oat FINAL { // Unzip or copy dex files straight to the oat file. std::unique_ptr<MemMap> opened_dex_files_map; std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + // No need to verify the dex file for: + // 1) dexlayout, which already verified it + // 2) when we have a vdex file, which means it was already verified. + bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile && + (input_vdex_file_ == nullptr); if (!oat_writers_[i]->WriteAndOpenDexFiles( kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(), rodata_.back(), instruction_set_, instruction_set_features_.get(), key_value_store_.get(), - /* verify */ true, + verify, &opened_dex_files_map, &opened_dex_files)) { return false; @@ -1585,6 +1634,9 @@ class Dex2Oat FINAL { dex_caches_.push_back(soa.AddLocalReference<jobject>( class_linker->RegisterDexFile(*dex_file, soa.Decode<mirror::ClassLoader>(class_loader_).Ptr()))); + // Pre-register dex files so that we can access verification results without locks during + // compilation and verification. + verification_results_->PreRegisterDexFile(dex_file); } return true; @@ -1662,7 +1714,7 @@ class Dex2Oat FINAL { swap_fd_, profile_compilation_info_.get())); driver_->SetDexFilesForOatFile(dex_files_); - driver_->CompileAll(class_loader_, dex_files_, /* verifier_deps */ nullptr, timings_); + driver_->CompileAll(class_loader_, dex_files_, input_vdex_file_.get(), timings_); } // Notes on the interleaving of creating the images and oat files to @@ -2235,7 +2287,14 @@ class Dex2Oat FINAL { bool AddDexFileSources() { TimingLogger::ScopedTiming t2("AddDexFileSources", timings_); - if (zip_fd_ != -1) { + if (input_vdex_file_ != nullptr) { + DCHECK_EQ(oat_writers_.size(), 1u); + const std::string& name = zip_location_.empty() ? dex_locations_[0] : zip_location_; + DCHECK(!name.empty()); + if (!oat_writers_[0]->AddVdexDexFilesSource(*input_vdex_file_.get(), name.c_str())) { + return false; + } + } else if (zip_fd_ != -1) { DCHECK_EQ(oat_writers_.size(), 1u); if (!oat_writers_[0]->AddZippedDexFilesSource(File(zip_fd_, /* check_usage */ false), zip_location_.c_str())) { @@ -2273,7 +2332,9 @@ class Dex2Oat FINAL { compiler_options_.get(), oat_file.get())); elf_writers_.back()->Start(); - oat_writers_.emplace_back(new OatWriter(IsBootImage(), timings_)); + bool do_dexlayout = compiler_options_->GetCompilerFilter() == CompilerFilter::kLayoutProfile; + oat_writers_.emplace_back(new OatWriter( + IsBootImage(), timings_, do_dexlayout ? profile_compilation_info_.get() : nullptr)); } } @@ -2591,6 +2652,8 @@ class Dex2Oat FINAL { int oat_fd_; int input_vdex_fd_; int output_vdex_fd_; + std::string input_vdex_; + std::unique_ptr<VdexFile> input_vdex_file_; std::vector<const char*> dex_filenames_; std::vector<const char*> dex_locations_; int zip_fd_; @@ -2787,6 +2850,8 @@ static int dex2oat(int argc, char** argv) { } } + art::MemMap::Init(); // For ZipEntry::ExtractToMemMap, and vdex. + // Check early that the result of compilation can be written if (!dex2oat->OpenFile()) { return EXIT_FAILURE; diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index fa32178796..714a58c8e5 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -24,6 +24,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/stringprintf.h" +#include "dex_file-inl.h" #include "dex2oat_environment_test.h" #include "oat.h" #include "oat_file.h" @@ -551,4 +552,107 @@ TEST_F(Dex2oatVeryLargeTest, UseVeryLarge) { RunTest(CompilerFilter::kSpeed, true, { "--very-large-app-threshold=100" }); } +static const char kDexFileLayoutInputProfile[] = "cHJvADAwMgABAAwAAQABAOqMEeFEZXhOb09hdC5qYXIBAAEA"; + +static void WriteFileBase64(const char* base64, const char* location) { + // Decode base64. + CHECK(base64 != nullptr); + size_t length; + std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length)); + CHECK(bytes.get() != nullptr); + + // Write to provided file. + std::unique_ptr<File> file(OS::CreateEmptyFile(location)); + CHECK(file.get() != nullptr); + if (!file->WriteFully(bytes.get(), length)) { + PLOG(FATAL) << "Failed to write base64 as file"; + } + if (file->FlushCloseOrErase() != 0) { + PLOG(FATAL) << "Could not flush and close test file."; + } +} + +class Dex2oatLayoutTest : public Dex2oatTest { + protected: + void CheckFilter(CompilerFilter::Filter input ATTRIBUTE_UNUSED, + CompilerFilter::Filter result ATTRIBUTE_UNUSED) OVERRIDE { + // Ignore, we'll do our own checks. + } + + void RunTest() { + std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; + std::string profile_location = GetScratchDir() + "/primary.prof"; + std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex"; + + Copy(GetDexSrc2(), dex_location); + WriteFileBase64(kDexFileLayoutInputProfile, profile_location.c_str()); + + const std::vector<std::string>& extra_args = { "--profile-file=" + profile_location }; + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kLayoutProfile, extra_args); + + CheckValidity(); + ASSERT_TRUE(success_); + CheckResult(dex_location, odex_location); + } + void CheckResult(const std::string& dex_location, const std::string& odex_location) { + // Host/target independent checks. + std::string error_msg; + std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), + odex_location.c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + dex_location.c_str(), + &error_msg)); + ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; + + const char* location = dex_location.c_str(); + std::vector<std::unique_ptr<const DexFile>> dex_files; + ASSERT_TRUE(DexFile::Open(location, location, true, &error_msg, &dex_files)); + EXPECT_EQ(dex_files.size(), 1U); + std::unique_ptr<const DexFile>& old_dex_file = dex_files[0]; + + for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) { + std::unique_ptr<const DexFile> new_dex_file = oat_dex_file->OpenDexFile(&error_msg); + ASSERT_TRUE(new_dex_file != nullptr); + uint32_t class_def_count = new_dex_file->NumClassDefs(); + ASSERT_LT(class_def_count, std::numeric_limits<uint16_t>::max()); + ASSERT_GE(class_def_count, 2U); + + // The new layout swaps the classes at indexes 0 and 1. + std::string old_class0 = old_dex_file->PrettyType(old_dex_file->GetClassDef(0).class_idx_); + std::string old_class1 = old_dex_file->PrettyType(old_dex_file->GetClassDef(1).class_idx_); + std::string new_class0 = new_dex_file->PrettyType(new_dex_file->GetClassDef(0).class_idx_); + std::string new_class1 = new_dex_file->PrettyType(new_dex_file->GetClassDef(1).class_idx_); + EXPECT_EQ(old_class0, new_class1); + EXPECT_EQ(old_class1, new_class0); + } + + EXPECT_EQ(odex_file->GetCompilerFilter(), CompilerFilter::kLayoutProfile); + } + + // Check whether the dex2oat run was really successful. + void CheckValidity() { + if (kIsTargetBuild) { + CheckTargetValidity(); + } else { + CheckHostValidity(); + } + } + + void CheckTargetValidity() { + // TODO: Ignore for now. + } + + // On the host, we can get the dex2oat output. Here, look for "dex2oat took." + void CheckHostValidity() { + EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_; + } + }; + +TEST_F(Dex2oatLayoutTest, TestLayout) { + RunTest(); +} + } // namespace art diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 30de28eaee..03d6227ae2 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -45,6 +45,7 @@ #include "base/stringprintf.h" #include "dexdump_cfg.h" #include "dex_file-inl.h" +#include "dex_file_types.h" #include "dex_instruction-inl.h" namespace art { @@ -482,7 +483,7 @@ static void dumpEncodedValue(const DexFile* pDexFile, const u1** data, u1 type, } case DexFile::kDexAnnotationType: { const u4 str_idx = static_cast<u4>(readVarWidth(data, arg, false)); - fputs(pDexFile->StringByTypeIdx(str_idx), gOutFile); + fputs(pDexFile->StringByTypeIdx(dex::TypeIndex(str_idx)), gOutFile); break; } case DexFile::kDexAnnotationField: @@ -511,7 +512,7 @@ static void dumpEncodedValue(const DexFile* pDexFile, const u1** data, u1 type, } case DexFile::kDexAnnotationAnnotation: { const u4 type_idx = DecodeUnsignedLeb128(data); - fputs(pDexFile->StringByTypeIdx(type_idx), gOutFile); + fputs(pDexFile->StringByTypeIdx(dex::TypeIndex(type_idx)), gOutFile); // Decode and display all name=value pairs. const u4 size = DecodeUnsignedLeb128(data); for (u4 i = 0; i < size; i++) { @@ -592,10 +593,10 @@ static void dumpClassDef(const DexFile* pDexFile, int idx) { // General class information. const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx); fprintf(gOutFile, "Class #%d header:\n", idx); - fprintf(gOutFile, "class_idx : %d\n", pClassDef.class_idx_); + fprintf(gOutFile, "class_idx : %d\n", pClassDef.class_idx_.index_); fprintf(gOutFile, "access_flags : %d (0x%04x)\n", pClassDef.access_flags_, pClassDef.access_flags_); - fprintf(gOutFile, "superclass_idx : %d\n", pClassDef.superclass_idx_); + fprintf(gOutFile, "superclass_idx : %d\n", pClassDef.superclass_idx_.index_); fprintf(gOutFile, "interfaces_off : %d (0x%06x)\n", pClassDef.interfaces_off_, pClassDef.interfaces_off_); fprintf(gOutFile, "source_file_idx : %d\n", pClassDef.source_file_idx_); @@ -747,9 +748,8 @@ static void dumpCatches(const DexFile* pDexFile, const DexFile::CodeItem* pCode) const u4 end = start + pTry->insn_count_; fprintf(gOutFile, " 0x%04x - 0x%04x\n", start, end); for (CatchHandlerIterator it(*pCode, *pTry); it.HasNext(); it.Next()) { - const u2 tidx = it.GetHandlerTypeIndex(); - const char* descriptor = - (tidx == DexFile::kDexNoIndex16) ? "<any>" : pDexFile->StringByTypeIdx(tidx); + const dex::TypeIndex tidx = it.GetHandlerTypeIndex(); + const char* descriptor = (!tidx.IsValid()) ? "<any>" : pDexFile->StringByTypeIdx(tidx); fprintf(gOutFile, " %s -> 0x%04x\n", descriptor, it.GetHandlerAddress()); } // for } // for @@ -834,7 +834,7 @@ static std::unique_ptr<char[]> indexString(const DexFile* pDexFile, break; case Instruction::kIndexTypeRef: if (index < pDexFile->GetHeader().type_ids_size_) { - const char* tp = pDexFile->StringByTypeIdx(index); + const char* tp = pDexFile->StringByTypeIdx(dex::TypeIndex(index)); outSize = snprintf(buf.get(), bufSize, "%s // type@%0*x", tp, width, index); } else { outSize = snprintf(buf.get(), bufSize, "<type?> // type@%0*x", width, index); @@ -1461,7 +1461,7 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) { // General class information. char* accessStr = createAccessFlagStr(pClassDef.access_flags_, kAccessForClass); const char* superclassDescriptor; - if (pClassDef.superclass_idx_ == DexFile::kDexNoIndex16) { + if (!pClassDef.superclass_idx_.IsValid()) { superclassDescriptor = nullptr; } else { superclassDescriptor = pDexFile->StringByTypeIdx(pClassDef.superclass_idx_); diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index b9266f75bf..9ee9ebd3d4 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -12,28 +12,46 @@ // See the License for the specific language governing permissions and // limitations under the License. -art_cc_binary { - name: "dexlayout", +art_cc_defaults { + name: "libart-dexlayout-defaults", host_supported: true, srcs: [ - "dexlayout_main.cc", "dexlayout.cc", "dex_ir.cc", "dex_ir_builder.cc", "dex_visualize.cc", "dex_writer.cc", ], + export_include_dirs: ["."], + shared_libs: ["libbase"], + static_libs: ["libz"], +} + +art_cc_library { + name: "libart-dexlayout", + defaults: ["libart-dexlayout-defaults"], + shared_libs: ["libart"], +} + +art_cc_library { + name: "libartd-dexlayout", + defaults: ["libart-dexlayout-defaults"], + shared_libs: ["libartd"], +} + +art_cc_binary { + name: "dexlayout", + host_supported: true, + srcs: ["dexlayout_main.cc"], cflags: ["-Wall"], shared_libs: [ "libart", - "libbase", + "libart-dexlayout", ], } art_cc_test { name: "art_dexlayout_tests", - defaults: [ - "art_gtest_defaults", - ], + defaults: ["art_gtest_defaults"], srcs: ["dexlayout_test.cc"], } diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index c3c763fee8..fe2bcce843 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -56,6 +56,36 @@ static void GetLocalsCb(void* context, const DexFile::LocalInfo& entry) { entry.end_address_, entry.reg_))); } +static uint32_t GetCodeItemSize(const DexFile& dex_file, const DexFile::CodeItem& disk_code_item) { + uintptr_t code_item_start = reinterpret_cast<uintptr_t>(&disk_code_item); + uint32_t insns_size = disk_code_item.insns_size_in_code_units_; + uint32_t tries_size = disk_code_item.tries_size_; + if (tries_size == 0) { + uintptr_t insns_end = reinterpret_cast<uintptr_t>(&disk_code_item.insns_[insns_size]); + return insns_end - code_item_start; + } else { + uint32_t last_handler_off = 0; + for (uint32_t i = 0; i < tries_size; ++i) { + // Iterate over the try items to find the last catch handler. + const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i); + uint16_t handler_off = disk_try_item->handler_off_; + if (handler_off > last_handler_off) { + last_handler_off = handler_off; + } + } + // Decode the final handler to see where it ends. + const uint8_t* handler_data = DexFile::GetCatchHandlerData(disk_code_item, last_handler_off); + int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2; + if (uleb128_count <= 0) { + uleb128_count = -uleb128_count + 1; + } + for (int32_t i = 0; i < uleb128_count; ++i) { + DecodeUnsignedLeb128(&handler_data); + } + return reinterpret_cast<uintptr_t>(handler_data) - code_item_start; + } +} + static uint32_t GetDebugInfoStreamSize(const uint8_t* debug_info_stream) { const uint8_t* stream = debug_info_stream; DecodeUnsignedLeb128(&stream); // line_start @@ -319,7 +349,7 @@ void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) { } void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) { - const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(i); + const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i)); TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_)); type_ids_.AddIndexedItem(type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i); } @@ -330,22 +360,22 @@ void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_); ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_), - GetTypeId(disk_proto_id.return_type_idx_), + GetTypeId(disk_proto_id.return_type_idx_.index_), parameter_type_list); proto_ids_.AddIndexedItem(proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i); } void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) { const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i); - FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_), - GetTypeId(disk_field_id.type_idx_), + FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_.index_), + GetTypeId(disk_field_id.type_idx_.index_), GetStringId(disk_field_id.name_idx_)); field_ids_.AddIndexedItem(field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i); } void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i); - MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_), + MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_), GetProtoId(disk_method_id.proto_idx_), GetStringId(disk_method_id.name_idx_)); method_ids_.AddIndexedItem(method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i); @@ -353,9 +383,9 @@ void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) { const DexFile::ClassDef& disk_class_def = dex_file.GetClassDef(i); - const TypeId* class_type = GetTypeId(disk_class_def.class_idx_); + const TypeId* class_type = GetTypeId(disk_class_def.class_idx_.index_); uint32_t access_flags = disk_class_def.access_flags_; - const TypeId* superclass = GetTypeIdOrNullPtr(disk_class_def.superclass_idx_); + const TypeId* superclass = GetTypeIdOrNullPtr(disk_class_def.superclass_idx_.index_); const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def); TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_); @@ -384,16 +414,14 @@ TypeList* Collections::CreateTypeList(const DexFile::TypeList* dex_type_list, ui if (dex_type_list == nullptr) { return nullptr; } - // TODO: Create more efficient lookup for existing type lists. - for (std::unique_ptr<TypeList>& type_list : TypeLists()) { - if (type_list->GetOffset() == offset) { - return type_list.get(); - } + auto found_type_list = TypeLists().find(offset); + if (found_type_list != TypeLists().end()) { + return found_type_list->second.get(); } TypeIdVector* type_vector = new TypeIdVector(); uint32_t size = dex_type_list->Size(); for (uint32_t index = 0; index < size; ++index) { - type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_)); + type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_)); } TypeList* new_type_list = new TypeList(type_vector); type_lists_.AddItem(new_type_list, offset); @@ -404,10 +432,9 @@ EncodedArrayItem* Collections::CreateEncodedArrayItem(const uint8_t* static_data if (static_data == nullptr) { return nullptr; } - for (std::unique_ptr<EncodedArrayItem>& existing_array_item : EncodedArrayItems()) { - if (existing_array_item->GetOffset() == offset) { - return existing_array_item.get(); - } + auto found_encoded_array_item = EncodedArrayItems().find(offset); + if (found_encoded_array_item != EncodedArrayItems().end()) { + return found_encoded_array_item->second.get(); } uint32_t size = DecodeUnsignedLeb128(&static_data); EncodedValueVector* values = new EncodedValueVector(); @@ -422,10 +449,9 @@ EncodedArrayItem* Collections::CreateEncodedArrayItem(const uint8_t* static_data AnnotationItem* Collections::CreateAnnotationItem(const DexFile::AnnotationItem* annotation, uint32_t offset) { - for (std::unique_ptr<AnnotationItem>& existing_annotation_item : AnnotationItems()) { - if (existing_annotation_item->GetOffset() == offset) { - return existing_annotation_item.get(); - } + auto found_annotation_item = AnnotationItems().find(offset); + if (found_annotation_item != AnnotationItems().end()) { + return found_annotation_item->second.get(); } uint8_t visibility = annotation->visibility_; const uint8_t* annotation_data = annotation->annotation_; @@ -444,10 +470,9 @@ AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file, if (disk_annotations_item.size_ == 0 && offset == 0) { return nullptr; } - for (std::unique_ptr<AnnotationSetItem>& existing_anno_set_item : AnnotationSetItems()) { - if (existing_anno_set_item->GetOffset() == offset) { - return existing_anno_set_item.get(); - } + auto found_anno_set_item = AnnotationSetItems().find(offset); + if (found_anno_set_item != AnnotationSetItems().end()) { + return found_anno_set_item->second.get(); } std::vector<AnnotationItem*>* items = new std::vector<AnnotationItem*>(); for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) { @@ -467,10 +492,9 @@ AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file, AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexFile& dex_file, const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) { - for (std::unique_ptr<AnnotationsDirectoryItem>& anno_dir_item : AnnotationsDirectoryItems()) { - if (anno_dir_item->GetOffset() == offset) { - return anno_dir_item.get(); - } + auto found_anno_dir_item = AnnotationsDirectoryItems().find(offset); + if (found_anno_dir_item != AnnotationsDirectoryItems().end()) { + return found_anno_dir_item->second.get(); } const DexFile::AnnotationSetItem* class_set_item = dex_file.GetClassAnnotationSet(disk_annotations_item); @@ -535,11 +559,9 @@ ParameterAnnotation* Collections::GenerateParameterAnnotation( const DexFile& dex_file, MethodId* method_id, const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset) { AnnotationSetRefList* set_ref_list = nullptr; - for (std::unique_ptr<AnnotationSetRefList>& existing_set_ref_list : AnnotationSetRefLists()) { - if (existing_set_ref_list->GetOffset() == offset) { - set_ref_list = existing_set_ref_list.get(); - break; - } + auto found_set_ref_list = AnnotationSetRefLists().find(offset); + if (found_set_ref_list != AnnotationSetRefLists().end()) { + set_ref_list = found_set_ref_list->second.get(); } if (set_ref_list == nullptr) { std::vector<AnnotationSetItem*>* annotations = new std::vector<AnnotationSetItem*>(); @@ -597,8 +619,8 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, bool catch_all = false; TypeAddrPairVector* addr_pairs = new TypeAddrPairVector(); for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) { - const uint16_t type_index = it.GetHandlerTypeIndex(); - const TypeId* type_id = GetTypeIdOrNullPtr(type_index); + const dex::TypeIndex type_index = it.GetHandlerTypeIndex(); + const TypeId* type_id = GetTypeIdOrNullPtr(type_index.index_); catch_all |= type_id == nullptr; addr_pairs->push_back(std::unique_ptr<const TypeAddrPair>( new TypeAddrPair(type_id, it.GetHandlerAddress()))); @@ -610,9 +632,10 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, tries->push_back(std::unique_ptr<const TryItem>(try_item)); } } - // TODO: Calculate the size of the code item. + uint32_t size = GetCodeItemSize(dex_file, disk_code_item); CodeItem* code_item = new CodeItem( registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list); + code_item->SetSize(size); code_items_.AddItem(code_item, offset); // Add "fixup" references to types, strings, methods, and fields. // This is temporary, as we will probably want more detailed parsing of the @@ -690,8 +713,8 @@ ClassData* Collections::CreateClassData( virtual_methods->push_back( std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii))); } - // TODO: Calculate the size of the class data. class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods); + class_data->SetSize(cdii.EndDataPointer() - encoded_data); class_datas_.AddItem(class_data, offset); } return class_data; diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 38eb0b1b4b..a2d1190d03 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -19,6 +19,7 @@ #ifndef ART_DEXLAYOUT_DEX_IR_H_ #define ART_DEXLAYOUT_DEX_IR_H_ +#include <map> #include <vector> #include <stdint.h> @@ -98,34 +99,52 @@ class AbstractDispatcher { }; // Collections become owners of the objects added by moving them into unique pointers. -template<class T> class CollectionWithOffset { +template<class T> class CollectionBase { public: - CollectionWithOffset() = default; - std::vector<std::unique_ptr<T>>& Collection() { return collection_; } - // Read-time support methods - void AddItem(T* object, uint32_t offset) { - object->SetOffset(offset); - collection_.push_back(std::unique_ptr<T>(object)); - } + CollectionBase() = default; + + uint32_t GetOffset() const { return offset_; } + void SetOffset(uint32_t new_offset) { offset_ = new_offset; } + + private: + uint32_t offset_ = 0; + + DISALLOW_COPY_AND_ASSIGN(CollectionBase); +}; + +template<class T> class CollectionVector : public CollectionBase<T> { + public: + CollectionVector() = default; + void AddIndexedItem(T* object, uint32_t offset, uint32_t index) { object->SetOffset(offset); object->SetIndex(index); collection_.push_back(std::unique_ptr<T>(object)); } - // Ordinary object insertion into collection. - void Insert(T object ATTRIBUTE_UNUSED) { - // TODO(sehr): add ordered insertion support. - UNIMPLEMENTED(FATAL) << "Insertion not ready"; - } - uint32_t GetOffset() const { return offset_; } - void SetOffset(uint32_t new_offset) { offset_ = new_offset; } uint32_t Size() const { return collection_.size(); } + std::vector<std::unique_ptr<T>>& Collection() { return collection_; } private: std::vector<std::unique_ptr<T>> collection_; - uint32_t offset_ = 0; - DISALLOW_COPY_AND_ASSIGN(CollectionWithOffset); + DISALLOW_COPY_AND_ASSIGN(CollectionVector); +}; + +template<class T> class CollectionMap : public CollectionBase<T> { + public: + CollectionMap() = default; + + void AddItem(T* object, uint32_t offset) { + object->SetOffset(offset); + collection_.emplace(offset, std::unique_ptr<T>(object)); + } + uint32_t Size() const { return collection_.size(); } + std::map<uint32_t, std::unique_ptr<T>>& Collection() { return collection_; } + + private: + std::map<uint32_t, std::unique_ptr<T>> collection_; + + DISALLOW_COPY_AND_ASSIGN(CollectionMap); }; class Collections { @@ -138,22 +157,23 @@ class Collections { std::vector<std::unique_ptr<FieldId>>& FieldIds() { return field_ids_.Collection(); } std::vector<std::unique_ptr<MethodId>>& MethodIds() { return method_ids_.Collection(); } std::vector<std::unique_ptr<ClassDef>>& ClassDefs() { return class_defs_.Collection(); } - std::vector<std::unique_ptr<StringData>>& StringDatas() { return string_datas_.Collection(); } - std::vector<std::unique_ptr<TypeList>>& TypeLists() { return type_lists_.Collection(); } - std::vector<std::unique_ptr<EncodedArrayItem>>& EncodedArrayItems() + std::map<uint32_t, std::unique_ptr<StringData>>& StringDatas() + { return string_datas_.Collection(); } + std::map<uint32_t, std::unique_ptr<TypeList>>& TypeLists() { return type_lists_.Collection(); } + std::map<uint32_t, std::unique_ptr<EncodedArrayItem>>& EncodedArrayItems() { return encoded_array_items_.Collection(); } - std::vector<std::unique_ptr<AnnotationItem>>& AnnotationItems() + std::map<uint32_t, std::unique_ptr<AnnotationItem>>& AnnotationItems() { return annotation_items_.Collection(); } - std::vector<std::unique_ptr<AnnotationSetItem>>& AnnotationSetItems() + std::map<uint32_t, std::unique_ptr<AnnotationSetItem>>& AnnotationSetItems() { return annotation_set_items_.Collection(); } - std::vector<std::unique_ptr<AnnotationSetRefList>>& AnnotationSetRefLists() + std::map<uint32_t, std::unique_ptr<AnnotationSetRefList>>& AnnotationSetRefLists() { return annotation_set_ref_lists_.Collection(); } - std::vector<std::unique_ptr<AnnotationsDirectoryItem>>& AnnotationsDirectoryItems() + std::map<uint32_t, std::unique_ptr<AnnotationsDirectoryItem>>& AnnotationsDirectoryItems() { return annotations_directory_items_.Collection(); } - std::vector<std::unique_ptr<DebugInfoItem>>& DebugInfoItems() + std::map<uint32_t, std::unique_ptr<DebugInfoItem>>& DebugInfoItems() { return debug_info_items_.Collection(); } - std::vector<std::unique_ptr<CodeItem>>& CodeItems() { return code_items_.Collection(); } - std::vector<std::unique_ptr<ClassData>>& ClassDatas() { return class_datas_.Collection(); } + std::map<uint32_t, std::unique_ptr<CodeItem>>& CodeItems() { return code_items_.Collection(); } + std::map<uint32_t, std::unique_ptr<ClassData>>& ClassDatas() { return class_datas_.Collection(); } void CreateStringId(const DexFile& dex_file, uint32_t i); void CreateTypeId(const DexFile& dex_file, uint32_t i); @@ -204,7 +224,7 @@ class Collections { uint32_t DebugInfoItemsOffset() const { return debug_info_items_.GetOffset(); } uint32_t CodeItemsOffset() const { return code_items_.GetOffset(); } uint32_t ClassDatasOffset() const { return class_datas_.GetOffset(); } - uint32_t MapItemOffset() const { return map_item_offset_; } + uint32_t MapListOffset() const { return map_list_offset_; } void SetStringIdsOffset(uint32_t new_offset) { string_ids_.SetOffset(new_offset); } void SetTypeIdsOffset(uint32_t new_offset) { type_ids_.SetOffset(new_offset); } @@ -226,7 +246,7 @@ class Collections { void SetDebugInfoItemsOffset(uint32_t new_offset) { debug_info_items_.SetOffset(new_offset); } void SetCodeItemsOffset(uint32_t new_offset) { code_items_.SetOffset(new_offset); } void SetClassDatasOffset(uint32_t new_offset) { class_datas_.SetOffset(new_offset); } - void SetMapItemOffset(uint32_t new_offset) { map_item_offset_ = new_offset; } + void SetMapListOffset(uint32_t new_offset) { map_list_offset_ = new_offset; } uint32_t StringIdsSize() const { return string_ids_.Size(); } uint32_t TypeIdsSize() const { return type_ids_.Size(); } @@ -254,25 +274,25 @@ class Collections { const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset); MethodItem* GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii); - CollectionWithOffset<StringId> string_ids_; - CollectionWithOffset<TypeId> type_ids_; - CollectionWithOffset<ProtoId> proto_ids_; - CollectionWithOffset<FieldId> field_ids_; - CollectionWithOffset<MethodId> method_ids_; - CollectionWithOffset<ClassDef> class_defs_; - - CollectionWithOffset<StringData> string_datas_; - CollectionWithOffset<TypeList> type_lists_; - CollectionWithOffset<EncodedArrayItem> encoded_array_items_; - CollectionWithOffset<AnnotationItem> annotation_items_; - CollectionWithOffset<AnnotationSetItem> annotation_set_items_; - CollectionWithOffset<AnnotationSetRefList> annotation_set_ref_lists_; - CollectionWithOffset<AnnotationsDirectoryItem> annotations_directory_items_; - CollectionWithOffset<DebugInfoItem> debug_info_items_; - CollectionWithOffset<CodeItem> code_items_; - CollectionWithOffset<ClassData> class_datas_; - - uint32_t map_item_offset_ = 0; + CollectionVector<StringId> string_ids_; + CollectionVector<TypeId> type_ids_; + CollectionVector<ProtoId> proto_ids_; + CollectionVector<FieldId> field_ids_; + CollectionVector<MethodId> method_ids_; + CollectionVector<ClassDef> class_defs_; + + CollectionMap<StringData> string_datas_; + CollectionMap<TypeList> type_lists_; + CollectionMap<EncodedArrayItem> encoded_array_items_; + CollectionMap<AnnotationItem> annotation_items_; + CollectionMap<AnnotationSetItem> annotation_set_items_; + CollectionMap<AnnotationSetRefList> annotation_set_ref_lists_; + CollectionMap<AnnotationsDirectoryItem> annotations_directory_items_; + CollectionMap<DebugInfoItem> debug_info_items_; + CollectionMap<CodeItem> code_items_; + CollectionMap<ClassData> class_datas_; + + uint32_t map_list_offset_ = 0; DISALLOW_COPY_AND_ASSIGN(Collections); }; @@ -539,20 +559,20 @@ using FieldItemVector = std::vector<std::unique_ptr<FieldItem>>; class MethodItem : public Item { public: - MethodItem(uint32_t access_flags, const MethodId* method_id, const CodeItem* code) + MethodItem(uint32_t access_flags, const MethodId* method_id, CodeItem* code) : access_flags_(access_flags), method_id_(method_id), code_(code) { } ~MethodItem() OVERRIDE { } uint32_t GetAccessFlags() const { return access_flags_; } const MethodId* GetMethodId() const { return method_id_; } - const CodeItem* GetCodeItem() const { return code_; } + CodeItem* GetCodeItem() { return code_; } void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); } private: uint32_t access_flags_; const MethodId* method_id_; - const CodeItem* code_; // This can be nullptr. + CodeItem* code_; // This can be nullptr. DISALLOW_COPY_AND_ASSIGN(MethodItem); }; diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc index 68ff2a253d..d0c5bf964e 100644 --- a/dexlayout/dex_ir_builder.cc +++ b/dexlayout/dex_ir_builder.cc @@ -71,7 +71,7 @@ Header* DexIrBuilder(const DexFile& dex_file) { collections.CreateClassDef(dex_file, i); } // MapItem. - collections.SetMapItemOffset(disk_header.map_off_); + collections.SetMapListOffset(disk_header.map_off_); CheckAndSetRemainingOffsets(dex_file, &collections); diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index bc9ca6de98..02274b25a3 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -263,11 +263,13 @@ class Dumper { DumpStringId(method_id->Name(), class_index); } - void DumpMethodItem(const dex_ir::MethodItem* method, const DexFile* dex_file, int class_index) { - if (profile_info_ != nullptr) { + void DumpMethodItem(dex_ir::MethodItem* method, + const DexFile* dex_file, + int class_index, + ProfileCompilationInfo* profile_info) { + if (profile_info != nullptr) { uint32_t method_idx = method->GetMethodId()->GetIndex(); - MethodReference mr(dex_file, method_idx); - if (!profile_info_->ContainsMethod(mr)) { + if (!profile_info->ContainsMethod(MethodReference(dex_file, method_idx))) { return; } } @@ -344,13 +346,17 @@ class Dumper { * Dumps a gnuplot data file showing the parts of the dex_file that belong to each class. * If profiling information is present, it dumps only those classes that are marked as hot. */ -void VisualizeDexLayout(dex_ir::Header* header, const DexFile* dex_file, size_t dex_file_index) { +void VisualizeDexLayout(dex_ir::Header* header, + const DexFile* dex_file, + size_t dex_file_index, + ProfileCompilationInfo* profile_info) { std::unique_ptr<Dumper> dumper(new Dumper(header->GetCollections(), dex_file_index)); const uint32_t class_defs_size = header->GetCollections().ClassDefsSize(); for (uint32_t class_index = 0; class_index < class_defs_size; class_index++) { dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(class_index); - if (profile_info_ != nullptr && !profile_info_->ContainsClass(*dex_file, class_index)) { + dex::TypeIndex type_idx(class_def->ClassType()->GetIndex()); + if (profile_info != nullptr && !profile_info->ContainsClass(*dex_file, type_idx)) { continue; } dumper->DumpAddressRange(class_def, class_index); @@ -383,12 +389,12 @@ void VisualizeDexLayout(dex_ir::Header* header, const DexFile* dex_file, size_t } if (class_data->DirectMethods()) { for (auto& method_item : *class_data->DirectMethods()) { - dumper->DumpMethodItem(method_item.get(), dex_file, class_index); + dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info); } } if (class_data->VirtualMethods()) { for (auto& method_item : *class_data->VirtualMethods()) { - dumper->DumpMethodItem(method_item.get(), dex_file, class_index); + dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info); } } } diff --git a/dexlayout/dex_visualize.h b/dexlayout/dex_visualize.h index b1d2ed79aa..09f830681b 100644 --- a/dexlayout/dex_visualize.h +++ b/dexlayout/dex_visualize.h @@ -28,11 +28,15 @@ namespace art { class DexFile; +class ProfileCompilationInfo; namespace dex_ir { class Header; } // namespace dex_ir -void VisualizeDexLayout(dex_ir::Header* header, const DexFile* dex_file, size_t dex_file_index); +void VisualizeDexLayout(dex_ir::Header* header, + const DexFile* dex_file, + size_t dex_file_index, + ProfileCompilationInfo* profile_info); } // namespace art diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index dba5da0fb1..7ffa38bfd4 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -104,7 +104,9 @@ size_t EncodeDoubleValue(double value, uint8_t* buffer) { } size_t DexWriter::Write(const void* buffer, size_t length, size_t offset) { - return dex_file_->PwriteFully(buffer, length, offset) ? length : 0; + DCHECK_LE(offset + length, mem_map_->Size()); + memcpy(mem_map_->Begin() + offset, buffer, length); + return length; } size_t DexWriter::WriteSleb128(uint32_t value, size_t offset) { @@ -236,12 +238,13 @@ size_t DexWriter::WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t void DexWriter::WriteStrings() { uint32_t string_data_off[1]; - for (std::unique_ptr<dex_ir::StringId>& string_id : header_.GetCollections().StringIds()) { + for (std::unique_ptr<dex_ir::StringId>& string_id : header_->GetCollections().StringIds()) { string_data_off[0] = string_id->DataItem()->GetOffset(); Write(string_data_off, string_id->GetSize(), string_id->GetOffset()); } - for (std::unique_ptr<dex_ir::StringData>& string_data : header_.GetCollections().StringDatas()) { + for (auto& string_data_pair : header_->GetCollections().StringDatas()) { + std::unique_ptr<dex_ir::StringData>& string_data = string_data_pair.second; uint32_t offset = string_data->GetOffset(); offset += WriteUleb128(CountModifiedUtf8Chars(string_data->Data()), offset); Write(string_data->Data(), strlen(string_data->Data()), offset); @@ -250,7 +253,7 @@ void DexWriter::WriteStrings() { void DexWriter::WriteTypes() { uint32_t descriptor_idx[1]; - for (std::unique_ptr<dex_ir::TypeId>& type_id : header_.GetCollections().TypeIds()) { + for (std::unique_ptr<dex_ir::TypeId>& type_id : header_->GetCollections().TypeIds()) { descriptor_idx[0] = type_id->GetStringId()->GetIndex(); Write(descriptor_idx, type_id->GetSize(), type_id->GetOffset()); } @@ -259,7 +262,8 @@ void DexWriter::WriteTypes() { void DexWriter::WriteTypeLists() { uint32_t size[1]; uint16_t list[1]; - for (std::unique_ptr<dex_ir::TypeList>& type_list : header_.GetCollections().TypeLists()) { + for (auto& type_list_pair : header_->GetCollections().TypeLists()) { + std::unique_ptr<dex_ir::TypeList>& type_list = type_list_pair.second; size[0] = type_list->GetTypeList()->size(); uint32_t offset = type_list->GetOffset(); offset += Write(size, sizeof(uint32_t), offset); @@ -272,7 +276,7 @@ void DexWriter::WriteTypeLists() { void DexWriter::WriteProtos() { uint32_t buffer[3]; - for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_.GetCollections().ProtoIds()) { + for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_->GetCollections().ProtoIds()) { buffer[0] = proto_id->Shorty()->GetIndex(); buffer[1] = proto_id->ReturnType()->GetIndex(); buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset(); @@ -282,7 +286,7 @@ void DexWriter::WriteProtos() { void DexWriter::WriteFields() { uint16_t buffer[4]; - for (std::unique_ptr<dex_ir::FieldId>& field_id : header_.GetCollections().FieldIds()) { + for (std::unique_ptr<dex_ir::FieldId>& field_id : header_->GetCollections().FieldIds()) { buffer[0] = field_id->Class()->GetIndex(); buffer[1] = field_id->Type()->GetIndex(); buffer[2] = field_id->Name()->GetIndex(); @@ -293,7 +297,7 @@ void DexWriter::WriteFields() { void DexWriter::WriteMethods() { uint16_t buffer[4]; - for (std::unique_ptr<dex_ir::MethodId>& method_id : header_.GetCollections().MethodIds()) { + for (std::unique_ptr<dex_ir::MethodId>& method_id : header_->GetCollections().MethodIds()) { buffer[0] = method_id->Class()->GetIndex(); buffer[1] = method_id->Proto()->GetIndex(); buffer[2] = method_id->Name()->GetIndex(); @@ -303,16 +307,16 @@ void DexWriter::WriteMethods() { } void DexWriter::WriteEncodedArrays() { - for (std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array : - header_.GetCollections().EncodedArrayItems()) { + for (auto& encoded_array_pair : header_->GetCollections().EncodedArrayItems()) { + std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array = encoded_array_pair.second; WriteEncodedArray(encoded_array->GetEncodedValues(), encoded_array->GetOffset()); } } void DexWriter::WriteAnnotations() { uint8_t visibility[1]; - for (std::unique_ptr<dex_ir::AnnotationItem>& annotation : - header_.GetCollections().AnnotationItems()) { + for (auto& annotation_pair : header_->GetCollections().AnnotationItems()) { + std::unique_ptr<dex_ir::AnnotationItem>& annotation = annotation_pair.second; visibility[0] = annotation->GetVisibility(); size_t offset = annotation->GetOffset(); offset += Write(visibility, sizeof(uint8_t), offset); @@ -323,8 +327,8 @@ void DexWriter::WriteAnnotations() { void DexWriter::WriteAnnotationSets() { uint32_t size[1]; uint32_t annotation_off[1]; - for (std::unique_ptr<dex_ir::AnnotationSetItem>& annotation_set : - header_.GetCollections().AnnotationSetItems()) { + for (auto& annotation_set_pair : header_->GetCollections().AnnotationSetItems()) { + std::unique_ptr<dex_ir::AnnotationSetItem>& annotation_set = annotation_set_pair.second; size[0] = annotation_set->GetItems()->size(); size_t offset = annotation_set->GetOffset(); offset += Write(size, sizeof(uint32_t), offset); @@ -338,8 +342,8 @@ void DexWriter::WriteAnnotationSets() { void DexWriter::WriteAnnotationSetRefs() { uint32_t size[1]; uint32_t annotations_off[1]; - for (std::unique_ptr<dex_ir::AnnotationSetRefList>& annotation_set_ref : - header_.GetCollections().AnnotationSetRefLists()) { + for (auto& anno_set_ref_pair : header_->GetCollections().AnnotationSetRefLists()) { + std::unique_ptr<dex_ir::AnnotationSetRefList>& annotation_set_ref = anno_set_ref_pair.second; size[0] = annotation_set_ref->GetItems()->size(); size_t offset = annotation_set_ref->GetOffset(); offset += Write(size, sizeof(uint32_t), offset); @@ -353,8 +357,9 @@ void DexWriter::WriteAnnotationSetRefs() { void DexWriter::WriteAnnotationsDirectories() { uint32_t directory_buffer[4]; uint32_t annotation_buffer[2]; - for (std::unique_ptr<dex_ir::AnnotationsDirectoryItem>& annotations_directory : - header_.GetCollections().AnnotationsDirectoryItems()) { + for (auto& annotations_directory_pair : header_->GetCollections().AnnotationsDirectoryItems()) { + std::unique_ptr<dex_ir::AnnotationsDirectoryItem>& annotations_directory = + annotations_directory_pair.second; directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 : annotations_directory->GetClassAnnotation()->GetOffset(); directory_buffer[1] = annotations_directory->GetFieldAnnotations() == nullptr ? 0 : @@ -393,15 +398,17 @@ void DexWriter::WriteAnnotationsDirectories() { } void DexWriter::WriteDebugInfoItems() { - for (std::unique_ptr<dex_ir::DebugInfoItem>& info : header_.GetCollections().DebugInfoItems()) { - Write(info->GetDebugInfo(), info->GetDebugInfoSize(), info->GetOffset()); + for (auto& debug_info_pair : header_->GetCollections().DebugInfoItems()) { + std::unique_ptr<dex_ir::DebugInfoItem>& debug_info = debug_info_pair.second; + Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize(), debug_info->GetOffset()); } } void DexWriter::WriteCodeItems() { uint16_t uint16_buffer[4]; uint32_t uint32_buffer[2]; - for (std::unique_ptr<dex_ir::CodeItem>& code_item : header_.GetCollections().CodeItems()) { + for (auto& code_item_pair : header_->GetCollections().CodeItems()) { + std::unique_ptr<dex_ir::CodeItem>& code_item = code_item_pair.second; uint16_buffer[0] = code_item->RegistersSize(); uint16_buffer[1] = code_item->InsSize(); uint16_buffer[2] = code_item->OutsSize(); @@ -446,7 +453,7 @@ void DexWriter::WriteCodeItems() { void DexWriter::WriteClasses() { uint32_t class_def_buffer[8]; - for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_.GetCollections().ClassDefs()) { + for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) { class_def_buffer[0] = class_def->ClassType()->GetIndex(); class_def_buffer[1] = class_def->GetAccessFlags(); class_def_buffer[2] = class_def->Superclass() == nullptr ? DexFile::kDexNoIndex : @@ -464,7 +471,8 @@ void DexWriter::WriteClasses() { Write(class_def_buffer, class_def->GetSize(), offset); } - for (std::unique_ptr<dex_ir::ClassData>& class_data : header_.GetCollections().ClassDatas()) { + for (auto& class_data_pair : header_->GetCollections().ClassDatas()) { + std::unique_ptr<dex_ir::ClassData>& class_data = class_data_pair.second; size_t offset = class_data->GetOffset(); offset += WriteUleb128(class_data->StaticFields()->size(), offset); offset += WriteUleb128(class_data->InstanceFields()->size(), offset); @@ -491,7 +499,7 @@ struct MapItemContainer { }; void DexWriter::WriteMapItem() { - dex_ir::Collections& collection = header_.GetCollections(); + dex_ir::Collections& collection = header_->GetCollections(); std::priority_queue<MapItemContainer> queue; // Header and index section. @@ -522,7 +530,7 @@ void DexWriter::WriteMapItem() { } // Data section. - queue.push(MapItemContainer(DexFile::kDexTypeMapList, 1, collection.MapItemOffset())); + queue.push(MapItemContainer(DexFile::kDexTypeMapList, 1, collection.MapListOffset())); if (collection.TypeListsSize() != 0) { queue.push(MapItemContainer(DexFile::kDexTypeTypeList, collection.TypeListsSize(), collection.TypeListsOffset())); @@ -564,7 +572,7 @@ void DexWriter::WriteMapItem() { collection.AnnotationsDirectoryItemsSize(), collection.AnnotationsDirectoryItemsOffset())); } - uint32_t offset = collection.MapItemOffset(); + uint32_t offset = collection.MapListOffset(); uint16_t uint16_buffer[2]; uint32_t uint32_buffer[2]; uint16_buffer[1] = 0; @@ -583,19 +591,19 @@ void DexWriter::WriteMapItem() { void DexWriter::WriteHeader() { uint32_t buffer[20]; - dex_ir::Collections& collections = header_.GetCollections(); + dex_ir::Collections& collections = header_->GetCollections(); size_t offset = 0; - offset += Write(header_.Magic(), 8 * sizeof(uint8_t), offset); - buffer[0] = header_.Checksum(); + offset += Write(header_->Magic(), 8 * sizeof(uint8_t), offset); + buffer[0] = header_->Checksum(); offset += Write(buffer, sizeof(uint32_t), offset); - offset += Write(header_.Signature(), 20 * sizeof(uint8_t), offset); - uint32_t file_size = header_.FileSize(); + offset += Write(header_->Signature(), 20 * sizeof(uint8_t), offset); + uint32_t file_size = header_->FileSize(); buffer[0] = file_size; - buffer[1] = header_.GetSize(); - buffer[2] = header_.EndianTag(); - buffer[3] = header_.LinkSize(); - buffer[4] = header_.LinkOffset(); - buffer[5] = collections.MapItemOffset(); + buffer[1] = header_->GetSize(); + buffer[2] = header_->EndianTag(); + buffer[3] = header_->LinkSize(); + buffer[4] = header_->LinkOffset(); + buffer[5] = collections.MapListOffset(); buffer[6] = collections.StringIdsSize(); buffer[7] = collections.StringIdsOffset(); buffer[8] = collections.TypeIdsSize(); @@ -617,12 +625,7 @@ void DexWriter::WriteHeader() { Write(buffer, 20 * sizeof(uint32_t), offset); } -void DexWriter::WriteFile() { - if (dex_file_.get() == nullptr) { - fprintf(stderr, "Can't open output dex file\n"); - return; - } - +void DexWriter::WriteMemMap() { WriteStrings(); WriteTypes(); WriteTypeLists(); @@ -641,8 +644,9 @@ void DexWriter::WriteFile() { WriteHeader(); } -void DexWriter::OutputDexFile(dex_ir::Header& header, const char* file_name) { - (new DexWriter(header, file_name))->WriteFile(); +void DexWriter::Output(dex_ir::Header* header, MemMap* mem_map) { + DexWriter dex_writer(header, mem_map); + dex_writer.WriteMemMap(); } } // namespace art diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h index 9104295472..fb76e5ccfc 100644 --- a/dexlayout/dex_writer.h +++ b/dexlayout/dex_writer.h @@ -21,19 +21,19 @@ #include "base/unix_file/fd_file.h" #include "dex_ir.h" +#include "mem_map.h" #include "os.h" namespace art { class DexWriter { public: - DexWriter(dex_ir::Header& header, const char* file_name) : header_(header), - dex_file_(OS::CreateEmptyFileWriteOnly(file_name)) { } + DexWriter(dex_ir::Header* header, MemMap* mem_map) : header_(header), mem_map_(mem_map) { } - static void OutputDexFile(dex_ir::Header& header, const char* file_name); + static void Output(dex_ir::Header* header, MemMap* mem_map); private: - void WriteFile(); + void WriteMemMap(); size_t Write(const void* buffer, size_t length, size_t offset); size_t WriteSleb128(uint32_t value, size_t offset); @@ -62,13 +62,12 @@ class DexWriter { void WriteMapItem(); void WriteHeader(); - dex_ir::Header& header_; - std::unique_ptr<File> dex_file_; + dex_ir::Header* const header_; + MemMap* const mem_map_; DISALLOW_COPY_AND_ASSIGN(DexWriter); }; - } // namespace art #endif // ART_DEXLAYOUT_DEX_WRITER_H_ diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index aa806557c2..cfe48378ae 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -37,27 +37,13 @@ #include "dex_visualize.h" #include "dex_writer.h" #include "jit/offline_profiling_info.h" +#include "mem_map.h" #include "os.h" #include "utils.h" namespace art { /* - * Options parsed in main driver. - */ -struct Options options_; - -/* - * Output file. Defaults to stdout. - */ -FILE* out_file_ = stdout; - -/* - * Profile information file. - */ -ProfileCompilationInfo* profile_info_ = nullptr; - -/* * Flags for use with createAccessFlagStr(). */ enum AccessFor { @@ -301,72 +287,209 @@ static void Asciify(char* out, const unsigned char* data, size_t len) { /* * Dumps a string value with some escape characters. */ -static void DumpEscapedString(const char* p) { - fputs("\"", out_file_); +static void DumpEscapedString(const char* p, FILE* out_file) { + fputs("\"", out_file); for (; *p; p++) { switch (*p) { case '\\': - fputs("\\\\", out_file_); + fputs("\\\\", out_file); break; case '\"': - fputs("\\\"", out_file_); + fputs("\\\"", out_file); break; case '\t': - fputs("\\t", out_file_); + fputs("\\t", out_file); break; case '\n': - fputs("\\n", out_file_); + fputs("\\n", out_file); break; case '\r': - fputs("\\r", out_file_); + fputs("\\r", out_file); break; default: - putc(*p, out_file_); + putc(*p, out_file); } // switch } // for - fputs("\"", out_file_); + fputs("\"", out_file); } /* * Dumps a string as an XML attribute value. */ -static void DumpXmlAttribute(const char* p) { +static void DumpXmlAttribute(const char* p, FILE* out_file) { for (; *p; p++) { switch (*p) { case '&': - fputs("&", out_file_); + fputs("&", out_file); break; case '<': - fputs("<", out_file_); + fputs("<", out_file); break; case '>': - fputs(">", out_file_); + fputs(">", out_file); break; case '"': - fputs(""", out_file_); + fputs(""", out_file); break; case '\t': - fputs("	", out_file_); + fputs("	", out_file); break; case '\n': - fputs("
", out_file_); + fputs("
", out_file); break; case '\r': - fputs("
", out_file_); + fputs("
", out_file); break; default: - putc(*p, out_file_); + putc(*p, out_file); } // switch } // for } -// Forward declare to resolve circular dependence. -static void DumpEncodedValue(const dex_ir::EncodedValue* data); +/* + * Helper for dumpInstruction(), which builds the string + * representation for the index in the given instruction. + * Returns a pointer to a buffer of sufficient size. + */ +static std::unique_ptr<char[]> IndexString(dex_ir::Header* header, + const Instruction* dec_insn, + size_t buf_size) { + std::unique_ptr<char[]> buf(new char[buf_size]); + // Determine index and width of the string. + uint32_t index = 0; + uint32_t secondary_index = DexFile::kDexNoIndex; + uint32_t width = 4; + switch (Instruction::FormatOf(dec_insn->Opcode())) { + // SOME NOT SUPPORTED: + // case Instruction::k20bc: + case Instruction::k21c: + case Instruction::k35c: + // case Instruction::k35ms: + case Instruction::k3rc: + // case Instruction::k3rms: + // case Instruction::k35mi: + // case Instruction::k3rmi: + index = dec_insn->VRegB(); + width = 4; + break; + case Instruction::k31c: + index = dec_insn->VRegB(); + width = 8; + break; + case Instruction::k22c: + // case Instruction::k22cs: + index = dec_insn->VRegC(); + width = 4; + break; + case Instruction::k45cc: + case Instruction::k4rcc: + index = dec_insn->VRegB(); + secondary_index = dec_insn->VRegH(); + width = 4; + default: + break; + } // switch + + // Determine index type. + size_t outSize = 0; + switch (Instruction::IndexTypeOf(dec_insn->Opcode())) { + case Instruction::kIndexUnknown: + // This function should never get called for this type, but do + // something sensible here, just to help with debugging. + outSize = snprintf(buf.get(), buf_size, "<unknown-index>"); + break; + case Instruction::kIndexNone: + // This function should never get called for this type, but do + // something sensible here, just to help with debugging. + outSize = snprintf(buf.get(), buf_size, "<no-index>"); + break; + case Instruction::kIndexTypeRef: + if (index < header->GetCollections().TypeIdsSize()) { + const char* tp = header->GetCollections().GetTypeId(index)->GetStringId()->Data(); + outSize = snprintf(buf.get(), buf_size, "%s // type@%0*x", tp, width, index); + } else { + outSize = snprintf(buf.get(), buf_size, "<type?> // type@%0*x", width, index); + } + break; + case Instruction::kIndexStringRef: + if (index < header->GetCollections().StringIdsSize()) { + const char* st = header->GetCollections().GetStringId(index)->Data(); + outSize = snprintf(buf.get(), buf_size, "\"%s\" // string@%0*x", st, width, index); + } else { + outSize = snprintf(buf.get(), buf_size, "<string?> // string@%0*x", width, index); + } + break; + case Instruction::kIndexMethodRef: + if (index < header->GetCollections().MethodIdsSize()) { + dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(index); + const char* name = method_id->Name()->Data(); + std::string type_descriptor = GetSignatureForProtoId(method_id->Proto()); + const char* back_descriptor = method_id->Class()->GetStringId()->Data(); + outSize = snprintf(buf.get(), buf_size, "%s.%s:%s // method@%0*x", + back_descriptor, name, type_descriptor.c_str(), width, index); + } else { + outSize = snprintf(buf.get(), buf_size, "<method?> // method@%0*x", width, index); + } + break; + case Instruction::kIndexFieldRef: + if (index < header->GetCollections().FieldIdsSize()) { + dex_ir::FieldId* field_id = header->GetCollections().GetFieldId(index); + const char* name = field_id->Name()->Data(); + const char* type_descriptor = field_id->Type()->GetStringId()->Data(); + const char* back_descriptor = field_id->Class()->GetStringId()->Data(); + outSize = snprintf(buf.get(), buf_size, "%s.%s:%s // field@%0*x", + back_descriptor, name, type_descriptor, width, index); + } else { + outSize = snprintf(buf.get(), buf_size, "<field?> // field@%0*x", width, index); + } + break; + case Instruction::kIndexVtableOffset: + outSize = snprintf(buf.get(), buf_size, "[%0*x] // vtable #%0*x", + width, index, width, index); + break; + case Instruction::kIndexFieldOffset: + outSize = snprintf(buf.get(), buf_size, "[obj+%0*x]", width, index); + break; + case Instruction::kIndexMethodAndProtoRef: { + std::string method("<method?>"); + std::string proto("<proto?>"); + if (index < header->GetCollections().MethodIdsSize()) { + dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(index); + const char* name = method_id->Name()->Data(); + std::string type_descriptor = GetSignatureForProtoId(method_id->Proto()); + const char* back_descriptor = method_id->Class()->GetStringId()->Data(); + method = StringPrintf("%s.%s:%s", back_descriptor, name, type_descriptor.c_str()); + } + if (secondary_index < header->GetCollections().ProtoIdsSize()) { + dex_ir::ProtoId* proto_id = header->GetCollections().GetProtoId(secondary_index); + proto = GetSignatureForProtoId(proto_id); + } + outSize = snprintf(buf.get(), buf_size, "%s, %s // method@%0*x, proto@%0*x", + method.c_str(), proto.c_str(), width, index, width, secondary_index); + } + break; + // SOME NOT SUPPORTED: + // case Instruction::kIndexVaries: + // case Instruction::kIndexInlineMethod: + default: + outSize = snprintf(buf.get(), buf_size, "<?>"); + break; + } // switch + + // Determine success of string construction. + if (outSize >= buf_size) { + // The buffer wasn't big enough; retry with computed size. Note: snprintf() + // doesn't count/ the '\0' as part of its returned size, so we add explicit + // space for it here. + return IndexString(header, dec_insn, outSize + 1); + } + return buf; +} /* * Dumps encoded annotation. */ -static void DumpEncodedAnnotation(dex_ir::EncodedAnnotation* annotation) { +void DexLayout::DumpEncodedAnnotation(dex_ir::EncodedAnnotation* annotation) { fputs(annotation->GetType()->GetStringId()->Data(), out_file_); // Display all name=value pairs. for (auto& subannotation : *annotation->GetAnnotationElements()) { @@ -379,7 +502,7 @@ static void DumpEncodedAnnotation(dex_ir::EncodedAnnotation* annotation) { /* * Dumps encoded value. */ -static void DumpEncodedValue(const dex_ir::EncodedValue* data) { +void DexLayout::DumpEncodedValue(const dex_ir::EncodedValue* data) { switch (data->Type()) { case DexFile::kDexAnnotationByte: fprintf(out_file_, "%" PRId8, data->GetByte()); @@ -407,9 +530,9 @@ static void DumpEncodedValue(const dex_ir::EncodedValue* data) { case DexFile::kDexAnnotationString: { dex_ir::StringId* string_id = data->GetStringId(); if (options_.output_format_ == kOutputPlain) { - DumpEscapedString(string_id->Data()); + DumpEscapedString(string_id->Data(), out_file_); } else { - DumpXmlAttribute(string_id->Data()); + DumpXmlAttribute(string_id->Data(), out_file_); } break; } @@ -458,22 +581,22 @@ static void DumpEncodedValue(const dex_ir::EncodedValue* data) { /* * Dumps the file header. */ -static void DumpFileHeader(dex_ir::Header* header) { +void DexLayout::DumpFileHeader() { char sanitized[8 * 2 + 1]; - dex_ir::Collections& collections = header->GetCollections(); + dex_ir::Collections& collections = header_->GetCollections(); fprintf(out_file_, "DEX file header:\n"); - Asciify(sanitized, header->Magic(), 8); + Asciify(sanitized, header_->Magic(), 8); fprintf(out_file_, "magic : '%s'\n", sanitized); - fprintf(out_file_, "checksum : %08x\n", header->Checksum()); + fprintf(out_file_, "checksum : %08x\n", header_->Checksum()); fprintf(out_file_, "signature : %02x%02x...%02x%02x\n", - header->Signature()[0], header->Signature()[1], - header->Signature()[DexFile::kSha1DigestSize - 2], - header->Signature()[DexFile::kSha1DigestSize - 1]); - fprintf(out_file_, "file_size : %d\n", header->FileSize()); - fprintf(out_file_, "header_size : %d\n", header->HeaderSize()); - fprintf(out_file_, "link_size : %d\n", header->LinkSize()); + header_->Signature()[0], header_->Signature()[1], + header_->Signature()[DexFile::kSha1DigestSize - 2], + header_->Signature()[DexFile::kSha1DigestSize - 1]); + fprintf(out_file_, "file_size : %d\n", header_->FileSize()); + fprintf(out_file_, "header_size : %d\n", header_->HeaderSize()); + fprintf(out_file_, "link_size : %d\n", header_->LinkSize()); fprintf(out_file_, "link_off : %d (0x%06x)\n", - header->LinkOffset(), header->LinkOffset()); + header_->LinkOffset(), header_->LinkOffset()); fprintf(out_file_, "string_ids_size : %d\n", collections.StringIdsSize()); fprintf(out_file_, "string_ids_off : %d (0x%06x)\n", collections.StringIdsOffset(), collections.StringIdsOffset()); @@ -492,17 +615,17 @@ static void DumpFileHeader(dex_ir::Header* header) { fprintf(out_file_, "class_defs_size : %d\n", collections.ClassDefsSize()); fprintf(out_file_, "class_defs_off : %d (0x%06x)\n", collections.ClassDefsOffset(), collections.ClassDefsOffset()); - fprintf(out_file_, "data_size : %d\n", header->DataSize()); + fprintf(out_file_, "data_size : %d\n", header_->DataSize()); fprintf(out_file_, "data_off : %d (0x%06x)\n\n", - header->DataOffset(), header->DataOffset()); + header_->DataOffset(), header_->DataOffset()); } /* * Dumps a class_def_item. */ -static void DumpClassDef(dex_ir::Header* header, int idx) { +void DexLayout::DumpClassDef(int idx) { // General class information. - dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(idx); + dex_ir::ClassDef* class_def = header_->GetCollections().GetClassDef(idx); fprintf(out_file_, "Class #%d header:\n", idx); fprintf(out_file_, "class_idx : %d\n", class_def->ClassType()->GetIndex()); fprintf(out_file_, "access_flags : %d (0x%04x)\n", @@ -558,7 +681,7 @@ static void DumpClassDef(dex_ir::Header* header, int idx) { /** * Dumps an annotation set item. */ -static void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item) { +void DexLayout::DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item) { if (set_item == nullptr || set_item->GetItems()->size() == 0) { fputs(" empty-annotation-set\n", out_file_); return; @@ -582,8 +705,8 @@ static void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item) { /* * Dumps class annotations. */ -static void DumpClassAnnotations(dex_ir::Header* header, int idx) { - dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(idx); +void DexLayout::DumpClassAnnotations(int idx) { + dex_ir::ClassDef* class_def = header_->GetCollections().GetClassDef(idx); dex_ir::AnnotationsDirectoryItem* annotations_directory = class_def->Annotations(); if (annotations_directory == nullptr) { return; // none @@ -646,7 +769,7 @@ static void DumpClassAnnotations(dex_ir::Header* header, int idx) { /* * Dumps an interface that a class declares to implement. */ -static void DumpInterface(const dex_ir::TypeId* type_item, int i) { +void DexLayout::DumpInterface(const dex_ir::TypeId* type_item, int i) { const char* interface_name = type_item->GetStringId()->Data(); if (options_.output_format_ == kOutputPlain) { fprintf(out_file_, " #%d : '%s'\n", i, interface_name); @@ -659,7 +782,7 @@ static void DumpInterface(const dex_ir::TypeId* type_item, int i) { /* * Dumps the catches table associated with the code. */ -static void DumpCatches(const dex_ir::CodeItem* code) { +void DexLayout::DumpCatches(const dex_ir::CodeItem* code) { const uint16_t tries_size = code->TriesSize(); // No catch table. @@ -687,7 +810,7 @@ static void DumpCatches(const dex_ir::CodeItem* code) { /* * Dumps all positions table entries associated with the code. */ -static void DumpPositionInfo(const dex_ir::CodeItem* code) { +void DexLayout::DumpPositionInfo(const dex_ir::CodeItem* code) { dex_ir::DebugInfoItem* debug_info = code->DebugInfo(); if (debug_info == nullptr) { return; @@ -701,7 +824,7 @@ static void DumpPositionInfo(const dex_ir::CodeItem* code) { /* * Dumps all locals table entries associated with the code. */ -static void DumpLocalInfo(const dex_ir::CodeItem* code) { +void DexLayout::DumpLocalInfo(const dex_ir::CodeItem* code) { dex_ir::DebugInfoItem* debug_info = code->DebugInfo(); if (debug_info == nullptr) { return; @@ -716,153 +839,13 @@ static void DumpLocalInfo(const dex_ir::CodeItem* code) { } /* - * Helper for dumpInstruction(), which builds the string - * representation for the index in the given instruction. - * Returns a pointer to a buffer of sufficient size. - */ -static std::unique_ptr<char[]> IndexString(dex_ir::Header* header, - const Instruction* dec_insn, - size_t buf_size) { - static const uint32_t kInvalidIndex = std::numeric_limits<uint32_t>::max(); - std::unique_ptr<char[]> buf(new char[buf_size]); - // Determine index and width of the string. - uint32_t index = 0; - uint32_t secondary_index = kInvalidIndex; - uint32_t width = 4; - switch (Instruction::FormatOf(dec_insn->Opcode())) { - // SOME NOT SUPPORTED: - // case Instruction::k20bc: - case Instruction::k21c: - case Instruction::k35c: - // case Instruction::k35ms: - case Instruction::k3rc: - // case Instruction::k3rms: - // case Instruction::k35mi: - // case Instruction::k3rmi: - index = dec_insn->VRegB(); - width = 4; - break; - case Instruction::k31c: - index = dec_insn->VRegB(); - width = 8; - break; - case Instruction::k22c: - // case Instruction::k22cs: - index = dec_insn->VRegC(); - width = 4; - break; - case Instruction::k45cc: - case Instruction::k4rcc: - index = dec_insn->VRegB(); - secondary_index = dec_insn->VRegH(); - width = 4; - break; - default: - break; - } // switch - - // Determine index type. - size_t outSize = 0; - switch (Instruction::IndexTypeOf(dec_insn->Opcode())) { - case Instruction::kIndexUnknown: - // This function should never get called for this type, but do - // something sensible here, just to help with debugging. - outSize = snprintf(buf.get(), buf_size, "<unknown-index>"); - break; - case Instruction::kIndexNone: - // This function should never get called for this type, but do - // something sensible here, just to help with debugging. - outSize = snprintf(buf.get(), buf_size, "<no-index>"); - break; - case Instruction::kIndexTypeRef: - if (index < header->GetCollections().TypeIdsSize()) { - const char* tp = header->GetCollections().GetTypeId(index)->GetStringId()->Data(); - outSize = snprintf(buf.get(), buf_size, "%s // type@%0*x", tp, width, index); - } else { - outSize = snprintf(buf.get(), buf_size, "<type?> // type@%0*x", width, index); - } - break; - case Instruction::kIndexStringRef: - if (index < header->GetCollections().StringIdsSize()) { - const char* st = header->GetCollections().GetStringId(index)->Data(); - outSize = snprintf(buf.get(), buf_size, "\"%s\" // string@%0*x", st, width, index); - } else { - outSize = snprintf(buf.get(), buf_size, "<string?> // string@%0*x", width, index); - } - break; - case Instruction::kIndexMethodRef: - if (index < header->GetCollections().MethodIdsSize()) { - dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(index); - const char* name = method_id->Name()->Data(); - std::string type_descriptor = GetSignatureForProtoId(method_id->Proto()); - const char* back_descriptor = method_id->Class()->GetStringId()->Data(); - outSize = snprintf(buf.get(), buf_size, "%s.%s:%s // method@%0*x", - back_descriptor, name, type_descriptor.c_str(), width, index); - } else { - outSize = snprintf(buf.get(), buf_size, "<method?> // method@%0*x", width, index); - } - break; - case Instruction::kIndexFieldRef: - if (index < header->GetCollections().FieldIdsSize()) { - dex_ir::FieldId* field_id = header->GetCollections().GetFieldId(index); - const char* name = field_id->Name()->Data(); - const char* type_descriptor = field_id->Type()->GetStringId()->Data(); - const char* back_descriptor = field_id->Class()->GetStringId()->Data(); - outSize = snprintf(buf.get(), buf_size, "%s.%s:%s // field@%0*x", - back_descriptor, name, type_descriptor, width, index); - } else { - outSize = snprintf(buf.get(), buf_size, "<field?> // field@%0*x", width, index); - } - break; - case Instruction::kIndexVtableOffset: - outSize = snprintf(buf.get(), buf_size, "[%0*x] // vtable #%0*x", - width, index, width, index); - break; - case Instruction::kIndexFieldOffset: - outSize = snprintf(buf.get(), buf_size, "[obj+%0*x]", width, index); - break; - // SOME NOT SUPPORTED: - // case Instruction::kIndexVaries: - // case Instruction::kIndexInlineMethod: - case Instruction::kIndexMethodAndProtoRef: { - std::string method("<method?>"); - std::string proto("<proto?>"); - if (index < header->GetCollections().MethodIdsSize()) { - dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(index); - const char* name = method_id->Name()->Data(); - std::string type_descriptor = GetSignatureForProtoId(method_id->Proto()); - const char* back_descriptor = method_id->Class()->GetStringId()->Data(); - method = StringPrintf("%s.%s:%s", back_descriptor, name, type_descriptor.c_str()); - } - if (secondary_index < header->GetCollections().ProtoIdsSize()) { - dex_ir::ProtoId* proto_id = header->GetCollections().GetProtoId(secondary_index); - proto = GetSignatureForProtoId(proto_id); - } - outSize = snprintf(buf.get(), buf_size, "%s, %s // method@%0*x, proto@%0*x", - method.c_str(), proto.c_str(), width, index, width, secondary_index); - } - break; - default: - outSize = snprintf(buf.get(), buf_size, "<?>"); - break; - } // switch - - // Determine success of string construction. - if (outSize >= buf_size) { - // The buffer wasn't big enough; retry with computed size. Note: snprintf() - // doesn't count/ the '\0' as part of its returned size, so we add explicit - // space for it here. - return IndexString(header, dec_insn, outSize + 1); - } - return buf; -} - -/* * Dumps a single instruction. */ -static void DumpInstruction(dex_ir::Header* header, const dex_ir::CodeItem* code, - uint32_t code_offset, uint32_t insn_idx, uint32_t insn_width, - const Instruction* dec_insn) { +void DexLayout::DumpInstruction(const dex_ir::CodeItem* code, + uint32_t code_offset, + uint32_t insn_idx, + uint32_t insn_width, + const Instruction* dec_insn) { // Address of instruction (expressed as byte offset). fprintf(out_file_, "%06x:", code_offset + 0x10 + insn_idx * 2); @@ -901,7 +884,7 @@ static void DumpInstruction(dex_ir::Header* header, const dex_ir::CodeItem* code // Set up additional argument. std::unique_ptr<char[]> index_buf; if (Instruction::IndexTypeOf(dec_insn->Opcode()) != Instruction::kIndexNone) { - index_buf = IndexString(header, dec_insn, 200); + index_buf = IndexString(header_, dec_insn, 200); } // Dump the instruction. @@ -1073,9 +1056,8 @@ static void DumpInstruction(dex_ir::Header* header, const dex_ir::CodeItem* code /* * Dumps a bytecode disassembly. */ -static void DumpBytecodes(dex_ir::Header* header, uint32_t idx, - const dex_ir::CodeItem* code, uint32_t code_offset) { - dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(idx); +void DexLayout::DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset) { + dex_ir::MethodId* method_id = header_->GetCollections().GetMethodId(idx); const char* name = method_id->Name()->Data(); std::string type_descriptor = GetSignatureForProtoId(method_id->Proto()); const char* back_descriptor = method_id->Class()->GetStringId()->Data(); @@ -1094,7 +1076,7 @@ static void DumpBytecodes(dex_ir::Header* header, uint32_t idx, fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", insn_idx); break; } - DumpInstruction(header, code, code_offset, insn_idx, insn_width, instruction); + DumpInstruction(code, code_offset, insn_idx, insn_width, instruction); insn_idx += insn_width; } // for } @@ -1102,8 +1084,7 @@ static void DumpBytecodes(dex_ir::Header* header, uint32_t idx, /* * Dumps code of a method. */ -static void DumpCode(dex_ir::Header* header, uint32_t idx, const dex_ir::CodeItem* code, - uint32_t code_offset) { +void DexLayout::DumpCode(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset) { fprintf(out_file_, " registers : %d\n", code->RegistersSize()); fprintf(out_file_, " ins : %d\n", code->InsSize()); fprintf(out_file_, " outs : %d\n", code->OutsSize()); @@ -1112,7 +1093,7 @@ static void DumpCode(dex_ir::Header* header, uint32_t idx, const dex_ir::CodeIte // Bytecode disassembly, if requested. if (options_.disassemble_) { - DumpBytecodes(header, idx, code, code_offset); + DumpBytecodes(idx, code, code_offset); } // Try-catch blocks. @@ -1128,14 +1109,13 @@ static void DumpCode(dex_ir::Header* header, uint32_t idx, const dex_ir::CodeIte /* * Dumps a method. */ -static void DumpMethod(dex_ir::Header* header, uint32_t idx, uint32_t flags, - const dex_ir::CodeItem* code, int i) { +void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* code, int i) { // Bail for anything private if export only requested. if (options_.exports_only_ && (flags & (kAccPublic | kAccProtected)) == 0) { return; } - dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(idx); + dex_ir::MethodId* method_id = header_->GetCollections().GetMethodId(idx); const char* name = method_id->Name()->Data(); char* type_descriptor = strdup(GetSignatureForProtoId(method_id->Proto()).c_str()); const char* back_descriptor = method_id->Class()->GetStringId()->Data(); @@ -1150,7 +1130,7 @@ static void DumpMethod(dex_ir::Header* header, uint32_t idx, uint32_t flags, fprintf(out_file_, " code : (none)\n"); } else { fprintf(out_file_, " code -\n"); - DumpCode(header, idx, code, code->GetOffset()); + DumpCode(idx, code, code->GetOffset()); } if (options_.disassemble_) { fputc('\n', out_file_); @@ -1233,14 +1213,13 @@ static void DumpMethod(dex_ir::Header* header, uint32_t idx, uint32_t flags, /* * Dumps a static (class) field. */ -static void DumpSField(dex_ir::Header* header, uint32_t idx, uint32_t flags, - int i, dex_ir::EncodedValue* init) { +void DexLayout::DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedValue* init) { // Bail for anything private if export only requested. if (options_.exports_only_ && (flags & (kAccPublic | kAccProtected)) == 0) { return; } - dex_ir::FieldId* field_id = header->GetCollections().GetFieldId(idx); + dex_ir::FieldId* field_id = header_->GetCollections().GetFieldId(idx); const char* name = field_id->Name()->Data(); const char* type_descriptor = field_id->Type()->GetStringId()->Data(); const char* back_descriptor = field_id->Class()->GetStringId()->Data(); @@ -1281,8 +1260,8 @@ static void DumpSField(dex_ir::Header* header, uint32_t idx, uint32_t flags, /* * Dumps an instance field. */ -static void DumpIField(dex_ir::Header* header, uint32_t idx, uint32_t flags, int i) { - DumpSField(header, idx, flags, i, nullptr); +void DexLayout::DumpIField(uint32_t idx, uint32_t flags, int i) { + DumpSField(idx, flags, i, nullptr); } /* @@ -1293,19 +1272,19 @@ static void DumpIField(dex_ir::Header* header, uint32_t idx, uint32_t flags, int * If "*last_package" is nullptr or does not match the current class' package, * the value will be replaced with a newly-allocated string. */ -static void DumpClass(dex_ir::Header* header, int idx, char** last_package) { - dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(idx); +void DexLayout::DumpClass(int idx, char** last_package) { + dex_ir::ClassDef* class_def = header_->GetCollections().GetClassDef(idx); // Omitting non-public class. if (options_.exports_only_ && (class_def->GetAccessFlags() & kAccPublic) == 0) { return; } if (options_.show_section_headers_) { - DumpClassDef(header, idx); + DumpClassDef(idx); } if (options_.show_annotations_) { - DumpClassAnnotations(header, idx); + DumpClassAnnotations(idx); } // For the XML output, show the package name. Ideally we'd gather @@ -1313,7 +1292,7 @@ static void DumpClass(dex_ir::Header* header, int idx, char** last_package) { // package name wouldn't jump around, but that's not a great plan // for something that needs to run on the device. const char* class_descriptor = - header->GetCollections().GetClassDef(idx)->ClassType()->GetStringId()->Data(); + header_->GetCollections().GetClassDef(idx)->ClassType()->GetStringId()->Data(); if (!(class_descriptor[0] == 'L' && class_descriptor[strlen(class_descriptor)-1] == ';')) { // Arrays and primitives should not be defined explicitly. Keep going? @@ -1406,8 +1385,7 @@ static void DumpClass(dex_ir::Header* header, int idx, char** last_package) { dex_ir::FieldItemVector* static_fields = class_data->StaticFields(); if (static_fields != nullptr) { for (uint32_t i = 0; i < static_fields->size(); i++) { - DumpSField(header, - (*static_fields)[i]->GetFieldId()->GetIndex(), + DumpSField((*static_fields)[i]->GetFieldId()->GetIndex(), (*static_fields)[i]->GetAccessFlags(), i, i < encoded_values_size ? (*encoded_values)[i].get() : nullptr); @@ -1423,8 +1401,7 @@ static void DumpClass(dex_ir::Header* header, int idx, char** last_package) { dex_ir::FieldItemVector* instance_fields = class_data->InstanceFields(); if (instance_fields != nullptr) { for (uint32_t i = 0; i < instance_fields->size(); i++) { - DumpIField(header, - (*instance_fields)[i]->GetFieldId()->GetIndex(), + DumpIField((*instance_fields)[i]->GetFieldId()->GetIndex(), (*instance_fields)[i]->GetAccessFlags(), i); } // for @@ -1439,8 +1416,7 @@ static void DumpClass(dex_ir::Header* header, int idx, char** last_package) { dex_ir::MethodItemVector* direct_methods = class_data->DirectMethods(); if (direct_methods != nullptr) { for (uint32_t i = 0; i < direct_methods->size(); i++) { - DumpMethod(header, - (*direct_methods)[i]->GetMethodId()->GetIndex(), + DumpMethod((*direct_methods)[i]->GetMethodId()->GetIndex(), (*direct_methods)[i]->GetAccessFlags(), (*direct_methods)[i]->GetCodeItem(), i); @@ -1456,8 +1432,7 @@ static void DumpClass(dex_ir::Header* header, int idx, char** last_package) { dex_ir::MethodItemVector* virtual_methods = class_data->VirtualMethods(); if (virtual_methods != nullptr) { for (uint32_t i = 0; i < virtual_methods->size(); i++) { - DumpMethod(header, - (*virtual_methods)[i]->GetMethodId()->GetIndex(), + DumpMethod((*virtual_methods)[i]->GetMethodId()->GetIndex(), (*virtual_methods)[i]->GetAccessFlags(), (*virtual_methods)[i]->GetCodeItem(), i); @@ -1481,24 +1456,10 @@ static void DumpClass(dex_ir::Header* header, int idx, char** last_package) { free(access_str); } -/* - * Dumps the requested sections of the file. - */ -static void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index) { - if (options_.verbose_) { - fprintf(out_file_, "Opened '%s', DEX version '%.3s'\n", - file_name, dex_file->GetHeader().magic_ + 4); - } - std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file)); - - if (options_.visualize_pattern_) { - VisualizeDexLayout(header.get(), dex_file, dex_file_index); - return; - } - +void DexLayout::DumpDexFile() { // Headers. if (options_.show_file_headers_) { - DumpFileHeader(header.get()); + DumpFileHeader(); } // Open XML context. @@ -1508,9 +1469,9 @@ static void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_ // Iterate over all classes. char* package = nullptr; - const uint32_t class_defs_size = header->GetCollections().ClassDefsSize(); + const uint32_t class_defs_size = header_->GetCollections().ClassDefsSize(); for (uint32_t i = 0; i < class_defs_size; i++) { - DumpClass(header.get(), i, &package); + DumpClass(i, &package); } // for // Free the last package allocated. @@ -1523,20 +1484,227 @@ static void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_ if (options_.output_format_ == kOutputXml) { fprintf(out_file_, "</api>\n"); } +} - // Output dex file. - if (options_.output_dex_directory_ != nullptr) { +std::vector<dex_ir::ClassDef*> DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { + std::vector<dex_ir::ClassDef*> new_class_def_order; + for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) { + dex::TypeIndex type_idx(class_def->ClassType()->GetIndex()); + if (info_->ContainsClass(*dex_file, type_idx)) { + new_class_def_order.push_back(class_def.get()); + } + } + for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) { + dex::TypeIndex type_idx(class_def->ClassType()->GetIndex()); + if (!info_->ContainsClass(*dex_file, type_idx)) { + new_class_def_order.push_back(class_def.get()); + } + } + uint32_t class_defs_offset = header_->GetCollections().ClassDefsOffset(); + uint32_t class_data_offset = header_->GetCollections().ClassDatasOffset(); + for (uint32_t i = 0; i < new_class_def_order.size(); ++i) { + dex_ir::ClassDef* class_def = new_class_def_order[i]; + class_def->SetIndex(i); + class_def->SetOffset(class_defs_offset); + class_defs_offset += dex_ir::ClassDef::ItemSize(); + if (class_def->GetClassData() != nullptr) { + class_def->GetClassData()->SetOffset(class_data_offset); + class_data_offset += class_def->GetClassData()->GetSize(); + } + } + return new_class_def_order; +} + +int32_t DexLayout::LayoutCodeItems(std::vector<dex_ir::ClassDef*> new_class_def_order) { + int32_t diff = 0; + uint32_t offset = header_->GetCollections().CodeItemsOffset(); + for (dex_ir::ClassDef* class_def : new_class_def_order) { + dex_ir::ClassData* class_data = class_def->GetClassData(); + if (class_data != nullptr) { + class_data->SetOffset(class_data->GetOffset() + diff); + for (auto& method : *class_data->DirectMethods()) { + dex_ir::CodeItem* code_item = method->GetCodeItem(); + if (code_item != nullptr) { + diff += UnsignedLeb128Size(offset) - UnsignedLeb128Size(code_item->GetOffset()); + code_item->SetOffset(offset); + offset += RoundUp(code_item->GetSize(), 4); + } + } + for (auto& method : *class_data->VirtualMethods()) { + dex_ir::CodeItem* code_item = method->GetCodeItem(); + if (code_item != nullptr) { + diff += UnsignedLeb128Size(offset) - UnsignedLeb128Size(code_item->GetOffset()); + code_item->SetOffset(offset); + offset += RoundUp(code_item->GetSize(), 4); + } + } + } + } + + return diff; +} + +// Adjust offsets of every item in the specified section by diff bytes. +template<class T> void DexLayout::FixupSection(std::map<uint32_t, std::unique_ptr<T>>& map, + uint32_t diff) { + for (auto& pair : map) { + std::unique_ptr<T>& item = pair.second; + item->SetOffset(item->GetOffset() + diff); + } +} + +// Adjust offsets of all sections with an address after the specified offset by diff bytes. +void DexLayout::FixupSections(uint32_t offset, uint32_t diff) { + dex_ir::Collections& collections = header_->GetCollections(); + uint32_t map_list_offset = collections.MapListOffset(); + if (map_list_offset > offset) { + collections.SetMapListOffset(map_list_offset + diff); + } + + uint32_t type_lists_offset = collections.TypeListsOffset(); + if (type_lists_offset > offset) { + collections.SetTypeListsOffset(type_lists_offset + diff); + FixupSection(collections.TypeLists(), diff); + } + + uint32_t annotation_set_ref_lists_offset = collections.AnnotationSetRefListsOffset(); + if (annotation_set_ref_lists_offset > offset) { + collections.SetAnnotationSetRefListsOffset(annotation_set_ref_lists_offset + diff); + FixupSection(collections.AnnotationSetRefLists(), diff); + } + + uint32_t annotation_set_items_offset = collections.AnnotationSetItemsOffset(); + if (annotation_set_items_offset > offset) { + collections.SetAnnotationSetItemsOffset(annotation_set_items_offset + diff); + FixupSection(collections.AnnotationSetItems(), diff); + } + + uint32_t class_datas_offset = collections.ClassDatasOffset(); + if (class_datas_offset > offset) { + collections.SetClassDatasOffset(class_datas_offset + diff); + FixupSection(collections.ClassDatas(), diff); + } + + uint32_t code_items_offset = collections.CodeItemsOffset(); + if (code_items_offset > offset) { + collections.SetCodeItemsOffset(code_items_offset + diff); + FixupSection(collections.CodeItems(), diff); + } + + uint32_t string_datas_offset = collections.StringDatasOffset(); + if (string_datas_offset > offset) { + collections.SetStringDatasOffset(string_datas_offset + diff); + FixupSection(collections.StringDatas(), diff); + } + + uint32_t debug_info_items_offset = collections.DebugInfoItemsOffset(); + if (debug_info_items_offset > offset) { + collections.SetDebugInfoItemsOffset(debug_info_items_offset + diff); + FixupSection(collections.DebugInfoItems(), diff); + } + + uint32_t annotation_items_offset = collections.AnnotationItemsOffset(); + if (annotation_items_offset > offset) { + collections.SetAnnotationItemsOffset(annotation_items_offset + diff); + FixupSection(collections.AnnotationItems(), diff); + } + + uint32_t encoded_array_items_offset = collections.EncodedArrayItemsOffset(); + if (encoded_array_items_offset > offset) { + collections.SetEncodedArrayItemsOffset(encoded_array_items_offset + diff); + FixupSection(collections.EncodedArrayItems(), diff); + } + + uint32_t annotations_directory_items_offset = collections.AnnotationsDirectoryItemsOffset(); + if (annotations_directory_items_offset > offset) { + collections.SetAnnotationsDirectoryItemsOffset(annotations_directory_items_offset + diff); + FixupSection(collections.AnnotationsDirectoryItems(), diff); + } +} + +void DexLayout::LayoutOutputFile(const DexFile* dex_file) { + std::vector<dex_ir::ClassDef*> new_class_def_order = LayoutClassDefsAndClassData(dex_file); + int32_t diff = LayoutCodeItems(new_class_def_order); + // Adjust diff to be 4-byte aligned. + diff = RoundUp(diff, 4); + // Move sections after ClassData by diff bytes. + FixupSections(header_->GetCollections().ClassDatasOffset(), diff); + // Update file size. + header_->SetFileSize(header_->FileSize() + diff); +} + +void DexLayout::OutputDexFile(const std::string& dex_file_location) { + std::string error_msg; + std::unique_ptr<File> new_file; + if (!options_.output_to_memmap_) { std::string output_location(options_.output_dex_directory_); - size_t last_slash = dex_file->GetLocation().rfind('/'); - output_location.append(dex_file->GetLocation().substr(last_slash)); - DexWriter::OutputDexFile(*header, output_location.c_str()); + size_t last_slash = dex_file_location.rfind("/"); + std::string dex_file_directory = dex_file_location.substr(0, last_slash + 1); + if (output_location == dex_file_directory) { + output_location = dex_file_location + ".new"; + } else if (last_slash != std::string::npos) { + output_location += dex_file_location.substr(last_slash); + } else { + output_location += "/" + dex_file_location + ".new"; + } + new_file.reset(OS::CreateEmptyFile(output_location.c_str())); + ftruncate(new_file->Fd(), header_->FileSize()); + mem_map_.reset(MemMap::MapFile(header_->FileSize(), PROT_READ | PROT_WRITE, MAP_SHARED, + new_file->Fd(), 0, /*low_4gb*/ false, output_location.c_str(), &error_msg)); + } else { + mem_map_.reset(MemMap::MapAnonymous("layout dex", nullptr, header_->FileSize(), + PROT_READ | PROT_WRITE, /* low_4gb */ false, /* reuse */ false, &error_msg)); + } + if (mem_map_ == nullptr) { + LOG(ERROR) << "Could not create mem map for dex writer output: " << error_msg; + if (new_file.get() != nullptr) { + new_file->Erase(); + } + return; + } + DexWriter::Output(header_, mem_map_.get()); + if (new_file != nullptr) { + UNUSED(new_file->FlushCloseOrErase()); + } +} + +/* + * Dumps the requested sections of the file. + */ +void DexLayout::ProcessDexFile(const char* file_name, + const DexFile* dex_file, + size_t dex_file_index) { + std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file)); + SetHeader(header.get()); + + if (options_.verbose_) { + fprintf(out_file_, "Opened '%s', DEX version '%.3s'\n", + file_name, dex_file->GetHeader().magic_ + 4); + } + + if (options_.visualize_pattern_) { + VisualizeDexLayout(header_, dex_file, dex_file_index, info_); + return; + } + + // Dump dex file. + if (options_.dump_) { + DumpDexFile(); + } + + // Output dex file as file or memmap. + if (options_.output_dex_directory_ != nullptr || options_.output_to_memmap_) { + if (info_ != nullptr) { + LayoutOutputFile(dex_file); + } + OutputDexFile(dex_file->GetLocation()); } } /* * Processes a single file (either direct .dex or indirect .zip/.jar/.apk). */ -int ProcessFile(const char* file_name) { +int DexLayout::ProcessFile(const char* file_name) { if (options_.verbose_) { fprintf(out_file_, "Processing '%s'...\n", file_name); } diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index a5bd99284e..ac1a4a6efb 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -26,8 +26,13 @@ #include <stdint.h> #include <stdio.h> +#include "dex_ir.h" +#include "mem_map.h" + namespace art { +class DexFile; +class Instruction; class ProfileCompilationInfo; /* Supported output formats. */ @@ -37,28 +42,90 @@ enum OutputFormat { }; /* Command-line options. */ -struct Options { - bool build_dex_ir_; - bool checksum_only_; - bool disassemble_; - bool exports_only_; - bool ignore_bad_checksum_; - bool show_annotations_; - bool show_file_headers_; - bool show_section_headers_; - bool verbose_; - bool visualize_pattern_; - OutputFormat output_format_; - const char* output_dex_directory_; - const char* output_file_name_; - const char* profile_file_name_; +class Options { + public: + Options() = default; + + bool dump_ = false; + bool build_dex_ir_ = false; + bool checksum_only_ = false; + bool disassemble_ = false; + bool exports_only_ = false; + bool ignore_bad_checksum_ = false; + bool output_to_memmap_ = false; + bool show_annotations_ = false; + bool show_file_headers_ = false; + bool show_section_headers_ = false; + bool verbose_ = false; + bool visualize_pattern_ = false; + OutputFormat output_format_ = kOutputPlain; + const char* output_dex_directory_ = nullptr; + const char* output_file_name_ = nullptr; + const char* profile_file_name_ = nullptr; }; -/* Prototypes. */ -extern struct Options options_; -extern FILE* out_file_; -extern ProfileCompilationInfo* profile_info_; -int ProcessFile(const char* file_name); +class DexLayout { + public: + DexLayout(Options& options, + ProfileCompilationInfo* info, + FILE* out_file, + dex_ir::Header* + header = nullptr) + : options_(options), info_(info), out_file_(out_file), header_(header) { } + + int ProcessFile(const char* file_name); + void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index); + + dex_ir::Header* GetHeader() const { return header_; } + void SetHeader(dex_ir::Header* header) { header_ = header; } + + MemMap* GetAndReleaseMemMap() { return mem_map_.release(); } + + private: + void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item); + void DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset); + void DumpCatches(const dex_ir::CodeItem* code); + void DumpClass(int idx, char** last_package); + void DumpClassAnnotations(int idx); + void DumpClassDef(int idx); + void DumpCode(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset); + void DumpEncodedAnnotation(dex_ir::EncodedAnnotation* annotation); + void DumpEncodedValue(const dex_ir::EncodedValue* data); + void DumpFileHeader(); + void DumpIField(uint32_t idx, uint32_t flags, int i); + void DumpInstruction(const dex_ir::CodeItem* code, + uint32_t code_offset, + uint32_t insn_idx, + uint32_t insn_width, + const Instruction* dec_insn); + void DumpInterface(const dex_ir::TypeId* type_item, int i); + void DumpLocalInfo(const dex_ir::CodeItem* code); + void DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* code, int i); + void DumpPositionInfo(const dex_ir::CodeItem* code); + void DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedValue* init); + void DumpDexFile(); + + std::vector<dex_ir::ClassDef*> LayoutClassDefsAndClassData(const DexFile* dex_file); + int32_t LayoutCodeItems(std::vector<dex_ir::ClassDef*> new_class_def_order); + template<class T> void FixupSection(std::map<uint32_t, std::unique_ptr<T>>& map, uint32_t diff); + void FixupSections(uint32_t offset, uint32_t diff); + + // Creates a new layout for the dex file based on profile info. + // Currently reorders ClassDefs, ClassDataItems, and CodeItems. + void LayoutOutputFile(const DexFile* dex_file); + void OutputDexFile(const std::string& dex_file_location); + + void DumpCFG(const DexFile* dex_file, int idx); + void DumpCFG(const DexFile* dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code); + + Options& options_; + ProfileCompilationInfo* info_; + FILE* out_file_; + dex_ir::Header* header_; + std::unique_ptr<MemMap> mem_map_; + + DISALLOW_COPY_AND_ASSIGN(DexLayout); +}; } // namespace art diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 825dd50355..5f8a118bde 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -68,64 +68,67 @@ int DexlayoutDriver(int argc, char** argv) { InitLogging(argv, Runtime::Aborter); MemMap::Init(); - // Reset options. + Options options; + options.dump_ = true; + options.verbose_ = true; bool want_usage = false; - memset(&options_, 0, sizeof(options_)); - options_.verbose_ = true; // Parse all arguments. while (1) { - const int ic = getopt(argc, argv, "abcdefghil:o:p:sw:"); + const int ic = getopt(argc, argv, "abcdefghil:mo:p:sw:"); if (ic < 0) { break; // done } switch (ic) { case 'a': // display annotations - options_.show_annotations_ = true; + options.show_annotations_ = true; break; case 'b': // build dex_ir - options_.build_dex_ir_ = true; + options.build_dex_ir_ = true; break; case 'c': // verify the checksum then exit - options_.checksum_only_ = true; + options.checksum_only_ = true; break; case 'd': // disassemble Dalvik instructions - options_.disassemble_ = true; + options.disassemble_ = true; break; case 'e': // exported items only - options_.exports_only_ = true; + options.exports_only_ = true; break; case 'f': // display outer file header - options_.show_file_headers_ = true; + options.show_file_headers_ = true; break; case 'h': // display section headers, i.e. all meta-data - options_.show_section_headers_ = true; + options.show_section_headers_ = true; break; case 'i': // continue even if checksum is bad - options_.ignore_bad_checksum_ = true; + options.ignore_bad_checksum_ = true; break; case 'l': // layout if (strcmp(optarg, "plain") == 0) { - options_.output_format_ = kOutputPlain; + options.output_format_ = kOutputPlain; } else if (strcmp(optarg, "xml") == 0) { - options_.output_format_ = kOutputXml; - options_.verbose_ = false; + options.output_format_ = kOutputXml; + options.verbose_ = false; } else { want_usage = true; } break; + case 'm': // output dex files to a memmap + options.output_to_memmap_ = true; + break; case 'o': // output file - options_.output_file_name_ = optarg; + options.output_file_name_ = optarg; break; case 'p': // profile file - options_.profile_file_name_ = optarg; + options.profile_file_name_ = optarg; break; case 's': // visualize access pattern - options_.visualize_pattern_ = true; - options_.verbose_ = false; + options.visualize_pattern_ = true; + options.verbose_ = false; break; case 'w': // output dex files directory - options_.output_dex_directory_ = optarg; + options.output_dex_directory_ = optarg; break; default: want_usage = true; @@ -138,7 +141,7 @@ int DexlayoutDriver(int argc, char** argv) { fprintf(stderr, "%s: no file specified\n", kProgramName); want_usage = true; } - if (options_.checksum_only_ && options_.ignore_bad_checksum_) { + if (options.checksum_only_ && options.ignore_bad_checksum_) { fprintf(stderr, "Can't specify both -c and -i\n"); want_usage = true; } @@ -148,32 +151,37 @@ int DexlayoutDriver(int argc, char** argv) { } // Open alternative output file. - if (options_.output_file_name_) { - out_file_ = fopen(options_.output_file_name_, "w"); - if (!out_file_) { - fprintf(stderr, "Can't open %s\n", options_.output_file_name_); + FILE* out_file = stdout; + if (options.output_file_name_) { + out_file = fopen(options.output_file_name_, "w"); + if (!out_file) { + fprintf(stderr, "Can't open %s\n", options.output_file_name_); return 1; } } // Open profile file. - if (options_.profile_file_name_) { - int profile_fd = open(options_.profile_file_name_, O_RDONLY); + ProfileCompilationInfo* profile_info = nullptr; + if (options.profile_file_name_) { + int profile_fd = open(options.profile_file_name_, O_RDONLY); if (profile_fd < 0) { - fprintf(stderr, "Can't open %s\n", options_.profile_file_name_); + fprintf(stderr, "Can't open %s\n", options.profile_file_name_); return 1; } - profile_info_ = new ProfileCompilationInfo(); - if (!profile_info_->Load(profile_fd)) { - fprintf(stderr, "Can't read profile info from %s\n", options_.profile_file_name_); + profile_info = new ProfileCompilationInfo(); + if (!profile_info->Load(profile_fd)) { + fprintf(stderr, "Can't read profile info from %s\n", options.profile_file_name_); return 1; } } + // Create DexLayout instance. + DexLayout dex_layout(options, profile_info, out_file); + // Process all files supplied on command line. int result = 0; while (optind < argc) { - result |= ProcessFile(argv[optind++]); + result |= dex_layout.ProcessFile(argv[optind++]); } // while return result != 0; } diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index c7f36be905..665baa6c73 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -22,11 +22,57 @@ #include <unistd.h> #include "base/stringprintf.h" +#include "base/unix_file/fd_file.h" #include "common_runtime_test.h" #include "utils.h" namespace art { +static const char kDexFileLayoutInputDex[] = + "ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH" + "AAAAcAAAAAQAAACMAAAAAQAAAJwAAAAAAAAAAAAAAAMAAACoAAAAAgAAAMAAAAAUAQAAAAEAADAB" + "AAA4AQAAQAEAAEgBAABNAQAAUgEAAGYBAAADAAAABAAAAAUAAAAGAAAABgAAAAMAAAAAAAAAAAAA" + "AAAAAAABAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAdQEAAAAAAAABAAAA" + "AAAAAAIAAAAAAAAAAgAAAAAAAAB/AQAAAAAAAAEAAQABAAAAaQEAAAQAAABwEAIAAAAOAAEAAQAB" + "AAAAbwEAAAQAAABwEAIAAAAOAAY8aW5pdD4ABkEuamF2YQAGQi5qYXZhAANMQTsAA0xCOwASTGph" + "dmEvbGFuZy9PYmplY3Q7AAFWAAQABw48AAQABw48AAAAAQAAgIAEgAIAAAEAAYCABJgCAAAACwAA" + "AAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAAEAAAAjAAAAAMAAAABAAAAnAAAAAUAAAADAAAA" + "qAAAAAYAAAACAAAAwAAAAAEgAAACAAAAAAEAAAIgAAAHAAAAMAEAAAMgAAACAAAAaQEAAAAgAAAC" + "AAAAdQEAAAAQAAABAAAAjAEAAA=="; + +static const char kDexFileLayoutInputProfile[] = + "cHJvADAwMgABAAsAAAABAPUpbf5jbGFzc2VzLmRleAEA"; + +static const char kDexFileLayoutExpectedOutputDex[] = + "ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH" + "AAAAcAAAAAQAAACMAAAAAQAAAJwAAAAAAAAAAAAAAAMAAACoAAAAAgAAAMAAAAAUAQAAAAEAADAB" + "AAA4AQAAQAEAAEgBAABNAQAAUgEAAGYBAAADAAAABAAAAAUAAAAGAAAABgAAAAMAAAAAAAAAAAAA" + "AAAAAAABAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAIAAAAAAAAAdQEAAAAAAAAAAAAA" + "AAAAAAIAAAAAAAAAAQAAAAAAAAB/AQAAAAAAAAEAAQABAAAAbwEAAAQAAABwEAIAAAAOAAEAAQAB" + "AAAAaQEAAAQAAABwEAIAAAAOAAY8aW5pdD4ABkEuamF2YQAGQi5qYXZhAANMQTsAA0xCOwASTGph" + "dmEvbGFuZy9PYmplY3Q7AAFWAAQABw48AAQABw48AAAAAQABgIAEgAIAAAEAAICABJgCAAAACwAA" + "AAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAAEAAAAjAAAAAMAAAABAAAAnAAAAAUAAAADAAAA" + "qAAAAAYAAAACAAAAwAAAAAEgAAACAAAAAAEAAAIgAAAHAAAAMAEAAAMgAAACAAAAaQEAAAAgAAAC" + "AAAAdQEAAAAQAAABAAAAjAEAAA=="; + +static void WriteFileBase64(const char* base64, const char* location) { + // Decode base64. + CHECK(base64 != nullptr); + size_t length; + std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length)); + CHECK(bytes.get() != nullptr); + + // Write to provided file. + std::unique_ptr<File> file(OS::CreateEmptyFile(location)); + CHECK(file.get() != nullptr); + if (!file->WriteFully(bytes.get(), length)) { + PLOG(FATAL) << "Failed to write base64 as file"; + } + if (file->FlushCloseOrErase() != 0) { + PLOG(FATAL) << "Could not flush and close test file."; + } +} + class DexLayoutTest : public CommonRuntimeTest { protected: virtual void SetUp() { @@ -51,7 +97,6 @@ class DexLayoutTest : public CommonRuntimeTest { { dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file }; std::vector<std::string> dexlayout_exec_argv = { dexlayout, "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file }; - if (!::art::Exec(dexdump_exec_argv, error_msg)) { return false; } @@ -78,13 +123,11 @@ class DexLayoutTest : public CommonRuntimeTest { for (const std::string &dex_file : GetLibCoreDexFileNames()) { std::vector<std::string> dexlayout_exec_argv = - { dexlayout, "-d", "-f", "-h", "-l", "plain", "-w", tmp_dir, "-o", tmp_name, dex_file }; - + { dexlayout, "-w", tmp_dir, "-o", tmp_name, dex_file }; if (!::art::Exec(dexlayout_exec_argv, error_msg)) { return false; } - - size_t dex_file_last_slash = dex_file.rfind('/'); + size_t dex_file_last_slash = dex_file.rfind("/"); std::string dex_file_name = dex_file.substr(dex_file_last_slash + 1); std::vector<std::string> unzip_exec_argv = { "/usr/bin/unzip", dex_file, "classes.dex", "-d", tmp_dir}; @@ -105,7 +148,44 @@ class DexLayoutTest : public CommonRuntimeTest { return false; } } + return true; + } + + // Runs DexFileOutput test. + bool DexFileLayoutExec(std::string* error_msg) { + ScratchFile tmp_file; + std::string tmp_name = tmp_file.GetFilename(); + size_t tmp_last_slash = tmp_name.rfind("/"); + std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); + + // Write inputs and expected outputs. + std::string dex_file = tmp_dir + "classes.dex"; + WriteFileBase64(kDexFileLayoutInputDex, dex_file.c_str()); + std::string profile_file = tmp_dir + "primary.prof"; + WriteFileBase64(kDexFileLayoutInputProfile, profile_file.c_str()); + std::string expected_output = tmp_dir + "expected.dex"; + WriteFileBase64(kDexFileLayoutExpectedOutputDex, expected_output.c_str()); + std::string output_dex = tmp_dir + "classes.dex.new"; + + std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; + EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; + + std::vector<std::string> dexlayout_exec_argv = + { dexlayout, "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; + if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + return false; + } + std::vector<std::string> diff_exec_argv = + { "/usr/bin/diff", expected_output, output_dex }; + if (!::art::Exec(diff_exec_argv, error_msg)) { + return false; + } + std::vector<std::string> rm_exec_argv = + { "/bin/rm", dex_file, profile_file, expected_output, output_dex }; + if (!::art::Exec(rm_exec_argv, error_msg)) { + return false; + } return true; } }; @@ -125,4 +205,11 @@ TEST_F(DexLayoutTest, DexFileOutput) { ASSERT_TRUE(DexFileOutputExec(&error_msg)) << error_msg; } +TEST_F(DexLayoutTest, DexFileLayout) { + // Disable test on target. + TEST_DISABLED_FOR_TARGET(); + std::string error_msg; + ASSERT_TRUE(DexFileLayoutExec(&error_msg)) << error_msg; +} + } // namespace art diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 30b708c0fc..3347dac535 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -63,9 +63,7 @@ class DisassemblerArm::CustomDisassembler FINAL : public PrintDisassembler { case kVld2Location: case kVld3Location: case kVld4Location: { - const uintptr_t pc_delta = disasm_->IsT32() - ? vixl::aarch32::kT32PcDelta - : vixl::aarch32::kA32PcDelta; + const uintptr_t pc_delta = label.GetLabel()->GetPcOffset(); const int32_t offset = label.GetLabel()->GetLocation(); os() << "[pc, #" << offset - pc_delta << "]"; @@ -77,7 +75,7 @@ class DisassemblerArm::CustomDisassembler FINAL : public PrintDisassembler { } } - DisassemblerStream& operator<<(const vixl::aarch32::Register reg) OVERRIDE { + DisassemblerStream& operator<<(vixl::aarch32::Register reg) OVERRIDE { if (reg.Is(tr)) { os() << "tr"; return *this; @@ -118,20 +116,11 @@ class DisassemblerArm::CustomDisassembler FINAL : public PrintDisassembler { CustomDisassembler(std::ostream& os, const DisassemblerOptions* options) : PrintDisassembler(&disassembler_stream_), disassembler_stream_(os, this, options) {} - void PrintPc(uint32_t prog_ctr) OVERRIDE { + void PrintCodeAddress(uint32_t prog_ctr) OVERRIDE { os() << "0x" << std::hex << std::setw(8) << std::setfill('0') << prog_ctr << ": "; } - bool IsT32() const { - return is_t32_; - } - - void SetT32(bool is_t32) { - is_t32_ = is_t32; - } - private: - bool is_t32_; CustomDisassemblerStream disassembler_stream_; }; @@ -152,7 +141,7 @@ void DisassemblerArm::CustomDisassembler::CustomDisassemblerStream::PrintLiteral sizeof(unaligned_float), sizeof(unaligned_double)}; const uintptr_t begin = reinterpret_cast<uintptr_t>(options_->base_address_); const uintptr_t end = reinterpret_cast<uintptr_t>(options_->end_address_); - uintptr_t literal_addr = RoundDown(disasm_->GetPc(), vixl::aarch32::kRegSizeInBytes) + offset; + uintptr_t literal_addr = RoundDown(disasm_->GetCodeAddress(), vixl::aarch32::kRegSizeInBytes) + offset; if (!options_->absolute_addresses_) { literal_addr += begin; @@ -208,12 +197,14 @@ size_t DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin) { // Remove the Thumb specifier bit; no effect if begin does not point to T32 code. const uintptr_t instr_ptr = reinterpret_cast<uintptr_t>(begin) & ~1; - disasm_->SetT32((reinterpret_cast<uintptr_t>(begin) & 1) != 0); - disasm_->JumpToPc(GetPc(instr_ptr)); + const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0; + disasm_->SetCodeAddress(GetPc(instr_ptr)); - if (disasm_->IsT32()) { + if (is_t32) { const uint16_t* const ip = reinterpret_cast<const uint16_t*>(instr_ptr); - next = reinterpret_cast<uintptr_t>(disasm_->DecodeT32At(ip)); + const uint16_t* const end_address = reinterpret_cast<const uint16_t*>( + GetDisassemblerOptions()->end_address_); + next = reinterpret_cast<uintptr_t>(disasm_->DecodeT32At(ip, end_address)); } else { const uint32_t* const ip = reinterpret_cast<const uint32_t*>(instr_ptr); next = reinterpret_cast<uintptr_t>(disasm_->DecodeA32At(ip)); @@ -230,10 +221,10 @@ void DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin, const uint8_t // Remove the Thumb specifier bit; no effect if begin does not point to T32 code. const uintptr_t base = reinterpret_cast<uintptr_t>(begin) & ~1; - disasm_->SetT32((reinterpret_cast<uintptr_t>(begin) & 1) != 0); - disasm_->JumpToPc(GetPc(base)); + const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0; + disasm_->SetCodeAddress(GetPc(base)); - if (disasm_->IsT32()) { + if (is_t32) { // The Thumb specifier bits cancel each other. disasm_->DisassembleT32Buffer(reinterpret_cast<const uint16_t*>(base), end - begin); } else { diff --git a/oatdump/Android.bp b/oatdump/Android.bp index bbe6cc1ac0..f1fcf3dad8 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -54,13 +54,22 @@ art_cc_binary { art_cc_binary { name: "oatdumps", - defaults: ["oatdump-defaults"], device_supported: false, + static_executable: true, + defaults: ["oatdump-defaults"], target: { darwin: { enabled: false, }, }, + ldflags: [ + // We need this because GC stress mode makes use of + // _Unwind_GetIP and _Unwind_Backtrace and the symbols are also + // defined in libgcc_eh.a(unwind-dw2.o) + // TODO: Having this is not ideal as it might obscure errors. + // Try to get rid of it. + "-z muldefs", + ], static_libs: [ "libart", "libart-compiler", @@ -72,16 +81,25 @@ art_cc_binary { art_cc_binary { name: "oatdumpds", + device_supported: false, + static_executable: true, defaults: [ "art_debug_defaults", "oatdump-defaults", ], - device_supported: false, target: { darwin: { enabled: false, }, }, + ldflags: [ + // We need this because GC stress mode makes use of + // _Unwind_GetIP and _Unwind_Backtrace and the symbols are also + // defined in libgcc_eh.a(unwind-dw2.o) + // TODO: Having this is not ideal as it might obscure errors. + // Try to get rid of it. + "-z muldefs", + ], static_libs: [ "libartd", "libartd-compiler", diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 3d208b5d28..3ad0f1e8ce 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -794,7 +794,7 @@ class OatDumper { uint32_t oat_class_offset = oat_dex_file.GetOatClassOffset(class_def_index); const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(class_def_index); os << StringPrintf("%zd: %s (offset=0x%08x) (type_idx=%d)", - class_def_index, descriptor, oat_class_offset, class_def.class_idx_) + class_def_index, descriptor, oat_class_offset, class_def.class_idx_.index_) << " (" << oat_class.GetStatus() << ")" << " (" << oat_class.GetType() << ")\n"; // TODO: include bitmap here if type is kOatClassSomeCompiled? @@ -2783,7 +2783,7 @@ class IMTDumper { bool result = klass->GetImt(pointer_size) == object_class->GetImt(pointer_size); - if (klass->GetIfTable() == nullptr) { + if (klass->GetIfTable()->Count() == 0) { DCHECK(result); } @@ -2889,25 +2889,23 @@ class IMTDumper { std::cerr << " Interfaces:" << std::endl; // Run through iftable, find methods that slot here, see if they fit. mirror::IfTable* if_table = klass->GetIfTable(); - if (if_table != nullptr) { - for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) { - mirror::Class* iface = if_table->GetInterface(i); - std::string iface_name; - std::cerr << " " << iface->GetDescriptor(&iface_name) << std::endl; - - for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) { - uint32_t class_hash, name_hash, signature_hash; - ImTable::GetImtHashComponents(&iface_method, &class_hash, &name_hash, &signature_hash); - uint32_t imt_slot = ImTable::GetImtIndex(&iface_method); - std::cerr << " " << iface_method.PrettyMethod(true) - << " slot=" << imt_slot - << std::hex - << " class_hash=0x" << class_hash - << " name_hash=0x" << name_hash - << " signature_hash=0x" << signature_hash - << std::dec - << std::endl; - } + for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) { + mirror::Class* iface = if_table->GetInterface(i); + std::string iface_name; + std::cerr << " " << iface->GetDescriptor(&iface_name) << std::endl; + + for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) { + uint32_t class_hash, name_hash, signature_hash; + ImTable::GetImtHashComponents(&iface_method, &class_hash, &name_hash, &signature_hash); + uint32_t imt_slot = ImTable::GetImtIndex(&iface_method); + std::cerr << " " << iface_method.PrettyMethod(true) + << " slot=" << imt_slot + << std::hex + << " class_hash=0x" << class_hash + << " name_hash=0x" << name_hash + << " signature_hash=0x" << signature_hash + << std::dec + << std::endl; } } } @@ -2972,18 +2970,16 @@ class IMTDumper { } else { // Run through iftable, find methods that slot here, see if they fit. mirror::IfTable* if_table = klass->GetIfTable(); - if (if_table != nullptr) { - for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) { - mirror::Class* iface = if_table->GetInterface(i); - size_t num_methods = iface->NumDeclaredVirtualMethods(); - if (num_methods > 0) { - for (ArtMethod& iface_method : iface->GetMethods(pointer_size)) { - if (ImTable::GetImtIndex(&iface_method) == index) { - std::string i_name = iface_method.PrettyMethod(true); - if (StartsWith(i_name, method.c_str())) { - std::cerr << " Slot " << index << " (1)" << std::endl; - std::cerr << " " << p_name << " (" << i_name << ")" << std::endl; - } + for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) { + mirror::Class* iface = if_table->GetInterface(i); + size_t num_methods = iface->NumDeclaredVirtualMethods(); + if (num_methods > 0) { + for (ArtMethod& iface_method : iface->GetMethods(pointer_size)) { + if (ImTable::GetImtIndex(&iface_method) == index) { + std::string i_name = iface_method.PrettyMethod(true); + if (StartsWith(i_name, method.c_str())) { + std::cerr << " Slot " << index << " (1)" << std::endl; + std::cerr << " " << p_name << " (" << i_name << ")" << std::endl; } } } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 3c8c1a397c..db28a3f494 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -498,7 +498,7 @@ bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename, return true; } -class PatchOatArtFieldVisitor : public ArtFieldVisitor { +class PatchOat::PatchOatArtFieldVisitor : public ArtFieldVisitor { public: explicit PatchOatArtFieldVisitor(PatchOat* patch_oat) : patch_oat_(patch_oat) {} @@ -517,7 +517,7 @@ void PatchOat::PatchArtFields(const ImageHeader* image_header) { image_header->VisitPackedArtFields(&visitor, heap_->Begin()); } -class PatchOatArtMethodVisitor : public ArtMethodVisitor { +class PatchOat::PatchOatArtMethodVisitor : public ArtMethodVisitor { public: explicit PatchOatArtMethodVisitor(PatchOat* patch_oat) : patch_oat_(patch_oat) {} @@ -558,7 +558,7 @@ void PatchOat::PatchImtConflictTables(const ImageHeader* image_header) { pointer_size); } -class FixupRootVisitor : public RootVisitor { +class PatchOat::FixupRootVisitor : public RootVisitor { public: explicit FixupRootVisitor(const PatchOat* patch_oat) : patch_oat_(patch_oat) { } @@ -610,7 +610,7 @@ void PatchOat::PatchClassTable(const ImageHeader* image_header) { } -class RelocatedPointerVisitor { +class PatchOat::RelocatedPointerVisitor { public: explicit RelocatedPointerVisitor(PatchOat* patch_oat) : patch_oat_(patch_oat) {} @@ -762,16 +762,14 @@ void PatchOat::VisitObject(mirror::Object* object) { if (vtable != nullptr) { vtable->Fixup(RelocatedCopyOfFollowImages(vtable), pointer_size, native_visitor); } - auto* iftable = klass->GetIfTable(); - if (iftable != nullptr) { - for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - if (iftable->GetMethodArrayCount(i) > 0) { - auto* method_array = iftable->GetMethodArray(i); - CHECK(method_array != nullptr); - method_array->Fixup(RelocatedCopyOfFollowImages(method_array), - pointer_size, - native_visitor); - } + mirror::IfTable* iftable = klass->GetIfTable(); + for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { + if (iftable->GetMethodArrayCount(i) > 0) { + auto* method_array = iftable->GetMethodArray(i); + CHECK(method_array != nullptr); + method_array->Fixup(RelocatedCopyOfFollowImages(method_array), + pointer_size, + native_visitor); } } } else if (object->GetClass() == mirror::Method::StaticClass() || diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index e7a3e91d81..a51963127a 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -230,10 +230,11 @@ class PatchOat { TimingLogger* timings_; - friend class FixupRootVisitor; - friend class RelocatedPointerVisitor; - friend class PatchOatArtFieldVisitor; - friend class PatchOatArtMethodVisitor; + class FixupRootVisitor; + class RelocatedPointerVisitor; + class PatchOatArtFieldVisitor; + class PatchOatArtMethodVisitor; + DISALLOW_IMPLICIT_CONSTRUCTORS(PatchOat); }; diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index cd0aa6fd7d..776c31a662 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -42,7 +42,7 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_TRUE(info->AddMethodIndex(dex_location2, dex_location_checksum2, i)); } for (uint16_t i = 0; i < number_of_classes; i++) { - ASSERT_TRUE(info->AddClassIndex(dex_location1, dex_location_checksum1, i)); + ASSERT_TRUE(info->AddClassIndex(dex_location1, dex_location_checksum1, dex::TypeIndex(i))); } ASSERT_TRUE(info->Save(GetFd(profile))); diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 2a12f1d84a..de72d3a18f 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -86,7 +86,7 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { DefaultInitEntryPoints(jpoints, qpoints); // Cast - qpoints->pInstanceofNonTrivial = artInstanceOfFromCode;; + qpoints->pInstanceofNonTrivial = artInstanceOfFromCode; qpoints->pCheckInstanceOf = art_quick_check_instance_of; // Math diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index cb0bdbfbee..6a442a55bf 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -156,17 +156,24 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { // JNI qpoints->pJniMethodStart = JniMethodStart; static_assert(!IsDirectEntrypoint(kQuickJniMethodStart), "Non-direct C stub marked direct."); + qpoints->pJniMethodFastStart = JniMethodFastStart; + static_assert(!IsDirectEntrypoint(kQuickJniMethodFastStart), "Non-direct C stub marked direct."); qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized; static_assert(!IsDirectEntrypoint(kQuickJniMethodStartSynchronized), "Non-direct C stub marked direct."); qpoints->pJniMethodEnd = JniMethodEnd; static_assert(!IsDirectEntrypoint(kQuickJniMethodEnd), "Non-direct C stub marked direct."); + qpoints->pJniMethodFastEnd = JniMethodFastEnd; + static_assert(!IsDirectEntrypoint(kQuickJniMethodFastEnd), "Non-direct C stub marked direct."); qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized; static_assert(!IsDirectEntrypoint(kQuickJniMethodEndSynchronized), "Non-direct C stub marked direct."); qpoints->pJniMethodEndWithReference = JniMethodEndWithReference; static_assert(!IsDirectEntrypoint(kQuickJniMethodEndWithReference), "Non-direct C stub marked direct."); + qpoints->pJniMethodFastEndWithReference = JniMethodFastEndWithReference; + static_assert(!IsDirectEntrypoint(kQuickJniMethodFastEndWithReference), + "Non-direct C stub marked direct."); qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized; static_assert(!IsDirectEntrypoint(kQuickJniMethodEndWithReferenceSynchronized), "Non-direct C stub marked direct."); diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index bbf9a8b93c..6665897c9d 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -1063,7 +1063,7 @@ TEST_F(StubTest, AllocObject) { EXPECT_FALSE(self->IsExceptionPending()); { // Use an arbitrary method from c to use as referrer - size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex()), // type_idx + size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex().index_), // type_idx // arbitrary reinterpret_cast<size_t>(c->GetVirtualMethod(0, kRuntimePointerSize)), 0U, @@ -1197,7 +1197,7 @@ TEST_F(StubTest, AllocObjectArray) { if ((false)) { // Use an arbitrary method from c to use as referrer size_t result = Invoke3( - static_cast<size_t>(c->GetDexTypeIndex()), // type_idx + static_cast<size_t>(c->GetDexTypeIndex().index_), // type_idx 10U, // arbitrary reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0, kRuntimePointerSize)), diff --git a/runtime/arch/x86/context_x86.cc b/runtime/arch/x86/context_x86.cc index 077d2db870..cb3dfec5f5 100644 --- a/runtime/arch/x86/context_x86.cc +++ b/runtime/arch/x86/context_x86.cc @@ -17,6 +17,7 @@ #include "context_x86.h" #include "base/bit_utils.h" +#include "base/memory_tool.h" #include "quick/quick_method_frame_info.h" namespace art { @@ -102,6 +103,7 @@ void X86Context::DoLongJump() { uintptr_t esp = gprs[kNumberOfCpuRegisters - ESP - 1] - sizeof(intptr_t); gprs[kNumberOfCpuRegisters] = esp; *(reinterpret_cast<uintptr_t*>(esp)) = eip_; + MEMORY_TOOL_HANDLE_NO_RETURN; __asm__ __volatile__( "movl %1, %%ebx\n\t" // Address base of FPRs. "movsd 0(%%ebx), %%xmm0\n\t" // Load up XMM0-XMM7. diff --git a/runtime/art_field.cc b/runtime/art_field.cc index b46b058b16..25b8ed295b 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -48,7 +48,7 @@ ObjPtr<mirror::Class> ArtField::ProxyFindSystemClass(const char* descriptor) { return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor); } -ObjPtr<mirror::Class> ArtField::ResolveGetType(uint32_t type_idx) { +ObjPtr<mirror::Class> ArtField::ResolveGetType(dex::TypeIndex type_idx) { return Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); } diff --git a/runtime/art_field.h b/runtime/art_field.h index 7c2f490706..cacb32450b 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -19,6 +19,7 @@ #include <jni.h> +#include "dex_file_types.h" #include "gc_root.h" #include "modifiers.h" #include "obj_ptr.h" @@ -216,7 +217,8 @@ class ArtField FINAL { private: ObjPtr<mirror::Class> ProxyFindSystemClass(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr<mirror::Class> ResolveGetType(uint32_t type_idx) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::Class> ResolveGetType(dex::TypeIndex type_idx) + REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr<mirror::String> ResolveGetStringName(Thread* self, const DexFile& dex_file, uint32_t string_idx, diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index a652178130..2dfdc16ac0 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -183,17 +183,17 @@ inline GcRoot<mirror::Class>* ArtMethod::GetDexCacheResolvedTypes(PointerSize po } template <bool kWithCheck> -inline mirror::Class* ArtMethod::GetDexCacheResolvedType(uint32_t type_index, +inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index, PointerSize pointer_size) { if (kWithCheck) { mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDeclaringClass()->GetDexCache(); - if (UNLIKELY(type_index >= dex_cache->NumResolvedTypes())) { - ThrowArrayIndexOutOfBoundsException(type_index, dex_cache->NumResolvedTypes()); + if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) { + ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes()); return nullptr; } } - mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index].Read(); + mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index.index_].Read(); return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr; } @@ -210,7 +210,7 @@ inline bool ArtMethod::HasSameDexCacheResolvedTypes(ArtMethod* other, PointerSiz return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size); } -inline mirror::Class* ArtMethod::GetClassFromTypeIndex(uint16_t type_idx, +inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve, PointerSize pointer_size) { mirror::Class* type = GetDexCacheResolvedType(type_idx, pointer_size); @@ -336,7 +336,7 @@ inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { return GetDeclaringClass()->GetDexFile().GetCodeItem(GetCodeItemOffset()); } -inline bool ArtMethod::IsResolvedTypeIdx(uint16_t type_idx, PointerSize pointer_size) { +inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) { DCHECK(!IsProxyMethod()); return GetDexCacheResolvedType(type_idx, pointer_size) != nullptr; } @@ -383,11 +383,10 @@ inline const char* ArtMethod::GetReturnTypeDescriptor() { const DexFile* dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); - uint16_t return_type_idx = proto_id.return_type_idx_; - return dex_file->GetTypeDescriptor(dex_file->GetTypeId(return_type_idx)); + return dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_)); } -inline const char* ArtMethod::GetTypeDescriptorFromTypeIdx(uint16_t type_idx) { +inline const char* ArtMethod::GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx) { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); return dex_file->GetTypeDescriptor(dex_file->GetTypeId(type_idx)); @@ -440,7 +439,7 @@ inline mirror::Class* ArtMethod::GetReturnType(bool resolve, PointerSize pointer const DexFile* dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); - uint16_t return_type_idx = proto_id.return_type_idx_; + dex::TypeIndex return_type_idx = proto_id.return_type_idx_; mirror::Class* type = GetDexCacheResolvedType(return_type_idx, pointer_size); if (type == nullptr && resolve) { type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this); diff --git a/runtime/art_method.cc b/runtime/art_method.cc index c550a1b6bd..d1454b646a 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -199,9 +199,9 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, // Iterate over the catch handlers associated with dex_pc. PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) { - uint16_t iter_type_idx = it.GetHandlerTypeIndex(); + dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex(); // Catch all case - if (iter_type_idx == DexFile::kDexNoIndex16) { + if (!iter_type_idx.IsValid()) { found_dex_pc = it.GetHandlerAddress(); break; } @@ -476,7 +476,11 @@ const uint8_t* ArtMethod::GetQuickenedInfo(PointerSize pointer_size) { } // The table is in the .vdex file. const OatFile::OatDexFile* oat_dex_file = GetDexCache()->GetDexFile()->GetOatDexFile(); - return oat_dex_file->GetOatFile()->DexBegin() + header->vmap_table_offset_; + const OatFile* oat_file = oat_dex_file->GetOatFile(); + if (oat_file == nullptr) { + return nullptr; + } + return oat_file->DexBegin() + header->vmap_table_offset_; } else { return oat_method.GetVmapTable(); } diff --git a/runtime/art_method.h b/runtime/art_method.h index b31999f5b4..0e1d7e7303 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -343,7 +343,7 @@ class ArtMethod FINAL { REQUIRES_SHARED(Locks::mutator_lock_); template <bool kWithCheck = true> - mirror::Class* GetDexCacheResolvedType(uint32_t type_idx, PointerSize pointer_size) + mirror::Class* GetDexCacheResolvedType(dex::TypeIndex type_idx, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types, PointerSize pointer_size) @@ -355,7 +355,9 @@ class ArtMethod FINAL { REQUIRES_SHARED(Locks::mutator_lock_); // Get the Class* from the type index into this method's dex cache. - mirror::Class* GetClassFromTypeIndex(uint16_t type_idx, bool resolve, PointerSize pointer_size) + mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, + bool resolve, + PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if this method has the same name and signature of the other method. @@ -527,7 +529,7 @@ class ArtMethod FINAL { const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_); - bool IsResolvedTypeIdx(uint16_t type_idx, PointerSize pointer_size) + bool IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); int32_t GetLineNumFromDexPC(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_); @@ -544,7 +546,7 @@ class ArtMethod FINAL { const char* GetReturnTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); - const char* GetTypeDescriptorFromTypeIdx(uint16_t type_idx) + const char* GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index 6b21a566ee..1dca4286da 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -80,7 +80,7 @@ void InitLogging(char* argv[], AbortFunction& abort_function) { gCmdLine.reset(new std::string("<unset>")); } -#ifdef __ANDROID__ +#ifdef ART_TARGET_ANDROID #define INIT_LOGGING_DEFAULT_LOGGER android::base::LogdLogger() #else #define INIT_LOGGING_DEFAULT_LOGGER android::base::StderrLogger diff --git a/runtime/base/memory_tool.h b/runtime/base/memory_tool.h index e1a2e07aca..42cbaa0389 100644 --- a/runtime/base/memory_tool.h +++ b/runtime/base/memory_tool.h @@ -40,7 +40,10 @@ constexpr bool kMemoryToolIsAvailable = true; constexpr bool kMemoryToolIsAvailable = false; #endif +extern "C" void __asan_handle_no_return(); + #define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return() #define RUNNING_ON_MEMORY_TOOL 1U constexpr bool kMemoryToolIsValgrind = false; constexpr bool kMemoryToolDetectsLeaks = true; @@ -55,6 +58,7 @@ constexpr size_t kMemoryToolStackGuardSizeScale = 2; #define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s) #define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s) #define ATTRIBUTE_NO_SANITIZE_ADDRESS +#define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0) #define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND constexpr bool kMemoryToolIsAvailable = true; constexpr bool kMemoryToolIsValgrind = true; diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 5d922989d9..e8ef69f778 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -48,7 +48,7 @@ Mutex* Locks::mem_maps_lock_ = nullptr; Mutex* Locks::modify_ldt_lock_ = nullptr; MutatorMutex* Locks::mutator_lock_ = nullptr; Mutex* Locks::profiler_lock_ = nullptr; -Mutex* Locks::verifier_deps_lock_ = nullptr; +ReaderWriterMutex* Locks::verifier_deps_lock_ = nullptr; ReaderWriterMutex* Locks::oat_file_manager_lock_ = nullptr; Mutex* Locks::host_dlopen_handles_lock_ = nullptr; Mutex* Locks::reference_processor_lock_ = nullptr; @@ -672,7 +672,7 @@ void ReaderWriterMutex::HandleSharedLockContention(Thread* self, int32_t cur_sta ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self)); ++num_pending_readers_; if (futex(state_.Address(), FUTEX_WAIT, cur_state, nullptr, nullptr, 0) != 0) { - if (errno != EAGAIN) { + if (errno != EAGAIN && errno != EINTR) { PLOG(FATAL) << "futex wait failed for " << name_; } } @@ -796,7 +796,7 @@ void ConditionVariable::Broadcast(Thread* self) { reinterpret_cast<const timespec*>(std::numeric_limits<int32_t>::max()), guard_.state_.Address(), cur_sequence) != -1; if (!done) { - if (errno != EAGAIN) { + if (errno != EAGAIN && errno != EINTR) { PLOG(FATAL) << "futex cmp requeue failed for " << name_; } } @@ -1039,7 +1039,7 @@ void Locks::Init() { UPDATE_CURRENT_LOCK_LEVEL(kVerifierDepsLock); DCHECK(verifier_deps_lock_ == nullptr); - verifier_deps_lock_ = new Mutex("verifier deps lock", current_lock_level); + verifier_deps_lock_ = new ReaderWriterMutex("verifier deps lock", current_lock_level); UPDATE_CURRENT_LOCK_LEVEL(kHostDlOpenHandlesLock); DCHECK(host_dlopen_handles_lock_ == nullptr); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 74b786c38e..7e73e0dbd9 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -658,8 +658,8 @@ class Locks { // Guards opened oat files in OatFileManager. static ReaderWriterMutex* oat_file_manager_lock_ ACQUIRED_AFTER(modify_ldt_lock_); - // Guards verifier dependency collection in VerifierDeps. - static Mutex* verifier_deps_lock_ ACQUIRED_AFTER(oat_file_manager_lock_); + // Guards extra string entries for VerifierDeps. + static ReaderWriterMutex* verifier_deps_lock_ ACQUIRED_AFTER(oat_file_manager_lock_); // Guards dlopen_handles_ in DlOpenOatFile. static Mutex* host_dlopen_handles_lock_ ACQUIRED_AFTER(verifier_deps_lock_); diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h index a53dcea2d7..d5f375a5d9 100644 --- a/runtime/base/stl_util.h +++ b/runtime/base/stl_util.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_BASE_STL_UTIL_H_ #include <algorithm> +#include <set> #include <sstream> #include "base/logging.h" @@ -187,6 +188,12 @@ struct Identity { using type = T; }; +// Merge `other` entries into `to_update`. +template <typename T> +static inline void MergeSets(std::set<T>& to_update, const std::set<T>& other) { + to_update.insert(other.begin(), other.end()); +} + } // namespace art #endif // ART_RUNTIME_BASE_STL_UTIL_H_ diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 350855bdba..81adaeb00a 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -69,7 +69,7 @@ inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx, ArtMethod Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); // MethodVerifier refuses methods with string_idx out of bounds. - DCHECK_LT(string_idx, declaring_class->GetDexFile().NumStringIds());; + DCHECK_LT(string_idx, declaring_class->GetDexFile().NumStringIds()); ObjPtr<mirror::String> string = mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(), string_idx, @@ -86,7 +86,7 @@ inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx, ArtMethod return string.Ptr(); } -inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, ArtMethod* referrer) { +inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) { Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::Class> resolved_type = referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_); @@ -103,7 +103,7 @@ inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, ArtMethod* ref return resolved_type.Ptr(); } -inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, ArtField* referrer) { +inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtField* referrer) { Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); ObjPtr<mirror::DexCache> dex_cache_ptr = declaring_class->GetDexCache(); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index ac5e6aae31..f3aba970b9 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -240,6 +240,12 @@ static void WrapExceptionInInitializer(Handle<mirror::Class> klass) ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred()); CHECK(cause.get() != nullptr); + // Boot classpath classes should not fail initialization. + if (!Runtime::Current()->IsAotCompiler()) { + std::string tmp; + CHECK(klass->GetClassLoader() != nullptr) << klass->GetDescriptor(&tmp); + } + env->ExceptionClear(); bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error); env->Throw(cause.get()); @@ -475,6 +481,9 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b SetClassRoot(kJavaLangString, java_lang_String.Get()); SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference.Get()); + // Fill in the empty iftable. Needs to be done after the kObjectArrayClass root is set. + java_lang_Object->SetIfTable(AllocIfTable(self, 0)); + // Setup the primitive type classes. SetClassRoot(kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); SetClassRoot(kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte)); @@ -727,10 +736,12 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b const DexFile& dex_file = java_lang_Object->GetDexFile(); const DexFile::TypeId* void_type_id = dex_file.FindTypeId("V"); CHECK(void_type_id != nullptr); - uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id); + dex::TypeIndex void_type_idx = dex_file.GetIndexForTypeId(*void_type_id); // Now we resolve void type so the dex cache contains it. We use java.lang.Object class // as referrer so the used dex cache is core's one. - ObjPtr<mirror::Class> resolved_type = ResolveType(dex_file, void_type_idx, java_lang_Object.Get()); + ObjPtr<mirror::Class> resolved_type = ResolveType(dex_file, + void_type_idx, + java_lang_Object.Get()); CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid)); self->AssertNoPendingException(); } @@ -916,13 +927,11 @@ static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_ SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_spaces); } } - auto* iftable = klass->GetIfTable(); - if (iftable != nullptr) { - for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - if (iftable->GetMethodArrayCount(i) > 0) { - SanityCheckArtMethodPointerArray( - iftable->GetMethodArray(i), nullptr, pointer_size, image_spaces); - } + mirror::IfTable* iftable = klass->GetIfTable(); + for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { + if (iftable->GetMethodArrayCount(i) > 0) { + SanityCheckArtMethodPointerArray( + iftable->GetMethodArray(i), nullptr, pointer_size, image_spaces); } } } @@ -3401,7 +3410,8 @@ void ClassLinker::FixupDexCaches(ArtMethod* resolution_method) { } mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { - ObjPtr<mirror::Class> klass = AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_)); + ObjPtr<mirror::Class> klass = + AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_)); if (UNLIKELY(klass == nullptr)) { self->AssertPendingOOMException(); return nullptr; @@ -3419,10 +3429,12 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(ObjPtr<mirror::Class> primi ObjectLock<mirror::Class> lock(self, h_class); h_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract); h_class->SetPrimitiveType(type); + h_class->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); mirror::Class::SetStatus(h_class, mirror::Class::kStatusInitialized, self); const char* descriptor = Primitive::Descriptor(type); - ObjPtr<mirror::Class> existing = InsertClass(descriptor, h_class.Get(), - ComputeModifiedUtf8Hash(descriptor)); + ObjPtr<mirror::Class> existing = InsertClass(descriptor, + h_class.Get(), + ComputeModifiedUtf8Hash(descriptor)); CHECK(existing == nullptr) << "InitPrimitiveClass(" << type << ") failed"; return h_class.Get(); } @@ -3996,7 +4008,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); // In case we run without an image there won't be a backing oat file. - if (oat_dex_file == nullptr) { + if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) { return false; } @@ -4086,7 +4098,7 @@ void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) { for (; iterator.HasNext(); iterator.Next()) { // Ensure exception types are resolved so that they don't need resolution to be delivered, // unresolved exception types will be ignored by exception delivery - if (iterator.GetHandlerTypeIndex() != DexFile::kDexNoIndex16) { + if (iterator.GetHandlerTypeIndex().IsValid()) { ObjPtr<mirror::Class> exception_type = ResolveType(iterator.GetHandlerTypeIndex(), method); if (exception_type == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); @@ -4121,6 +4133,8 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot); klass->SetName(soa.Decode<mirror::String>(name)); klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache()); + // Object has an empty iftable, copy it for that reason. + klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self); std::string descriptor(GetDescriptorForProxy(klass.Get())); const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str()); @@ -4750,7 +4764,7 @@ static void ThrowSignatureCheckResolveReturnTypeException(Handle<mirror::Class> const DexFile* dex_file = m->GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(m->GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); - uint16_t return_type_idx = proto_id.return_type_idx_; + dex::TypeIndex return_type_idx = proto_id.return_type_idx_; std::string return_type = dex_file->PrettyType(return_type_idx); std::string class_loader = mirror::Object::PrettyTypeOf(m->GetDeclaringClass()->GetClassLoader()); ThrowWrappedLinkageError(klass.Get(), @@ -4768,7 +4782,7 @@ static void ThrowSignatureCheckResolveArgException(Handle<mirror::Class> klass, ArtMethod* method, ArtMethod* m, uint32_t index, - uint32_t arg_type_idx) + dex::TypeIndex arg_type_idx) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(Thread::Current()->IsExceptionPending()); DCHECK(!m->IsProxyMethod()); @@ -4858,7 +4872,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, } for (uint32_t i = 0; i < num_types; ++i) { StackHandleScope<1> hs(self); - uint32_t param_type_idx = types1->GetTypeItem(i).type_idx_; + dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_; Handle<mirror::Class> param_type(hs.NewHandle( method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size))); if (UNLIKELY(param_type.Get() == nullptr)) { @@ -4866,7 +4880,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, method1, i, param_type_idx); return false; } - uint32_t other_param_type_idx = types2->GetTypeItem(i).type_idx_; + dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_; ObjPtr<mirror::Class> other_param_type = method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size); if (UNLIKELY(other_param_type == nullptr)) { @@ -5350,8 +5364,8 @@ static bool CheckSuperClassChange(Handle<mirror::Class> klass, bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) { CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus()); const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex()); - uint16_t super_class_idx = class_def.superclass_idx_; - if (super_class_idx != DexFile::kDexNoIndex16) { + dex::TypeIndex super_class_idx = class_def.superclass_idx_; + if (super_class_idx.IsValid()) { // Check that a class does not inherit from itself directly. // // TODO: This is a cheap check to detect the straightforward case @@ -5388,7 +5402,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def); if (interfaces != nullptr) { for (size_t i = 0; i < interfaces->Size(); i++) { - uint16_t idx = interfaces->GetTypeItem(i).type_idx_; + dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_; ObjPtr<mirror::Class> interface = ResolveType(dex_file, idx, klass.Get()); if (interface == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); @@ -6381,16 +6395,18 @@ static size_t FillIfTable(ObjPtr<mirror::IfTable> iftable, bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces) { StackHandleScope<1> hs(self); - const size_t super_ifcount = - klass->HasSuperClass() ? klass->GetSuperClass()->GetIfTableCount() : 0U; + const bool has_superclass = klass->HasSuperClass(); + const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U; const bool have_interfaces = interfaces.Get() != nullptr; const size_t num_interfaces = have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces(); if (num_interfaces == 0) { if (super_ifcount == 0) { + if (LIKELY(has_superclass)) { + klass->SetIfTable(klass->GetSuperClass()->GetIfTable()); + } // Class implements no interfaces. DCHECK_EQ(klass->GetIfTableCount(), 0); - DCHECK(klass->GetIfTable() == nullptr); return true; } // Class implements same interfaces as parent, are any of these not marker interfaces? @@ -6583,7 +6599,7 @@ void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass, } else { // No imt in the super class, need to reconstruct from the iftable. ObjPtr<mirror::IfTable> if_table = super_class->GetIfTable(); - if (if_table != nullptr) { + if (if_table->Count() != 0) { // Ignore copied methods since we will handle these in LinkInterfaceMethods. FillIMTFromIfTable(if_table, unimplemented_method, @@ -7502,7 +7518,7 @@ mirror::String* ClassLinker::LookupString(const DexFile& dex_file, } ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader) { ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); @@ -7528,7 +7544,7 @@ ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const DexFile& dex_file, } mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, ObjPtr<mirror::Class> referrer) { StackHandleScope<2> hs(Thread::Current()); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); @@ -7537,7 +7553,7 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, } mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { DCHECK(dex_cache.Get() != nullptr); @@ -7931,7 +7947,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file, int32_t i = 0; MutableHandle<mirror::Class> param_class = hs.NewHandle<mirror::Class>(nullptr); for (; it.HasNext(); it.Next()) { - const uint16_t type_idx = it.GetTypeIdx(); + const dex::TypeIndex type_idx = it.GetTypeIdx(); param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader)); if (param_class.Get() == nullptr) { DCHECK(self->IsExceptionPending()); @@ -8342,10 +8358,10 @@ std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_bo dex_file->GetBaseLocation(), dex_file->GetLocationChecksum()); size_t num_resolved = 0; - std::unordered_set<uint16_t> class_set; + std::unordered_set<dex::TypeIndex> class_set; CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); for (size_t i = 0; i < num_types; ++i) { - ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(i); + ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(dex::TypeIndex(i)); // Filter out null class loader since that is the boot class loader. if (klass == nullptr || (ignore_boot_classes && klass->GetClassLoader() == nullptr)) { continue; @@ -8358,17 +8374,16 @@ std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_bo } ObjPtr<mirror::DexCache> klass_dex_cache = klass->GetDexCache(); if (klass_dex_cache == dex_cache) { - const size_t class_def_idx = klass->GetDexClassDefIndex(); DCHECK(klass->IsResolved()); - CHECK_LT(class_def_idx, num_class_defs); - class_set.insert(class_def_idx); + CHECK_LT(klass->GetDexClassDefIndex(), num_class_defs); + class_set.insert(klass->GetDexTypeIndex()); } } if (!class_set.empty()) { auto it = ret.find(resolved_classes); if (it != ret.end()) { - // Already have the key, union the class def idxs. + // Already have the key, union the class type indexes. it->AddClasses(class_set.begin(), class_set.end()); } else { resolved_classes.AddClasses(class_set.begin(), class_set.end()); @@ -8411,13 +8426,8 @@ std::unordered_set<std::string> ClassLinker::GetClassDescriptorsForProfileKeys( VLOG(profiler) << "Found opened dex file for " << dex_file->GetLocation() << " with " << info.GetClasses().size() << " classes"; DCHECK_EQ(dex_file->GetLocationChecksum(), info.GetLocationChecksum()); - for (uint16_t class_def_idx : info.GetClasses()) { - if (class_def_idx >= dex_file->NumClassDefs()) { - LOG(WARNING) << "Class def index " << class_def_idx << " >= " << dex_file->NumClassDefs(); - continue; - } - const DexFile::TypeId& type_id = dex_file->GetTypeId( - dex_file->GetClassDef(class_def_idx).class_idx_); + for (dex::TypeIndex type_idx : info.GetClasses()) { + const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx); const char* descriptor = dex_file->GetTypeDescriptor(type_id); ret.insert(descriptor); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 1d29e319d5..95634484fc 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -32,6 +32,7 @@ #include "class_table.h" #include "dex_cache_resolved_classes.h" #include "dex_file.h" +#include "dex_file_types.h" #include "gc_root.h" #include "jni.h" #include "mirror/class.h" @@ -255,7 +256,7 @@ class ClassLinker { // result in the DexCache. The referrer is used to identify the // target DexCache and ClassLoader to use for resolution. mirror::Class* ResolveType(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, ObjPtr<mirror::Class> referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); @@ -263,18 +264,18 @@ class ClassLinker { // Resolve a Type with the given index from the DexFile, storing the // result in the DexCache. The referrer is used to identify the // target DexCache and ClassLoader to use for resolution. - mirror::Class* ResolveType(uint16_t type_idx, ArtMethod* referrer) + mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); - mirror::Class* ResolveType(uint16_t type_idx, ArtField* referrer) + mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtField* referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); // Look up a resolved type with the given ID from the DexFile. The ClassLoader is used to search // for the type, since it may be referenced from but not contained within the given DexFile. ObjPtr<mirror::Class> LookupResolvedType(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_); @@ -284,7 +285,7 @@ class ClassLinker { // type, since it may be referenced from but not contained within // the given DexFile. mirror::Class* ResolveType(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index ab2d9d0a95..9e17be2518 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -25,6 +25,7 @@ #include "class_linker-inl.h" #include "common_runtime_test.h" #include "dex_file.h" +#include "dex_file_types.h" #include "experimental_flags.h" #include "entrypoints/entrypoint_utils-inl.h" #include "gc/heap.h" @@ -101,7 +102,8 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_EQ(0U, primitive->NumDirectInterfaces()); EXPECT_FALSE(primitive->HasVTable()); EXPECT_EQ(0, primitive->GetIfTableCount()); - EXPECT_TRUE(primitive->GetIfTable() == nullptr); + EXPECT_TRUE(primitive->GetIfTable() != nullptr); + EXPECT_EQ(primitive->GetIfTable()->Count(), 0u); EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags()); } @@ -428,7 +430,7 @@ class ClassLinkerTest : public CommonRuntimeTest { } // Verify all the types referenced by this file for (size_t i = 0; i < dex.NumTypeIds(); i++) { - const DexFile::TypeId& type_id = dex.GetTypeId(i); + const DexFile::TypeId& type_id = dex.GetTypeId(dex::TypeIndex(i)); const char* descriptor = dex.GetTypeDescriptor(type_id); AssertDexFileClass(class_loader, descriptor); } @@ -890,7 +892,7 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("MyClass")))); AssertNonExistentClass("LMyClass;"); ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader); - uint32_t type_idx = klass->GetClassDef()->class_idx_; + dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache(); const DexFile& dex_file = klass->GetDexFile(); EXPECT_OBJ_PTR_EQ(dex_cache->GetResolvedType(type_idx), klass); @@ -1153,7 +1155,7 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;", kRuntimePointerSize); const DexFile::TypeId* type_id = dex_file->FindTypeId("LStaticsFromCode;"); ASSERT_TRUE(type_id != nullptr); - uint32_t type_idx = dex_file->GetIndexForTypeId(*type_id); + dex::TypeIndex type_idx = dex_file->GetIndexForTypeId(*type_id); mirror::Class* uninit = ResolveVerifyAndClinit(type_idx, clinit, soa.Self(), true, false); EXPECT_TRUE(uninit != nullptr); EXPECT_FALSE(uninit->IsInitialized()); diff --git a/runtime/class_table.cc b/runtime/class_table.cc index b44104e299..0fcce6b307 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -170,7 +170,7 @@ bool ClassTable::InsertStrongRoot(ObjPtr<mirror::Object> obj) { const DexFile* dex_file = ObjPtr<mirror::DexCache>::DownCast(obj)->GetDexFile(); if (dex_file != nullptr && dex_file->GetOatDexFile() != nullptr) { const OatFile* oat_file = dex_file->GetOatDexFile()->GetOatFile(); - if (!oat_file->GetBssGcRoots().empty()) { + if (oat_file != nullptr && !oat_file->GetBssGcRoots().empty()) { InsertOatFileLocked(oat_file); // Ignore return value. } } diff --git a/runtime/class_table.h b/runtime/class_table.h index bc9eaf4c2d..558c144013 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -48,7 +48,7 @@ class ClassTable { uint32_t operator()(const GcRoot<mirror::Class>& root) const NO_THREAD_SAFETY_ANALYSIS; // Same class loader and descriptor. bool operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b) const - NO_THREAD_SAFETY_ANALYSIS;; + NO_THREAD_SAFETY_ANALYSIS; // Same descriptor. bool operator()(const GcRoot<mirror::Class>& a, const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS; diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc index dc197c1066..6e3e1d87e5 100644 --- a/runtime/compiler_filter.cc +++ b/runtime/compiler_filter.cc @@ -33,6 +33,7 @@ bool CompilerFilter::IsBytecodeCompilationEnabled(Filter filter) { case CompilerFilter::kTime: case CompilerFilter::kSpeedProfile: case CompilerFilter::kSpeed: + case CompilerFilter::kLayoutProfile: case CompilerFilter::kEverythingProfile: case CompilerFilter::kEverything: return true; } @@ -52,6 +53,7 @@ bool CompilerFilter::IsJniCompilationEnabled(Filter filter) { case CompilerFilter::kTime: case CompilerFilter::kSpeedProfile: case CompilerFilter::kSpeed: + case CompilerFilter::kLayoutProfile: case CompilerFilter::kEverythingProfile: case CompilerFilter::kEverything: return true; } @@ -71,6 +73,7 @@ bool CompilerFilter::IsVerificationEnabled(Filter filter) { case CompilerFilter::kTime: case CompilerFilter::kSpeedProfile: case CompilerFilter::kSpeed: + case CompilerFilter::kLayoutProfile: case CompilerFilter::kEverythingProfile: case CompilerFilter::kEverything: return true; } @@ -97,6 +100,7 @@ bool CompilerFilter::DependsOnProfile(Filter filter) { case CompilerFilter::kVerifyProfile: case CompilerFilter::kSpaceProfile: case CompilerFilter::kSpeedProfile: + case CompilerFilter::kLayoutProfile: case CompilerFilter::kEverythingProfile: return true; } UNREACHABLE(); @@ -121,6 +125,7 @@ CompilerFilter::Filter CompilerFilter::GetNonProfileDependentFilterFrom(Filter f return CompilerFilter::kSpace; case CompilerFilter::kSpeedProfile: + case CompilerFilter::kLayoutProfile: return CompilerFilter::kSpeed; case CompilerFilter::kEverythingProfile: @@ -146,6 +151,7 @@ std::string CompilerFilter::NameOfFilter(Filter filter) { case CompilerFilter::kTime: return "time"; case CompilerFilter::kSpeedProfile: return "speed-profile"; case CompilerFilter::kSpeed: return "speed"; + case CompilerFilter::kLayoutProfile: return "layout-profile"; case CompilerFilter::kEverythingProfile: return "everything-profile"; case CompilerFilter::kEverything: return "everything"; } @@ -173,6 +179,8 @@ bool CompilerFilter::ParseCompilerFilter(const char* option, Filter* filter) { *filter = kSpeed; } else if (strcmp(option, "speed-profile") == 0) { *filter = kSpeedProfile; + } else if (strcmp(option, "layout-profile") == 0) { + *filter = kLayoutProfile; } else if (strcmp(option, "everything") == 0) { *filter = kEverything; } else if (strcmp(option, "everything-profile") == 0) { diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h index 37631cc6d2..781d43aa6e 100644 --- a/runtime/compiler_filter.h +++ b/runtime/compiler_filter.h @@ -39,6 +39,7 @@ class CompilerFilter FINAL { kSpace, // Maximize space savings. kBalanced, // Good performance return on compilation investment. kSpeedProfile, // Maximize runtime performance based on profile. + kLayoutProfile, // Temporary filter for dexlayout. Will be merged with kSpeedProfile. kSpeed, // Maximize runtime performance. kEverythingProfile, // Compile everything capable of being compiled based on profile. kEverything, // Compile everything capable of being compiled. diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index d717ec0fcb..b0c4597d73 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -136,7 +136,9 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { + "/core.art"; } - bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const { + bool GetCachedImageFile(const std::string& image_location, + /*out*/std::string* image, + /*out*/std::string* error_msg) const { std::string cache; bool have_android_data; bool dalvik_cache_exists; @@ -151,7 +153,14 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { *error_msg = "Failed to create dalvik cache"; return false; } - return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg); + return GetDalvikCacheFilename(image_location.c_str(), cache.c_str(), image, error_msg); + } + + // Returns the path to an image location whose contents differ from the + // image at GetImageLocation(). This is used for testing mismatched + // image checksums in the oat_file_assistant_tests. + std::string GetImageLocation2() const { + return GetImageDirectory() + "/core-npic.art"; } std::string GetDexSrc1() const { diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h index 0febbedf03..f53ca4acb6 100644 --- a/runtime/dex_cache_resolved_classes.h +++ b/runtime/dex_cache_resolved_classes.h @@ -21,6 +21,8 @@ #include <unordered_set> #include <vector> +#include "dex_file_types.h" + namespace art { // Data structure for passing around which classes belonging to a dex cache / dex file are resolved. @@ -59,7 +61,7 @@ class DexCacheResolvedClasses { return location_checksum_; } - const std::unordered_set<uint16_t>& GetClasses() const { + const std::unordered_set<dex::TypeIndex>& GetClasses() const { return classes_; } @@ -68,7 +70,7 @@ class DexCacheResolvedClasses { const std::string base_location_; const uint32_t location_checksum_; // Array of resolved class def indexes. - mutable std::unordered_set<uint16_t> classes_; + mutable std::unordered_set<dex::TypeIndex> classes_; }; inline bool operator<(const DexCacheResolvedClasses& a, const DexCacheResolvedClasses& b) { diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 621b2c5f21..77a63c11ed 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -58,12 +58,12 @@ inline const char* DexFile::StringDataByIdx(uint32_t idx) const { return StringDataAndUtf16LengthByIdx(idx, &unicode_length); } -inline const char* DexFile::StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const { +inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const { const TypeId& type_id = GetTypeId(idx); return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length); } -inline const char* DexFile::StringByTypeIdx(uint32_t idx) const { +inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx) const { const TypeId& type_id = GetTypeId(idx); return StringDataByIdx(type_id.descriptor_idx_); } diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 2ef7509b68..cc544fd51c 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -26,6 +26,7 @@ #include <memory> #include <sstream> +#include <type_traits> #include "base/enums.h" #include "base/file_magic.h" @@ -44,6 +45,9 @@ namespace art { +static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); +static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial"); + static constexpr OatDexFile* kNoOatDexFile = nullptr; const char* DexFile::kClassesDex = "classes.dex"; @@ -550,7 +554,7 @@ uint32_t DexFile::Header::GetVersion() const { return atoi(version); } -const DexFile::ClassDef* DexFile::FindClassDef(uint16_t type_idx) const { +const DexFile::ClassDef* DexFile::FindClassDef(dex::TypeIndex type_idx) const { size_t num_class_defs = NumClassDefs(); // Fast path for rare no class defs case. if (num_class_defs == 0) { @@ -597,9 +601,9 @@ const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_kl const DexFile::StringId& name, const DexFile::TypeId& type) const { // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx - const uint16_t class_idx = GetIndexForTypeId(declaring_klass); + const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); const uint32_t name_idx = GetIndexForStringId(name); - const uint16_t type_idx = GetIndexForTypeId(type); + const dex::TypeIndex type_idx = GetIndexForTypeId(type); int32_t lo = 0; int32_t hi = NumFieldIds() - 1; while (hi >= lo) { @@ -632,7 +636,7 @@ const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_ const DexFile::StringId& name, const DexFile::ProtoId& signature) const { // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx - const uint16_t class_idx = GetIndexForTypeId(declaring_klass); + const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); const uint32_t name_idx = GetIndexForStringId(name); const uint16_t proto_idx = GetIndexForProtoId(signature); int32_t lo = 0; @@ -687,7 +691,7 @@ const DexFile::TypeId* DexFile::FindTypeId(const char* string) const { int32_t hi = NumTypeIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; - const TypeId& type_id = GetTypeId(mid); + const TypeId& type_id = GetTypeId(dex::TypeIndex(mid)); const DexFile::StringId& str_id = GetStringId(type_id.descriptor_idx_); const char* str = GetStringData(str_id); int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); @@ -726,7 +730,7 @@ const DexFile::TypeId* DexFile::FindTypeId(uint32_t string_idx) const { int32_t hi = NumTypeIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; - const TypeId& type_id = GetTypeId(mid); + const TypeId& type_id = GetTypeId(dex::TypeIndex(mid)); if (string_idx > type_id.descriptor_idx_) { lo = mid + 1; } else if (string_idx < type_id.descriptor_idx_) { @@ -738,20 +742,20 @@ const DexFile::TypeId* DexFile::FindTypeId(uint32_t string_idx) const { return nullptr; } -const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx, - const uint16_t* signature_type_idxs, +const DexFile::ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx, + const dex::TypeIndex* signature_type_idxs, uint32_t signature_length) const { int32_t lo = 0; int32_t hi = NumProtoIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; const DexFile::ProtoId& proto = GetProtoId(mid); - int compare = return_type_idx - proto.return_type_idx_; + int compare = return_type_idx.index_ - proto.return_type_idx_.index_; if (compare == 0) { DexFileParameterIterator it(*this, proto); size_t i = 0; while (it.HasNext() && i < signature_length && compare == 0) { - compare = signature_type_idxs[i] - it.GetTypeIdx(); + compare = signature_type_idxs[i].index_ - it.GetTypeIdx().index_; it.Next(); i++; } @@ -775,8 +779,9 @@ const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx, } // Given a signature place the type ids into the given vector -bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx, - std::vector<uint16_t>* param_type_idxs) const { +bool DexFile::CreateTypeList(const StringPiece& signature, + dex::TypeIndex* return_type_idx, + std::vector<dex::TypeIndex>* param_type_idxs) const { if (signature[0] != '(') { return false; } @@ -813,7 +818,7 @@ bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type if (type_id == nullptr) { return false; } - uint16_t type_idx = GetIndexForTypeId(*type_id); + dex::TypeIndex type_idx = GetIndexForTypeId(*type_id); if (!process_return) { param_type_idxs->push_back(type_idx); } else { @@ -825,8 +830,8 @@ bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type } const Signature DexFile::CreateSignature(const StringPiece& signature) const { - uint16_t return_type_idx; - std::vector<uint16_t> param_type_indices; + dex::TypeIndex return_type_idx; + std::vector<dex::TypeIndex> param_type_indices; bool success = CreateTypeList(signature, &return_type_idx, ¶m_type_indices); if (!success) { return Signature::NoSignature(); @@ -971,7 +976,8 @@ bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, bool is_static, ui } local_in_reg[reg].name_ = StringDataByIdx(name_idx); - local_in_reg[reg].descriptor_ = StringByTypeIdx(descriptor_idx); + local_in_reg[reg].descriptor_ = + StringByTypeIdx(dex::TypeIndex(dchecked_integral_cast<uint16_t>(descriptor_idx)));; local_in_reg[reg].signature_ = StringDataByIdx(signature_idx); local_in_reg[reg].start_address_ = address; local_in_reg[reg].reg_ = reg; @@ -1225,9 +1231,9 @@ std::string DexFile::PrettyField(uint32_t field_idx, bool with_type) const { return result; } -std::string DexFile::PrettyType(uint32_t type_idx) const { - if (type_idx >= NumTypeIds()) { - return StringPrintf("<<invalid-type-idx-%d>>", type_idx); +std::string DexFile::PrettyType(dex::TypeIndex type_idx) const { + if (type_idx.index_ >= NumTypeIds()) { + return StringPrintf("<<invalid-type-idx-%d>>", type_idx.index_); } const DexFile::TypeId& type_id = GetTypeId(type_idx); return PrettyDescriptor(GetTypeDescriptor(type_id)); @@ -1457,14 +1463,14 @@ void CatchHandlerIterator::Init(const uint8_t* handler_data) { void CatchHandlerIterator::Next() { if (remaining_count_ > 0) { - handler_.type_idx_ = DecodeUnsignedLeb128(¤t_data_); + handler_.type_idx_ = dex::TypeIndex(DecodeUnsignedLeb128(¤t_data_)); handler_.address_ = DecodeUnsignedLeb128(¤t_data_); remaining_count_--; return; } if (catch_all_) { - handler_.type_idx_ = DexFile::kDexNoIndex16; + handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16); handler_.address_ = DecodeUnsignedLeb128(¤t_data_); catch_all_ = false; return; @@ -1474,4 +1480,13 @@ void CatchHandlerIterator::Next() { remaining_count_ = -1; } +namespace dex { + +std::ostream& operator<<(std::ostream& os, const TypeIndex& index) { + os << "TypeIndex[" << index.index_ << "]"; + return os; +} + +} // namespace dex + } // namespace art diff --git a/runtime/dex_file.h b/runtime/dex_file.h index da9fa505f2..2384eb614b 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -23,6 +23,7 @@ #include "base/logging.h" #include "base/value_object.h" +#include "dex_file_types.h" #include "globals.h" #include "invoke_type.h" #include "jni.h" @@ -159,17 +160,28 @@ class DexFile { // Raw field_id_item. struct FieldId { - uint16_t class_idx_; // index into type_ids_ array for defining class - uint16_t type_idx_; // index into type_ids_ array for field type + dex::TypeIndex class_idx_; // index into type_ids_ array for defining class + dex::TypeIndex type_idx_; // index into type_ids_ array for field type uint32_t name_idx_; // index into string_ids_ array for field name private: DISALLOW_COPY_AND_ASSIGN(FieldId); }; + // Raw proto_id_item. + struct ProtoId { + uint32_t shorty_idx_; // index into string_ids array for shorty descriptor + dex::TypeIndex return_type_idx_; // index into type_ids array for return type + uint16_t pad_; // padding = 0 + uint32_t parameters_off_; // file offset to type_list for parameter types + + private: + DISALLOW_COPY_AND_ASSIGN(ProtoId); + }; + // Raw method_id_item. struct MethodId { - uint16_t class_idx_; // index into type_ids_ array for defining class + dex::TypeIndex class_idx_; // index into type_ids_ array for defining class uint16_t proto_idx_; // index into proto_ids_ array for method prototype uint32_t name_idx_; // index into string_ids_ array for method name @@ -177,23 +189,12 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(MethodId); }; - // Raw proto_id_item. - struct ProtoId { - uint32_t shorty_idx_; // index into string_ids array for shorty descriptor - uint16_t return_type_idx_; // index into type_ids array for return type - uint16_t pad_; // padding = 0 - uint32_t parameters_off_; // file offset to type_list for parameter types - - private: - DISALLOW_COPY_AND_ASSIGN(ProtoId); - }; - // Raw class_def_item. struct ClassDef { - uint16_t class_idx_; // index into type_ids_ array for this class + dex::TypeIndex class_idx_; // index into type_ids_ array for this class uint16_t pad1_; // padding = 0 uint32_t access_flags_; - uint16_t superclass_idx_; // index into type_ids_ array for superclass + dex::TypeIndex superclass_idx_; // index into type_ids_ array for superclass uint16_t pad2_; // padding = 0 uint32_t interfaces_off_; // file offset to TypeList uint32_t source_file_idx_; // index into string_ids_ for source file name @@ -225,7 +226,7 @@ class DexFile { // Raw type_item. struct TypeItem { - uint16_t type_idx_; // index into type_ids section + dex::TypeIndex type_idx_; // index into type_ids section private: DISALLOW_COPY_AND_ASSIGN(TypeItem); @@ -540,23 +541,23 @@ class DexFile { } // Returns the TypeId at the specified index. - const TypeId& GetTypeId(uint32_t idx) const { - DCHECK_LT(idx, NumTypeIds()) << GetLocation(); - return type_ids_[idx]; + const TypeId& GetTypeId(dex::TypeIndex idx) const { + DCHECK_LT(idx.index_, NumTypeIds()) << GetLocation(); + return type_ids_[idx.index_]; } - uint16_t GetIndexForTypeId(const TypeId& type_id) const { + dex::TypeIndex GetIndexForTypeId(const TypeId& type_id) const { CHECK_GE(&type_id, type_ids_) << GetLocation(); CHECK_LT(&type_id, type_ids_ + header_->type_ids_size_) << GetLocation(); size_t result = &type_id - type_ids_; DCHECK_LT(result, 65536U) << GetLocation(); - return static_cast<uint16_t>(result); + return dex::TypeIndex(static_cast<uint16_t>(result)); } // Get the descriptor string associated with a given type index. - const char* StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const; + const char* StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const; - const char* StringByTypeIdx(uint32_t idx) const; + const char* StringByTypeIdx(dex::TypeIndex idx) const; // Returns the type descriptor string of a type id. const char* GetTypeDescriptor(const TypeId& type_id) const; @@ -671,7 +672,7 @@ class DexFile { const char* GetClassDescriptor(const ClassDef& class_def) const; // Looks up a class definition by its type index. - const ClassDef* FindClassDef(uint16_t type_idx) const; + const ClassDef* FindClassDef(dex::TypeIndex type_idx) const; const TypeList* GetInterfacesList(const ClassDef& class_def) const { if (class_def.interfaces_off_ == 0) { @@ -711,7 +712,7 @@ class DexFile { } // Returns the ProtoId at the specified index. - const ProtoId& GetProtoId(uint32_t idx) const { + const ProtoId& GetProtoId(uint16_t idx) const { DCHECK_LT(idx, NumProtoIds()) << GetLocation(); return proto_ids_[idx]; } @@ -723,16 +724,18 @@ class DexFile { } // Looks up a proto id for a given return type and signature type list - const ProtoId* FindProtoId(uint16_t return_type_idx, - const uint16_t* signature_type_idxs, uint32_t signature_length) const; - const ProtoId* FindProtoId(uint16_t return_type_idx, - const std::vector<uint16_t>& signature_type_idxs) const { + const ProtoId* FindProtoId(dex::TypeIndex return_type_idx, + const dex::TypeIndex* signature_type_idxs, + uint32_t signature_length) const; + const ProtoId* FindProtoId(dex::TypeIndex return_type_idx, + const std::vector<dex::TypeIndex>& signature_type_idxs) const { return FindProtoId(return_type_idx, &signature_type_idxs[0], signature_type_idxs.size()); } // Given a signature place the type ids into the given vector, returns true on success - bool CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx, - std::vector<uint16_t>* param_type_idxs) const; + bool CreateTypeList(const StringPiece& signature, + dex::TypeIndex* return_type_idx, + std::vector<dex::TypeIndex>* param_type_idxs) const; // Create a Signature from the given string signature or return Signature::NoSignature if not // possible. @@ -1010,6 +1013,11 @@ class DexFile { return oat_dex_file_; } + // Used by oat writer. + void SetOatDexFile(OatDexFile* oat_dex_file) const { + oat_dex_file_ = oat_dex_file; + } + // Utility methods for reading integral values from a buffer. static int32_t ReadSignedInt(const uint8_t* ptr, int zwidth); static uint32_t ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right); @@ -1021,7 +1029,7 @@ class DexFile { // Returns a human-readable form of the field at an index. std::string PrettyField(uint32_t field_idx, bool with_type = true) const; // Returns a human-readable form of the type at an index. - std::string PrettyType(uint32_t type_idx) const; + std::string PrettyType(dex::TypeIndex type_idx) const; private: static std::unique_ptr<const DexFile> OpenFile(int fd, @@ -1138,9 +1146,10 @@ class DexFile { // If this dex file was loaded from an oat file, oat_dex_file_ contains a // pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is // null. - const OatDexFile* oat_dex_file_; + mutable const OatDexFile* oat_dex_file_; friend class DexFileVerifierTest; + friend class OatWriter; ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for constructor }; @@ -1165,11 +1174,11 @@ class DexFileParameterIterator { bool HasNext() const { return pos_ < size_; } size_t Size() const { return size_; } void Next() { ++pos_; } - uint16_t GetTypeIdx() { + dex::TypeIndex GetTypeIdx() { return type_list_->GetTypeItem(pos_).type_idx_; } const char* GetDescriptor() { - return dex_file_.StringByTypeIdx(GetTypeIdx()); + return dex_file_.StringByTypeIdx(dex::TypeIndex(GetTypeIdx())); } private: const DexFile& dex_file_; @@ -1455,7 +1464,7 @@ class CatchHandlerIterator { Init(handler_data); } - uint16_t GetHandlerTypeIndex() const { + dex::TypeIndex GetHandlerTypeIndex() const { return handler_.type_idx_; } uint32_t GetHandlerAddress() const { @@ -1476,7 +1485,7 @@ class CatchHandlerIterator { void Init(const uint8_t* handler_data); struct CatchHandlerItem { - uint16_t type_idx_; // type index of the caught exception type + dex::TypeIndex type_idx_; // type index of the caught exception type uint32_t address_; // handler address } handler_; const uint8_t* current_data_; // the current handler in dex file. diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 835f456c9b..3fe2c409ca 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -90,7 +90,7 @@ const DexFile::AnnotationItem* SearchAnnotationSet(const DexFile& dex_file, const uint8_t* annotation = annotation_item->annotation_; uint32_t type_index = DecodeUnsignedLeb128(&annotation); - if (strcmp(descriptor, dex_file.StringByTypeIdx(type_index)) == 0) { + if (strcmp(descriptor, dex_file.StringByTypeIdx(dex::TypeIndex(type_index))) == 0) { result = annotation_item; break; } @@ -246,7 +246,7 @@ mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint StackHandleScope<2> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> annotation_class(hs.NewHandle( - class_linker->ResolveType(klass->GetDexFile(), type_index, klass.Get()))); + class_linker->ResolveType(klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get()))); if (annotation_class.Get() == nullptr) { LOG(INFO) << "Unable to resolve " << klass->PrettyClass() << " annotation class " << type_index; DCHECK(Thread::Current()->IsExceptionPending()); @@ -370,13 +370,14 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, if (result_style == DexFile::kAllRaw) { annotation_value->value_.SetI(index); } else { + dex::TypeIndex type_index(index); element_object = Runtime::Current()->GetClassLinker()->ResolveType( - klass->GetDexFile(), index, klass.Get()); + klass->GetDexFile(), type_index, klass.Get()); set_object = true; if (element_object == nullptr) { CHECK(self->IsExceptionPending()); if (result_style == DexFile::kAllObjects) { - const char* msg = dex_file.StringByTypeIdx(index); + const char* msg = dex_file.StringByTypeIdx(type_index); self->ThrowNewWrappedException("Ljava/lang/TypeNotPresentException;", msg); element_object = self->GetException(); self->ClearException(); @@ -665,7 +666,7 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( const uint8_t* annotation = annotation_item->annotation_; uint32_t type_index = DecodeUnsignedLeb128(&annotation); mirror::Class* resolved_class = Runtime::Current()->GetClassLinker()->ResolveType( - klass->GetDexFile(), type_index, klass.Get()); + klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get()); if (resolved_class == nullptr) { std::string temp; LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d", @@ -1345,7 +1346,9 @@ void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) c break; } case kType: { - mirror::Class* resolved = linker_->ResolveType(dex_file_, jval_.i, *dex_cache_, + mirror::Class* resolved = linker_->ResolveType(dex_file_, + dex::TypeIndex(jval_.i), + *dex_cache_, *class_loader_); field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved); break; diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 8e1501ff16..f94d07b252 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -415,14 +415,14 @@ TEST_F(DexFileTest, FindStringId) { TEST_F(DexFileTest, FindTypeId) { for (size_t i = 0; i < java_lang_dex_file_->NumTypeIds(); i++) { - const char* type_str = java_lang_dex_file_->StringByTypeIdx(i); + const char* type_str = java_lang_dex_file_->StringByTypeIdx(dex::TypeIndex(i)); const DexFile::StringId* type_str_id = java_lang_dex_file_->FindStringId(type_str); ASSERT_TRUE(type_str_id != nullptr); uint32_t type_str_idx = java_lang_dex_file_->GetIndexForStringId(*type_str_id); const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId(type_str_idx); ASSERT_EQ(type_id, java_lang_dex_file_->FindTypeId(type_str)); ASSERT_TRUE(type_id != nullptr); - EXPECT_EQ(java_lang_dex_file_->GetIndexForTypeId(*type_id), i); + EXPECT_EQ(java_lang_dex_file_->GetIndexForTypeId(*type_id).index_, i); } } @@ -430,7 +430,7 @@ TEST_F(DexFileTest, FindProtoId) { for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) { const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i); const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find); - std::vector<uint16_t> to_find_types; + std::vector<dex::TypeIndex> to_find_types; if (to_find_tl != nullptr) { for (size_t j = 0; j < to_find_tl->Size(); j++) { to_find_types.push_back(to_find_tl->GetTypeItem(j).type_idx_); diff --git a/runtime/dex_file_types.h b/runtime/dex_file_types.h new file mode 100644 index 0000000000..c6d95a1d19 --- /dev/null +++ b/runtime/dex_file_types.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_DEX_FILE_TYPES_H_ +#define ART_RUNTIME_DEX_FILE_TYPES_H_ + +#include <limits> +#include <ostream> + +namespace art { +namespace dex { + +class TypeIndex { + public: + uint16_t index_; + + TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} + explicit TypeIndex(uint16_t idx) : index_(idx) {} + + bool IsValid() const { + return index_ != std::numeric_limits<decltype(index_)>::max(); + } + static TypeIndex Invalid() { + return TypeIndex(std::numeric_limits<decltype(index_)>::max()); + } + + bool operator==(const TypeIndex& other) const { + return index_ == other.index_; + } + bool operator!=(const TypeIndex& other) const { + return index_ != other.index_; + } + bool operator<(const TypeIndex& other) const { + return index_ < other.index_; + } + bool operator<=(const TypeIndex& other) const { + return index_ <= other.index_; + } + bool operator>(const TypeIndex& other) const { + return index_ > other.index_; + } + bool operator>=(const TypeIndex& other) const { + return index_ >= other.index_; + } +}; +std::ostream& operator<<(std::ostream& os, const TypeIndex& index); + +} // namespace dex +} // namespace art + +namespace std { + +template<> struct hash<art::dex::TypeIndex> { + size_t operator()(const art::dex::TypeIndex& index) const { + return hash<uint16_t>()(index.index_); + } +}; + +} // namespace std + +#endif // ART_RUNTIME_DEX_FILE_TYPES_H_ diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index be25803f59..ed507110bd 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -19,6 +19,7 @@ #include <inttypes.h> #include <zlib.h> +#include <limits> #include <memory> #include "base/stringprintf.h" @@ -31,6 +32,16 @@ namespace art { +static constexpr uint32_t kTypeIdLimit = std::numeric_limits<uint16_t>::max(); + +static bool IsValidOrNoTypeId(uint16_t low, uint16_t high) { + return (high == 0) || ((high == 0xffffU) && (low == 0xffffU)); +} + +static bool IsValidTypeId(uint16_t low ATTRIBUTE_UNUSED, uint16_t high) { + return (high == 0); +} + static uint32_t MapTypeToBitMask(uint32_t map_type) { switch (map_type) { case DexFile::kDexTypeHeaderItem: return 1 << 0; @@ -76,8 +87,9 @@ const char* DexFileVerifier::CheckLoadStringByIdx(uint32_t idx, const char* erro return dex_file_->StringDataByIdx(idx); } -const char* DexFileVerifier::CheckLoadStringByTypeIdx(uint32_t type_idx, const char* error_string) { - if (UNLIKELY(!CheckIndex(type_idx, dex_file_->NumTypeIds(), error_string))) { +const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, + const char* error_string) { + if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) { return nullptr; } const DexFile::TypeId& type_id = dex_file_->GetTypeId(type_idx); @@ -525,7 +537,7 @@ bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_it bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, uint32_t access_flags, uint32_t class_access_flags, - uint16_t class_type_index, + dex::TypeIndex class_type_index, bool expect_static) { // Check for overflow. if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) { @@ -533,13 +545,13 @@ bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, } // Check that it's the right class. - uint16_t my_class_index = + dex::TypeIndex my_class_index = (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + idx)-> class_idx_; if (class_type_index != my_class_index) { ErrorStringPrintf("Field's class index unexpected, %" PRIu16 "vs %" PRIu16, - my_class_index, - class_type_index); + my_class_index.index_, + class_type_index.index_); return false; } @@ -563,7 +575,7 @@ bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, uint32_t class_access_flags, - uint16_t class_type_index, + dex::TypeIndex class_type_index, uint32_t code_offset, std::unordered_set<uint32_t>* direct_method_indexes, bool expect_direct) { @@ -574,13 +586,13 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, } // Check that it's the right class. - uint16_t my_class_index = + dex::TypeIndex my_class_index = (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + idx)-> class_idx_; if (class_type_index != my_class_index) { - ErrorStringPrintf("Method's class index unexpected, %" PRIu16 "vs %" PRIu16, - my_class_index, - class_type_index); + ErrorStringPrintf("Method's class index unexpected, %" PRIu16 " vs %" PRIu16, + my_class_index.index_, + class_type_index.index_); return false; } @@ -789,7 +801,7 @@ bool DexFileVerifier::CheckEncodedAnnotation() { bool DexFileVerifier::FindClassFlags(uint32_t index, bool is_field, - uint16_t* class_type_index, + dex::TypeIndex* class_type_index, uint32_t* class_access_flags) { DCHECK(class_type_index != nullptr); DCHECK(class_access_flags != nullptr); @@ -811,7 +823,7 @@ bool DexFileVerifier::FindClassFlags(uint32_t index, } // Check if that is valid. - if (*class_type_index >= header_->type_ids_size_) { + if (class_type_index->index_ >= header_->type_ids_size_) { return false; } @@ -836,7 +848,7 @@ bool DexFileVerifier::CheckOrderAndGetClassFlags(bool is_field, uint32_t curr_index, uint32_t prev_index, bool* have_class, - uint16_t* class_type_index, + dex::TypeIndex* class_type_index, uint32_t* class_access_flags) { if (curr_index < prev_index) { ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32, @@ -862,7 +874,7 @@ bool DexFileVerifier::CheckOrderAndGetClassFlags(bool is_field, template <bool kStatic> bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it, bool* have_class, - uint16_t* class_type_index, + dex::TypeIndex* class_type_index, uint32_t* class_access_flags) { DCHECK(it != nullptr); // These calls use the raw access flags to check whether the whole dex field is valid. @@ -897,7 +909,7 @@ bool DexFileVerifier::CheckIntraClassDataItemMethods( ClassDataItemIterator* it, std::unordered_set<uint32_t>* direct_method_indexes, bool* have_class, - uint16_t* class_type_index, + dex::TypeIndex* class_type_index, uint32_t* class_access_flags) { uint32_t prev_index = 0; for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) { @@ -935,7 +947,7 @@ bool DexFileVerifier::CheckIntraClassDataItem() { // So we need to explicitly search with the first item we find (either field or method), and then, // as the lookup is expensive, cache the result. bool have_class = false; - uint16_t class_type_index; + dex::TypeIndex class_type_index; uint32_t class_access_flags; // Check fields. @@ -1682,26 +1694,27 @@ bool DexFileVerifier::CheckOffsetToTypeMap(size_t offset, uint16_t type) { return true; } -uint16_t DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bool* success) { +dex::TypeIndex DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bool* success) { ClassDataItemIterator it(*dex_file_, ptr); *success = true; if (it.HasNextStaticField() || it.HasNextInstanceField()) { LOAD_FIELD(field, it.GetMemberIndex(), "first_class_data_definer field_id", - *success = false; return DexFile::kDexNoIndex16) + *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) return field->class_idx_; } if (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { LOAD_METHOD(method, it.GetMemberIndex(), "first_class_data_definer method_id", - *success = false; return DexFile::kDexNoIndex16) + *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) return method->class_idx_; } - return DexFile::kDexNoIndex16; + return dex::TypeIndex(DexFile::kDexNoIndex16); } -uint16_t DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success) { +dex::TypeIndex DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, + bool* success) { const DexFile::AnnotationsDirectoryItem* item = reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr); *success = true; @@ -1709,25 +1722,25 @@ uint16_t DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const uint8_t* pt if (item->fields_size_ != 0) { DexFile::FieldAnnotationsItem* field_items = (DexFile::FieldAnnotationsItem*) (item + 1); LOAD_FIELD(field, field_items[0].field_idx_, "first_annotations_dir_definer field_id", - *success = false; return DexFile::kDexNoIndex16) + *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) return field->class_idx_; } if (item->methods_size_ != 0) { DexFile::MethodAnnotationsItem* method_items = (DexFile::MethodAnnotationsItem*) (item + 1); LOAD_METHOD(method, method_items[0].method_idx_, "first_annotations_dir_definer method id", - *success = false; return DexFile::kDexNoIndex16) + *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) return method->class_idx_; } if (item->parameters_size_ != 0) { DexFile::ParameterAnnotationsItem* parameter_items = (DexFile::ParameterAnnotationsItem*) (item + 1); LOAD_METHOD(method, parameter_items[0].method_idx_, "first_annotations_dir_definer method id", - *success = false; return DexFile::kDexNoIndex16) + *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) return method->class_idx_; } - return DexFile::kDexNoIndex16; + return dex::TypeIndex(DexFile::kDexNoIndex16); } bool DexFileVerifier::CheckInterStringIdItem() { @@ -1788,6 +1801,12 @@ bool DexFileVerifier::CheckInterProtoIdItem() { return false; } + // Check that return type is representable as a uint16_t; + if (UNLIKELY(!IsValidOrNoTypeId(item->return_type_idx_.index_, item->pad_))) { + ErrorStringPrintf("proto with return type idx outside uint16_t range '%x:%x'", + item->pad_, item->return_type_idx_.index_); + return false; + } // Check the return type and advance the shorty. LOAD_STRING_BY_TYPE(return_type, item->return_type_idx_, "inter_proto_id_item return_type_idx") if (!CheckShortyDescriptorMatch(*shorty, return_type, true)) { @@ -1797,7 +1816,8 @@ bool DexFileVerifier::CheckInterProtoIdItem() { DexFileParameterIterator it(*dex_file_, *item); while (it.HasNext() && *shorty != '\0') { - if (!CheckIndex(it.GetTypeIdx(), dex_file_->NumTypeIds(), + if (!CheckIndex(it.GetTypeIdx().index_, + dex_file_->NumTypeIds(), "inter_proto_id_item shorty type_idx")) { return false; } @@ -1824,10 +1844,10 @@ bool DexFileVerifier::CheckInterProtoIdItem() { DexFileParameterIterator prev_it(*dex_file_, *prev); while (curr_it.HasNext() && prev_it.HasNext()) { - uint16_t prev_idx = prev_it.GetTypeIdx(); - uint16_t curr_idx = curr_it.GetTypeIdx(); - DCHECK_NE(prev_idx, DexFile::kDexNoIndex16); - DCHECK_NE(curr_idx, DexFile::kDexNoIndex16); + dex::TypeIndex prev_idx = prev_it.GetTypeIdx(); + dex::TypeIndex curr_idx = curr_it.GetTypeIdx(); + DCHECK_NE(prev_idx, dex::TypeIndex(DexFile::kDexNoIndex16)); + DCHECK_NE(curr_idx, dex::TypeIndex(DexFile::kDexNoIndex16)); if (prev_idx < curr_idx) { break; @@ -1949,9 +1969,21 @@ bool DexFileVerifier::CheckInterMethodIdItem() { bool DexFileVerifier::CheckInterClassDefItem() { const DexFile::ClassDef* item = reinterpret_cast<const DexFile::ClassDef*>(ptr_); + // Check that class_idx_ is representable as a uint16_t; + if (UNLIKELY(!IsValidTypeId(item->class_idx_.index_, item->pad1_))) { + ErrorStringPrintf("class with type idx outside uint16_t range '%x:%x'", item->pad1_, + item->class_idx_.index_); + return false; + } + // Check that superclass_idx_ is representable as a uint16_t; + if (UNLIKELY(!IsValidOrNoTypeId(item->superclass_idx_.index_, item->pad2_))) { + ErrorStringPrintf("class with superclass type idx outside uint16_t range '%x:%x'", item->pad2_, + item->superclass_idx_.index_); + return false; + } // Check for duplicate class def. if (defined_classes_.find(item->class_idx_) != defined_classes_.end()) { - ErrorStringPrintf("Redefinition of class with type idx: '%d'", item->class_idx_); + ErrorStringPrintf("Redefinition of class with type idx: '%d'", item->class_idx_.index_); return false; } defined_classes_.insert(item->class_idx_); @@ -1985,12 +2017,13 @@ bool DexFileVerifier::CheckInterClassDefItem() { return false; } - if (item->superclass_idx_ != DexFile::kDexNoIndex16) { + if (item->superclass_idx_.IsValid()) { if (header_->GetVersion() >= DexFile::kClassDefinitionOrderEnforcedVersion) { // Check that a class does not inherit from itself directly (by having // the same type idx as its super class). if (UNLIKELY(item->superclass_idx_ == item->class_idx_)) { - ErrorStringPrintf("Class with same type idx as its superclass: '%d'", item->class_idx_); + ErrorStringPrintf("Class with same type idx as its superclass: '%d'", + item->class_idx_.index_); return false; } @@ -2004,8 +2037,8 @@ bool DexFileVerifier::CheckInterClassDefItem() { ErrorStringPrintf("Invalid class definition ordering:" " class with type idx: '%d' defined before" " superclass with type idx: '%d'", - item->class_idx_, - item->superclass_idx_); + item->class_idx_.index_, + item->superclass_idx_.index_); return false; } } @@ -2029,7 +2062,7 @@ bool DexFileVerifier::CheckInterClassDefItem() { // same type idx as one of its immediate implemented interfaces). if (UNLIKELY(interfaces->GetTypeItem(i).type_idx_ == item->class_idx_)) { ErrorStringPrintf("Class with same type idx as implemented interface: '%d'", - item->class_idx_); + item->class_idx_.index_); return false; } @@ -2044,8 +2077,8 @@ bool DexFileVerifier::CheckInterClassDefItem() { ErrorStringPrintf("Invalid class definition ordering:" " class with type idx: '%d' defined before" " implemented interface with type idx: '%d'", - item->class_idx_, - interfaces->GetTypeItem(i).type_idx_); + item->class_idx_.index_, + interfaces->GetTypeItem(i).type_idx_.index_); return false; } } @@ -2065,9 +2098,9 @@ bool DexFileVerifier::CheckInterClassDefItem() { * practice the number of interfaces implemented by any given class is low. */ for (uint32_t i = 1; i < size; i++) { - uint32_t idx1 = interfaces->GetTypeItem(i).type_idx_; + dex::TypeIndex idx1 = interfaces->GetTypeItem(i).type_idx_; for (uint32_t j =0; j < i; j++) { - uint32_t idx2 = interfaces->GetTypeItem(j).type_idx_; + dex::TypeIndex idx2 = interfaces->GetTypeItem(j).type_idx_; if (UNLIKELY(idx1 == idx2)) { ErrorStringPrintf("Duplicate interface: '%s'", dex_file_->StringByTypeIdx(idx1)); return false; @@ -2080,11 +2113,12 @@ bool DexFileVerifier::CheckInterClassDefItem() { if (item->class_data_off_ != 0) { const uint8_t* data = begin_ + item->class_data_off_; bool success; - uint16_t data_definer = FindFirstClassDataDefiner(data, &success); + dex::TypeIndex data_definer = FindFirstClassDataDefiner(data, &success); if (!success) { return false; } - if (UNLIKELY((data_definer != item->class_idx_) && (data_definer != DexFile::kDexNoIndex16))) { + if (UNLIKELY((data_definer != item->class_idx_) && + (data_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) { ErrorStringPrintf("Invalid class_data_item"); return false; } @@ -2099,12 +2133,12 @@ bool DexFileVerifier::CheckInterClassDefItem() { } const uint8_t* data = begin_ + item->annotations_off_; bool success; - uint16_t annotations_definer = FindFirstAnnotationsDirectoryDefiner(data, &success); + dex::TypeIndex annotations_definer = FindFirstAnnotationsDirectoryDefiner(data, &success); if (!success) { return false; } if (UNLIKELY((annotations_definer != item->class_idx_) && - (annotations_definer != DexFile::kDexNoIndex16))) { + (annotations_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) { ErrorStringPrintf("Invalid annotations_directory_item"); return false; } @@ -2165,7 +2199,7 @@ bool DexFileVerifier::CheckInterAnnotationSetItem() { bool DexFileVerifier::CheckInterClassDataItem() { ClassDataItemIterator it(*dex_file_, ptr_); bool success; - uint16_t defining_class = FindFirstClassDataDefiner(ptr_, &success); + dex::TypeIndex defining_class = FindFirstClassDataDefiner(ptr_, &success); if (!success) { return false; } @@ -2197,7 +2231,7 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() { const DexFile::AnnotationsDirectoryItem* item = reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_); bool success; - uint16_t defining_class = FindFirstAnnotationsDirectoryDefiner(ptr_, &success); + dex::TypeIndex defining_class = FindFirstAnnotationsDirectoryDefiner(ptr_, &success); if (!success) { return false; } @@ -2315,6 +2349,14 @@ bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, ui break; } case DexFile::kDexTypeClassDefItem: { + // There shouldn't be more class definitions than type ids allow. + // This check should be redundant, since there are checks that the + // class_idx_ is within range and that there is only one definition + // for a given type id. + if (i > kTypeIdLimit) { + ErrorStringPrintf("Too many class definition items"); + return false; + } if (!CheckInterClassDefItem()) { return false; } @@ -2333,6 +2375,14 @@ bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, ui break; } case DexFile::kDexTypeClassDataItem: { + // There shouldn't be more class data than type ids allow. + // This check should be redundant, since there are checks that the + // class_idx_ is within range and that there is only one definition + // for a given type id. + if (i > kTypeIdLimit) { + ErrorStringPrintf("Too many class data items"); + return false; + } if (!CheckInterClassDataItem()) { return false; } @@ -2471,15 +2521,15 @@ static std::string GetStringOrError(const uint8_t* const begin, static std::string GetClassOrError(const uint8_t* const begin, const DexFile::Header* const header, - uint32_t class_idx) { + dex::TypeIndex class_idx) { // The `class_idx` is either `FieldId::class_idx_` or `MethodId::class_idx_` and // it has already been checked in `DexFileVerifier::CheckClassDataItemField()` // or `DexFileVerifier::CheckClassDataItemMethod()`, respectively, to match // a valid defining class. - CHECK_LT(class_idx, header->type_ids_size_); + CHECK_LT(class_idx.index_, header->type_ids_size_); const DexFile::TypeId* type_id = - reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx; + reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx.index_; // Assume that the data is OK at this point. Type id offsets have been checked at this point. diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h index 133e4326bc..19a89de2f5 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex_file_verifier.h @@ -20,6 +20,7 @@ #include <unordered_set> #include "dex_file.h" +#include "dex_file_types.h" #include "safe_map.h" namespace art { @@ -76,12 +77,12 @@ class DexFileVerifier { bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, uint32_t class_access_flags, - uint16_t class_type_index, + dex::TypeIndex class_type_index, bool expect_static); bool CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, uint32_t class_access_flags, - uint16_t class_type_index, + dex::TypeIndex class_type_index, uint32_t code_offset, std::unordered_set<uint32_t>* direct_method_indexes, bool expect_direct); @@ -90,7 +91,7 @@ class DexFileVerifier { uint32_t curr_index, uint32_t prev_index, bool* have_class, - uint16_t* class_type_index, + dex::TypeIndex* class_type_index, uint32_t* class_access_flags); bool CheckPadding(size_t offset, uint32_t aligned_offset); @@ -104,7 +105,7 @@ class DexFileVerifier { template <bool kStatic> bool CheckIntraClassDataItemFields(ClassDataItemIterator* it, bool* have_class, - uint16_t* class_type_index, + dex::TypeIndex* class_type_index, uint32_t* class_access_flags); // Check all methods of the given type from the given iterator. Load the class data from the first // method, if necessary (and return it), or use the given values. @@ -112,7 +113,7 @@ class DexFileVerifier { bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it, std::unordered_set<uint32_t>* direct_method_indexes, bool* have_class, - uint16_t* class_type_index, + dex::TypeIndex* class_type_index, uint32_t* class_access_flags); bool CheckIntraCodeItem(); @@ -130,8 +131,8 @@ class DexFileVerifier { // Note: as sometimes kDexNoIndex16, being 0xFFFF, is a valid return value, we need an // additional out parameter to signal any errors loading an index. - uint16_t FindFirstClassDataDefiner(const uint8_t* ptr, bool* success); - uint16_t FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success); + dex::TypeIndex FindFirstClassDataDefiner(const uint8_t* ptr, bool* success); + dex::TypeIndex FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success); bool CheckInterStringIdItem(); bool CheckInterTypeIdItem(); @@ -150,7 +151,7 @@ class DexFileVerifier { // Load a string by (type) index. Checks whether the index is in bounds, printing the error if // not. If there is an error, null is returned. const char* CheckLoadStringByIdx(uint32_t idx, const char* error_fmt); - const char* CheckLoadStringByTypeIdx(uint32_t type_idx, const char* error_fmt); + const char* CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_fmt); // Load a field/method Id by index. Checks whether the index is in bounds, printing the error if // not. If there is an error, null is returned. @@ -168,7 +169,7 @@ class DexFileVerifier { // linear search. The output values should thus be cached by the caller. bool FindClassFlags(uint32_t index, bool is_field, - uint16_t* class_type_index, + dex::TypeIndex* class_type_index, uint32_t* class_access_flags); // Check validity of the given access flags, interpreted for a field in the context of a class diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index 3801c228c0..0e0929f21f 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -26,6 +26,7 @@ #include "base/macros.h" #include "common_runtime_test.h" #include "dex_file-inl.h" +#include "dex_file_types.h" #include "leb128.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" @@ -155,7 +156,7 @@ TEST_F(DexFileVerifierTest, MethodId) { "method_id_class_idx", [](DexFile* dex_file) { DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0)); - method_id->class_idx_ = 0xFF; + method_id->class_idx_ = dex::TypeIndex(0xFF); }, "could not find declaring class for direct method index 0"); diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index c766b54c0a..751bd51dab 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -208,9 +208,9 @@ std::string Instruction::DumpString(const DexFile* file) const { case CONST_CLASS: case NEW_INSTANCE: if (file != nullptr) { - uint32_t type_idx = VRegB_21c(); - os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyType(type_idx) - << " // type@" << type_idx; + dex::TypeIndex type_idx(VRegB_21c()); + os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " + << file->PrettyType(type_idx) << " // type@" << type_idx; break; } FALLTHROUGH_INTENDED; @@ -302,17 +302,19 @@ std::string Instruction::DumpString(const DexFile* file) const { FALLTHROUGH_INTENDED; case INSTANCE_OF: if (file != nullptr) { - uint32_t type_idx = VRegC_22c(); - os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " - << file->PrettyType(type_idx) << " // type@" << type_idx; + dex::TypeIndex type_idx(VRegC_22c()); + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" + << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx) + << " // type@" << type_idx.index_; break; } FALLTHROUGH_INTENDED; case NEW_ARRAY: if (file != nullptr) { - uint32_t type_idx = VRegC_22c(); - os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", " - << file->PrettyType(type_idx) << " // type@" << type_idx; + dex::TypeIndex type_idx(VRegC_22c()); + os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" + << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx) + << " // type@" << type_idx.index_; break; } FALLTHROUGH_INTENDED; diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index 8eb1a79420..99b9f9dd13 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -480,6 +480,18 @@ class Instruction { insns[1] = val; } + void SetVRegA_21c(uint8_t val) { + DCHECK(FormatOf(Opcode()) == k21c); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[0] = (val << 8) | (insns[0] & 0x00ff); + } + + void SetVRegB_21c(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k21c); + uint16_t* insns = reinterpret_cast<uint16_t*>(this); + insns[1] = val; + } + // Returns the format of the given opcode. static Format FormatOf(Code opcode) { return kInstructionFormats[opcode]; diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index ed60f598d1..ac52f4e287 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -129,7 +129,7 @@ inline ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveTyp template <const bool kAccessCheck> ALWAYS_INLINE -inline mirror::Class* CheckObjectAlloc(uint32_t type_idx, +inline mirror::Class* CheckObjectAlloc(dex::TypeIndex type_idx, ArtMethod* method, Thread* self, bool* slow_path) { @@ -219,7 +219,7 @@ inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass, // check. template <bool kAccessCheck, bool kInstrumented> ALWAYS_INLINE -inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, +inline mirror::Object* AllocObjectFromCode(dex::TypeIndex type_idx, ArtMethod* method, Thread* self, gc::AllocatorType allocator_type) { @@ -275,7 +275,7 @@ inline mirror::Object* AllocObjectFromCodeInitialized(mirror::Class* klass, template <bool kAccessCheck> ALWAYS_INLINE -inline mirror::Class* CheckArrayAlloc(uint32_t type_idx, +inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, int32_t component_count, ArtMethod* method, bool* slow_path) { @@ -313,7 +313,7 @@ inline mirror::Class* CheckArrayAlloc(uint32_t type_idx, // check. template <bool kAccessCheck, bool kInstrumented> ALWAYS_INLINE -inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, +inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, int32_t component_count, ArtMethod* method, Thread* self, @@ -562,7 +562,7 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, StackHandleScope<2> hs2(self); HandleWrapperObjPtr<mirror::Object> h_this(hs2.NewHandleWrapper(this_object)); Handle<mirror::Class> h_referring_class(hs2.NewHandle(referrer->GetDeclaringClass())); - const uint16_t method_type_idx = + const dex::TypeIndex method_type_idx = h_referring_class->GetDexFile().GetMethodId(method_idx).class_idx_; mirror::Class* method_reference_class = class_linker->ResolveType(method_type_idx, referrer); if (UNLIKELY(method_reference_class == nullptr)) { @@ -758,7 +758,8 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, return resolved_method; } else if (type == kSuper) { // TODO This lookup is rather slow. - uint16_t method_type_idx = referring_class->GetDexFile().GetMethodId(method_idx).class_idx_; + dex::TypeIndex method_type_idx = + referring_class->GetDexFile().GetMethodId(method_idx).class_idx_; mirror::Class* method_reference_class = referring_class->GetDexCache()->GetResolvedType(method_type_idx); if (method_reference_class == nullptr) { @@ -788,8 +789,11 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, } } -inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, ArtMethod* referrer, Thread* self, - bool can_run_clinit, bool verify_access) { +inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, + ArtMethod* referrer, + Thread* self, + bool can_run_clinit, + bool verify_access) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::Class* klass = class_linker->ResolveType(type_idx, referrer); if (UNLIKELY(klass == nullptr)) { diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 1ccb4b004c..5390165ecd 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -38,7 +38,7 @@ namespace art { -static inline mirror::Class* CheckFilledNewArrayAlloc(uint32_t type_idx, +static inline mirror::Class* CheckFilledNewArrayAlloc(dex::TypeIndex type_idx, int32_t component_count, ArtMethod* referrer, Thread* self, @@ -82,10 +82,12 @@ static inline mirror::Class* CheckFilledNewArrayAlloc(uint32_t type_idx, } // Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, int32_t component_count, - ArtMethod* referrer, Thread* self, +mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx, + int32_t component_count, + ArtMethod* referrer, + Thread* self, bool access_check, - gc::AllocatorType /* allocator_type */) { + gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) { mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self, access_check); if (UNLIKELY(klass == nullptr)) { @@ -101,12 +103,13 @@ mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, int32_t component_c } // Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check, - gc::AllocatorType /* allocator_type */) { +mirror::Array* CheckAndAllocArrayFromCodeInstrumented( + dex::TypeIndex type_idx, + int32_t component_count, + ArtMethod* referrer, + Thread* self, + bool access_check, + gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) { mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self, access_check); if (UNLIKELY(klass == nullptr)) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index bcddfb0508..d87dc674bc 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -23,6 +23,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "dex_instruction.h" +#include "dex_file_types.h" #include "gc/allocator_type.h" #include "handle.h" #include "invoke_type.h" @@ -45,7 +46,7 @@ class ScopedObjectAccessAlreadyRunnable; class Thread; template <const bool kAccessCheck> -ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(uint32_t type_idx, +ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(dex::TypeIndex type_idx, ArtMethod* method, Thread* self, bool* slow_path) @@ -63,7 +64,7 @@ ALWAYS_INLINE inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror:: // When verification/compiler hasn't been able to verify access, optionally perform an access // check. template <bool kAccessCheck, bool kInstrumented> -ALWAYS_INLINE inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, +ALWAYS_INLINE inline mirror::Object* AllocObjectFromCode(dex::TypeIndex type_idx, ArtMethod* method, Thread* self, gc::AllocatorType allocator_type) @@ -89,7 +90,7 @@ ALWAYS_INLINE inline mirror::Object* AllocObjectFromCodeInitialized( template <bool kAccessCheck> -ALWAYS_INLINE inline mirror::Class* CheckArrayAlloc(uint32_t type_idx, +ALWAYS_INLINE inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, int32_t component_count, ArtMethod* method, bool* slow_path) @@ -101,7 +102,7 @@ ALWAYS_INLINE inline mirror::Class* CheckArrayAlloc(uint32_t type_idx, // When verification/compiler hasn't been able to verify access, optionally perform an access // check. template <bool kAccessCheck, bool kInstrumented> -ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, +ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, int32_t component_count, ArtMethod* method, Thread* self, @@ -118,19 +119,21 @@ ALWAYS_INLINE inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* kl REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, int32_t component_count, - ArtMethod* method, Thread* self, - bool access_check, - gc::AllocatorType allocator_type) +mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx, + int32_t component_count, + ArtMethod* method, + Thread* self, + bool access_check, + gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -extern mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type) +mirror::Array* CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex type_idx, + int32_t component_count, + ArtMethod* method, + Thread* self, + bool access_check, + gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); @@ -177,7 +180,7 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, InvokeType type) REQUIRES_SHARED(Locks::mutator_lock_); -inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, +inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, ArtMethod* referrer, Thread* self, bool can_run_clinit, diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 4a7e8194e3..d2fee9a766 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -19,6 +19,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "callee_save_frame.h" +#include "dex_file_types.h" #include "entrypoints/entrypoint_utils-inl.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" @@ -34,7 +35,8 @@ extern "C" mirror::Object* artAllocObjectFromCode ##suffix##suffix2( \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ if (kUseTlabFastPath && !(instrumented_bool) && (allocator_type) == gc::kAllocatorTypeTLAB) { \ - mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, kRuntimePointerSize); \ + mirror::Class* klass = method->GetDexCacheResolvedType<false>(dex::TypeIndex(type_idx), \ + kRuntimePointerSize); \ if (LIKELY(klass != nullptr && klass->IsInitialized() && !klass->IsFinalizable())) { \ size_t byte_count = klass->GetObjectSize(); \ byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \ @@ -51,7 +53,10 @@ extern "C" mirror::Object* artAllocObjectFromCode ##suffix##suffix2( \ } \ } \ } \ - return AllocObjectFromCode<false, instrumented_bool>(type_idx, method, self, allocator_type); \ + return AllocObjectFromCode<false, instrumented_bool>(dex::TypeIndex(type_idx), \ + method, \ + self, \ + allocator_type); \ } \ extern "C" mirror::Object* artAllocObjectFromCodeResolved##suffix##suffix2( \ mirror::Class* klass, ArtMethod* method ATTRIBUTE_UNUSED, Thread* self) \ @@ -101,13 +106,19 @@ extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheck##suffix##suffix uint32_t type_idx, ArtMethod* method, Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ - return AllocObjectFromCode<true, instrumented_bool>(type_idx, method, self, allocator_type); \ + return AllocObjectFromCode<true, instrumented_bool>(dex::TypeIndex(type_idx), \ + method, \ + self, \ + allocator_type); \ } \ extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \ uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ - return AllocArrayFromCode<false, instrumented_bool>(type_idx, component_count, method, self, \ + return AllocArrayFromCode<false, instrumented_bool>(dex::TypeIndex(type_idx), \ + component_count, \ + method, \ + self, \ allocator_type); \ } \ extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \ @@ -121,7 +132,10 @@ extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ - return AllocArrayFromCode<true, instrumented_bool>(type_idx, component_count, method, self, \ + return AllocArrayFromCode<true, instrumented_bool>(dex::TypeIndex(type_idx), \ + component_count, \ + method, \ + self, \ allocator_type); \ } \ extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \ @@ -129,9 +143,19 @@ extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ if (!(instrumented_bool)) { \ - return CheckAndAllocArrayFromCode(type_idx, component_count, method, self, false, allocator_type); \ + return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \ + component_count, \ + method, \ + self, \ + false, \ + allocator_type); \ } else { \ - return CheckAndAllocArrayFromCodeInstrumented(type_idx, component_count, method, self, false, allocator_type); \ + return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \ + component_count, \ + method, \ + self, \ + false, \ + allocator_type); \ } \ } \ extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ @@ -139,9 +163,19 @@ extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix## REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ if (!(instrumented_bool)) { \ - return CheckAndAllocArrayFromCode(type_idx, component_count, method, self, true, allocator_type); \ + return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \ + component_count, \ + method, \ + self, \ + true, \ + allocator_type); \ } else { \ - return CheckAndAllocArrayFromCodeInstrumented(type_idx, component_count, method, self, true, allocator_type); \ + return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \ + component_count, \ + method, \ + self, \ + true, \ + allocator_type); \ } \ } \ extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \ diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index d4384182b2..b1259e1b8d 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -20,6 +20,7 @@ #include "class_linker-inl.h" #include "class_table-inl.h" #include "dex_file-inl.h" +#include "dex_file_types.h" #include "gc/heap.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" @@ -37,7 +38,7 @@ extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, // given by inheritance. ScopedQuickEntrypointChecks sqec(self); auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kSaveRefsOnly); - return ResolveVerifyAndClinit(type_idx, caller, self, true, false); + return ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false); } extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* self) @@ -45,7 +46,7 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* s // Called when method->dex_cache_resolved_types_[] misses. ScopedQuickEntrypointChecks sqec(self); auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kSaveRefsOnly); - return ResolveVerifyAndClinit(type_idx, caller, self, false, false); + return ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false); } extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Thread* self) @@ -54,7 +55,7 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type // unpopulated. ScopedQuickEntrypointChecks sqec(self); auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kSaveRefsOnly); - return ResolveVerifyAndClinit(type_idx, caller, self, false, true); + return ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true); } extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index b0463d7f11..12836602d5 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -94,8 +94,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, opeer, jpeer, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jpeer, stack_begin, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_begin, stack_size, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_size, stack_trace_sample, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_trace_sample, wait_next, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_size, deps_or_stack_trace_sample, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, deps_or_stack_trace_sample, wait_next, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, wait_next, monitor_enter_object, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, monitor_enter_object, top_handle_scope, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, top_handle_scope, class_loader_override, sizeof(void*)); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3e1cbeb38e..ae9741ffa5 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -264,6 +264,10 @@ Heap::Heap(size_t initial_size, if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; } + if (kUseReadBarrier) { + CHECK_EQ(foreground_collector_type_, kCollectorTypeCC); + CHECK_EQ(background_collector_type_, kCollectorTypeCCBackground); + } CHECK_GE(large_object_threshold, kMinLargeObjectThreshold); ScopedTrace trace(__FUNCTION__); Runtime* const runtime = Runtime::Current(); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 0b602e946a..6019540c9e 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1002,7 +1002,7 @@ class ImageSpaceLoader { mirror::IfTable* iftable = as_klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>(); // Ensure iftable arrays are fixed up since we need GetMethodArray to return the valid // contents. - if (iftable != nullptr && IsInAppImage(iftable)) { + if (IsInAppImage(iftable)) { operator()(iftable); for (int32_t i = 0, count = iftable->Count(); i < count; ++i) { if (iftable->GetMethodArrayCount<kVerifyNone, kWithoutReadBarrier>(i) > 0) { diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index a32c800491..1b3d339f36 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -543,7 +543,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // This is a suspend point. But it's ok since value has been set into shadow_frame. ObjPtr<mirror::Class> klass = class_linker->ResolveType( - instr->VRegB_21c(), shadow_frame->GetMethod()); + dex::TypeIndex(instr->VRegB_21c()), shadow_frame->GetMethod()); DCHECK(klass->IsStringClass()); } } else { diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index cb775cd7a3..72dbe6aace 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -427,7 +427,7 @@ static inline bool DoFieldPutCommon(Thread* self, if (!reg->VerifierInstanceOf(field_class.Ptr())) { // This should never happen. std::string temp1, temp2, temp3; - self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;", + self->ThrowNewExceptionF("Ljava/lang/InternalError;", "Put '%s' that is not instance of field '%s' in '%s'", reg->GetClass()->GetDescriptor(&temp1), field_class->GetDescriptor(&temp2), @@ -922,7 +922,7 @@ inline bool DoInvokePolymorphic(Thread* self, ThrowWrongMethodTypeException(check_type.Ptr(), callsite_type.Get()); return false; } - } else { + } else if (!IsInvokeTransform(handle_kind)) { if (UNLIKELY(!IsCallerTransformer(callsite_type) && !callsite_type->IsConvertible(check_type.Ptr()))) { ThrowWrongMethodTypeException(check_type.Ptr(), callsite_type.Get()); @@ -990,37 +990,41 @@ inline bool DoInvokePolymorphic(Thread* self, CHECK(called_method != nullptr); } - bool call_success; - if (handle_kind == kInvokeTransform) { - call_success = DoCallTransform<is_range>(called_method, - callsite_type, - handle_type, - self, - shadow_frame, - method_handle /* receiver */, - result, - arg, - first_src_reg); + if (IsInvokeTransform(handle_kind)) { + // There are two cases here - method handles representing regular + // transforms and those representing call site transforms. Method + // handles for call site transforms adapt their MethodType to match + // the call site. For these, the |callee_type| is the same as the + // |callsite_type|. The VarargsCollector is such a tranform, its + // method type depends on the call site, ie. x(a) or x(a, b), or + // x(a, b, c). The VarargsCollector invokes a variable arity method + // with the arity arguments in an array. + Handle<mirror::MethodType> callee_type = + (handle_kind == kInvokeCallSiteTransform) ? callsite_type : handle_type; + return DoCallTransform<is_range>(called_method, + callsite_type, + callee_type, + self, + shadow_frame, + method_handle /* receiver */, + result, + arg, + first_src_reg); } else { - call_success = DoCallPolymorphic<is_range>(called_method, - callsite_type, - handle_type, - self, - shadow_frame, - result, - arg, - first_src_reg, - handle_kind); - } - if (LIKELY(call_success && ConvertReturnValue(callsite_type, handle_type, result))) { - return true; + return DoCallPolymorphic<is_range>(called_method, + callsite_type, + handle_type, + self, + shadow_frame, + result, + arg, + first_src_reg, + handle_kind); } - DCHECK(self->IsExceptionPending()); - return false; } else { DCHECK(!is_range); ArtField* field = method_handle->GetTargetField(); - Primitive::Type field_type = field->GetTypeAsPrimitiveType();; + Primitive::Type field_type = field->GetTypeAsPrimitiveType(); switch (handle_kind) { case kInstanceGet: { @@ -1097,7 +1101,6 @@ static inline size_t GetInsForProxyOrNativeMethod(ArtMethod* method) return num_ins; } - inline void PerformCall(Thread* self, const DexFile::CodeItem* code_item, ArtMethod* caller_method, @@ -1251,18 +1254,31 @@ static inline bool DoCallPolymorphic(ArtMethod* called_method, } PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result); + if (self->IsExceptionPending()) { + return false; + } // If the caller of this signature polymorphic method was a transformer, // we need to copy the result back out to the emulated stack frame. - if (is_caller_transformer && !self->IsExceptionPending()) { - ObjPtr<mirror::EmulatedStackFrame> emulated_stack_frame( - reinterpret_cast<mirror::EmulatedStackFrame*>( - shadow_frame.GetVRegReference(first_src_reg))); - - emulated_stack_frame->SetReturnValue(self, *result); + if (is_caller_transformer) { + StackHandleScope<2> hs(self); + Handle<mirror::EmulatedStackFrame> emulated_stack_frame( + hs.NewHandle(reinterpret_cast<mirror::EmulatedStackFrame*>( + shadow_frame.GetVRegReference(first_src_reg)))); + Handle<mirror::MethodType> emulated_stack_type(hs.NewHandle(emulated_stack_frame->GetType())); + JValue local_result; + local_result.SetJ(result->GetJ()); + + if (ConvertReturnValue(emulated_stack_type, target_type, &local_result)) { + emulated_stack_frame->SetReturnValue(self, local_result); + return true; + } else { + DCHECK(self->IsExceptionPending()); + return false; + } + } else { + return ConvertReturnValue(callsite_type, target_type, result); } - - return !self->IsExceptionPending(); } template <bool is_range> @@ -1329,14 +1345,14 @@ static inline bool DoCallTransform(ArtMethod* called_method, 0 /* first dest reg */, new_shadow_frame, result); + if (self->IsExceptionPending()) { + return false; + } // If the called transformer method we called has returned a value, then we // need to copy it back to |result|. - if (!self->IsExceptionPending()) { - sf->GetReturnValue(self, result); - } - - return !self->IsExceptionPending(); + sf->GetReturnValue(self, result); + return ConvertReturnValue(callsite_type, callee_type, result); } template <bool is_range, @@ -1460,7 +1476,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg); if (do_assignability_check && o != nullptr) { PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - const uint32_t type_idx = params->GetTypeItem(shorty_pos).type_idx_; + const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_; ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx, pointer_size); if (arg_type == nullptr) { @@ -1477,7 +1493,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, if (!o->VerifierInstanceOf(arg_type)) { // This should never happen. std::string temp1, temp2; - self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;", + self->ThrowNewExceptionF("Ljava/lang/InternalError;", "Invoking %s with bad arg %d, type '%s' not instance of '%s'", new_shadow_frame->GetMethod()->GetName(), shorty_pos, o->GetClass()->GetDescriptor(&temp1), @@ -1568,7 +1584,7 @@ bool DoFilledNewArray(const Instruction* inst, return false; } uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - ObjPtr<mirror::Class> array_class = ResolveVerifyAndClinit(type_idx, + ObjPtr<mirror::Class> array_class = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), shadow_frame.GetMethod(), self, false, diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 435ac62a9d..22c0fe09be 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -287,7 +287,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (!obj_result->VerifierInstanceOf(return_type)) { // This should never happen. std::string temp1, temp2; - self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;", + self->ThrowNewExceptionF("Ljava/lang/InternalError;", "Returning '%s' that is not instance of return type '%s'", obj_result->GetClass()->GetDescriptor(&temp1), return_type->GetDescriptor(&temp2)); @@ -395,7 +395,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } case Instruction::CONST_CLASS: { PREAMBLE(); - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegB_21c(), + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), shadow_frame.GetMethod(), self, false, @@ -434,7 +434,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } case Instruction::CHECK_CAST: { PREAMBLE(); - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegB_21c(), + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), shadow_frame.GetMethod(), self, false, @@ -454,7 +454,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } case Instruction::INSTANCE_OF: { PREAMBLE(); - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegC_22c(), + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegC_22c()), shadow_frame.GetMethod(), self, false, @@ -484,7 +484,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, case Instruction::NEW_INSTANCE: { PREAMBLE(); ObjPtr<mirror::Object> obj = nullptr; - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegB_21c(), + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), shadow_frame.GetMethod(), self, false, @@ -495,8 +495,10 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, obj = mirror::String::AllocEmptyString<true>(self, allocator_type); } else { obj = AllocObjectFromCode<do_access_check, true>( - inst->VRegB_21c(), shadow_frame.GetMethod(), self, - Runtime::Current()->GetHeap()->GetCurrentAllocator()); + dex::TypeIndex(inst->VRegB_21c()), + shadow_frame.GetMethod(), + self, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); } } if (UNLIKELY(obj == nullptr)) { @@ -520,7 +522,10 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, PREAMBLE(); int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data)); ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check, true>( - inst->VRegC_22c(), length, shadow_frame.GetMethod(), self, + dex::TypeIndex(inst->VRegC_22c()), + length, + shadow_frame.GetMethod(), + self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); if (UNLIKELY(obj == nullptr)) { HANDLE_PENDING_EXCEPTION(); @@ -572,7 +577,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) { // This should never happen. std::string temp; - self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;", + self->ThrowNewExceptionF("Ljava/lang/InternalError;", "Throwing '%s' that is not instance of Throwable", exception->GetClass()->GetDescriptor(&temp)); } else { diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 2bd47bbfcf..fbfed402ee 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -304,7 +304,11 @@ extern "C" size_t MterpConstClass(uint32_t index, ShadowFrame* shadow_frame, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* c = ResolveVerifyAndClinit(index, shadow_frame->GetMethod(), self, false, false); + mirror::Class* c = ResolveVerifyAndClinit(dex::TypeIndex(index), + shadow_frame->GetMethod(), + self, + false, + false); if (UNLIKELY(c == nullptr)) { return true; } @@ -317,7 +321,11 @@ extern "C" size_t MterpCheckCast(uint32_t index, art::ArtMethod* method, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(index, method, self, false, false); + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(index), + method, + self, + false, + false); if (UNLIKELY(c == nullptr)) { return true; } @@ -335,7 +343,11 @@ extern "C" size_t MterpInstanceOf(uint32_t index, art::ArtMethod* method, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(index, method, self, false, false); + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(index), + method, + self, + false, + false); if (UNLIKELY(c == nullptr)) { return false; // Caller will check for pending exception. Return value unimportant. } @@ -353,7 +365,7 @@ extern "C" size_t MterpNewInstance(ShadowFrame* shadow_frame, Thread* self, uint REQUIRES_SHARED(Locks::mutator_lock_) { const Instruction* inst = Instruction::At(shadow_frame->GetDexPCPtr()); mirror::Object* obj = nullptr; - mirror::Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), + mirror::Class* c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), shadow_frame->GetMethod(), self, false, @@ -363,9 +375,10 @@ extern "C" size_t MterpNewInstance(ShadowFrame* shadow_frame, Thread* self, uint gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); obj = mirror::String::AllocEmptyString<true>(self, allocator_type); } else { - obj = AllocObjectFromCode<false, true>( - inst->VRegB_21c(), shadow_frame->GetMethod(), self, - Runtime::Current()->GetHeap()->GetCurrentAllocator()); + obj = AllocObjectFromCode<false, true>(dex::TypeIndex(inst->VRegB_21c()), + shadow_frame->GetMethod(), + self, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); } } if (UNLIKELY(obj == nullptr)) { @@ -446,7 +459,7 @@ extern "C" size_t MterpNewArray(ShadowFrame* shadow_frame, const Instruction* inst = Instruction::At(dex_pc_ptr); int32_t length = shadow_frame->GetVReg(inst->VRegB_22c(inst_data)); mirror::Object* obj = AllocArrayFromCode<false, true>( - inst->VRegC_22c(), length, shadow_frame->GetMethod(), self, + dex::TypeIndex(inst->VRegC_22c()), length, shadow_frame->GetMethod(), self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); if (UNLIKELY(obj == nullptr)) { return false; diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 2257fd6395..a5b1038d5a 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -424,7 +424,7 @@ static std::unique_ptr<MemMap> FindAndExtractEntry(const std::string& jar_file, std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(jar_file.c_str(), error_msg)); if (zip_archive == nullptr) { - return nullptr;; + return nullptr; } std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(entry_name, error_msg)); if (zip_entry == nullptr) { diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index fad7d90ab8..5574a117a7 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -621,8 +621,8 @@ void JdwpState::SendRequestAndPossiblySuspend(ExpandBuf* pReq, JdwpSuspendPolicy Thread* const self = Thread::Current(); self->AssertThreadSuspensionIsAllowable(); CHECK(pReq != nullptr); + CHECK_EQ(threadId, Dbg::GetThreadSelfId()) << "Only the current thread can suspend itself"; /* send request and possibly suspend ourselves */ - JDWP::ObjectId thread_self_id = Dbg::GetThreadSelfId(); ScopedThreadSuspension sts(self, kWaitingForDebuggerSend); if (suspend_policy != SP_NONE) { AcquireJdwpTokenForEvent(threadId); @@ -631,7 +631,7 @@ void JdwpState::SendRequestAndPossiblySuspend(ExpandBuf* pReq, JdwpSuspendPolicy { // Before suspending, we change our state to kSuspended so the debugger sees us as RUNNING. ScopedThreadStateChange stsc(self, kSuspended); - SuspendByPolicy(suspend_policy, thread_self_id); + SuspendByPolicy(suspend_policy, threadId); } } @@ -658,13 +658,10 @@ void JdwpState::ReleaseJdwpTokenForCommand() { } void JdwpState::AcquireJdwpTokenForEvent(ObjectId threadId) { - CHECK_NE(Thread::Current(), GetDebugThread()) << "Expected event thread"; - CHECK_NE(debug_thread_id_, threadId) << "Not expected debug thread"; SetWaitForJdwpToken(threadId); } void JdwpState::ReleaseJdwpTokenForEvent() { - CHECK_NE(Thread::Current(), GetDebugThread()) << "Expected event thread"; ClearWaitForJdwpToken(); } @@ -685,23 +682,28 @@ void JdwpState::SetWaitForJdwpToken(ObjectId threadId) { /* this is held for very brief periods; contention is unlikely */ MutexLock mu(self, jdwp_token_lock_); - CHECK_NE(jdwp_token_owner_thread_id_, threadId) << "Thread is already holding event thread lock"; + if (jdwp_token_owner_thread_id_ == threadId) { + // Only the debugger thread may already hold the event token. For instance, it may trigger + // a CLASS_PREPARE event while processing a command that initializes a class. + CHECK_EQ(threadId, debug_thread_id_) << "Non-debugger thread is already holding event token"; + } else { + /* + * If another thread is already doing stuff, wait for it. This can + * go to sleep indefinitely. + */ - /* - * If another thread is already doing stuff, wait for it. This can - * go to sleep indefinitely. - */ - while (jdwp_token_owner_thread_id_ != 0) { - VLOG(jdwp) << StringPrintf("event in progress (%#" PRIx64 "), %#" PRIx64 " sleeping", - jdwp_token_owner_thread_id_, threadId); - waited = true; - jdwp_token_cond_.Wait(self); - } + while (jdwp_token_owner_thread_id_ != 0) { + VLOG(jdwp) << StringPrintf("event in progress (%#" PRIx64 "), %#" PRIx64 " sleeping", + jdwp_token_owner_thread_id_, threadId); + waited = true; + jdwp_token_cond_.Wait(self); + } - if (waited || threadId != debug_thread_id_) { - VLOG(jdwp) << StringPrintf("event token grabbed (%#" PRIx64 ")", threadId); + if (waited || threadId != debug_thread_id_) { + VLOG(jdwp) << StringPrintf("event token grabbed (%#" PRIx64 ")", threadId); + } + jdwp_token_owner_thread_id_ = threadId; } - jdwp_token_owner_thread_id_ = threadId; } /* @@ -1224,14 +1226,15 @@ void JdwpState::PostClassPrepare(mirror::Class* klass) { VLOG(jdwp) << " suspend_policy=" << suspend_policy; } - if (thread_id == debug_thread_id_) { + ObjectId reported_thread_id = thread_id; + if (reported_thread_id == debug_thread_id_) { /* * JDWP says that, for a class prep in the debugger thread, we * should set thread to null and if any threads were supposed * to be suspended then we suspend all other threads. */ VLOG(jdwp) << " NOTE: class prepare in debugger thread!"; - thread_id = 0; + reported_thread_id = 0; if (suspend_policy == SP_EVENT_THREAD) { suspend_policy = SP_ALL; } @@ -1244,7 +1247,7 @@ void JdwpState::PostClassPrepare(mirror::Class* klass) { for (const JdwpEvent* pEvent : match_list) { expandBufAdd1(pReq, pEvent->eventKind); expandBufAdd4BE(pReq, pEvent->requestId); - expandBufAddObjectId(pReq, thread_id); + expandBufAddObjectId(pReq, reported_thread_id); expandBufAdd1(pReq, tag); expandBufAddRefTypeId(pReq, class_id); expandBufAddUtf8String(pReq, signature); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 4c1006360b..dac2e60987 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -114,7 +114,7 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt } else { jit_options->invoke_transition_weight_ = std::max( jit_options->warmup_threshold_ / Jit::kDefaultInvokeTransitionWeightRatio, - static_cast<size_t>(1));; + static_cast<size_t>(1)); } return jit_options; @@ -274,6 +274,15 @@ bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { << ArtMethod::PrettyMethod(method_to_compile) << " osr=" << std::boolalpha << osr; } + if (kIsDebugBuild) { + if (self->IsExceptionPending()) { + mirror::Throwable* exception = self->GetException(); + LOG(FATAL) << "No pending exception expected after compiling " + << ArtMethod::PrettyMethod(method) + << ": " + << exception->Dump(); + } + } return success; } @@ -701,5 +710,24 @@ void Jit::WaitForCompilationToFinish(Thread* self) { } } +ScopedJitSuspend::ScopedJitSuspend() { + jit::Jit* jit = Runtime::Current()->GetJit(); + was_on_ = (jit != nullptr) && (jit->GetThreadPool() != nullptr); + if (was_on_) { + Thread* self = Thread::Current(); + jit->WaitForCompilationToFinish(self); + jit->GetThreadPool()->StopWorkers(self); + jit->WaitForCompilationToFinish(self); + } +} + +ScopedJitSuspend::~ScopedJitSuspend() { + if (was_on_) { + DCHECK(Runtime::Current()->GetJit() != nullptr); + DCHECK(Runtime::Current()->GetJit()->GetThreadPool() != nullptr); + Runtime::Current()->GetJit()->GetThreadPool()->StartWorkers(Thread::Current()); + } +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index a7824378c2..a230c78033 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -175,6 +175,10 @@ class Jit { static bool LoadCompilerLibrary(std::string* error_msg); + ThreadPool* GetThreadPool() const { + return thread_pool_.get(); + } + private: Jit(); @@ -278,6 +282,16 @@ class JitOptions { DISALLOW_COPY_AND_ASSIGN(JitOptions); }; +// Helper class to stop the JIT for a given scope. This will wait for the JIT to quiesce. +class ScopedJitSuspend { + public: + ScopedJitSuspend(); + ~ScopedJitSuspend(); + + private: + bool was_on_; +}; + } // namespace jit } // namespace art diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index a26d8502a3..3531852933 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -80,8 +80,18 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, std::string error_str; // Map name specific for android_os_Debug.cpp accounting. + // Map in low 4gb to simplify accessing root tables for x86_64. + // We could do PC-relative addressing to avoid this problem, but that + // would require reserving code and data area before submitting, which + // means more windows for the code memory to be RWX. MemMap* data_map = MemMap::MapAnonymous( - "data-code-cache", nullptr, max_capacity, kProtAll, false, false, &error_str, use_ashmem); + "data-code-cache", nullptr, + max_capacity, + kProtAll, + /* low_4gb */ true, + /* reuse */ false, + &error_str, + use_ashmem); if (data_map == nullptr) { std::ostringstream oss; oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity; @@ -197,34 +207,40 @@ class ScopedCodeCacheWrite : ScopedTrace { uint8_t* JitCodeCache::CommitCode(Thread* self, ArtMethod* method, - const uint8_t* vmap_table, + uint8_t* stack_map, + uint8_t* roots_data, size_t frame_size_in_bytes, size_t core_spill_mask, size_t fp_spill_mask, const uint8_t* code, size_t code_size, - bool osr) { + bool osr, + Handle<mirror::ObjectArray<mirror::Object>> roots) { uint8_t* result = CommitCodeInternal(self, method, - vmap_table, + stack_map, + roots_data, frame_size_in_bytes, core_spill_mask, fp_spill_mask, code, code_size, - osr); + osr, + roots); if (result == nullptr) { // Retry. GarbageCollectCache(self); result = CommitCodeInternal(self, method, - vmap_table, + stack_map, + roots_data, frame_size_in_bytes, core_spill_mask, fp_spill_mask, code, code_size, - osr); + osr, + roots); } return result; } @@ -243,20 +259,82 @@ static uintptr_t FromCodeToAllocation(const void* code) { return reinterpret_cast<uintptr_t>(code) - RoundUp(sizeof(OatQuickMethodHeader), alignment); } +static uint32_t ComputeRootTableSize(uint32_t number_of_roots) { + return sizeof(uint32_t) + number_of_roots * sizeof(GcRoot<mirror::Object>); +} + +static uint32_t GetNumberOfRoots(const uint8_t* stack_map) { + // The length of the table is stored just before the stack map (and therefore at the end of + // the table itself), in order to be able to fetch it from a `stack_map` pointer. + return reinterpret_cast<const uint32_t*>(stack_map)[-1]; +} + +static void FillRootTableLength(uint8_t* roots_data, uint32_t length) { + // Store the length of the table at the end. This will allow fetching it from a `stack_map` + // pointer. + reinterpret_cast<uint32_t*>(roots_data)[length] = length; +} + +static void FillRootTable(uint8_t* roots_data, Handle<mirror::ObjectArray<mirror::Object>> roots) + REQUIRES_SHARED(Locks::mutator_lock_) { + GcRoot<mirror::Object>* gc_roots = reinterpret_cast<GcRoot<mirror::Object>*>(roots_data); + const uint32_t length = roots->GetLength(); + // Put all roots in `roots_data`. + for (uint32_t i = 0; i < length; ++i) { + ObjPtr<mirror::Object> object = roots->Get(i); + if (kIsDebugBuild) { + // Ensure the string is strongly interned. b/32995596 + CHECK(object->IsString()); + ObjPtr<mirror::String> str = reinterpret_cast<mirror::String*>(object.Ptr()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + CHECK(class_linker->GetInternTable()->LookupStrong(Thread::Current(), str) != nullptr); + } + gc_roots[i] = GcRoot<mirror::Object>(object); + } + FillRootTableLength(roots_data, length); +} + +static uint8_t* GetRootTable(const void* code_ptr, uint32_t* number_of_roots = nullptr) { + OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + uint8_t* data = method_header->GetOptimizedCodeInfoPtr(); + uint32_t roots = GetNumberOfRoots(data); + if (number_of_roots != nullptr) { + *number_of_roots = roots; + } + return data - ComputeRootTableSize(roots); +} + +void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { + MutexLock mu(Thread::Current(), lock_); + for (const auto& entry : method_code_map_) { + uint32_t number_of_roots = 0; + uint8_t* roots_data = GetRootTable(entry.first, &number_of_roots); + GcRoot<mirror::Object>* roots = reinterpret_cast<GcRoot<mirror::Object>*>(roots_data); + for (uint32_t i = 0; i < number_of_roots; ++i) { + // This does not need a read barrier because this is called by GC. + mirror::Object* object = roots[i].Read<kWithoutReadBarrier>(); + DCHECK(object != nullptr); + mirror::Object* new_object = visitor->IsMarked(object); + // We know the string is marked because it's a strongly-interned string that + // is always alive. The IsMarked implementation of the CMS collector returns + // null for newly allocated objects, but we know those haven't moved. Therefore, + // only update the entry if we get a different non-null string. + // TODO: Do not use IsMarked for j.l.Class, and adjust once we move this method + // out of the weak access/creation pause. b/32167580 + if (new_object != nullptr && new_object != object) { + DCHECK(new_object->IsString()); + roots[i] = GcRoot<mirror::Object>(new_object); + } + } + } +} + void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UNUSED) { uintptr_t allocation = FromCodeToAllocation(code_ptr); - const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. DeleteJITCodeEntryForAddress(reinterpret_cast<uintptr_t>(code_ptr)); - - // Use the offset directly to prevent sanity check that the method is - // compiled with optimizing. - // TODO(ngeoffray): Clean up. - if (method_header->vmap_table_offset_ != 0) { - const uint8_t* data = method_header->code_ - method_header->vmap_table_offset_; - FreeData(const_cast<uint8_t*>(data)); - } + FreeData(GetRootTable(code_ptr)); FreeCode(reinterpret_cast<uint8_t*>(allocation)); } @@ -308,17 +386,21 @@ void JitCodeCache::ClearGcRootsInInlineCaches(Thread* self) { uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, ArtMethod* method, - const uint8_t* vmap_table, + uint8_t* stack_map, + uint8_t* roots_data, size_t frame_size_in_bytes, size_t core_spill_mask, size_t fp_spill_mask, const uint8_t* code, size_t code_size, - bool osr) { + bool osr, + Handle<mirror::ObjectArray<mirror::Object>> roots) { + DCHECK(stack_map != nullptr); size_t alignment = GetInstructionSetAlignment(kRuntimeISA); // Ensure the header ends up at expected instruction alignment. size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment); size_t total_size = header_size + code_size; + const uint32_t num_roots = roots->GetLength(); OatQuickMethodHeader* method_header = nullptr; uint8_t* code_ptr = nullptr; @@ -331,6 +413,9 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, ScopedCodeCacheWrite scc(code_map_.get()); memory = AllocateCode(total_size); if (memory == nullptr) { + // Fill root table length so that ClearData works correctly in case of failure. Otherwise + // the length will be 0 and cause incorrect DCHECK failure. + FillRootTableLength(roots_data, num_roots); return nullptr; } code_ptr = memory + header_size; @@ -338,21 +423,29 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, std::copy(code, code + code_size, code_ptr); method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); new (method_header) OatQuickMethodHeader( - (vmap_table == nullptr) ? 0 : code_ptr - vmap_table, + code_ptr - stack_map, frame_size_in_bytes, core_spill_mask, fp_spill_mask, code_size); + // Flush caches before we remove write permission because on some ARMv8 hardware, + // flushing caches require write permissions. + // + // For reference, here are kernel patches discussing about this issue: + // https://android.googlesource.com/kernel/msm/%2B/0e7f7bcc3fc87489cda5aa6aff8ce40eed912279 + // https://patchwork.kernel.org/patch/9047921/ + FlushInstructionCache(reinterpret_cast<char*>(code_ptr), + reinterpret_cast<char*>(code_ptr + code_size)); } - FlushInstructionCache(reinterpret_cast<char*>(code_ptr), - reinterpret_cast<char*>(code_ptr + code_size)); number_of_compilations_++; } // We need to update the entry point in the runnable state for the instrumentation. { MutexLock mu(self, lock_); method_code_map_.Put(code_ptr, method); + // Fill the root table before updating the entry point. + FillRootTable(roots_data, roots); if (osr) { number_of_osr_compilations_++; osr_code_map_.Put(method, code_ptr); @@ -403,13 +496,26 @@ size_t JitCodeCache::DataCacheSizeLocked() { return used_memory_for_data_; } -void JitCodeCache::ClearData(Thread* self, void* data) { +static const uint8_t* FromStackMapToRoots(const uint8_t* stack_map_data) { + return stack_map_data - ComputeRootTableSize(GetNumberOfRoots(stack_map_data)); +} + +void JitCodeCache::ClearData(Thread* self, + uint8_t* stack_map_data, + uint8_t* roots_data) { + DCHECK_EQ(FromStackMapToRoots(stack_map_data), roots_data); MutexLock mu(self, lock_); - FreeData(reinterpret_cast<uint8_t*>(data)); + FreeData(reinterpret_cast<uint8_t*>(roots_data)); } -uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size, ArtMethod* method) { - size = RoundUp(size, sizeof(void*)); +void JitCodeCache::ReserveData(Thread* self, + size_t stack_map_size, + size_t number_of_roots, + ArtMethod* method, + uint8_t** stack_map_data, + uint8_t** roots_data) { + size_t table_size = ComputeRootTableSize(number_of_roots); + size_t size = RoundUp(stack_map_size + table_size, sizeof(void*)); uint8_t* result = nullptr; { @@ -436,7 +542,8 @@ uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size, ArtMethod* method) << " for stack maps of " << ArtMethod::PrettyMethod(method); } - return result; + *roots_data = result; + *stack_map_data = result + table_size; } class MarkCodeVisitor FINAL : public StackVisitor { diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index e15c93a448..40112feb05 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -92,13 +92,15 @@ class JitCodeCache { // Allocate and write code and its metadata to the code cache. uint8_t* CommitCode(Thread* self, ArtMethod* method, - const uint8_t* vmap_table, + uint8_t* stack_map, + uint8_t* roots_data, size_t frame_size_in_bytes, size_t core_spill_mask, size_t fp_spill_mask, const uint8_t* code, size_t code_size, - bool osr) + bool osr, + Handle<mirror::ObjectArray<mirror::Object>> roots) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!lock_); @@ -108,13 +110,19 @@ class JitCodeCache { // Return true if the code cache contains this method. bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_); - // Reserve a region of data of size at least "size". Returns null if there is no more room. - uint8_t* ReserveData(Thread* self, size_t size, ArtMethod* method) + // Allocate a region of data that contain `size` bytes, and potentially space + // for storing `number_of_roots` roots. Returns null if there is no more room. + void ReserveData(Thread* self, + size_t size, + size_t number_of_roots, + ArtMethod* method, + uint8_t** stack_map_data, + uint8_t** roots_data) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!lock_); // Clear data from the data portion of the code cache. - void ClearData(Thread* self, void* data) + void ClearData(Thread* self, uint8_t* stack_map_data, uint8_t* roots_data) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!lock_); @@ -188,6 +196,10 @@ class JitCodeCache { bool IsOsrCompiled(ArtMethod* method) REQUIRES(!lock_); + void SweepRootTables(IsMarkedVisitor* visitor) + REQUIRES(!lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + private: // Take ownership of maps. JitCodeCache(MemMap* code_map, @@ -201,13 +213,15 @@ class JitCodeCache { // allocation fails. Return null if the allocation fails. uint8_t* CommitCodeInternal(Thread* self, ArtMethod* method, - const uint8_t* vmap_table, + uint8_t* stack_map, + uint8_t* roots_data, size_t frame_size_in_bytes, size_t core_spill_mask, size_t fp_spill_mask, const uint8_t* code, size_t code_size, - bool osr) + bool osr, + Handle<mirror::ObjectArray<mirror::Object>> roots) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index f535151d38..6f2a8c673f 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -37,7 +37,7 @@ namespace art { const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' }; -const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '1', '\0' }; +const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '2', '\0' }; static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; @@ -235,7 +235,7 @@ bool ProfileCompilationInfo::Save(int fd) { AddUintToBuffer(&buffer, method_it); } for (auto class_id : dex_data.class_set) { - AddUintToBuffer(&buffer, class_id); + AddUintToBuffer(&buffer, class_id.index_); } DCHECK_EQ(required_capacity, buffer.size()) << "Failed to add the expected number of bytes in the buffer"; @@ -282,12 +282,12 @@ bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location, bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location, uint32_t checksum, - uint16_t class_idx) { + dex::TypeIndex type_idx) { DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); if (data == nullptr) { return false; } - data->class_set.insert(class_idx); + data->class_set.insert(type_idx); return true; } @@ -304,8 +304,8 @@ bool ProfileCompilationInfo::ProcessLine(SafeBuffer& line_buffer, } for (uint16_t i = 0; i < class_set_size; i++) { - uint16_t class_def_idx = line_buffer.ReadUintAndAdvance<uint16_t>(); - if (!AddClassIndex(dex_location, checksum, class_def_idx)) { + uint16_t type_idx = line_buffer.ReadUintAndAdvance<uint16_t>(); + if (!AddClassIndex(dex_location, checksum, dex::TypeIndex(type_idx))) { return false; } } @@ -569,14 +569,14 @@ bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) c return false; } -bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const { +bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const { auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation())); if (info_it != info_.end()) { if (!ChecksumMatch(dex_file, info_it->second.checksum)) { return false; } - const std::set<uint16_t>& classes = info_it->second.class_set; - return classes.find(class_def_idx) != classes.end(); + const std::set<dex::TypeIndex>& classes = info_it->second.class_set; + return classes.find(type_idx) != classes.end(); } return false; } @@ -637,7 +637,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* os << "\n\tclasses: "; for (const auto class_it : dex_data.class_set) { if (dex_file != nullptr) { - os << "\n\t\t" << dex_file->GetClassDescriptor(dex_file->GetClassDef(class_it)); + os << "\n\t\t" << dex_file->PrettyType(class_it); } else { os << class_it << ","; } @@ -702,11 +702,11 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd, } for (uint16_t c = 0; c < number_of_classes; c++) { - uint16_t class_idx = rand() % max_classes; + uint16_t type_idx = rand() % max_classes; if (c < (number_of_classes / kFavorSplit)) { - class_idx %= kFavorFirstN; + type_idx %= kFavorFirstN; } - info.AddClassIndex(profile_key, 0, class_idx); + info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx)); } } return info.Save(fd); diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index fdca078bc7..413648829a 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -23,6 +23,7 @@ #include "atomic.h" #include "dex_cache_resolved_classes.h" #include "dex_file.h" +#include "dex_file_types.h" #include "method_reference.h" #include "safe_map.h" @@ -65,8 +66,8 @@ class ProfileCompilationInfo { // Returns true if the method reference is present in the profiling info. bool ContainsMethod(const MethodReference& method_ref) const; - // Returns true if the class is present in the profiling info. - bool ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const; + // Returns true if the class's type is present in the profiling info. + bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const; // Dumps all the loaded profile info into a string and returns it. // If dex_files is not null then the method indices will be resolved to their @@ -104,7 +105,7 @@ class ProfileCompilationInfo { explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {} uint32_t checksum; std::set<uint16_t> method_set; - std::set<uint16_t> class_set; + std::set<dex::TypeIndex> class_set; bool operator==(const DexFileData& other) const { return checksum == other.checksum && method_set == other.method_set; @@ -115,7 +116,7 @@ class ProfileCompilationInfo { DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum); bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx); - bool AddClassIndex(const std::string& dex_location, uint32_t checksum, uint16_t class_idx); + bool AddClassIndex(const std::string& dex_location, uint32_t checksum, dex::TypeIndex type_idx); bool AddResolvedClasses(const DexCacheResolvedClasses& classes); // Parsing functionality. diff --git a/runtime/method_handles.h b/runtime/method_handles.h index 54c772a4f2..d0a4902cbc 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -46,12 +46,13 @@ enum MethodHandleKind { kInvokeStatic, kInvokeInterface, kInvokeTransform, + kInvokeCallSiteTransform, kInstanceGet, kInstancePut, kStaticGet, kStaticPut, kLastValidKind = kStaticPut, - kLastInvokeKind = kInvokeTransform + kLastInvokeKind = kInvokeCallSiteTransform }; // Whether the given method handle kind is some variant of an invoke. @@ -59,6 +60,11 @@ inline bool IsInvoke(const MethodHandleKind handle_kind) { return handle_kind <= kLastInvokeKind; } +// Whether the given method handle kind is some variant of a tranform. +inline bool IsInvokeTransform(const MethodHandleKind handle_kind) { + return handle_kind == kInvokeTransform || handle_kind == kInvokeCallSiteTransform; +} + // Returns true if there is a possible conversion from |from| to |to| // for a MethodHandle parameter. bool IsParameterTypeConvertible(ObjPtr<mirror::Class> from, diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index bbdb2af16a..aa5da2e83b 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -372,7 +372,7 @@ inline bool Class::ResolvedFieldAccessTest(ObjPtr<Class> access_to, // to access the field if the FieldId specifies an accessible subclass of the declaring // class rather than the declaring class itself. ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache; - uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_; + dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_; // The referenced class has already been resolved with the field, but may not be in the dex // cache. Use LookupResolveType here to search the class table if it is not in the dex cache. // should be no thread suspension due to the class being resolved. @@ -410,7 +410,7 @@ inline bool Class::ResolvedMethodAccessTest(ObjPtr<Class> access_to, // to access the method if the MethodId specifies an accessible subclass of the declaring // class rather than the declaring class itself. ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache; - uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; + dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; // The referenced class has already been resolved with the method, but may not be in the dex // cache. ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( @@ -526,18 +526,17 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline IfTable* Class::GetIfTable() { - return GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>(IfTableOffset()); + ObjPtr<IfTable> ret = GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>(IfTableOffset()); + DCHECK(ret != nullptr) << PrettyClass(this); + return ret.Ptr(); } inline int32_t Class::GetIfTableCount() { - ObjPtr<IfTable> iftable = GetIfTable(); - if (iftable == nullptr) { - return 0; - } - return iftable->Count(); + return GetIfTable()->Count(); } inline void Class::SetIfTable(ObjPtr<IfTable> new_iftable) { + DCHECK(new_iftable != nullptr) << PrettyClass(this); SetFieldObject<false>(IfTableOffset(), new_iftable); } @@ -895,7 +894,8 @@ inline void Class::InitializeClassVisitor::operator()(ObjPtr<Object> obj, klass->SetClassSize(class_size_); klass->SetPrimitiveType(Primitive::kPrimNot); // Default to not being primitive. klass->SetDexClassDefIndex(DexFile::kDexNoIndex16); // Default to no valid class def index. - klass->SetDexTypeIndex(DexFile::kDexNoIndex16); // Default to no valid type index. + klass->SetDexTypeIndex(dex::TypeIndex(DexFile::kDexNoIndex16)); // Default to no valid type + // index. // Default to force slow path until initialized. klass->SetObjectSizeAllocFastPath(std::numeric_limits<uint32_t>::max()); } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index db46027bc8..0cfe29bed9 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -923,7 +923,7 @@ const DexFile::ClassDef* Class::GetClassDef() { return &GetDexFile().GetClassDef(class_def_idx); } -uint16_t Class::GetDirectInterfaceTypeIdx(uint32_t idx) { +dex::TypeIndex Class::GetDirectInterfaceTypeIdx(uint32_t idx) { DCHECK(!IsPrimitive()); DCHECK(!IsArrayClass()); return GetInterfaceTypeList()->GetTypeItem(idx).type_idx_; @@ -947,10 +947,11 @@ ObjPtr<Class> Class::GetDirectInterface(Thread* self, DCHECK(interfaces != nullptr); return interfaces->Get(idx); } else { - uint16_t type_idx = klass->GetDirectInterfaceTypeIdx(idx); + dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); ObjPtr<Class> interface = klass->GetDexCache()->GetResolvedType(type_idx); if (interface == nullptr) { - interface = Runtime::Current()->GetClassLinker()->ResolveType(klass->GetDexFile(), type_idx, + interface = Runtime::Current()->GetClassLinker()->ResolveType(klass->GetDexFile(), + type_idx, klass.Get()); CHECK(interface != nullptr || self->IsExceptionPending()); } @@ -1130,10 +1131,12 @@ uint32_t Class::Depth() { return depth; } -uint32_t Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) { +dex::TypeIndex Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) { std::string temp; const DexFile::TypeId* type_id = dex_file.FindTypeId(GetDescriptor(&temp)); - return (type_id == nullptr) ? DexFile::kDexNoIndex : dex_file.GetIndexForTypeId(*type_id); + return (type_id == nullptr) + ? dex::TypeIndex(DexFile::kDexNoIndex) + : dex_file.GetIndexForTypeId(*type_id); } template <PointerSize kPointerSize, bool kTransactionActive> diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 711914dc61..792f626eff 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -20,6 +20,7 @@ #include "base/enums.h" #include "base/iteration_range.h" #include "dex_file.h" +#include "dex_file_types.h" #include "class_flags.h" #include "gc_root.h" #include "gc/allocator_type.h" @@ -1148,16 +1149,17 @@ class MANAGED Class FINAL : public Object { SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx); } - uint16_t GetDexTypeIndex() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_)); + dex::TypeIndex GetDexTypeIndex() REQUIRES_SHARED(Locks::mutator_lock_) { + return dex::TypeIndex( + static_cast<uint16_t>(GetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_)))); } - void SetDexTypeIndex(uint16_t type_idx) REQUIRES_SHARED(Locks::mutator_lock_) { + void SetDexTypeIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_) { // Not called within a transaction. - SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx); + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx.index_); } - uint32_t FindTypeIndexInOtherDexFile(const DexFile& dex_file) + dex::TypeIndex FindTypeIndexInOtherDexFile(const DexFile& dex_file) REQUIRES_SHARED(Locks::mutator_lock_); static Class* GetJavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1198,7 +1200,7 @@ class MANAGED Class FINAL : public Object { ALWAYS_INLINE uint32_t NumDirectInterfaces() REQUIRES_SHARED(Locks::mutator_lock_); - uint16_t GetDirectInterfaceTypeIdx(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_); + dex::TypeIndex GetDirectInterfaceTypeIdx(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_); static ObjPtr<Class> GetDirectInterface(Thread* self, Handle<Class> klass, diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index c7a123b580..d903f71604 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -69,15 +69,15 @@ inline void DexCache::ClearString(uint32_t string_idx) { } } -inline Class* DexCache::GetResolvedType(uint32_t type_idx) { - DCHECK_LT(type_idx, NumResolvedTypes()); - return GetResolvedTypes()[type_idx].Read(); +inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) { + DCHECK_LT(type_idx.index_, NumResolvedTypes()); + return GetResolvedTypes()[type_idx.index_].Read(); } -inline void DexCache::SetResolvedType(uint32_t type_idx, ObjPtr<Class> resolved) { - DCHECK_LT(type_idx, NumResolvedTypes()); // NOTE: Unchecked, i.e. not throwing AIOOB. +inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) { + DCHECK_LT(type_idx.index_, NumResolvedTypes()); // NOTE: Unchecked, i.e. not throwing AIOOB. // TODO default transaction support. - GetResolvedTypes()[type_idx] = GcRoot<Class>(resolved); + GetResolvedTypes()[type_idx.index_] = GcRoot<Class>(resolved); // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); } diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 1ae694d789..7d82d3ad29 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -21,6 +21,7 @@ #include "art_field.h" #include "art_method.h" #include "class.h" +#include "dex_file_types.h" #include "object.h" #include "object_array.h" @@ -223,9 +224,9 @@ class MANAGED DexCache FINAL : public Object { // the string isn't kept live. void ClearString(uint32_t string_idx) REQUIRES_SHARED(Locks::mutator_lock_); - Class* GetResolvedType(uint32_t type_idx) REQUIRES_SHARED(Locks::mutator_lock_); + Class* GetResolvedType(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); - void SetResolvedType(uint32_t type_idx, ObjPtr<Class> resolved) + void SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE ArtMethod* GetResolvedMethod(uint32_t method_idx, PointerSize ptr_size) diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h index 9fa06b7639..d83a53652b 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -58,6 +58,10 @@ class MANAGED EmulatedStackFrame : public Object { // Sets the return value slot of this emulated stack frame to |value|. void SetReturnValue(Thread* self, const JValue& value) REQUIRES_SHARED(Locks::mutator_lock_); + mirror::MethodType* GetType() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, type_)); + } + static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); @@ -67,10 +71,6 @@ class MANAGED EmulatedStackFrame : public Object { return static_class_.Read(); } - mirror::MethodType* GetType() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, type_)); - } - mirror::ObjectArray<mirror::Object>* GetReferences() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject<mirror::ObjectArray<mirror::Object>>( OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, references_)); diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index be5d446d58..0fdf1323eb 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -150,12 +150,12 @@ inline void ObjectArray<T>::AssignableMemmove(int32_t dst_pos, uintptr_t fake_address_dependency; if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) { baker_non_gray_case = true; - DCHECK_EQ(fake_address_dependency, 0U) << fake_address_dependency; + DCHECK_EQ(fake_address_dependency, 0U); src.Assign(reinterpret_cast<ObjectArray<T>*>( reinterpret_cast<uintptr_t>(src.Ptr()) | fake_address_dependency)); for (int i = 0; i < count; ++i) { // We can skip the RB here because 'src' isn't gray. - Object* obj = src->template GetWithoutChecks<kDefaultVerifyFlags, kWithoutReadBarrier>( + T* obj = src->template GetWithoutChecks<kDefaultVerifyFlags, kWithoutReadBarrier>( src_pos + i); SetWithoutChecksAndWriteBarrier<false>(dst_pos + i, obj); } @@ -164,7 +164,7 @@ inline void ObjectArray<T>::AssignableMemmove(int32_t dst_pos, if (!baker_non_gray_case) { for (int i = 0; i < count; ++i) { // We need a RB here. ObjectArray::GetWithoutChecks() contains a RB. - Object* obj = src->GetWithoutChecks(src_pos + i); + T* obj = src->GetWithoutChecks(src_pos + i); SetWithoutChecksAndWriteBarrier<false>(dst_pos + i, obj); } } @@ -175,12 +175,12 @@ inline void ObjectArray<T>::AssignableMemmove(int32_t dst_pos, uintptr_t fake_address_dependency; if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) { baker_non_gray_case = true; - DCHECK_EQ(fake_address_dependency, 0U) << fake_address_dependency; + DCHECK_EQ(fake_address_dependency, 0U); src.Assign(reinterpret_cast<ObjectArray<T>*>( reinterpret_cast<uintptr_t>(src.Ptr()) | fake_address_dependency)); for (int i = count - 1; i >= 0; --i) { // We can skip the RB here because 'src' isn't gray. - Object* obj = src->template GetWithoutChecks<kDefaultVerifyFlags, kWithoutReadBarrier>( + T* obj = src->template GetWithoutChecks<kDefaultVerifyFlags, kWithoutReadBarrier>( src_pos + i); SetWithoutChecksAndWriteBarrier<false>(dst_pos + i, obj); } @@ -189,7 +189,7 @@ inline void ObjectArray<T>::AssignableMemmove(int32_t dst_pos, if (!baker_non_gray_case) { for (int i = count - 1; i >= 0; --i) { // We need a RB here. ObjectArray::GetWithoutChecks() contains a RB. - Object* obj = src->GetWithoutChecks(src_pos + i); + T* obj = src->GetWithoutChecks(src_pos + i); SetWithoutChecksAndWriteBarrier<false>(dst_pos + i, obj); } } @@ -225,7 +225,7 @@ inline void ObjectArray<T>::AssignableMemcpy(int32_t dst_pos, uintptr_t fake_address_dependency; if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) { baker_non_gray_case = true; - DCHECK_EQ(fake_address_dependency, 0U) << fake_address_dependency; + DCHECK_EQ(fake_address_dependency, 0U); src.Assign(reinterpret_cast<ObjectArray<T>*>( reinterpret_cast<uintptr_t>(src.Ptr()) | fake_address_dependency)); for (int i = 0; i < count; ++i) { @@ -266,14 +266,14 @@ inline void ObjectArray<T>::AssignableCheckingMemcpy(int32_t dst_pos, Class* dst_class = GetClass()->GetComponentType(); Class* lastAssignableElementClass = dst_class; - Object* o = nullptr; + T* o = nullptr; int i = 0; bool baker_non_gray_case = false; if (kUseReadBarrier && kUseBakerReadBarrier) { uintptr_t fake_address_dependency; if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) { baker_non_gray_case = true; - DCHECK_EQ(fake_address_dependency, 0U) << fake_address_dependency; + DCHECK_EQ(fake_address_dependency, 0U); src.Assign(reinterpret_cast<ObjectArray<T>*>( reinterpret_cast<uintptr_t>(src.Ptr()) | fake_address_dependency)); for (; i < count; ++i) { diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 5bf254d961..4b47f7f614 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -313,7 +313,7 @@ TEST_F(ObjectTest, CheckAndAllocArrayFromCode) { ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", kRuntimePointerSize); const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I"); ASSERT_TRUE(type_id != nullptr); - uint32_t type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id); + dex::TypeIndex type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id); Object* array = CheckAndAllocArrayFromCodeInstrumented( type_idx, 3, sort, Thread::Current(), false, Runtime::Current()->GetHeap()->GetCurrentAllocator()); diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index b866a63aef..ade4e87afd 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -104,7 +104,7 @@ std::string Throwable::Dump() { CHECK_EQ(array_len % 2, 0); const auto depth = array_len / 2; if (depth == 0) { - result += "(Throwable with empty stack trace)"; + result += "(Throwable with empty stack trace)\n"; } else { const PointerSize ptr_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (int32_t i = 0; i < depth; ++i) { @@ -124,7 +124,7 @@ std::string Throwable::Dump() { ObjPtr<ObjectArray<StackTraceElement>> ste_array = ObjPtr<ObjectArray<StackTraceElement>>::DownCast(stack_trace); if (ste_array->GetLength() == 0) { - result += "(Throwable with empty stack trace)"; + result += "(Throwable with empty stack trace)\n"; } else { for (int32_t i = 0; i < ste_array->GetLength(); ++i) { StackTraceElement* ste = ste_array->Get(i); @@ -139,7 +139,7 @@ std::string Throwable::Dump() { } } } else { - result += "(Throwable with no stack trace)"; + result += "(Throwable with no stack trace)\n"; } } ObjPtr<Throwable> cause = GetFieldObject<Throwable>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_)); diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index df0849a628..1a77072921 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -413,33 +413,7 @@ static jstring DexFile_getDexFileStatus(JNIEnv* env, OatFileAssistant oat_file_assistant(filename.c_str(), target_instruction_set, false /* load_executable */); - - std::ostringstream status; - bool oat_file_exists = false; - bool odex_file_exists = false; - if (oat_file_assistant.OatFileExists()) { - oat_file_exists = true; - status << *oat_file_assistant.OatFileName() << " [compilation_filter="; - status << CompilerFilter::NameOfFilter(oat_file_assistant.OatFileCompilerFilter()); - status << ", status=" << oat_file_assistant.OatFileStatus(); - } - - if (oat_file_assistant.OdexFileExists()) { - odex_file_exists = true; - if (oat_file_exists) { - status << "] "; - } - status << *oat_file_assistant.OdexFileName() << " [compilation_filter="; - status << CompilerFilter::NameOfFilter(oat_file_assistant.OdexFileCompilerFilter()); - status << ", status=" << oat_file_assistant.OdexFileStatus(); - } - - if (!oat_file_exists && !odex_file_exists) { - status << "invalid["; - } - - status << "]"; - return env->NewStringUTF(status.str().c_str()); + return env->NewStringUTF(oat_file_assistant.GetStatusDump().c_str()); } static jint DexFile_getDexOptNeeded(JNIEnv* env, diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 866dc7f73f..48feb11924 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -34,6 +34,7 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "common_throws.h" #include "debugger.h" #include "dex_file-inl.h" +#include "dex_file_types.h" #include "gc/accounting/card_table-inl.h" #include "gc/allocator/dlmalloc.h" #include "gc/heap.h" @@ -305,7 +306,7 @@ static void PreloadDexCachesResolveString( // Based on ClassLinker::ResolveType. static void PreloadDexCachesResolveType(Thread* self, ObjPtr<mirror::DexCache> dex_cache, - uint32_t type_idx) + dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(type_idx); if (klass != nullptr) { @@ -455,7 +456,7 @@ static void PreloadDexCachesStatsFilled(DexCacheStats* filled) } } for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) { - ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(j); + ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(dex::TypeIndex(j)); if (klass != nullptr) { filled->num_types++; } @@ -519,7 +520,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { if (kPreloadDexCachesTypes) { for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) { - PreloadDexCachesResolveType(soa.Self(), dex_cache.Get(), j); + PreloadDexCachesResolveType(soa.Self(), dex_cache.Get(), dex::TypeIndex(j)); } } diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc index 71379a51a5..f6de593017 100644 --- a/runtime/native/java_lang_DexCache.cc +++ b/runtime/native/java_lang_DexCache.cc @@ -17,6 +17,7 @@ #include "java_lang_DexCache.h" #include "dex_file.h" +#include "dex_file_types.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" @@ -53,7 +54,7 @@ static jobject DexCache_getResolvedType(JNIEnv* env, jobject javaDexCache, jint ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache); CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes()); - return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(type_index)); + return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(dex::TypeIndex(type_index))); } static jobject DexCache_getResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index) { @@ -68,7 +69,7 @@ static void DexCache_setResolvedType(JNIEnv* env, jobject javaDexCache, jint typ ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache); CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes()); - dex_cache->SetResolvedType(type_index, soa.Decode<mirror::Class>(type)); + dex_cache->SetResolvedType(dex::TypeIndex(type_index), soa.Decode<mirror::Class>(type)); } static void DexCache_setResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index, diff --git a/runtime/oat.h b/runtime/oat.h index 3aef707dc3..8c84d42043 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '9', '1', '\0' }; + static constexpr uint8_t kOatVersion[] = { '0', '9', '2', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c14b6169fb..bdf8b0e65d 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -38,6 +38,7 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" +#include "dex_file_types.h" #include "elf_file.h" #include "elf_utils.h" #include "gc_root.h" @@ -718,7 +719,7 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename, dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo); #else UNUSED(oat_file_begin); - static_assert(!kIsTargetBuild, "host_dlopen_handles_ will leak handles"); + static_assert(!kIsTargetBuild || kIsTargetLinux, "host_dlopen_handles_ will leak handles"); MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_); dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW); if (dlopen_handle_ != nullptr) { @@ -1252,13 +1253,14 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, if (lookup_table_data_ + TypeLookupTable::RawDataLength(num_class_defs) > GetOatFile()->End()) { LOG(WARNING) << "found truncated lookup table in " << dex_file_location_; } else { - lookup_table_.reset(TypeLookupTable::Open(dex_file_pointer_, - lookup_table_data_, - num_class_defs)); + lookup_table_ = TypeLookupTable::Open(dex_file_pointer_, lookup_table_data_, num_class_defs); } } } +OatFile::OatDexFile::OatDexFile(std::unique_ptr<TypeLookupTable>&& lookup_table) + : lookup_table_(std::move(lookup_table)) {} + OatFile::OatDexFile::~OatDexFile() {} size_t OatFile::OatDexFile::FileSize() const { @@ -1342,7 +1344,7 @@ const DexFile::ClassDef* OatFile::OatDexFile::FindClassDef(const DexFile& dex_fi } const DexFile::TypeId* type_id = dex_file.FindTypeId(descriptor); if (type_id != nullptr) { - uint16_t type_idx = dex_file.GetIndexForTypeId(*type_id); + dex::TypeIndex type_idx = dex_file.GetIndexForTypeId(*type_id); return dex_file.FindClassDef(type_idx); } return nullptr; @@ -1540,7 +1542,7 @@ OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, bool* found) { DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16); const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); - if (oat_dex_file == nullptr) { + if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) { *found = false; return OatFile::OatClass::Invalid(); } @@ -1548,4 +1550,8 @@ OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, return oat_dex_file->GetOatClass(class_def_idx); } +void OatFile::OatDexFile::AssertAotCompiler() { + CHECK(Runtime::Current()->IsAotCompiler()); +} + } // namespace art diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 63a0e14bf3..29add5b2b2 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -384,7 +384,13 @@ class OatDexFile FINAL { // Opens the DexFile referred to by this OatDexFile from within the containing OatFile. std::unique_ptr<const DexFile> OpenDexFile(std::string* error_msg) const; + // May return null if the OatDexFile only contains a type lookup table. This case only happens + // for the compiler to speed up compilation. const OatFile* GetOatFile() const { + // Avoid pulling in runtime.h in the header file. + if (kIsDebugBuild && oat_file_ == nullptr) { + AssertAotCompiler(); + } return oat_file_; } @@ -436,6 +442,9 @@ class OatDexFile FINAL { ~OatDexFile(); + // Create only with a type lookup table, used by the compiler to speed up compilation. + explicit OatDexFile(std::unique_ptr<TypeLookupTable>&& lookup_table); + private: OatDexFile(const OatFile* oat_file, const std::string& dex_file_location, @@ -446,14 +455,16 @@ class OatDexFile FINAL { const uint32_t* oat_class_offsets_pointer, uint8_t* dex_cache_arrays); - const OatFile* const oat_file_; + static void AssertAotCompiler(); + + const OatFile* const oat_file_ = nullptr; const std::string dex_file_location_; const std::string canonical_dex_file_location_; - const uint32_t dex_file_location_checksum_; - const uint8_t* const dex_file_pointer_; - const uint8_t* lookup_table_data_; - const uint32_t* const oat_class_offsets_pointer_; - uint8_t* const dex_cache_arrays_; + const uint32_t dex_file_location_checksum_ = 0u; + const uint8_t* const dex_file_pointer_ = nullptr; + const uint8_t* lookup_table_data_ = nullptr; + const uint32_t* const oat_class_offsets_pointer_ = 0u; + uint8_t* const dex_cache_arrays_ = nullptr; mutable std::unique_ptr<TypeLookupTable> lookup_table_; friend class OatFile; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 0679360a15..4d1e1eafc5 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -16,6 +16,8 @@ #include "oat_file_assistant.h" +#include <sstream> + #include <sys/stat.h> #include "base/logging.h" #include "base/stringprintf.h" @@ -34,15 +36,21 @@ namespace art { std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) { switch (status) { - case OatFileAssistant::kOatOutOfDate: - stream << "kOatOutOfDate"; + case OatFileAssistant::kOatCannotOpen: + stream << "kOatCannotOpen"; + break; + case OatFileAssistant::kOatDexOutOfDate: + stream << "kOatDexOutOfDate"; + break; + case OatFileAssistant::kOatBootImageOutOfDate: + stream << "kOatBootImageOutOfDate"; + break; + case OatFileAssistant::kOatRelocationOutOfDate: + stream << "kOatRelocationOutOfDate"; break; case OatFileAssistant::kOatUpToDate: stream << "kOatUpToDate"; break; - case OatFileAssistant::kOatNeedsRelocation: - stream << "kOatNeedsRelocation"; - break; default: UNREACHABLE(); } @@ -60,7 +68,10 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, const char* oat_location, const InstructionSet isa, bool load_executable) - : isa_(isa), load_executable_(load_executable), odex_(this), oat_(this) { + : isa_(isa), + load_executable_(load_executable), + odex_(this, /*is_oat_location*/ false), + oat_(this, /*is_oat_location*/ true) { CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location"; dex_location_.assign(dex_location); @@ -135,51 +146,13 @@ bool OatFileAssistant::Lock(std::string* error_msg) { return true; } -OatFileAssistant::DexOptNeeded -OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) { - bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target); - - // See if the oat file is in good shape as is. - bool oat_okay = oat_.CompilerFilterIsOkay(target, profile_changed); - if (oat_okay) { - if (compilation_desired) { - if (oat_.IsUpToDate()) { - return kNoDexOptNeeded; - } - } else { - if (!oat_.IsOutOfDate()) { - return kNoDexOptNeeded; - } - } +int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) { + OatFileInfo& info = GetBestInfo(); + DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed); + if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) { + return dexopt_needed; } - - // See if the odex file is in good shape as is. - bool odex_okay = odex_.CompilerFilterIsOkay(target, profile_changed); - if (odex_okay) { - if (compilation_desired) { - if (odex_.IsUpToDate()) { - return kNoDexOptNeeded; - } - } else { - if (!odex_.IsOutOfDate()) { - return kNoDexOptNeeded; - } - } - } - - // See if we can get an up-to-date file by running patchoat. - if (compilation_desired) { - if (odex_okay && odex_.NeedsRelocation() && odex_.HasPatchInfo()) { - return kPatchOatNeeded; - } - - if (oat_okay && oat_.NeedsRelocation() && oat_.HasPatchInfo()) { - return kSelfPatchOatNeeded; - } - } - - // We can only run dex2oat if there are original dex files. - return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded; + return -dexopt_needed; } // Figure out the currently specified compile filter option in the runtime. @@ -205,7 +178,7 @@ static bool GetRuntimeCompilerFilterOption(CompilerFilter::Filter* filter, } bool OatFileAssistant::IsUpToDate() { - return OatFileIsUpToDate() || OdexFileIsUpToDate(); + return GetBestInfo().Status() == kOatUpToDate; } OatFileAssistant::ResultOfAttemptToUpdate @@ -215,59 +188,66 @@ OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) { return kUpdateNotAttempted; } - switch (GetDexOptNeeded(target, profile_changed)) { - case kNoDexOptNeeded: return kUpdateSucceeded; - case kDex2OatNeeded: return GenerateOatFile(error_msg); - case kPatchOatNeeded: return RelocateOatFile(odex_.Filename(), error_msg); - case kSelfPatchOatNeeded: return RelocateOatFile(oat_.Filename(), error_msg); + OatFileInfo& info = GetBestInfo(); + switch (info.GetDexOptNeeded(target, profile_changed)) { + case kNoDexOptNeeded: + return kUpdateSucceeded; + + // TODO: For now, don't bother with all the different ways we can call + // dex2oat to generate the oat file. Always generate the oat file as if it + // were kDex2OatFromScratch. + case kDex2OatFromScratch: + case kDex2OatForBootImage: + case kDex2OatForRelocation: + case kDex2OatForFilter: + return GenerateOatFile(error_msg); + + case kPatchoatForRelocation: { + return RelocateOatFile(info.Filename(), error_msg); + } } UNREACHABLE(); } std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() { - // The best oat files are, in descending order of bestness: - // 1. Properly relocated files. These may be opened executable. - // 2. Not out-of-date files that are already opened non-executable. - // 3. Not out-of-date files that we must reopen non-executable. - - if (oat_.IsUpToDate()) { - return oat_.ReleaseFile(); - } - - if (odex_.IsUpToDate()) { - return odex_.ReleaseFile(); - } + return GetBestInfo().ReleaseFileForUse(); +} - VLOG(oat) << "Oat File Assistant: No relocated oat file found," - << " attempting to fall back to interpreting oat file instead."; +std::string OatFileAssistant::GetStatusDump() { + std::ostringstream status; + bool oat_file_exists = false; + bool odex_file_exists = false; + if (oat_.Status() != kOatCannotOpen) { + // If we can open the file, neither Filename nor GetFile should return null. + CHECK(oat_.Filename() != nullptr); + CHECK(oat_.GetFile() != nullptr); - if (!oat_.IsOutOfDate() && !oat_.IsExecutable()) { - return oat_.ReleaseFile(); + oat_file_exists = true; + status << *oat_.Filename() << " [compilation_filter="; + status << CompilerFilter::NameOfFilter(oat_.GetFile()->GetCompilerFilter()); + status << ", status=" << oat_.Status(); } - if (!odex_.IsOutOfDate() && !odex_.IsExecutable()) { - return odex_.ReleaseFile(); - } + if (odex_.Status() != kOatCannotOpen) { + // If we can open the file, neither Filename nor GetFile should return null. + CHECK(odex_.Filename() != nullptr); + CHECK(odex_.GetFile() != nullptr); - if (!oat_.IsOutOfDate()) { - load_executable_ = false; - oat_.Reset(); - if (!oat_.IsOutOfDate()) { - CHECK(!oat_.IsExecutable()); - return oat_.ReleaseFile(); + odex_file_exists = true; + if (oat_file_exists) { + status << "] "; } + status << *odex_.Filename() << " [compilation_filter="; + status << CompilerFilter::NameOfFilter(odex_.GetFile()->GetCompilerFilter()); + status << ", status=" << odex_.Status(); } - if (!odex_.IsOutOfDate()) { - load_executable_ = false; - odex_.Reset(); - if (!odex_.IsOutOfDate()) { - CHECK(!odex_.IsExecutable()); - return odex_.ReleaseFile(); - } + if (!oat_file_exists && !odex_file_exists) { + status << "invalid["; } - return std::unique_ptr<OatFile>(); + status << "]"; + return status.str(); } std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles( @@ -317,62 +297,14 @@ bool OatFileAssistant::HasOriginalDexFiles() { return has_original_dex_files_; } -const std::string* OatFileAssistant::OdexFileName() { - return odex_.Filename(); -} - -bool OatFileAssistant::OdexFileExists() { - return odex_.Exists(); -} - OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() { return odex_.Status(); } -bool OatFileAssistant::OdexFileIsOutOfDate() { - return odex_.IsOutOfDate(); -} - -bool OatFileAssistant::OdexFileNeedsRelocation() { - return odex_.NeedsRelocation(); -} - -bool OatFileAssistant::OdexFileIsUpToDate() { - return odex_.IsUpToDate(); -} - -CompilerFilter::Filter OatFileAssistant::OdexFileCompilerFilter() { - return odex_.CompilerFilter(); -} - -const std::string* OatFileAssistant::OatFileName() { - return oat_.Filename(); -} - -bool OatFileAssistant::OatFileExists() { - return oat_.Exists(); -} - OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() { return oat_.Status(); } -bool OatFileAssistant::OatFileIsOutOfDate() { - return oat_.IsOutOfDate(); -} - -bool OatFileAssistant::OatFileNeedsRelocation() { - return oat_.NeedsRelocation(); -} - -bool OatFileAssistant::OatFileIsUpToDate() { - return oat_.IsUpToDate(); -} - -CompilerFilter::Filter OatFileAssistant::OatFileCompilerFilter() { - return oat_.CompilerFilter(); -} - OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) { // Verify the dex checksum. // Note: GetOatDexFile will return null if the dex checksum doesn't match @@ -383,7 +315,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& dex_location_.c_str(), dex_checksum_pointer, &error_msg); if (oat_dex_file == nullptr) { VLOG(oat) << error_msg; - return kOatOutOfDate; + return kOatDexOutOfDate; } // Verify the dex checksums for any secondary multidex files @@ -406,7 +338,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& << secondary_dex_location << ". Expected: " << expected_secondary_checksum << ", Actual: " << actual_secondary_checksum; - return kOatOutOfDate; + return kOatDexOutOfDate; } } else { // If we can't get the checksum for the secondary location, we assume @@ -425,7 +357,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& VLOG(oat) << "No image for oat image checksum to match against."; if (HasOriginalDexFiles()) { - return kOatOutOfDate; + return kOatBootImageOutOfDate; } // If there is no original dex file to fall back to, grudgingly accept @@ -439,7 +371,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& } else if (file.GetOatHeader().GetImageFileLocationOatChecksum() != GetCombinedImageChecksum()) { VLOG(oat) << "Oat image checksum does not match image checksum."; - return kOatOutOfDate; + return kOatBootImageOutOfDate; } } else { VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter; @@ -450,7 +382,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& const ImageInfo* image_info = GetImageInfo(); if (image_info == nullptr) { VLOG(oat) << "No image to check oat relocation against."; - return kOatNeedsRelocation; + return kOatRelocationOutOfDate; } // Verify the oat_data_begin recorded for the image in the oat file matches @@ -462,7 +394,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& ": Oat file image oat_data_begin (" << oat_data_begin << ")" << " does not match actual image oat_data_begin (" << image_info->oat_data_begin << ")"; - return kOatNeedsRelocation; + return kOatRelocationOutOfDate; } // Verify the oat_patch_delta recorded for the image in the oat file matches @@ -473,7 +405,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& ": Oat file image patch delta (" << oat_patch_delta << ")" << " does not match actual image patch delta (" << image_info->patch_delta << ")"; - return kOatNeedsRelocation; + return kOatRelocationOutOfDate; } } else { // Oat files compiled in PIC mode do not require relocation. @@ -841,6 +773,11 @@ uint32_t OatFileAssistant::GetCombinedImageChecksum() { return combined_image_checksum_; } +OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() { + bool use_oat = oat_.IsUseable() || odex_.Status() == kOatCannotOpen; + return use_oat ? oat_ : odex_; +} + std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const OatFile* oat_file) { DCHECK(oat_file != nullptr); std::string art_file = ReplaceFileExtension(oat_file->GetLocation(), "art"); @@ -857,16 +794,29 @@ std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const Oa return ret; } -OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant) - : oat_file_assistant_(oat_file_assistant) +OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant, + bool is_oat_location) + : oat_file_assistant_(oat_file_assistant), is_oat_location_(is_oat_location) {} +bool OatFileAssistant::OatFileInfo::IsOatLocation() { + return is_oat_location_; +} + const std::string* OatFileAssistant::OatFileInfo::Filename() { return filename_provided_ ? &filename_ : nullptr; } -bool OatFileAssistant::OatFileInfo::Exists() { - return GetFile() != nullptr; +bool OatFileAssistant::OatFileInfo::IsUseable() { + switch (Status()) { + case kOatCannotOpen: + case kOatDexOutOfDate: + case kOatBootImageOutOfDate: return false; + + case kOatRelocationOutOfDate: + case kOatUpToDate: return true; + } + UNREACHABLE(); } OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() { @@ -874,7 +824,7 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() { status_attempted_ = true; const OatFile* file = GetFile(); if (file == nullptr) { - status_ = kOatOutOfDate; + status_ = kOatCannotOpen; } else { status_ = oat_file_assistant_->GivenOatFileStatus(*file); VLOG(oat) << file->GetLocation() << " is " << status_ @@ -884,22 +834,46 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() { return status_; } -bool OatFileAssistant::OatFileInfo::IsOutOfDate() { - return Status() == kOatOutOfDate; -} +OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded( + CompilerFilter::Filter target, bool profile_changed) { + bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target); + bool filter_okay = CompilerFilterIsOkay(target, profile_changed); -bool OatFileAssistant::OatFileInfo::NeedsRelocation() { - return Status() == kOatNeedsRelocation; -} + if (filter_okay && Status() == kOatUpToDate) { + // The oat file is in good shape as is. + return kNoDexOptNeeded; + } -bool OatFileAssistant::OatFileInfo::IsUpToDate() { - return Status() == kOatUpToDate; -} + if (filter_okay && !compilation_desired && Status() == kOatRelocationOutOfDate) { + // If no compilation is desired, then it doesn't matter if the oat + // file needs relocation. It's in good shape as is. + return kNoDexOptNeeded; + } -CompilerFilter::Filter OatFileAssistant::OatFileInfo::CompilerFilter() { - const OatFile* file = GetFile(); - CHECK(file != nullptr); - return file->GetCompilerFilter(); + if (filter_okay && Status() == kOatRelocationOutOfDate && HasPatchInfo()) { + return kPatchoatForRelocation; + } + + if (oat_file_assistant_->HasOriginalDexFiles()) { + // Run dex2oat for relocation if we didn't have the patch info necessary + // to use patchoat. + if (filter_okay && Status() == kOatRelocationOutOfDate) { + return kDex2OatForRelocation; + } + + if (IsUseable()) { + return kDex2OatForFilter; + } + + if (Status() == kOatBootImageOutOfDate) { + return kDex2OatForBootImage; + } + + return kDex2OatFromScratch; + } + + // Otherwise there is nothing we can do, even if we want to. + return kNoDexOptNeeded; } const OatFile* OatFileAssistant::OatFileInfo::GetFile() { @@ -967,5 +941,31 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFile() { return std::move(file_); } +std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() { + if (Status() == kOatUpToDate) { + return ReleaseFile(); + } + + VLOG(oat) << "Oat File Assistant: No relocated oat file found," + << " attempting to fall back to interpreting oat file instead."; + + if (Status() == kOatRelocationOutOfDate && !IsExecutable()) { + return ReleaseFile(); + } + + if (Status() == kOatRelocationOutOfDate) { + // We are loading an oat file for runtime use that needs relocation. + // Reload the file non-executable to ensure that we interpret out of the + // dex code in the oat file rather than trying to execute the unrelocated + // compiled code. + oat_file_assistant_->load_executable_ = false; + Reset(); + if (IsUseable()) { + CHECK(!IsExecutable()); + return ReleaseFile(); + } + } + return std::unique_ptr<OatFile>(); +} } // namespace art diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 3f018dceff..bed1edc6c8 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -48,41 +48,56 @@ class ImageSpace; class OatFileAssistant { public: enum DexOptNeeded { - // kNoDexOptNeeded - The code for this dex location is up to date and can - // be used as is. + // No dexopt should (or can) be done to update the apk/jar. // Matches Java: dalvik.system.DexFile.NO_DEXOPT_NEEDED = 0 kNoDexOptNeeded = 0, - // kDex2OatNeeded - In order to make the code for this dex location up to - // date, dex2oat must be run on the dex file. - // Matches Java: dalvik.system.DexFile.DEX2OAT_NEEDED = 1 - kDex2OatNeeded = 1, - - // kPatchOatNeeded - In order to make the code for this dex location up to - // date, patchoat must be run on the odex file. - // Matches Java: dalvik.system.DexFile.PATCHOAT_NEEDED = 2 - kPatchOatNeeded = 2, - - // kSelfPatchOatNeeded - In order to make the code for this dex location - // up to date, patchoat must be run on the oat file. - // Matches Java: dalvik.system.DexFile.SELF_PATCHOAT_NEEDED = 3 - kSelfPatchOatNeeded = 3, + // dex2oat should be run to update the apk/jar from scratch. + // Matches Java: dalvik.system.DexFile.DEX2OAT_FROM_SCRATCH = 1 + kDex2OatFromScratch = 1, + + // dex2oat should be run to update the apk/jar because the existing code + // is out of date with respect to the boot image. + // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_BOOT_IMAGE + kDex2OatForBootImage = 2, + + // dex2oat should be run to update the apk/jar because the existing code + // is out of date with respect to the target compiler filter. + // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_FILTER + kDex2OatForFilter = 3, + + // dex2oat should be run to update the apk/jar because the existing code + // is not relocated to match the boot image and does not have the + // necessary patch information to use patchoat. + // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_RELOCATION + kDex2OatForRelocation = 4, + + // patchoat should be run to update the apk/jar. + // Matches Java: dalvik.system.DexFile.PATCHOAT_FOR_RELOCATION + kPatchoatForRelocation = 5, }; enum OatStatus { - // kOatOutOfDate - An oat file is said to be out of date if the file does - // not exist, is out of date with respect to the dex file or boot image, - // or does not meet the target compilation type. - kOatOutOfDate, - - // kOatNeedsRelocation - An oat file is said to need relocation if the - // code is up to date, but not yet properly relocated for address space - // layout randomization (ASLR). In this case, the oat file is neither - // "out of date" nor "up to date". - kOatNeedsRelocation, - - // kOatUpToDate - An oat file is said to be up to date if it is not out of - // date and has been properly relocated for the purposes of ASLR. + // kOatCannotOpen - The oat file cannot be opened, because it does not + // exist, is unreadable, or otherwise corrupted. + kOatCannotOpen, + + // kOatDexOutOfDate - The oat file is out of date with respect to the dex file. + kOatDexOutOfDate, + + // kOatBootImageOutOfDate - The oat file is up to date with respect to the + // dex file, but is out of date with respect to the boot image. + kOatBootImageOutOfDate, + + // kOatRelocationOutOfDate - The oat file is up to date with respect to + // the dex file and boot image, but contains compiled code that has the + // wrong patch delta with respect to the boot image. Patchoat should be + // run on the oat file to update the patch delta of the compiled code to + // match the boot image. + kOatRelocationOutOfDate, + + // kOatUpToDate - The oat file is completely up to date with respect to + // the dex file and boot image. kOatUpToDate, }; @@ -142,8 +157,10 @@ class OatFileAssistant { // dex location that is at least as good as an oat file generated with the // given compiler filter. profile_changed should be true to indicate the // profile has recently changed for this dex location. - DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, - bool profile_changed = false); + // Returns a positive status code if the status refers to the oat file in + // the oat location. Returns a negative status code if the status refers to + // the oat file in the odex location. + int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, bool profile_changed = false); // Returns true if there is up-to-date code for this dex location, // irrespective of the compiler filter of the up-to-date code. @@ -179,6 +196,10 @@ class OatFileAssistant { // the OatFileAssistant object. std::unique_ptr<OatFile> GetBestOatFile(); + // Returns a human readable description of the status of the code for the + // dex file. The returned description is for debugging purposes only. + std::string GetStatusDump(); + // Open and returns an image space associated with the oat file. static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file); @@ -205,43 +226,16 @@ class OatFileAssistant { // really an oat file. The odex file will often, but not always, have a // patch delta of 0 and need to be relocated before use for the purposes of // ASLR. The odex file is treated as if it were read-only. - // These methods return the location and status of the odex file for the dex - // location. - // Notes: - // * OdexFileName may return null if the odex file name could not be - // determined. - const std::string* OdexFileName(); - bool OdexFileExists(); + // + // Returns the status of the odex file for the dex location. OatStatus OdexFileStatus(); - bool OdexFileIsOutOfDate(); - bool OdexFileNeedsRelocation(); - bool OdexFileIsUpToDate(); - // Must only be called if the associated odex file exists, i.e, if - // |OdexFileExists() == true|. - CompilerFilter::Filter OdexFileCompilerFilter(); // When the dex files is compiled on the target device, the oat file is the // result. The oat file will have been relocated to some // (possibly-out-of-date) offset for ASLR. - // These methods return the location and status of the target oat file for - // the dex location. // - // Notes: - // * OatFileName may return null if the oat file name could not be - // determined. - const std::string* OatFileName(); - bool OatFileExists(); + // Returns the status of the oat file for the dex location. OatStatus OatFileStatus(); - bool OatFileIsOutOfDate(); - bool OatFileNeedsRelocation(); - bool OatFileIsUpToDate(); - // Must only be called if the associated oat file exists, i.e, if - // |OatFileExists() == true|. - CompilerFilter::Filter OatFileCompilerFilter(); - - // Return the status for a given opened oat file with respect to the dex - // location. - OatStatus GivenOatFileStatus(const OatFile& file); // Generates the oat file by relocation from the named input file. // This does not check the current status before attempting to relocate the @@ -311,29 +305,39 @@ class OatFileAssistant { // Initially the info is for no file in particular. It will treat the // file as out of date until Reset is called with a real filename to use // the cache for. - explicit OatFileInfo(OatFileAssistant* oat_file_assistant); + // Pass true for is_oat_location if the information associated with this + // OatFileInfo is for the oat location, as opposed to the odex location. + OatFileInfo(OatFileAssistant* oat_file_assistant, bool is_oat_location); + + bool IsOatLocation(); const std::string* Filename(); - bool Exists(); + + // Returns true if this oat file can be used for running code. The oat + // file can be used for running code as long as it is not out of date with + // respect to the dex code or boot image. An oat file that is out of date + // with respect to relocation is considered useable, because it's possible + // to interpret the dex code rather than run the unrelocated compiled + // code. + bool IsUseable(); + + // Returns the status of this oat file. OatStatus Status(); - bool IsOutOfDate(); - bool NeedsRelocation(); - bool IsUpToDate(); - // Must only be called if the associated file exists, i.e, if - // |Exists() == true|. - CompilerFilter::Filter CompilerFilter(); + + // Return the DexOptNeeded value for this oat file with respect to the + // given target_compilation_filter. + // profile_changed should be true to indicate the profile has recently + // changed for this dex location. + // If patchoat is needed, this function will return the kPatchOatNeeded + // status, not the kSelfPatchOatNeeded status. + DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, + bool profile_changed); // Returns the loaded file. // Loads the file if needed. Returns null if the file failed to load. // The caller shouldn't clean up or free the returned pointer. const OatFile* GetFile(); - // Returns true if the compiler filter used to generate the file is at - // least as good as the given target filter. profile_changed should be - // true to indicate the profile has recently changed for this dex - // location. - bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed); - // Returns true if the file is opened executable. bool IsExecutable(); @@ -348,6 +352,23 @@ class OatFileAssistant { // file with the given filename. void Reset(const std::string& filename); + // Release the loaded oat file for runtime use. + // Returns null if the oat file hasn't been loaded or is out of date. + // Ensures the returned file is not loaded executable if it has unuseable + // compiled code. + // + // After this call, no other methods of the OatFileInfo should be + // called, because access to the loaded oat file has been taken away from + // the OatFileInfo object. + std::unique_ptr<OatFile> ReleaseFileForUse(); + + private: + // Returns true if the compiler filter used to generate the file is at + // least as good as the given target filter. profile_changed should be + // true to indicate the profile has recently changed for this dex + // location. + bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed); + // Release the loaded oat file. // Returns null if the oat file hasn't been loaded. // @@ -356,8 +377,8 @@ class OatFileAssistant { // the OatFileInfo object. std::unique_ptr<OatFile> ReleaseFile(); - private: OatFileAssistant* oat_file_assistant_; + const bool is_oat_location_; bool filename_provided_ = false; std::string filename_; @@ -374,6 +395,13 @@ class OatFileAssistant { bool file_released_ = false; }; + // Return info for the best oat file. + OatFileInfo& GetBestInfo(); + + // Return the status for a given opened oat file with respect to the dex + // location. + OatStatus GivenOatFileStatus(const OatFile& file); + // Returns the current image location. // Returns an empty string if the image location could not be retrieved. // diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index d4337b971b..5730cf2492 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -49,9 +49,9 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { // Pre-Relocate the image to a known non-zero offset so we don't have to // deal with the runtime randomly relocating the image by 0 and messing up // the expected results of the tests. - bool PreRelocateImage(std::string* error_msg) { + bool PreRelocateImage(const std::string& image_location, std::string* error_msg) { std::string image; - if (!GetCachedImageFile(&image, error_msg)) { + if (!GetCachedImageFile(image_location, &image, error_msg)) { return false; } @@ -60,7 +60,7 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { std::vector<std::string> argv; argv.push_back(patchoat); - argv.push_back("--input-image-location=" + GetImageLocation()); + argv.push_back("--input-image-location=" + image_location); argv.push_back("--output-image-file=" + image); argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); argv.push_back("--base-offset-delta=0x00008000"); @@ -69,8 +69,8 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { virtual void PreRuntimeCreate() { std::string error_msg; - ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg; - + ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg; + ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg; UnreserveImageSpace(); } @@ -78,24 +78,32 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { ReserveImageSpace(); } - // Generate a non-PIC odex file for the purposes of test. - // The generated odex file will be un-relocated. - void GenerateOdexForTest(const std::string& dex_location, - const std::string& odex_location, - CompilerFilter::Filter filter, - bool pic = false, - bool with_patch_info = true) { - // Temporarily redirect the dalvik cache so dex2oat doesn't find the - // relocated image file. + // Generate an oat file for the purposes of test. + void GenerateOatForTest(const std::string& dex_location, + const std::string& oat_location, + CompilerFilter::Filter filter, + bool relocate, + bool pic, + bool with_patch_info, + bool with_alternate_image) { std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA)); std::string dalvik_cache_tmp = dalvik_cache + ".redirected"; - ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno); + + if (!relocate) { + // Temporarily redirect the dalvik cache so dex2oat doesn't find the + // relocated image file. + ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno); + } std::vector<std::string> args; args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); + args.push_back("--oat-file=" + oat_location); args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); args.push_back("--runtime-arg"); + + // Use -Xnorelocate regardless of the relocate argument. + // We control relocation by redirecting the dalvik cache when needed + // rather than use this flag. args.push_back("-Xnorelocate"); if (pic) { @@ -106,14 +114,22 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { args.push_back("--include-patch-information"); } + std::string image_location = GetImageLocation(); + if (with_alternate_image) { + args.push_back("--boot-image=" + GetImageLocation2()); + } + std::string error_msg; ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno); - // Verify the odex file was generated as expected and really is - // unrelocated. - std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), - odex_location.c_str(), + if (!relocate) { + // Restore the dalvik cache if needed. + ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno); + } + + // Verify the odex file was generated as expected. + std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(), + oat_location.c_str(), nullptr, nullptr, false, @@ -125,24 +141,59 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo()); EXPECT_EQ(filter, odex_file->GetCompilerFilter()); + std::unique_ptr<ImageHeader> image_header( + gc::space::ImageSpace::ReadImageHeader(image_location.c_str(), + kRuntimeISA, + &error_msg)); + ASSERT_TRUE(image_header != nullptr) << error_msg; + const OatHeader& oat_header = odex_file->GetOatHeader(); + uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum(); + + if (CompilerFilter::DependsOnImageChecksum(filter)) { + if (with_alternate_image) { + EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum()); + } else { + EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum()); + } + } + if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) { - const std::vector<gc::space::ImageSpace*> image_spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr); - const ImageHeader& image_header = image_spaces[0]->GetImageHeader(); - const OatHeader& oat_header = odex_file->GetOatHeader(); - uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum(); - EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum()); - EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()), - oat_header.GetImageFileLocationOatDataBegin()); - EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta()); + if (relocate) { + EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()), + oat_header.GetImageFileLocationOatDataBegin()); + EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta()); + } else { + EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()), + oat_header.GetImageFileLocationOatDataBegin()); + EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta()); + } } } + // Generate a non-PIC odex file for the purposes of test. + // The generated odex file will be un-relocated. + void GenerateOdexForTest(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter) { + GenerateOatForTest(dex_location, + odex_location, + filter, + /*relocate*/false, + /*pic*/false, + /*with_patch_info*/true, + /*with_alternate_image*/false); + } + void GeneratePicOdexForTest(const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter) { - GenerateOdexForTest(dex_location, odex_location, filter, true, false); + GenerateOatForTest(dex_location, + odex_location, + filter, + /*relocate*/false, + /*pic*/true, + /*with_patch_info*/false, + /*with_alternate_image*/false); } // Generate a non-PIC odex file without patch information for the purposes @@ -150,7 +201,43 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { void GenerateNoPatchOdexForTest(const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter) { - GenerateOdexForTest(dex_location, odex_location, filter, false, false); + GenerateOatForTest(dex_location, + odex_location, + filter, + /*relocate*/false, + /*pic*/false, + /*with_patch_info*/false, + /*with_alternate_image*/false); + } + + // Generate an oat file in the oat location. + void GenerateOatForTest(const char* dex_location, + CompilerFilter::Filter filter, + bool relocate, + bool pic, + bool with_patch_info, + bool with_alternate_image) { + std::string oat_location; + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( + dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; + GenerateOatForTest(dex_location, + oat_location, + filter, + relocate, + pic, + with_patch_info, + with_alternate_image); + } + + // Generate a standard oat file in the oat location. + void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) { + GenerateOatForTest(dex_location, + filter, + /*relocate*/true, + /*pic*/false, + /*with_patch_info*/false, + /*with_alternate_image*/false); } private: @@ -211,36 +298,6 @@ class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest { } }; -// Generate an oat file for the purposes of test, as opposed to testing -// generation of oat files. -static void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) { - // Use an oat file assistant to find the proper oat location. - std::string oat_location; - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( - dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; - - std::vector<std::string> args; - args.push_back("--dex-file=" + std::string(dex_location)); - args.push_back("--oat-file=" + oat_location); - args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); - args.push_back("--runtime-arg"); - args.push_back("-Xnorelocate"); - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - - // Verify the oat file was generated as expected. - std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location.c_str(), - oat_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location, - &error_msg)); - ASSERT_TRUE(oat_file.get() != nullptr) << error_msg; - EXPECT_EQ(filter, oat_file->GetCompilerFilter()); -} - // Case: We have a DEX file, but no OAT file for it. // Expect: The status is kDex2OatNeeded. TEST_F(OatFileAssistantTest, DexNoOat) { @@ -249,26 +306,18 @@ TEST_F(OatFileAssistantTest, DexNoOat) { OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OdexFileStatus()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); - EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -307,17 +356,11 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileExists()); - EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation()); - EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -337,17 +380,12 @@ TEST_F(OatFileAssistantTest, OatForDifferentDex) { OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); } // Case: We have a DEX file and speed-profile OAT file for it. @@ -364,19 +402,13 @@ TEST_F(OatFileAssistantTest, ProfileOatUpToDate) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, false)); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly, false)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, true)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly, true)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileExists()); - EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation()); - EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -416,7 +448,7 @@ TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) { Copy(GetMultiDexSrc2(), dex_location); OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed, false)); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -453,10 +485,10 @@ TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) { EXPECT_EQ(2u, dex_files.size()); } -// Case: We have a DEX file and out-of-date OAT file. -// Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, OatOutOfDate) { - std::string dex_location = GetScratchDir() + "/OatOutOfDate.jar"; +// Case: We have a DEX file and an OAT file out of date with respect to the +// dex checksum. +TEST_F(OatFileAssistantTest, OatDexOutOfDate) { + std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar"; // We create a dex, generate an oat for it, then overwrite the dex with a // different dex to make the oat out of date. @@ -465,18 +497,68 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) { Copy(GetDexSrc2(), dex_location); OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + + EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus()); + EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); +} + +// Case: We have a DEX file and an OAT file out of date with respect to the +// boot image. +TEST_F(OatFileAssistantTest, OatImageOutOfDate) { + std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar"; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), + CompilerFilter::kSpeed, + /*relocate*/true, + /*pic*/false, + /*with_patch_info*/false, + /*with_alternate_image*/true); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); + EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OatFileStatus()); + EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); +} + +// Case: We have a DEX file and a verify-at-runtime OAT file out of date with +// respect to the boot image. +// It shouldn't matter that the OAT file is out of date, because it is +// verify-at-runtime. +TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) { + std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar"; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), + CompilerFilter::kVerifyAtRuntime, + /*relocate*/true, + /*pic*/false, + /*with_patch_info*/false, + /*with_alternate_image*/true); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); + EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); + + EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -495,17 +577,12 @@ TEST_F(OatFileAssistantTest, DexOdexNoOat) { EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); - EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, + EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); // We should still be able to get the non-executable odex file to run from. @@ -529,16 +606,12 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) { // Verify the status. OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); - EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, + EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); // Make the oat file up to date. @@ -551,12 +624,8 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileExists()); - EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); // Verify we can load the dex files from it. @@ -590,19 +659,14 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexOat) { EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); - EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, + EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, // Can't run dex2oat because dex file is stripped. oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); // Make the oat file up to date. @@ -617,14 +681,8 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexOat) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileExists()); - EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation()); - EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); // Verify we can load the dex files from it. @@ -654,13 +712,8 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); // Make the oat file up to date. This should have no effect. @@ -673,13 +726,8 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); } @@ -699,20 +747,14 @@ TEST_F(OatFileAssistantTest, SelfRelocation) { EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); - EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, + EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); // Make the oat file up to date. @@ -725,14 +767,8 @@ TEST_F(OatFileAssistantTest, SelfRelocation) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileExists()); - EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation()); - EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); @@ -757,7 +793,7 @@ TEST_F(OatFileAssistantTest, NoSelfRelocation) { OatFileAssistant oat_file_assistant(dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatForRelocation, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); // Make the oat file up to date. @@ -778,7 +814,7 @@ TEST_F(OatFileAssistantTest, NoSelfRelocation) { // Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and // OAT files both have patch delta of 0. -// Expect: It shouldn't crash, and status is kPatchOatNeeded. +// Expect: It shouldn't crash, and status is kSelfPatchOatNeeded. TEST_F(OatFileAssistantTest, OdexOatOverlap) { std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar"; std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex"; @@ -796,16 +832,15 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { OatFileAssistant oat_file_assistant(dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); - EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, + // kPatchoatForRelocation is expected rather than -kPatchoatForRelocation + // based on the assumption that the oat location is more up-to-date than the odex + // location, even if they both need relocation. + EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileExists()); - EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); // Things aren't relocated, so it should fall back to interpreted. @@ -833,16 +868,12 @@ TEST_F(OatFileAssistantTest, DexPicOdexNoOat) { EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -861,16 +892,12 @@ TEST_F(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) { EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -958,7 +985,7 @@ TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) { // Verify it didn't create an oat in the default location. OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false); - EXPECT_FALSE(ofm.OatFileExists()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OatFileStatus()); } // Case: We have a DEX file but can't write the oat file. @@ -1043,14 +1070,10 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) { OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); } // Case: Very short, non-existent Dex location. @@ -1063,12 +1086,8 @@ TEST_F(OatFileAssistantTest, ShortDexLocation) { EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); // Trying to make it up to date should have no effect. @@ -1087,16 +1106,12 @@ TEST_F(OatFileAssistantTest, LongDexExtension) { OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_FALSE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); } // A task to generate a dex location. Used by the RaceToGenerate test. @@ -1226,7 +1241,7 @@ TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) { oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg; EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly)); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, + EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); @@ -1263,6 +1278,15 @@ TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) { // Verify the dexopt status values from dalvik.system.DexFile // match the OatFileAssistant::DexOptStatus values. TEST_F(OatFileAssistantTest, DexOptStatusValues) { + std::pair<OatFileAssistant::DexOptNeeded, const char*> mapping[] = { + {OatFileAssistant::kNoDexOptNeeded, "NO_DEXOPT_NEEDED"}, + {OatFileAssistant::kDex2OatFromScratch, "DEX2OAT_FROM_SCRATCH"}, + {OatFileAssistant::kDex2OatForBootImage, "DEX2OAT_FOR_BOOT_IMAGE"}, + {OatFileAssistant::kDex2OatForFilter, "DEX2OAT_FOR_FILTER"}, + {OatFileAssistant::kDex2OatForRelocation, "DEX2OAT_FOR_RELOCATION"}, + {OatFileAssistant::kPatchoatForRelocation, "PATCHOAT_FOR_RELOCATION"} + }; + ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); ClassLinker* linker = Runtime::Current()->GetClassLinker(); @@ -1271,35 +1295,16 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { ASSERT_FALSE(dexfile.Get() == nullptr); linker->EnsureInitialized(soa.Self(), dexfile, true, true); - ArtField* no_dexopt_needed = mirror::Class::FindStaticField( - soa.Self(), dexfile, "NO_DEXOPT_NEEDED", "I"); - ASSERT_FALSE(no_dexopt_needed == nullptr); - EXPECT_EQ(no_dexopt_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, no_dexopt_needed->GetInt(dexfile.Get())); - - ArtField* dex2oat_needed = mirror::Class::FindStaticField( - soa.Self(), dexfile, "DEX2OAT_NEEDED", "I"); - ASSERT_FALSE(dex2oat_needed == nullptr); - EXPECT_EQ(dex2oat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, dex2oat_needed->GetInt(dexfile.Get())); - - ArtField* patchoat_needed = mirror::Class::FindStaticField( - soa.Self(), dexfile, "PATCHOAT_NEEDED", "I"); - ASSERT_FALSE(patchoat_needed == nullptr); - EXPECT_EQ(patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt); - EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, patchoat_needed->GetInt(dexfile.Get())); - - ArtField* self_patchoat_needed = mirror::Class::FindStaticField( - soa.Self(), dexfile, "SELF_PATCHOAT_NEEDED", "I"); - ASSERT_FALSE(self_patchoat_needed == nullptr); - EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt); - EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get())); + for (std::pair<OatFileAssistant::DexOptNeeded, const char*> field : mapping) { + ArtField* art_field = mirror::Class::FindStaticField( + soa.Self(), dexfile, field.second, "I"); + ASSERT_FALSE(art_field == nullptr); + EXPECT_EQ(art_field->GetTypeAsPrimitiveType(), Primitive::kPrimInt); + EXPECT_EQ(field.first, art_field->GetInt(dexfile.Get())); + } } // TODO: More Tests: -// * Image checksum change is out of date for kIntepretOnly, but not -// kVerifyAtRuntime. But target of kVerifyAtRuntime still says current -// kInterpretOnly is out of date. // * Test class linker falls back to unquickened dex for DexNoOat // * Test class linker falls back to unquickened dex for MultiDexNoOat // * Test using secondary isa @@ -1313,5 +1318,4 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { // because it's unrelocated and no dex2oat // * Test unrelocated specific target compilation type can be relocated to // make it up to date. - } // namespace art diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index ee5002f84a..4afca7d828 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -67,6 +67,11 @@ class PACKED(4) OatQuickMethodHeader { return data; } + uint8_t* GetOptimizedCodeInfoPtr() { + DCHECK(IsOptimized()); + return code_ - vmap_table_offset_; + } + CodeInfo GetOptimizedCodeInfo() const { return CodeInfo(GetOptimizedCodeInfoPtr()); } diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc index 0eff469884..5e588a8a36 100644 --- a/runtime/openjdkjvmti/ti_heap.cc +++ b/runtime/openjdkjvmti/ti_heap.cc @@ -269,6 +269,12 @@ class FollowReferencesHelper FINAL { ReportRoot(root_obj, info); } + // Remove NO_THREAD_SAFETY_ANALYSIS once ASSERT_CAPABILITY works correctly. + art::Thread* FindThread(const art::RootInfo& info) NO_THREAD_SAFETY_ANALYSIS { + art::Locks::thread_list_lock_->AssertExclusiveHeld(art::Thread::Current()); + return art::Runtime::Current()->GetThreadList()->FindThreadByThreadId(info.GetThreadId()); + } + jvmtiHeapReferenceKind GetReferenceKind(const art::RootInfo& info, jvmtiHeapReferenceInfo* ref_info) REQUIRES_SHARED(art::Locks::mutator_lock_) { @@ -280,7 +286,34 @@ class FollowReferencesHelper FINAL { return JVMTI_HEAP_REFERENCE_JNI_GLOBAL; case art::RootType::kRootJNILocal: + { + uint32_t thread_id = info.GetThreadId(); + ref_info->jni_local.thread_id = thread_id; + + art::Thread* thread = FindThread(info); + if (thread != nullptr) { + art::mirror::Object* thread_obj = thread->GetPeer(); + if (thread->IsStillStarting()) { + thread_obj = nullptr; + } else { + thread_obj = thread->GetPeer(); + } + if (thread_obj != nullptr) { + ref_info->jni_local.thread_tag = tag_table_->GetTagOrZero(thread_obj); + } + } + + // TODO: We don't have this info. + if (thread != nullptr) { + ref_info->jni_local.depth = 0; + art::ArtMethod* method = thread->GetCurrentMethod(nullptr, false /* abort_on_error */); + if (method != nullptr) { + ref_info->jni_local.method = art::jni::EncodeArtMethod(method); + } + } + return JVMTI_HEAP_REFERENCE_JNI_LOCAL; + } case art::RootType::kRootJavaFrame: return JVMTI_HEAP_REFERENCE_STACK_LOCAL; diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc index e391a9d680..ffa5ac7e32 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -78,7 +78,9 @@ jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env, } // TODO: Support generic signature. - *generic_ptr = nullptr; + if (generic_ptr != nullptr) { + *generic_ptr = nullptr; + } // Everything is fine, release the buffers. name_copy.release(); diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc index fa2983c19c..7bb5205f05 100644 --- a/runtime/openjdkjvmti/transform.cc +++ b/runtime/openjdkjvmti/transform.cc @@ -33,6 +33,7 @@ #include "class_linker.h" #include "dex_file.h" +#include "dex_file_types.h" #include "gc_root-inl.h" #include "globals.h" #include "jni_env_ext-inl.h" @@ -108,10 +109,10 @@ static void InvalidateExistingMethods(art::Thread* self, // Find the code_item for the method then find the dex_method_index and dex_code_item_offset to // set. const art::DexFile::StringId* new_name_id = dex_file->FindStringId(method.GetName()); - uint16_t method_return_idx = + art::dex::TypeIndex method_return_idx = dex_file->GetIndexForTypeId(*dex_file->FindTypeId(method.GetReturnTypeDescriptor())); const auto* old_type_list = method.GetParameterTypeList(); - std::vector<uint16_t> new_type_list; + std::vector<art::dex::TypeIndex> new_type_list; for (uint32_t i = 0; old_type_list != nullptr && i < old_type_list->Size(); i++) { new_type_list.push_back( dex_file->GetIndexForTypeId( diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 3128380f76..8446b525ad 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -363,7 +363,7 @@ static void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args) Thread* const self = Thread::Current(); PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (uint32_t i = 0; i < num_params; i++) { - uint16_t type_idx = params->GetTypeItem(i).type_idx_; + dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_; ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve*/, pointer_size)); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 6177ef91d7..ee4d669fe0 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -81,6 +81,7 @@ #include "intern_table.h" #include "interpreter/interpreter.h" #include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "jni_internal.h" #include "linear_alloc.h" #include "mirror/array.h" @@ -372,6 +373,7 @@ struct AbortState { void Dump(std::ostream& os) const { if (gAborting > 1) { os << "Runtime aborting --- recursively, so no thread-specific detail!\n"; + DumpRecursiveAbort(os); return; } gAborting++; @@ -428,6 +430,21 @@ struct AbortState { } } } + + // For recursive aborts. + void DumpRecursiveAbort(std::ostream& os) const NO_THREAD_SAFETY_ANALYSIS { + // The only thing we'll attempt is dumping the native stack of the current thread. We will only + // try this if we haven't exceeded an arbitrary amount of recursions, to recover and actually + // die. + // Note: as we're using a global counter for the recursive abort detection, there is a potential + // race here and it is not OK to just print when the counter is "2" (one from + // Runtime::Abort(), one from previous Dump() call). Use a number that seems large enough. + static constexpr size_t kOnlyPrintWhenRecursionLessThan = 100u; + if (gAborting < kOnlyPrintWhenRecursionLessThan) { + gAborting++; + DumpNativeStack(os, GetTid()); + } + } }; void Runtime::Abort(const char* msg) { @@ -442,8 +459,16 @@ void Runtime::Abort(const char* msg) { // Many people have difficulty distinguish aborts from crashes, // so be explicit. + // Note: use cerr on the host to print log lines immediately, so we get at least some output + // in case of recursive aborts. We lose annotation with the source file and line number + // here, which is a minor issue. The same is significantly more complicated on device, + // which is why we ignore the issue there. AbortState state; - LOG(FATAL_WITHOUT_ABORT) << Dumpable<AbortState>(state); + if (kIsTargetBuild) { + LOG(FATAL_WITHOUT_ABORT) << Dumpable<AbortState>(state); + } else { + std::cerr << Dumpable<AbortState>(state); + } // Sometimes we dump long messages, and the Android abort message only retains the first line. // In those cases, just log the message again, to avoid logcat limits. @@ -492,6 +517,14 @@ void Runtime::SweepSystemWeaks(IsMarkedVisitor* visitor) { GetMonitorList()->SweepMonitorList(visitor); GetJavaVM()->SweepJniWeakGlobals(visitor); GetHeap()->SweepAllocationRecords(visitor); + if (GetJit() != nullptr) { + // Visit JIT literal tables. Objects in these tables are classes and strings + // and only classes can be affected by class unloading. The strings always + // stay alive as they are strongly interned. + // TODO: Move this closer to CleanupClassLoaders, to avoid blocking weak accesses + // from mutators. See b/32167580. + GetJit()->GetCodeCache()->SweepRootTables(visitor); + } // All other generic system-weak holders. for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) { @@ -1029,8 +1062,10 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity), runtime_options.GetOrDefault(Opt::Image), runtime_options.GetOrDefault(Opt::ImageInstructionSet), - xgc_option.collector_type_, - runtime_options.GetOrDefault(Opt::BackgroundGc), + // Override the collector type to CC if the read barrier config. + kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_, + kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground) + : runtime_options.GetOrDefault(Opt::BackgroundGc), runtime_options.GetOrDefault(Opt::LargeObjectSpace), runtime_options.GetOrDefault(Opt::LargeObjectThreshold), runtime_options.GetOrDefault(Opt::ParallelGCThreads), @@ -2148,7 +2183,7 @@ void Runtime::RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder) { NO_RETURN void Runtime::Aborter(const char* abort_message) { -#ifdef __ANDROID__ +#ifdef ART_TARGET_ANDROID android_set_abort_message(abort_message); #endif Runtime::Abort(abort_message); diff --git a/runtime/thread.cc b/runtime/thread.cc index debd13ab76..c92e38b6e8 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1906,7 +1906,7 @@ Thread::~Thread() { } delete tlsPtr_.instrumentation_stack; delete tlsPtr_.name; - delete tlsPtr_.stack_trace_sample; + delete tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample; free(tlsPtr_.nested_signal_state); Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this); diff --git a/runtime/thread.h b/runtime/thread.h index 207964691f..35226f2230 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -39,6 +39,7 @@ #include "jvalue.h" #include "object_callbacks.h" #include "offsets.h" +#include "runtime.h" #include "runtime_stats.h" #include "stack.h" #include "thread_state.h" @@ -70,7 +71,8 @@ namespace mirror { } // namespace mirror namespace verifier { -class MethodVerifier; + class MethodVerifier; + class VerifierDeps; } // namespace verifier class ArtMethod; @@ -947,11 +949,27 @@ class Thread { } std::vector<ArtMethod*>* GetStackTraceSample() const { - return tlsPtr_.stack_trace_sample; + DCHECK(!Runtime::Current()->IsAotCompiler()); + return tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample; } void SetStackTraceSample(std::vector<ArtMethod*>* sample) { - tlsPtr_.stack_trace_sample = sample; + DCHECK(!Runtime::Current()->IsAotCompiler()); + tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample = sample; + } + + verifier::VerifierDeps* GetVerifierDeps() const { + DCHECK(Runtime::Current()->IsAotCompiler()); + return tlsPtr_.deps_or_stack_trace_sample.verifier_deps; + } + + // It is the responsability of the caller to make sure the verifier_deps + // entry in the thread is cleared before destruction of the actual VerifierDeps + // object, or the thread. + void SetVerifierDeps(verifier::VerifierDeps* verifier_deps) { + DCHECK(Runtime::Current()->IsAotCompiler()); + DCHECK(verifier_deps == nullptr || tlsPtr_.deps_or_stack_trace_sample.verifier_deps == nullptr); + tlsPtr_.deps_or_stack_trace_sample.verifier_deps = verifier_deps; } uint64_t GetTraceClockBase() const { @@ -1378,7 +1396,7 @@ class Thread { tls_ptr_sized_values() : card_table(nullptr), exception(nullptr), stack_end(nullptr), managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), tmp_jni_env(nullptr), self(nullptr), opeer(nullptr), jpeer(nullptr), stack_begin(nullptr), stack_size(0), - stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr), + deps_or_stack_trace_sample(), wait_next(nullptr), monitor_enter_object(nullptr), top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr), instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr), stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr), @@ -1432,8 +1450,18 @@ class Thread { // Size of the stack. size_t stack_size; - // Pointer to previous stack trace captured by sampling profiler. - std::vector<ArtMethod*>* stack_trace_sample; + // Sampling profiler and AOT verification cannot happen on the same run, so we share + // the same entry for the stack trace and the verifier deps. + union DepsOrStackTraceSample { + DepsOrStackTraceSample() { + verifier_deps = nullptr; + stack_trace_sample = nullptr; + } + // Pointer to previous stack trace captured by sampling profiler. + std::vector<ArtMethod*>* stack_trace_sample; + // When doing AOT verification, per-thread VerifierDeps. + verifier::VerifierDeps* verifier_deps; + } deps_or_stack_trace_sample; // The next thread in the wait set this thread is part of or null if not waiting. Thread* wait_next; diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index b14f340c4c..d9d2ea31a2 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -85,6 +85,7 @@ void* ThreadPoolWorker::Callback(void* arg) { ThreadPoolWorker* worker = reinterpret_cast<ThreadPoolWorker*>(arg); Runtime* runtime = Runtime::Current(); CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false)); + worker->thread_ = Thread::Current(); // Do work until its time to shut down. worker->Run(); runtime->DetachCurrentThread(); @@ -177,7 +178,7 @@ Task* ThreadPool::GetTask(Thread* self) { } ++waiting_count_; - if (waiting_count_ == GetThreadCount() && tasks_.empty()) { + if (waiting_count_ == GetThreadCount() && !HasOutstandingTasks()) { // We may be done, lets broadcast to the completion condition. completion_condition_.Broadcast(self); } @@ -200,7 +201,7 @@ Task* ThreadPool::TryGetTask(Thread* self) { } Task* ThreadPool::TryGetTaskLocked() { - if (started_ && !tasks_.empty()) { + if (HasOutstandingTasks()) { Task* task = tasks_.front(); tasks_.pop_front(); return task; @@ -218,7 +219,7 @@ void ThreadPool::Wait(Thread* self, bool do_work, bool may_hold_locks) { } // Wait until each thread is waiting and the task list is empty. MutexLock mu(self, task_queue_lock_); - while (!shutting_down_ && (waiting_count_ != GetThreadCount() || !tasks_.empty())) { + while (!shutting_down_ && (waiting_count_ != GetThreadCount() || HasOutstandingTasks())) { if (!may_hold_locks) { completion_condition_.Wait(self); } else { diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h index b6c6f02db8..eaadfe0215 100644 --- a/runtime/thread_pool.h +++ b/runtime/thread_pool.h @@ -62,6 +62,8 @@ class ThreadPoolWorker { // Set the "nice" priorty for this worker. void SetPthreadPriority(int priority); + Thread* GetThread() const { return thread_; } + protected: ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name, size_t stack_size); static void* Callback(void* arg) REQUIRES(!Locks::mutator_lock_); @@ -71,6 +73,7 @@ class ThreadPoolWorker { const std::string name_; std::unique_ptr<MemMap> stack_; pthread_t pthread_; + Thread* thread_; private: friend class ThreadPool; @@ -84,6 +87,10 @@ class ThreadPool { return threads_.size(); } + const std::vector<ThreadPoolWorker*>& GetWorkers() const { + return threads_; + } + // Broadcast to the workers and tell them to empty out the work queue. void StartWorkers(Thread* self) REQUIRES(!task_queue_lock_); @@ -100,7 +107,8 @@ class ThreadPool { ThreadPool(const char* name, size_t num_threads); virtual ~ThreadPool(); - // Wait for all tasks currently on queue to get completed. + // Wait for all tasks currently on queue to get completed. If the pool has been stopped, only + // wait till all already running tasks are done. void Wait(Thread* self, bool do_work, bool may_hold_locks) REQUIRES(!task_queue_lock_); size_t GetTaskCount(Thread* self) REQUIRES(!task_queue_lock_); @@ -130,6 +138,10 @@ class ThreadPool { return shutting_down_; } + bool HasOutstandingTasks() const REQUIRES(task_queue_lock_) { + return started_ && !tasks_.empty(); + } + const std::string name_; Mutex task_queue_lock_; ConditionVariable task_queue_condition_ GUARDED_BY(task_queue_lock_); diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc index d5f17d17b1..14c2c3bac8 100644 --- a/runtime/thread_pool_test.cc +++ b/runtime/thread_pool_test.cc @@ -98,6 +98,29 @@ TEST_F(ThreadPoolTest, StopStart) { thread_pool.Wait(self, false, false); } +TEST_F(ThreadPoolTest, StopWait) { + Thread* self = Thread::Current(); + ThreadPool thread_pool("Thread pool test thread pool", num_threads); + + AtomicInteger count(0); + static const int32_t num_tasks = num_threads * 100; + for (int32_t i = 0; i < num_tasks; ++i) { + thread_pool.AddTask(self, new CountTask(&count)); + } + + // Signal the threads to start processing tasks. + thread_pool.StartWorkers(self); + usleep(200); + thread_pool.StopWorkers(self); + + thread_pool.Wait(self, false, false); // We should not deadlock here. + + // Drain the task list. Note: we have to restart here, as no tasks will be finished when + // the pool is stopped. + thread_pool.StartWorkers(self); + thread_pool.Wait(self, /* do_work */ true, false); +} + class TreeTask : public Task { public: TreeTask(ThreadPool* const thread_pool, AtomicInteger* count, int depth) diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc index 56e9262573..16cd7227f1 100644 --- a/runtime/type_lookup_table.cc +++ b/runtime/type_lookup_table.cc @@ -50,17 +50,19 @@ bool TypeLookupTable::SupportedSize(uint32_t num_class_defs) { return num_class_defs != 0u && num_class_defs <= std::numeric_limits<uint16_t>::max(); } -TypeLookupTable* TypeLookupTable::Create(const DexFile& dex_file, uint8_t* storage) { +std::unique_ptr<TypeLookupTable> TypeLookupTable::Create(const DexFile& dex_file, + uint8_t* storage) { const uint32_t num_class_defs = dex_file.NumClassDefs(); - return SupportedSize(num_class_defs) + return std::unique_ptr<TypeLookupTable>(SupportedSize(num_class_defs) ? new TypeLookupTable(dex_file, storage) - : nullptr; + : nullptr); } -TypeLookupTable* TypeLookupTable::Open(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs) { - return new TypeLookupTable(dex_file_pointer, raw_data, num_class_defs); +std::unique_ptr<TypeLookupTable> TypeLookupTable::Open(const uint8_t* dex_file_pointer, + const uint8_t* raw_data, + uint32_t num_class_defs) { + return std::unique_ptr<TypeLookupTable>( + new TypeLookupTable(dex_file_pointer, raw_data, num_class_defs)); } TypeLookupTable::TypeLookupTable(const DexFile& dex_file, uint8_t* storage) diff --git a/runtime/type_lookup_table.h b/runtime/type_lookup_table.h index 9595743e15..3f6f76f510 100644 --- a/runtime/type_lookup_table.h +++ b/runtime/type_lookup_table.h @@ -60,13 +60,14 @@ class TypeLookupTable { } // Method creates lookup table for dex file - static TypeLookupTable* Create(const DexFile& dex_file, uint8_t* storage = nullptr); + static std::unique_ptr<TypeLookupTable> Create(const DexFile& dex_file, + uint8_t* storage = nullptr); // Method opens lookup table from binary data. Lookups will traverse strings and other // data contained in dex_file as well. Lookup table does not own raw_data or dex_file. - static TypeLookupTable* Open(const uint8_t* dex_file_pointer, - const uint8_t* raw_data, - uint32_t num_class_defs); + static std::unique_ptr<TypeLookupTable> Open(const uint8_t* dex_file_pointer, + const uint8_t* raw_data, + uint32_t num_class_defs); // Method returns pointer to binary data of lookup table. Used by the oat writer. const uint8_t* RawData() const { diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index c7875b56ec..bd1b044dae 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -65,8 +65,8 @@ static constexpr PointerSize GcRootAsPointerSize() { return PointerSize::k32; } -inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const { - return types_offset_ + ElementOffset(GcRootAsPointerSize<mirror::Class>(), type_idx); +inline size_t DexCacheArraysLayout::TypeOffset(dex::TypeIndex type_idx) const { + return types_offset_ + ElementOffset(GcRootAsPointerSize<mirror::Class>(), type_idx.index_); } inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const { diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h index ae3bfab38c..7d4b23a8dd 100644 --- a/runtime/utils/dex_cache_arrays_layout.h +++ b/runtime/utils/dex_cache_arrays_layout.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_ #include "dex_file.h" +#include "dex_file_types.h" namespace art { @@ -59,7 +60,7 @@ class DexCacheArraysLayout { return types_offset_; } - size_t TypeOffset(uint32_t type_idx) const; + size_t TypeOffset(dex::TypeIndex type_idx) const; size_t TypesSize(size_t num_elements) const; diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index b3dab581d5..843be92c00 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -20,6 +20,7 @@ #include "base/logging.h" #include "base/unix_file/fd_file.h" +#include "dex_file.h" namespace art { @@ -73,10 +74,19 @@ VdexFile* VdexFile::Open(const std::string& vdex_filename, return nullptr; } + return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, error_msg); +} + +VdexFile* VdexFile::Open(int file_fd, + size_t vdex_length, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg) { std::unique_ptr<MemMap> mmap(MemMap::MapFile(vdex_length, writable ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, - vdex_file->Fd(), + file_fd, 0 /* start offset */, low_4gb, vdex_filename.c_str(), @@ -90,4 +100,16 @@ VdexFile* VdexFile::Open(const std::string& vdex_filename, return new VdexFile(mmap.release()); } +const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const { + DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End())); + if (cursor == nullptr) { + // Beginning of the iteration, return the first dex file if there is one. + return HasDexSection() ? DexBegin() : nullptr; + } else { + // Fetch the next dex file. Return null if there is none. + const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_; + return (data == DexEnd()) ? nullptr : data; + } +} + } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index edd6ffe89d..75a0d5e31e 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -71,6 +71,13 @@ class VdexFile { bool low_4gb, std::string* error_msg); + static VdexFile* Open(int file_fd, + size_t vdex_length, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg); + const uint8_t* Begin() const { return mmap_->Begin(); } const uint8_t* End() const { return mmap_->End(); } size_t Size() const { return mmap_->Size(); } @@ -84,9 +91,37 @@ class VdexFile { Begin() + sizeof(Header) + GetHeader().GetDexSize(), GetHeader().GetVerifierDepsSize()); } + ArrayRef<const uint8_t> GetQuickeningInfo() const { + return ArrayRef<const uint8_t>( + GetVerifierDepsData().data() + GetHeader().GetVerifierDepsSize(), + GetHeader().GetQuickeningInfoSize()); + } + + bool IsValid() const { + return mmap_->Size() >= sizeof(Header) && GetHeader().IsValid(); + } + + // This method is for iterating over the dex files in the vdex. If `cursor` is null, + // the first dex file is returned. If `cursor` is not null, it must point to a dex + // file and this method returns the next dex file if there is one, or null if there + // is none. + const uint8_t* GetNextDexFileData(const uint8_t* cursor) const; + private: explicit VdexFile(MemMap* mmap) : mmap_(mmap) {} + bool HasDexSection() const { + return GetHeader().GetDexSize() != 0; + } + + const uint8_t* DexBegin() const { + return Begin() + sizeof(Header); + } + + const uint8_t* DexEnd() const { + return Begin() + sizeof(Header) + GetHeader().GetDexSize(); + } + std::unique_ptr<MemMap> mmap_; DISALLOW_COPY_AND_ASSIGN(VdexFile); diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index def61db81a..363bd8f54f 100644 --- a/runtime/verifier/method_verifier-inl.h +++ b/runtime/verifier/method_verifier-inl.h @@ -74,7 +74,7 @@ inline bool MethodVerifier::HasFailures() const { return !failure_messages_.empty(); } -inline const RegType& MethodVerifier::ResolveCheckedClass(uint32_t class_idx) { +inline const RegType& MethodVerifier::ResolveCheckedClass(dex::TypeIndex class_idx) { DCHECK(!HasFailures()); const RegType& result = ResolveClassAndCheckAccess(class_idx); DCHECK(!HasFailures()); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index d9e3ea7a0d..7137db8773 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -100,8 +100,18 @@ PcToRegisterLineTable::~PcToRegisterLineTable() {} ALWAYS_INLINE static inline bool FailOrAbort(MethodVerifier* verifier, bool condition, const char* error_msg, uint32_t work_insn_idx) { if (kIsDebugBuild) { - // In a debug build, abort if the error condition is wrong. - DCHECK(condition) << error_msg << work_insn_idx; + // In a debug build, abort if the error condition is wrong. Only warn if + // we are already aborting (as this verification is likely run to print + // lock information). + if (LIKELY(gAborting == 0)) { + DCHECK(condition) << error_msg << work_insn_idx; + } else { + if (!condition) { + LOG(ERROR) << error_msg << work_insn_idx; + verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx; + return true; + } + } } else { // In a non-debug build, just fail the class. if (!condition) { @@ -1062,7 +1072,7 @@ bool MethodVerifier::ScanTryCatchBlocks() { GetInstructionFlags(dex_pc).SetBranchTarget(); // Ensure exception types are resolved so that they don't need resolution to be delivered, // unresolved exception types will be ignored by exception delivery - if (iterator.GetHandlerTypeIndex() != DexFile::kDexNoIndex16) { + if (iterator.GetHandlerTypeIndex().IsValid()) { mirror::Class* exception_type = linker->ResolveType(*dex_file_, iterator.GetHandlerTypeIndex(), dex_cache_, class_loader_); @@ -1145,13 +1155,13 @@ bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_of result = result && CheckMethodIndex(inst->VRegB()); break; case Instruction::kVerifyRegBNewInstance: - result = result && CheckNewInstance(inst->VRegB()); + result = result && CheckNewInstance(dex::TypeIndex(inst->VRegB())); break; case Instruction::kVerifyRegBString: result = result && CheckStringIndex(inst->VRegB()); break; case Instruction::kVerifyRegBType: - result = result && CheckTypeIndex(inst->VRegB()); + result = result && CheckTypeIndex(dex::TypeIndex(inst->VRegB())); break; case Instruction::kVerifyRegBWide: result = result && CheckWideRegisterIndex(inst->VRegB()); @@ -1165,10 +1175,10 @@ bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_of result = result && CheckFieldIndex(inst->VRegC()); break; case Instruction::kVerifyRegCNewArray: - result = result && CheckNewArray(inst->VRegC()); + result = result && CheckNewArray(dex::TypeIndex(inst->VRegC())); break; case Instruction::kVerifyRegCType: - result = result && CheckTypeIndex(inst->VRegC()); + result = result && CheckTypeIndex(dex::TypeIndex(inst->VRegC())); break; case Instruction::kVerifyRegCWide: result = result && CheckWideRegisterIndex(inst->VRegC()); @@ -1260,9 +1270,9 @@ inline bool MethodVerifier::CheckMethodIndex(uint32_t idx) { return true; } -inline bool MethodVerifier::CheckNewInstance(uint32_t idx) { - if (idx >= dex_file_->GetHeader().type_ids_size_) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max " +inline bool MethodVerifier::CheckNewInstance(dex::TypeIndex idx) { + if (idx.index_ >= dex_file_->GetHeader().type_ids_size_) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max " << dex_file_->GetHeader().type_ids_size_ << ")"; return false; } @@ -1288,18 +1298,18 @@ inline bool MethodVerifier::CheckStringIndex(uint32_t idx) { return true; } -inline bool MethodVerifier::CheckTypeIndex(uint32_t idx) { - if (idx >= dex_file_->GetHeader().type_ids_size_) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max " +inline bool MethodVerifier::CheckTypeIndex(dex::TypeIndex idx) { + if (idx.index_ >= dex_file_->GetHeader().type_ids_size_) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max " << dex_file_->GetHeader().type_ids_size_ << ")"; return false; } return true; } -bool MethodVerifier::CheckNewArray(uint32_t idx) { - if (idx >= dex_file_->GetHeader().type_ids_size_) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max " +bool MethodVerifier::CheckNewArray(dex::TypeIndex idx) { + if (idx.index_ >= dex_file_->GetHeader().type_ids_size_) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max " << dex_file_->GetHeader().type_ids_size_ << ")"; return false; } @@ -1935,7 +1945,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { // Returns the index of the first final instance field of the given class, or kDexNoIndex if there // is no such field. -static uint32_t GetFirstFinalInstanceFieldIndex(const DexFile& dex_file, uint16_t type_idx) { +static uint32_t GetFirstFinalInstanceFieldIndex(const DexFile& dex_file, dex::TypeIndex type_idx) { const DexFile::ClassDef* class_def = dex_file.FindClassDef(type_idx); DCHECK(class_def != nullptr); const uint8_t* class_data = dex_file.GetClassData(*class_def); @@ -2283,7 +2293,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::CONST_CLASS: { // Get type from instruction if unresolved then we need an access check // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved - const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c()); + const RegType& res_type = ResolveClassAndCheckAccess(dex::TypeIndex(inst->VRegB_21c())); // Register holds class, ie its type is class, on error it will hold Conflict. work_line_->SetRegisterType<LockOp::kClear>( this, inst->VRegA_21c(), res_type.IsConflict() ? res_type @@ -2353,7 +2363,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * dec_insn.vA when branching to a handler. */ const bool is_checkcast = (inst->Opcode() == Instruction::CHECK_CAST); - const uint32_t type_idx = (is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c(); + const dex::TypeIndex type_idx((is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c()); const RegType& res_type = ResolveClassAndCheckAccess(type_idx); if (res_type.IsConflict()) { // If this is a primitive type, fail HARD. @@ -2423,7 +2433,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } case Instruction::NEW_INSTANCE: { - const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c()); + const RegType& res_type = ResolveClassAndCheckAccess(dex::TypeIndex(inst->VRegB_21c())); if (res_type.IsConflict()) { DCHECK_NE(failures_.size(), 0U); break; // bad class @@ -2635,7 +2645,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // ensure that subsequent merges don't lose type information - such as becoming an // interface from a class that would lose information relevant to field checks. const RegType& orig_type = work_line_->GetRegisterType(this, instance_of_inst->VRegB_22c()); - const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c()); + const RegType& cast_type = ResolveClassAndCheckAccess( + dex::TypeIndex(instance_of_inst->VRegC_22c())); if (!orig_type.Equals(cast_type) && !cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() && @@ -2873,7 +2884,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (return_type == nullptr) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + dex::TypeIndex return_type_idx = + dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx); return_type = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false); } @@ -2896,7 +2908,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); is_constructor = strcmp("<init>", dex_file_->StringDataByIdx(method_id.name_idx_)) == 0; - uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + dex::TypeIndex return_type_idx = + dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; return_type_descriptor = dex_file_->StringByTypeIdx(return_type_idx); } else { is_constructor = called_method->IsConstructor(); @@ -2972,7 +2985,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (called_method == nullptr) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + dex::TypeIndex return_type_idx = + dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; descriptor = dex_file_->StringByTypeIdx(return_type_idx); } else { descriptor = called_method->GetReturnTypeDescriptor(); @@ -3026,7 +3040,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (abs_method == nullptr) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + dex::TypeIndex return_type_idx = + dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; descriptor = dex_file_->StringByTypeIdx(return_type_idx); } else { descriptor = abs_method->GetReturnTypeDescriptor(); @@ -3490,8 +3505,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { ClassLinker* linker = Runtime::Current()->GetClassLinker(); for (; iterator.HasNext(); iterator.Next()) { - uint16_t handler_type_idx = iterator.GetHandlerTypeIndex(); - if (handler_type_idx == DexFile::kDexNoIndex16) { + dex::TypeIndex handler_type_idx = iterator.GetHandlerTypeIndex(); + if (!handler_type_idx.IsValid()) { has_catch_all_handler = true; } else { // It is also a catch-all if it is java.lang.Throwable. @@ -3618,7 +3633,7 @@ inline bool MethodVerifier::IsInstantiableOrPrimitive(mirror::Class* klass) { return klass->IsInstantiable() || klass->IsPrimitive(); } -const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) { +const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_idx) { mirror::Class* klass = dex_cache_->GetResolvedType(class_idx); const RegType* result = nullptr; if (klass != nullptr) { @@ -3674,7 +3689,7 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { CatchHandlerIterator iterator(handlers_ptr); for (; iterator.HasNext(); iterator.Next()) { if (iterator.GetHandlerAddress() == (uint32_t) work_insn_idx_) { - if (iterator.GetHandlerTypeIndex() == DexFile::kDexNoIndex16) { + if (!iterator.GetHandlerTypeIndex().IsValid()) { common_super = ®_types_.JavaLangThrowable(false); } else { const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex()); @@ -3931,7 +3946,7 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( klass->CannotBeAssignedFromOtherTypes()); } else { const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - const uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_; + const dex::TypeIndex class_idx = dex_file_->GetMethodId(method_idx).class_idx_; res_method_class = ®_types_.FromDescriptor( GetClassLoader(), dex_file_->StringByTypeIdx(class_idx), @@ -4068,7 +4083,7 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( // If we're using invoke-super(method), make sure that the executing method's class' superclass // has a vtable entry for the target method. Or the target is on a interface. if (method_type == METHOD_SUPER) { - uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_; + dex::TypeIndex class_idx = dex_file_->GetMethodId(method_idx).class_idx_; const RegType& reference_type = reg_types_.FromDescriptor( GetClassLoader(), dex_file_->StringByTypeIdx(class_idx), @@ -4277,16 +4292,16 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, } void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range) { - uint32_t type_idx; + dex::TypeIndex type_idx; if (!is_filled) { DCHECK_EQ(inst->Opcode(), Instruction::NEW_ARRAY); - type_idx = inst->VRegC_22c(); + type_idx = dex::TypeIndex(inst->VRegC_22c()); } else if (!is_range) { DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY); - type_idx = inst->VRegB_35c(); + type_idx = dex::TypeIndex(inst->VRegB_35c()); } else { DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY_RANGE); - type_idx = inst->VRegB_3rc(); + type_idx = dex::TypeIndex(inst->VRegB_3rc()); } const RegType& res_type = ResolveClassAndCheckAccess(type_idx); if (res_type.IsConflict()) { // bad class @@ -5001,7 +5016,7 @@ const RegType& MethodVerifier::GetMethodReturnType() { if (return_type_ == nullptr) { const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id); - uint16_t return_type_idx = proto_id.return_type_idx_; + dex::TypeIndex return_type_idx = proto_id.return_type_idx_; const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx)); return_type_ = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false); } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index c6ce583ab4..f3faecd9a7 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -27,6 +27,7 @@ #include "base/stl_util.h" #include "base/value_object.h" #include "dex_file.h" +#include "dex_file_types.h" #include "handle.h" #include "instruction_flags.h" #include "method_reference.h" @@ -261,7 +262,7 @@ class MethodVerifier { return have_any_pending_runtime_throw_failure_; } - const RegType& ResolveCheckedClass(uint32_t class_idx) + const RegType& ResolveCheckedClass(dex::TypeIndex class_idx) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the method of a quick invoke or null if it cannot be found. ArtMethod* GetQuickInvokedMethod(const Instruction* inst, RegisterLine* reg_line, @@ -471,18 +472,18 @@ class MethodVerifier { // Perform static checks on a "new-instance" instruction. Specifically, make sure the class // reference isn't for an array class. - bool CheckNewInstance(uint32_t idx); + bool CheckNewInstance(dex::TypeIndex idx); /* Ensure that the string index is in the valid range. */ bool CheckStringIndex(uint32_t idx); // Perform static checks on an instruction that takes a class constant. Ensure that the class // index is in the valid range. - bool CheckTypeIndex(uint32_t idx); + bool CheckTypeIndex(dex::TypeIndex idx); // Perform static checks on a "new-array" instruction. Specifically, make sure they aren't // creating an array of arrays that causes the number of dimensions to exceed 255. - bool CheckNewArray(uint32_t idx); + bool CheckNewArray(dex::TypeIndex idx); // Verify an array data table. "cur_offset" is the offset of the fill-array-data instruction. bool CheckArrayData(uint32_t cur_offset); @@ -625,7 +626,7 @@ class MethodVerifier { // Resolves a class based on an index and performs access checks to ensure the referrer can // access the resolved class. - const RegType& ResolveClassAndCheckAccess(uint32_t class_idx) + const RegType& ResolveClassAndCheckAccess(dex::TypeIndex class_idx) REQUIRES_SHARED(Locks::mutator_lock_); /* diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index c395612d72..01af5ec00a 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -16,6 +16,9 @@ #include "verifier_deps.h" +#include <cstring> + +#include "base/stl_util.h" #include "compiler_callbacks.h" #include "leb128.h" #include "mirror/class-inl.h" @@ -26,7 +29,6 @@ namespace art { namespace verifier { VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files) { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (const DexFile* dex_file : dex_files) { DCHECK(GetDexFileDeps(*dex_file) == nullptr); std::unique_ptr<DexFileDeps> deps(new DexFileDeps()); @@ -34,6 +36,28 @@ VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files) { } } +void VerifierDeps::MergeWith(const VerifierDeps& other, + const std::vector<const DexFile*>& dex_files) { + DCHECK(dex_deps_.size() == other.dex_deps_.size()); + for (const DexFile* dex_file : dex_files) { + DexFileDeps* my_deps = GetDexFileDeps(*dex_file); + const DexFileDeps& other_deps = *other.GetDexFileDeps(*dex_file); + // We currently collect extra strings only on the main `VerifierDeps`, + // which should be the one passed as `this` in this method. + DCHECK(other_deps.strings_.empty()); + MergeSets(my_deps->assignable_types_, other_deps.assignable_types_); + MergeSets(my_deps->unassignable_types_, other_deps.unassignable_types_); + MergeSets(my_deps->classes_, other_deps.classes_); + MergeSets(my_deps->fields_, other_deps.fields_); + MergeSets(my_deps->direct_methods_, other_deps.direct_methods_); + MergeSets(my_deps->virtual_methods_, other_deps.virtual_methods_); + MergeSets(my_deps->interface_methods_, other_deps.interface_methods_); + for (dex::TypeIndex entry : other_deps.unverified_classes_) { + my_deps->unverified_classes_.push_back(entry); + } + } +} + VerifierDeps::DexFileDeps* VerifierDeps::GetDexFileDeps(const DexFile& dex_file) { auto it = dex_deps_.find(&dex_file); return (it == dex_deps_.end()) ? nullptr : it->second.get(); @@ -56,17 +80,114 @@ uint16_t VerifierDeps::GetAccessFlags(T* element) { } } -template <typename T> -uint32_t VerifierDeps::GetDeclaringClassStringId(const DexFile& dex_file, T* element) { +uint32_t VerifierDeps::GetClassDescriptorStringId(const DexFile& dex_file, + ObjPtr<mirror::Class> klass) { + DCHECK(klass != nullptr); + ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache(); + // Array and proxy classes do not have a dex cache. + if (!klass->IsArrayClass() && !klass->IsProxyClass()) { + DCHECK(dex_cache != nullptr) << klass->PrettyClass(); + if (dex_cache->GetDexFile() == &dex_file) { + // FindStringId is slow, try to go through the class def if we have one. + const DexFile::ClassDef* class_def = klass->GetClassDef(); + DCHECK(class_def != nullptr) << klass->PrettyClass(); + const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def->class_idx_); + if (kIsDebugBuild) { + std::string temp; + CHECK_EQ(GetIdFromString(dex_file, klass->GetDescriptor(&temp)), type_id.descriptor_idx_); + } + return type_id.descriptor_idx_; + } + } + std::string temp; + return GetIdFromString(dex_file, klass->GetDescriptor(&temp)); +} + +// Try to find the string descriptor of the class. type_idx is a best guess of a matching string id. +static uint32_t TryGetClassDescriptorStringId(const DexFile& dex_file, + dex::TypeIndex type_idx, + ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!klass->IsArrayClass()) { + const DexFile::TypeId& type_id = dex_file.GetTypeId(type_idx); + const DexFile& klass_dex = klass->GetDexFile(); + const DexFile::TypeId& klass_type_id = klass_dex.GetTypeId(klass->GetClassDef()->class_idx_); + if (strcmp(dex_file.GetTypeDescriptor(type_id), + klass_dex.GetTypeDescriptor(klass_type_id)) == 0) { + return type_id.descriptor_idx_; + } + } + return DexFile::kDexNoIndex; +} + +uint32_t VerifierDeps::GetMethodDeclaringClassStringId(const DexFile& dex_file, + uint32_t dex_method_index, + ArtMethod* method) { static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant"); - if (element == nullptr) { + if (method == nullptr) { return VerifierDeps::kUnresolvedMarker; - } else { - std::string temp; - uint32_t string_id = GetIdFromString( - dex_file, element->GetDeclaringClass()->GetDescriptor(&temp)); + } + const uint32_t string_id = TryGetClassDescriptorStringId( + dex_file, + dex_file.GetMethodId(dex_method_index).class_idx_, + method->GetDeclaringClass()); + if (string_id != DexFile::kDexNoIndex) { + // Got lucky using the original dex file, return based on the input dex file. + DCHECK_EQ(GetClassDescriptorStringId(dex_file, method->GetDeclaringClass()), string_id); + return string_id; + } + return GetClassDescriptorStringId(dex_file, method->GetDeclaringClass()); +} + +uint32_t VerifierDeps::GetFieldDeclaringClassStringId(const DexFile& dex_file, + uint32_t dex_field_idx, + ArtField* field) { + static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant"); + if (field == nullptr) { + return VerifierDeps::kUnresolvedMarker; + } + const uint32_t string_id = TryGetClassDescriptorStringId( + dex_file, + dex_file.GetFieldId(dex_field_idx).class_idx_, + field->GetDeclaringClass()); + if (string_id != DexFile::kDexNoIndex) { + // Got lucky using the original dex file, return based on the input dex file. + DCHECK_EQ(GetClassDescriptorStringId(dex_file, field->GetDeclaringClass()), string_id); return string_id; } + return GetClassDescriptorStringId(dex_file, field->GetDeclaringClass()); +} + +static inline VerifierDeps* GetMainVerifierDeps() { + // The main VerifierDeps is the one set in the compiler callbacks, which at the + // end of verification will have all the per-thread VerifierDeps merged into it. + CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks(); + if (callbacks == nullptr) { + return nullptr; + } + return callbacks->GetVerifierDeps(); +} + +static inline VerifierDeps* GetThreadLocalVerifierDeps() { + // During AOT, each thread has its own VerifierDeps, to avoid lock contention. At the end + // of full verification, these VerifierDeps will be merged into the main one. + if (!Runtime::Current()->IsAotCompiler()) { + return nullptr; + } + return Thread::Current()->GetVerifierDeps(); +} + +static bool FindExistingStringId(const std::vector<std::string>& strings, + const std::string& str, + uint32_t* found_id) { + uint32_t num_extra_ids = strings.size(); + for (size_t i = 0; i < num_extra_ids; ++i) { + if (strings[i] == str) { + *found_id = i; + return true; + } + } + return false; } uint32_t VerifierDeps::GetIdFromString(const DexFile& dex_file, const std::string& str) { @@ -79,25 +200,32 @@ uint32_t VerifierDeps::GetIdFromString(const DexFile& dex_file, const std::strin // String is not in the DEX file. Assign a new ID to it which is higher than // the number of strings in the DEX file. - DexFileDeps* deps = GetDexFileDeps(dex_file); + // We use the main `VerifierDeps` for adding new strings to simplify + // synchronization/merging of these entries between threads. + VerifierDeps* singleton = GetMainVerifierDeps(); + DexFileDeps* deps = singleton->GetDexFileDeps(dex_file); DCHECK(deps != nullptr); uint32_t num_ids_in_dex = dex_file.NumStringIds(); - uint32_t num_extra_ids = deps->strings_.size(); + uint32_t found_id; - for (size_t i = 0; i < num_extra_ids; ++i) { - if (deps->strings_[i] == str) { - return num_ids_in_dex + i; + { + ReaderMutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + if (FindExistingStringId(deps->strings_, str, &found_id)) { + return num_ids_in_dex + found_id; } } - - deps->strings_.push_back(str); - - uint32_t new_id = num_ids_in_dex + num_extra_ids; - CHECK_GE(new_id, num_ids_in_dex); // check for overflows - DCHECK_EQ(str, GetStringFromId(dex_file, new_id)); - - return new_id; + { + WriterMutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + if (FindExistingStringId(deps->strings_, str, &found_id)) { + return num_ids_in_dex + found_id; + } + deps->strings_.push_back(str); + uint32_t new_id = num_ids_in_dex + deps->strings_.size() - 1; + CHECK_GE(new_id, num_ids_in_dex); // check for overflows + DCHECK_EQ(str, singleton->GetStringFromId(dex_file, new_id)); + return new_id; + } } std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, uint32_t string_id) const { @@ -137,7 +265,7 @@ bool VerifierDeps::IsInClassPath(ObjPtr<mirror::Class> klass) const { } void VerifierDeps::AddClassResolution(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, mirror::Class* klass) { DexFileDeps* dex_deps = GetDexFileDeps(dex_file); if (dex_deps == nullptr) { @@ -151,7 +279,6 @@ void VerifierDeps::AddClassResolution(const DexFile& dex_file, return; } - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); dex_deps->classes_.emplace(ClassResolution(type_idx, GetAccessFlags(klass))); } @@ -170,9 +297,11 @@ void VerifierDeps::AddFieldResolution(const DexFile& dex_file, return; } - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); - dex_deps->fields_.emplace(FieldResolution( - field_idx, GetAccessFlags(field), GetDeclaringClassStringId(dex_file, field))); + dex_deps->fields_.emplace(FieldResolution(field_idx, + GetAccessFlags(field), + GetFieldDeclaringClassStringId(dex_file, + field_idx, + field))); } void VerifierDeps::AddMethodResolution(const DexFile& dex_file, @@ -191,10 +320,9 @@ void VerifierDeps::AddMethodResolution(const DexFile& dex_file, return; } - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); MethodResolution method_tuple(method_idx, GetAccessFlags(method), - GetDeclaringClassStringId(dex_file, method)); + GetMethodDeclaringClassStringId(dex_file, method_idx, method)); if (resolution_kind == kDirectMethodResolution) { dex_deps->direct_methods_.emplace(method_tuple); } else if (resolution_kind == kVirtualMethodResolution) { @@ -260,15 +388,9 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file, return; } - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); - // Get string IDs for both descriptors and store in the appropriate set. - - std::string temp1, temp2; - std::string destination_desc(destination->GetDescriptor(&temp1)); - std::string source_desc(source->GetDescriptor(&temp2)); - uint32_t destination_id = GetIdFromString(dex_file, destination_desc); - uint32_t source_id = GetIdFromString(dex_file, source_desc); + uint32_t destination_id = GetClassDescriptorStringId(dex_file, destination); + uint32_t source_id = GetClassDescriptorStringId(dex_file, source); if (is_assignable) { dex_deps->assignable_types_.emplace(TypeAssignability(destination_id, source_id)); @@ -277,45 +399,36 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file, } } -static inline VerifierDeps* GetVerifierDepsSingleton() { - CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks(); - if (callbacks == nullptr) { - return nullptr; - } - return callbacks->GetVerifierDeps(); -} - void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, MethodVerifier::FailureKind failure_kind) { if (failure_kind == MethodVerifier::kNoFailure) { // We only record classes that did not fully verify at compile time. return; } - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - DexFileDeps* dex_deps = singleton->GetDexFileDeps(dex_file); - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + DexFileDeps* dex_deps = thread_deps->GetDexFileDeps(dex_file); dex_deps->unverified_classes_.push_back(type_idx); } } void VerifierDeps::MaybeRecordClassResolution(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, mirror::Class* klass) { - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - singleton->AddClassResolution(dex_file, type_idx, klass); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + thread_deps->AddClassResolution(dex_file, type_idx, klass); } } void VerifierDeps::MaybeRecordFieldResolution(const DexFile& dex_file, uint32_t field_idx, ArtField* field) { - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - singleton->AddFieldResolution(dex_file, field_idx, field); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + thread_deps->AddFieldResolution(dex_file, field_idx, field); } } @@ -323,9 +436,9 @@ void VerifierDeps::MaybeRecordMethodResolution(const DexFile& dex_file, uint32_t method_idx, MethodResolutionKind resolution_kind, ArtMethod* method) { - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - singleton->AddMethodResolution(dex_file, method_idx, resolution_kind, method); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + thread_deps->AddMethodResolution(dex_file, method_idx, resolution_kind, method); } } @@ -334,42 +447,68 @@ void VerifierDeps::MaybeRecordAssignability(const DexFile& dex_file, mirror::Class* source, bool is_strict, bool is_assignable) { - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - singleton->AddAssignability(dex_file, destination, source, is_strict, is_assignable); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + thread_deps->AddAssignability(dex_file, destination, source, is_strict, is_assignable); } } +namespace { + static inline uint32_t DecodeUint32WithOverflowCheck(const uint8_t** in, const uint8_t* end) { CHECK_LT(*in, end); return DecodeUnsignedLeb128(in); } +template<typename T> inline uint32_t Encode(T in); + +template<> inline uint32_t Encode<uint16_t>(uint16_t in) { + return in; +} +template<> inline uint32_t Encode<uint32_t>(uint32_t in) { + return in; +} +template<> inline uint32_t Encode<dex::TypeIndex>(dex::TypeIndex in) { + return in.index_; +} + +template<typename T> inline T Decode(uint32_t in); + +template<> inline uint16_t Decode<uint16_t>(uint32_t in) { + return dchecked_integral_cast<uint16_t>(in); +} +template<> inline uint32_t Decode<uint32_t>(uint32_t in) { + return in; +} +template<> inline dex::TypeIndex Decode<dex::TypeIndex>(uint32_t in) { + return dex::TypeIndex(in); +} + template<typename T1, typename T2> static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2>& t) { - EncodeUnsignedLeb128(out, std::get<0>(t)); - EncodeUnsignedLeb128(out, std::get<1>(t)); + EncodeUnsignedLeb128(out, Encode(std::get<0>(t))); + EncodeUnsignedLeb128(out, Encode(std::get<1>(t))); } template<typename T1, typename T2> static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, std::tuple<T1, T2>* t) { - T1 v1 = static_cast<T1>(DecodeUint32WithOverflowCheck(in, end)); - T2 v2 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end)); + T1 v1 = Decode<T1>(DecodeUint32WithOverflowCheck(in, end)); + T2 v2 = Decode<T2>(DecodeUint32WithOverflowCheck(in, end)); *t = std::make_tuple(v1, v2); } template<typename T1, typename T2, typename T3> static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2, T3>& t) { - EncodeUnsignedLeb128(out, std::get<0>(t)); - EncodeUnsignedLeb128(out, std::get<1>(t)); - EncodeUnsignedLeb128(out, std::get<2>(t)); + EncodeUnsignedLeb128(out, Encode(std::get<0>(t))); + EncodeUnsignedLeb128(out, Encode(std::get<1>(t))); + EncodeUnsignedLeb128(out, Encode(std::get<2>(t))); } template<typename T1, typename T2, typename T3> static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, std::tuple<T1, T2, T3>* t) { - T1 v1 = static_cast<T1>(DecodeUint32WithOverflowCheck(in, end)); - T2 v2 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end)); - T3 v3 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end)); + T1 v1 = Decode<T1>(DecodeUint32WithOverflowCheck(in, end)); + T2 v2 = Decode<T2>(DecodeUint32WithOverflowCheck(in, end)); + T3 v3 = Decode<T2>(DecodeUint32WithOverflowCheck(in, end)); *t = std::make_tuple(v1, v2, v3); } @@ -381,11 +520,12 @@ static inline void EncodeSet(std::vector<uint8_t>* out, const std::set<T>& set) } } +template <typename T> static inline void EncodeUint16Vector(std::vector<uint8_t>* out, - const std::vector<uint16_t>& vector) { + const std::vector<T>& vector) { EncodeUnsignedLeb128(out, vector.size()); - for (uint16_t entry : vector) { - EncodeUnsignedLeb128(out, entry); + for (const T& entry : vector) { + EncodeUnsignedLeb128(out, Encode(entry)); } } @@ -400,14 +540,16 @@ static inline void DecodeSet(const uint8_t** in, const uint8_t* end, std::set<T> } } +template<typename T> static inline void DecodeUint16Vector(const uint8_t** in, const uint8_t* end, - std::vector<uint16_t>* vector) { + std::vector<T>* vector) { DCHECK(vector->empty()); size_t num_entries = DecodeUint32WithOverflowCheck(in, end); vector->reserve(num_entries); for (size_t i = 0; i < num_entries; ++i) { - vector->push_back(dchecked_integral_cast<uint16_t>(DecodeUint32WithOverflowCheck(in, end))); + vector->push_back( + Decode<T>(dchecked_integral_cast<uint16_t>(DecodeUint32WithOverflowCheck(in, end)))); } } @@ -436,9 +578,10 @@ static inline void DecodeStringVector(const uint8_t** in, } } +} // namespace + void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (const DexFile* dex_file : dex_files) { const DexFileDeps& deps = *GetDexFileDeps(*dex_file); EncodeStringVector(buffer, deps.strings_); @@ -480,8 +623,6 @@ VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, } bool VerifierDeps::Equals(const VerifierDeps& rhs) const { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); - if (dex_deps_.size() != rhs.dex_deps_.size()) { return false; } @@ -599,7 +740,7 @@ void VerifierDeps::Dump(VariableIndentationOutputStream* vios) const { } } - for (uint16_t type_index : dep.second->unverified_classes_) { + for (dex::TypeIndex type_index : dep.second->unverified_classes_) { vios->Stream() << dex_file.StringByTypeIdx(type_index) << " is expected to be verified at runtime\n"; diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index 7b419d4260..a12071b8b6 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -42,29 +42,30 @@ namespace verifier { // which are being compiled. Classes defined in DEX files outside of this set // (or synthesized classes without associated DEX files) are considered being // in the classpath. -// During code-flow verification, the MethodVerifier informs the VerifierDeps -// singleton about the outcome of every resolution and assignability test, and -// the singleton records them if their outcome may change with changes in the -// classpath. +// During code-flow verification, the MethodVerifier informs VerifierDeps +// about the outcome of every resolution and assignability test, and +// the VerifierDeps object records them if their outcome may change with +// changes in the classpath. class VerifierDeps { public: - explicit VerifierDeps(const std::vector<const DexFile*>& dex_files) - REQUIRES(!Locks::verifier_deps_lock_); + explicit VerifierDeps(const std::vector<const DexFile*>& dex_files); - VerifierDeps(const std::vector<const DexFile*>& dex_files, - ArrayRef<const uint8_t> data) - REQUIRES(!Locks::verifier_deps_lock_); + VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<const uint8_t> data); + + // Merge `other` into this `VerifierDeps`'. `other` and `this` must be for the + // same set of dex files. + void MergeWith(const VerifierDeps& other, const std::vector<const DexFile*>& dex_files); // Record the verification status of the class at `type_idx`. static void MaybeRecordVerificationStatus(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, MethodVerifier::FailureKind failure_kind) REQUIRES(!Locks::verifier_deps_lock_); // Record the outcome `klass` of resolving type `type_idx` from `dex_file`. // If `klass` is null, the class is assumed unresolved. static void MaybeRecordClassResolution(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); @@ -101,38 +102,30 @@ class VerifierDeps { // Serialize the recorded dependencies and store the data into `buffer`. // `dex_files` provides the order of the dex files in which the dependencies // should be emitted. - void Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const - REQUIRES(!Locks::verifier_deps_lock_); + void Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const; - // NO_THREAD_SAFETY_ANALYSIS as Dump iterates over dex_deps_, which is guarded by - // verifier_deps_lock_, but we expect Dump to be called once the deps collection is done. - void Dump(VariableIndentationOutputStream* vios) const - NO_THREAD_SAFETY_ANALYSIS; + void Dump(VariableIndentationOutputStream* vios) const; // Verify the encoded dependencies of this `VerifierDeps` are still valid. - // NO_THREAD_SAFETY_ANALYSIS, as this must be called on a read-only `VerifierDeps`. bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader, Thread* self) const - NO_THREAD_SAFETY_ANALYSIS; + REQUIRES_SHARED(Locks::mutator_lock_); - // NO_THREAD_SAFETY_ANALSYS, as this is queried when the VerifierDeps are - // fully created. - const std::vector<uint16_t>& GetUnverifiedClasses(const DexFile& dex_file) const - NO_THREAD_SAFETY_ANALYSIS { + const std::vector<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const { return GetDexFileDeps(dex_file)->unverified_classes_; } private: static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1); - using ClassResolutionBase = std::tuple<uint32_t, uint16_t>; + using ClassResolutionBase = std::tuple<dex::TypeIndex, uint16_t>; struct ClassResolution : public ClassResolutionBase { ClassResolution() = default; ClassResolution(const ClassResolution&) = default; - ClassResolution(uint32_t type_idx, uint16_t access_flags) + ClassResolution(dex::TypeIndex type_idx, uint16_t access_flags) : ClassResolutionBase(type_idx, access_flags) {} bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; } - uint32_t GetDexTypeIndex() const { return std::get<0>(*this); } + dex::TypeIndex GetDexTypeIndex() const { return std::get<0>(*this); } uint16_t GetAccessFlags() const { return std::get<1>(*this); } }; @@ -193,20 +186,16 @@ class VerifierDeps { std::set<MethodResolution> interface_methods_; // List of classes that were not fully verified in that dex file. - std::vector<uint16_t> unverified_classes_; + std::vector<dex::TypeIndex> unverified_classes_; bool Equals(const DexFileDeps& rhs) const; }; // Finds the DexFileDep instance associated with `dex_file`, or nullptr if // `dex_file` is not reported as being compiled. - // We disable thread safety analysis. The method only reads the key set of - // `dex_deps_` which stays constant after initialization. - DexFileDeps* GetDexFileDeps(const DexFile& dex_file) - NO_THREAD_SAFETY_ANALYSIS; + DexFileDeps* GetDexFileDeps(const DexFile& dex_file); - const DexFileDeps* GetDexFileDeps(const DexFile& dex_file) const - NO_THREAD_SAFETY_ANALYSIS; + const DexFileDeps* GetDexFileDeps(const DexFile& dex_file) const; // Returns true if `klass` is null or not defined in any of dex files which // were reported as being compiled. @@ -218,11 +207,10 @@ class VerifierDeps { // of the corresponding DexFileDeps structure (either provided or inferred from // `dex_file`). uint32_t GetIdFromString(const DexFile& dex_file, const std::string& str) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES(!Locks::verifier_deps_lock_); // Returns the string represented by `id`. - std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id) const - REQUIRES(Locks::verifier_deps_lock_); + std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id) const; // Returns the bytecode access flags of `element` (bottom 16 bits), or // `kUnresolvedMarker` if `element` is null. @@ -232,13 +220,22 @@ class VerifierDeps { // Returns a string ID of the descriptor of the declaring class of `element`, // or `kUnresolvedMarker` if `element` is null. - template <typename T> - uint32_t GetDeclaringClassStringId(const DexFile& dex_file, T* element) + uint32_t GetMethodDeclaringClassStringId(const DexFile& dex_file, + uint32_t dex_method_idx, + ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t GetFieldDeclaringClassStringId(const DexFile& dex_file, + uint32_t dex_field_idx, + ArtField* field) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Returns a string ID of the descriptor of the class. + uint32_t GetClassDescriptorStringId(const DexFile& dex_file, ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES(!Locks::verifier_deps_lock_); void AddClassResolution(const DexFile& dex_file, - uint16_t type_idx, + dex::TypeIndex type_idx, mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); @@ -261,11 +258,9 @@ class VerifierDeps { mirror::Class* source, bool is_strict, bool is_assignable) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); - bool Equals(const VerifierDeps& rhs) const - REQUIRES(!Locks::verifier_deps_lock_); + bool Equals(const VerifierDeps& rhs) const; // Verify `dex_file` according to the `deps`, that is going over each // `DexFileDeps` field, and checking that the recorded information still @@ -274,16 +269,14 @@ class VerifierDeps { const DexFile& dex_file, const DexFileDeps& deps, Thread* self) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, const std::set<TypeAssignability>& assignables, bool expected_assignability, Thread* self) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Verify that the set of resolved classes at the point of creation // of this `VerifierDeps` is still the same. @@ -291,8 +284,7 @@ class VerifierDeps { const DexFile& dex_file, const std::set<ClassResolution>& classes, Thread* self) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Verify that the set of resolved fields at the point of creation // of this `VerifierDeps` is still the same, and each field resolves to the @@ -302,7 +294,7 @@ class VerifierDeps { const std::set<FieldResolution>& classes, Thread* self) const REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES(!Locks::verifier_deps_lock_); // Verify that the set of resolved methods at the point of creation // of this `VerifierDeps` is still the same, and each method resolves to the @@ -312,12 +304,10 @@ class VerifierDeps { const std::set<MethodResolution>& methods, MethodResolutionKind kind, Thread* self) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Map from DexFiles into dependencies collected from verification of their methods. - std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_ - GUARDED_BY(Locks::verifier_deps_lock_); + std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_; friend class VerifierDepsTest; ART_FRIEND_TEST(VerifierDepsTest, StringToId); diff --git a/test/015-switch/src/Main.java b/test/015-switch/src/Main.java index 2a7995a075..2b724a1e57 100644 --- a/test/015-switch/src/Main.java +++ b/test/015-switch/src/Main.java @@ -113,7 +113,7 @@ public class Main { } // Long packed-switch that might lead to not creating chained-ifs. - public static void packedSwitch7(int value) { + public static long packedSwitch7(int value) { switch (value) { case 1: System.out.println(1); break; @@ -148,6 +148,113 @@ public class Main { default: System.out.println("default"); break; } + + // Jump tables previously were emitted in the end of the method code buffer. The + // following boilerplate code aims to fill the emitted code buffer extensively + // and check that even for big method jump table is correctly emitted, its address + // is within a range of corresponded pc-relative instructions (this applies to + // ARM mainly). + long temp = value; + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + temp = Long.rotateLeft(temp, value); + + return temp; } // Sparse switch, just leave a gap. diff --git a/test/445-checker-licm/expected.txt b/test/445-checker-licm/expected.txt index e69de29bb2..b0aad4deb5 100644 --- a/test/445-checker-licm/expected.txt +++ b/test/445-checker-licm/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java index 061fe6e026..00ce3a9f8e 100644 --- a/test/445-checker-licm/src/Main.java +++ b/test/445-checker-licm/src/Main.java @@ -164,8 +164,43 @@ public class Main { return result; } + // + // All operations up to the null check can be hoisted out of the + // loop. The null check itself sees the induction in its environment. + // + /// CHECK-START: int Main.doWhile(int) licm (before) + /// CHECK-DAG: <<Add:i\d+>> Add loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: LoadClass loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get:l\d+>> StaticFieldGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: NullCheck [<<Get>>] env:[[<<Add>>,<<Get>>,{{i\d+}}]] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayLength loop:<<Loop>> outer_loop:none + /// CHECK-DAG: BoundsCheck loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.doWhile(int) licm (after) + /// CHECK-NOT: LoadClass loop:{{B\d+}} + /// CHECK-NOT: StaticFieldGet loop:{{B\d+}} + // + /// CHECK-START: int Main.doWhile(int) licm (after) + /// CHECK-DAG: LoadClass loop:none + /// CHECK-DAG: <<Get:l\d+>> StaticFieldGet loop:none + /// CHECK-DAG: <<Add:i\d+>> Add loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: NullCheck [<<Get>>] env:[[<<Add>>,<<Get>>,{{i\d+}}]] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayLength loop:<<Loop>> outer_loop:none + /// CHECK-DAG: BoundsCheck loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + public static int doWhile(int k) { + int i = k; + do { + i += 2; + } while (staticArray[i] == 0); + return i; + } + public static int staticField = 42; + public static int[] staticArray = null; + public static void assertEquals(int expected, int actual) { if (expected != actual) { throw new Error("Expected " + expected + ", got " + actual); @@ -181,5 +216,24 @@ public class Main { assertEquals(21, divAndIntrinsic(new int[] { 4, -2, 8, -3 })); assertEquals(45, invariantBoundIntrinsic(-10)); assertEquals(30, invariantBodyIntrinsic(2, 3)); + + staticArray = null; + try { + doWhile(0); + throw new Error("Expected NPE"); + } catch (NullPointerException e) { + } + staticArray = new int[5]; + staticArray[4] = 1; + assertEquals(4, doWhile(-2)); + assertEquals(4, doWhile(0)); + assertEquals(4, doWhile(2)); + try { + doWhile(1); + throw new Error("Expected IOOBE"); + } catch (IndexOutOfBoundsException e) { + } + + System.out.println("passed"); } } diff --git a/test/530-checker-loops3/src/Main.java b/test/530-checker-loops3/src/Main.java index 6b5c657294..209786a113 100644 --- a/test/530-checker-loops3/src/Main.java +++ b/test/530-checker-loops3/src/Main.java @@ -246,7 +246,7 @@ public class Main { oneConstantIndex(a, b); for (int i = 0; i < a.length; i++) { - expectEquals(2, a[i]);; + expectEquals(2, a[i]); } try { oneConstantIndex(a, b1); @@ -256,7 +256,7 @@ public class Main { multipleConstantIndices(a, b); for (int i = 0; i < a.length; i++) { - expectEquals(6, a[i]);; + expectEquals(6, a[i]); } try { multipleConstantIndices(a, b1); @@ -266,7 +266,7 @@ public class Main { oneInvariantIndex(a, b, 1); for (int i = 0; i < a.length; i++) { - expectEquals(2, a[i]);; + expectEquals(2, a[i]); } try { oneInvariantIndex(a, b1, 1); @@ -276,7 +276,7 @@ public class Main { multipleInvariantIndices(a, b, 1); for (int i = 0; i < a.length; i++) { - expectEquals(6, a[i]);; + expectEquals(6, a[i]); } try { multipleInvariantIndices(a, b1, 1); @@ -286,18 +286,18 @@ public class Main { oneUnitStride(a, b); for (int i = 0; i < a.length; i++) { - expectEquals(i + 1, a[i]);; + expectEquals(i + 1, a[i]); } try { oneUnitStride(a, b1); throw new Error("Should throw AIOOBE"); } catch (ArrayIndexOutOfBoundsException e) { - expectEquals(100, a[0]);; + expectEquals(100, a[0]); } multipleUnitStrides(a, b); for (int i = 1; i < a.length - 1; i++) { - expectEquals(3 * i + 3, a[i]);; + expectEquals(3 * i + 3, a[i]); } try { multipleUnitStrides(a, b1); @@ -308,7 +308,7 @@ public class Main { multipleUnitStridesConditional(a, b); for (int i = 2; i < a.length - 2; i++) { int e = 3 * i + 3 + (((i & 1) == 0) ? i + 2 : i); - expectEquals(e, a[i]);; + expectEquals(e, a[i]); } try { multipleUnitStridesConditional(a, b1); diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java index 02c609ef7c..6b25747463 100644 --- a/test/538-checker-embed-constants/src/Main.java +++ b/test/538-checker-embed-constants/src/Main.java @@ -30,7 +30,7 @@ public class Main { /// CHECK-START-ARM: int Main.and255(int) disassembly (after) /// CHECK-NOT: movs {{r\d+}}, #255 - /// CHECK: and {{r\d+}}, {{r\d+}}, #255 + /// CHECK: and {{r\d+}}, {{r\d+}}, #0xff public static int and255(int arg) { return arg & 255; @@ -46,7 +46,7 @@ public class Main { /// CHECK-START-ARM: int Main.andNot15(int) disassembly (after) /// CHECK-NOT: mvn {{r\d+}}, #15 - /// CHECK: bic {{r\d+}}, {{r\d+}}, #15 + /// CHECK: bic {{r\d+}}, {{r\d+}}, #0xf public static int andNot15(int arg) { return arg & ~15; @@ -54,7 +54,7 @@ public class Main { /// CHECK-START-ARM: int Main.or255(int) disassembly (after) /// CHECK-NOT: movs {{r\d+}}, #255 - /// CHECK: orr {{r\d+}}, {{r\d+}}, #255 + /// CHECK: orr {{r\d+}}, {{r\d+}}, #0xff public static int or255(int arg) { return arg | 255; @@ -70,7 +70,7 @@ public class Main { /// CHECK-START-ARM: int Main.orNot15(int) disassembly (after) /// CHECK-NOT: mvn {{r\d+}}, #15 - /// CHECK: orn {{r\d+}}, {{r\d+}}, #15 + /// CHECK: orn {{r\d+}}, {{r\d+}}, #0xf public static int orNot15(int arg) { return arg | ~15; @@ -78,7 +78,7 @@ public class Main { /// CHECK-START-ARM: int Main.xor255(int) disassembly (after) /// CHECK-NOT: movs {{r\d+}}, #255 - /// CHECK: eor {{r\d+}}, {{r\d+}}, #255 + /// CHECK: eor {{r\d+}}, {{r\d+}}, #0xff public static int xor255(int arg) { return arg ^ 255; @@ -104,7 +104,7 @@ public class Main { /// CHECK-NOT: movs {{r\d+}}, #255 /// CHECK-NOT: and{{(\.w)?}} /// CHECK-NOT: bic{{(\.w)?}} - /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #255 + /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #0xff /// CHECK-DAG: movs {{r\d+}}, #0 /// CHECK-NOT: and{{(\.w)?}} /// CHECK-NOT: bic{{(\.w)?}} @@ -131,7 +131,7 @@ public class Main { /// CHECK-NOT: mvn {{r\d+}}, #15 /// CHECK-NOT: and{{(\.w)?}} /// CHECK-NOT: bic{{(\.w)?}} - /// CHECK: bic {{r\d+}}, {{r\d+}}, #15 + /// CHECK: bic {{r\d+}}, {{r\d+}}, #0xf /// CHECK-NOT: and{{(\.w)?}} /// CHECK-NOT: bic{{(\.w)?}} @@ -144,8 +144,8 @@ public class Main { /// CHECK-NOT: mvn {{r\d+}}, #15 /// CHECK-NOT: and{{(\.w)?}} /// CHECK-NOT: bic{{(\.w)?}} - /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #15 - /// CHECK-DAG: bic {{r\d+}}, {{r\d+}}, #15 + /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #0xf + /// CHECK-DAG: bic {{r\d+}}, {{r\d+}}, #0xf /// CHECK-NOT: and{{(\.w)?}} /// CHECK-NOT: bic{{(\.w)?}} @@ -157,7 +157,7 @@ public class Main { /// CHECK-NOT: movs {{r\d+}}, #255 /// CHECK-NOT: orr{{(\.w)?}} /// CHECK-NOT: orn - /// CHECK: orr {{r\d+}}, {{r\d+}}, #255 + /// CHECK: orr {{r\d+}}, {{r\d+}}, #0xff /// CHECK-NOT: orr{{(\.w)?}} /// CHECK-NOT: orn @@ -183,7 +183,7 @@ public class Main { /// CHECK-NOT: mvn {{r\d+}}, #15 /// CHECK-NOT: orr{{(\.w)?}} /// CHECK-NOT: orn - /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #15 + /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #0xf /// CHECK-DAG: mvn {{r\d+}}, #0 /// CHECK-NOT: orr{{(\.w)?}} /// CHECK-NOT: orn @@ -197,8 +197,8 @@ public class Main { /// CHECK-NOT: mvn {{r\d+}}, #15 /// CHECK-NOT: orr{{(\.w)?}} /// CHECK-NOT: orn - /// CHECK-DAG: orr {{r\d+}}, {{r\d+}}, #15 - /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #15 + /// CHECK-DAG: orr {{r\d+}}, {{r\d+}}, #0xf + /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #0xf /// CHECK-NOT: orr{{(\.w)?}} /// CHECK-NOT: orn @@ -209,7 +209,7 @@ public class Main { /// CHECK-START-ARM: long Main.xor255(long) disassembly (after) /// CHECK-NOT: movs {{r\d+}}, #255 /// CHECK-NOT: eor{{(\.w)?}} - /// CHECK: eor {{r\d+}}, {{r\d+}}, #255 + /// CHECK: eor {{r\d+}}, {{r\d+}}, #0xff /// CHECK-NOT: eor{{(\.w)?}} public static long xor255(long arg) { @@ -257,8 +257,8 @@ public class Main { /// CHECK-NOT: movs {{r\d+}}, #15 /// CHECK-NOT: mov.w {{r\d+}}, #-268435456 /// CHECK-NOT: eor{{(\.w)?}} - /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #15 - /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #4026531840 + /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #0xf + /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #0xf0000000 /// CHECK-NOT: eor{{(\.w)?}} public static long xor0xf00000000000000f(long arg) { diff --git a/test/586-checker-null-array-get/src/Main.java b/test/586-checker-null-array-get/src/Main.java index e0782bc84d..0ea7d34043 100644 --- a/test/586-checker-null-array-get/src/Main.java +++ b/test/586-checker-null-array-get/src/Main.java @@ -100,7 +100,7 @@ public class Main { /// CHECK-DAG: Return [<<ArrayGet2>>] public static float test1() { Test1 test1 = getNullTest1(); - Test2 test2 = getNullTest2();; + Test2 test2 = getNullTest2(); int[] iarr = test1.iarr; float[] farr = test2.farr; iarr[0] = iarr[1]; diff --git a/test/611-checker-simplify-if/src/Main.java b/test/611-checker-simplify-if/src/Main.java index 774f239df3..c1d75ec357 100644 --- a/test/611-checker-simplify-if/src/Main.java +++ b/test/611-checker-simplify-if/src/Main.java @@ -144,7 +144,7 @@ public class Main { /// CHECK-NOT: GreaterThanOrEqual /// CHECK-NOT: If public static void testGreaterCondition(String[] args) { - int a = 42;; + int a = 42; if (args.length == 42) { a = 34; } else { diff --git a/test/625-checker-licm-regressions/src/Main.java b/test/625-checker-licm-regressions/src/Main.java index cc1e07cabf..f372b1c7b5 100644 --- a/test/625-checker-licm-regressions/src/Main.java +++ b/test/625-checker-licm-regressions/src/Main.java @@ -47,14 +47,83 @@ public class Main { } while (j < arr.length); } + // + // Similar situation as in foo(), but now a proper induction value + // is assigned to the field inside the do-while loop. + // + /// CHECK-START: void Main.bar(int[]) licm (before) + /// CHECK-DAG: LoadClass loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: StaticFieldSet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: NullCheck loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayLength loop:<<Loop>> outer_loop:none + // + /// CHECK-START: void Main.bar(int[]) licm (after) + /// CHECK-DAG: LoadClass loop:none + /// CHECK-DAG: StaticFieldSet loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: NullCheck loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayLength loop:<<Loop>> outer_loop:none + // + /// CHECK-START: void Main.bar(int[]) licm (after) + /// CHECK-NOT: LoadClass loop:{{B\d+}} outer_loop:none + static void bar(int[] arr) { + int j = 0; + do { + j++; + sA = j; + } while (j < arr.length); + } + + // + // Similar situation as in bar(), but now an explicit catch + // statement may need the latest value of local j. + // + /// CHECK-START: int Main.catcher(int[]) licm (before) + /// CHECK-DAG: NullCheck loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArrayLength loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.catcher(int[]) licm (after) + /// CHECK-DAG: NullCheck loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArrayLength loop:<<Loop>> outer_loop:none + static int catcher(int[] arr) { + int j = 0; + try { + do { + j++; + } while (j < arr.length); + } catch (NullPointerException e) { + return -j; // flag exception with negative value + } + return j; + } + public static void main(String[] args) { sA = 0; try { foo(null); - } catch (Exception e) { + throw new Error("Expected NPE"); + } catch (NullPointerException e) { } expectEquals(1, sA); + sA = 0; + try { + bar(null); + throw new Error("Expected NPE"); + } catch (NullPointerException e) { + } + expectEquals(1, sA); + + for (int i = 0; i < 5; i++) { + sA = 0; + bar(new int[i]); + expectEquals(i == 0 ? 1 : i, sA); + } + + expectEquals(-1, catcher(null)); + for (int i = 0; i < 5; i++) { + expectEquals(i == 0 ? 1 : i, catcher(new int[i])); + } + System.out.println("passed"); } diff --git a/test/626-set-resolved-string/expected.txt b/test/626-set-resolved-string/expected.txt new file mode 100644 index 0000000000..f4983b5870 --- /dev/null +++ b/test/626-set-resolved-string/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +foo diff --git a/test/626-set-resolved-string/info.txt b/test/626-set-resolved-string/info.txt new file mode 100644 index 0000000000..e3a512fd23 --- /dev/null +++ b/test/626-set-resolved-string/info.txt @@ -0,0 +1,3 @@ +Test that even if Java code calls DexCache.setResolvedString and does +not strongly intern the given string, the JIT will ensure that the +strings it references are strongly interned. diff --git a/test/626-set-resolved-string/src/Main.java b/test/626-set-resolved-string/src/Main.java new file mode 100644 index 0000000000..868b9d1e40 --- /dev/null +++ b/test/626-set-resolved-string/src/Main.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + public static void main(String[] args) { + System.loadLibrary(args[0]); + + // Get all methods. We cannot call getDeclaredMethod("foo") as + // that would make "foo" a strong root. + Method[] methods = Main.class.getDeclaredMethods(); + + // Call getName on the methods, which is implemented by using the dex + // cache and calling setResolvedString. + for (int i = 0; i < methods.length; i++) { + methods[i].getName(); + } + + // Compile Main.foo. "foo" needs to be a strong root for JIT compilation. + // We stress test this: + // - avoid strongly interning "foo" by doing "f" + "oo" + // - call GC so that weaks can be collected. + // - invoke foo() to make sure "foo" hasn't been collected. + ensureJitCompiled(Main.class, "f" + "oo"); + Runtime.getRuntime().gc(); + foo(); + } + + public static void foo() { + System.out.println("foo"); + } + + public static native void ensureJitCompiled(Class cls, String method_name); +} diff --git a/test/628-vdex/expected.txt b/test/628-vdex/expected.txt new file mode 100644 index 0000000000..d0f61f692c --- /dev/null +++ b/test/628-vdex/expected.txt @@ -0,0 +1,2 @@ +In foo +In foo diff --git a/test/628-vdex/info.txt b/test/628-vdex/info.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/628-vdex/info.txt diff --git a/test/103-string-append/run b/test/628-vdex/run index e27a622f40..f1b0a95f64 100755..100644 --- a/test/103-string-append/run +++ b/test/628-vdex/run @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (C) 2012 The Android Open Source Project +# Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,5 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# As this is a performance test we always use the non-debug build. -exec ${RUN} "${@/#libartd.so/libart.so}" +exec ${RUN} --vdex "${@}" diff --git a/test/628-vdex/src/Main.java b/test/628-vdex/src/Main.java new file mode 100644 index 0000000000..7ceab2c952 --- /dev/null +++ b/test/628-vdex/src/Main.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + Main() { + // Will be quickened with RETURN_VOID_NO_BARRIER. + } + + public static void main(String[] args) { + Main m = new Main(); + Object o = m; + // The call and field accesses will be quickened. + m.foo(m.a); + + // The checkcast will be quickened. + m.foo(((Main)o).a); + } + + int a; + void foo(int a) { + System.out.println("In foo"); + } +} + diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc index 8f0850b9fa..3ed91d7a17 100644 --- a/test/910-methods/methods.cc +++ b/test/910-methods/methods.cc @@ -66,6 +66,15 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getMethodName( jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen)); } + // Also run GetMethodName with all parameter pointers null to check for segfaults. + jvmtiError result2 = jvmti_env->GetMethodName(id, nullptr, nullptr, nullptr); + if (result2 != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(result2, &err); + printf("Failure running GetMethodName(null, null, null): %s\n", err); + return nullptr; + } + return ret; } diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index d1ddbae8a9..8002cfa8c5 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -45,7 +45,7 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] -root@root --(jni-local)--> 1@1000 [size=16, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] root@root --(stack-local)--> 1@1000 [size=16, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] root@root --(thread)--> 3000@0 [size=132, length=-1] @@ -67,7 +67,7 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] -root@root --(jni-local)--> 1@1000 [size=16, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] root@root --(stack-local)--> 1@1000 [size=16, length=-1] root@root --(stack-local)--> 2@1000 [size=16, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index 0c627d6af8..340671da19 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -185,82 +185,248 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_followReferences(JNIEnv* env } } - lines_.push_back( - StringPrintf("%s --(%s)--> %" PRId64 "@%" PRId64 " [size=%" PRId64 ", length=%d]", - referrer_str.c_str(), - GetReferenceTypeStr(reference_kind, reference_info).c_str(), - *tag_ptr, - class_tag, - adapted_size, - length)); + std::string referree_str = StringPrintf("%" PRId64 "@%" PRId64, *tag_ptr, class_tag); + + lines_.push_back(CreateElem(referrer_str, + referree_str, + reference_kind, + reference_info, + adapted_size, + length)); if (reference_kind == JVMTI_HEAP_REFERENCE_THREAD && *tag_ptr == 1000) { DumpStacks(); } } - static void DumpStacks() NO_THREAD_SAFETY_ANALYSIS { - auto dump_function = [](art::Thread* t, void* data ATTRIBUTE_UNUSED) { - std::string name; - t->GetThreadName(name); - LOG(ERROR) << name; - art::DumpNativeStack(LOG_STREAM(ERROR), t->GetTid()); - }; - art::Runtime::Current()->GetThreadList()->ForEach(dump_function, nullptr); + std::vector<std::string> GetLines() const { + std::vector<std::string> ret; + for (const std::unique_ptr<Elem>& e : lines_) { + ret.push_back(e->Print()); + } + return ret; } - static std::string GetReferenceTypeStr(jvmtiHeapReferenceKind reference_kind, - const jvmtiHeapReferenceInfo* reference_info) { + private: + // We need to postpone some printing, as required functions are not callback-safe. + class Elem { + public: + Elem(const std::string& referrer, const std::string& referree, jlong size, jint length) + : referrer_(referrer), referree_(referree), size_(size), length_(length) {} + virtual ~Elem() {} + + std::string Print() const { + return StringPrintf("%s --(%s)--> %s [size=%" PRId64 ", length=%d]", + referrer_.c_str(), + PrintArrowType().c_str(), + referree_.c_str(), + size_, + length_); + } + + protected: + virtual std::string PrintArrowType() const = 0; + + private: + std::string referrer_; + std::string referree_; + jlong size_; + jint length_; + }; + + class JNILocalElement : public Elem { + public: + JNILocalElement(const std::string& referrer, + const std::string& referree, + jlong size, + jint length, + const jvmtiHeapReferenceInfo* reference_info) + : Elem(referrer, referree, size, length) { + memcpy(&info_, reference_info, sizeof(jvmtiHeapReferenceInfo)); + } + + protected: + std::string PrintArrowType() const OVERRIDE { + char* name = nullptr; + if (info_.jni_local.method != nullptr) { + jvmti_env->GetMethodName(info_.jni_local.method, &name, nullptr, nullptr); + } + std::string ret = StringPrintf("jni-local[id=%" PRId64 ",tag=%" PRId64 ",depth=%d," + "method=%s]", + info_.jni_local.thread_id, + info_.jni_local.thread_tag, + info_.jni_local.depth, + name == nullptr ? "<null>" : name); + if (name != nullptr) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name)); + } + + return ret; + } + + private: + const std::string string_; + jvmtiHeapReferenceInfo info_; + }; + + // For simple or unimplemented cases. + class StringElement : public Elem { + public: + StringElement(const std::string& referrer, + const std::string& referree, + jlong size, + jint length, + const std::string& string) + : Elem(referrer, referree, size, length), string_(string) {} + + protected: + std::string PrintArrowType() const OVERRIDE { + return string_; + } + + private: + const std::string string_; + }; + + static std::unique_ptr<Elem> CreateElem(const std::string& referrer, + const std::string& referree, + jvmtiHeapReferenceKind reference_kind, + const jvmtiHeapReferenceInfo* reference_info, + jlong size, + jint length) { switch (reference_kind) { case JVMTI_HEAP_REFERENCE_CLASS: - return "class"; - case JVMTI_HEAP_REFERENCE_FIELD: - return StringPrintf("field@%d", reference_info->field.index); - case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT: - return StringPrintf("array-element@%d", reference_info->array.index); + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "class")); + case JVMTI_HEAP_REFERENCE_FIELD: { + std::string tmp = StringPrintf("field@%d", reference_info->field.index); + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + tmp)); + } + case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT: { + std::string tmp = StringPrintf("array-element@%d", reference_info->array.index); + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + tmp)); + } case JVMTI_HEAP_REFERENCE_CLASS_LOADER: - return "classloader"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "classloader")); case JVMTI_HEAP_REFERENCE_SIGNERS: - return "signers"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "signers")); case JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN: - return "protection-domain"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "protection-domain")); case JVMTI_HEAP_REFERENCE_INTERFACE: - return "interface"; - case JVMTI_HEAP_REFERENCE_STATIC_FIELD: - return StringPrintf("static-field@%d", reference_info->field.index); + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "interface")); + case JVMTI_HEAP_REFERENCE_STATIC_FIELD: { + std::string tmp = StringPrintf("array-element@%d", reference_info->array.index); + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + tmp));; + } case JVMTI_HEAP_REFERENCE_CONSTANT_POOL: - return "constant-pool"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "constant-pool")); case JVMTI_HEAP_REFERENCE_SUPERCLASS: - return "superclass"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "superclass")); case JVMTI_HEAP_REFERENCE_JNI_GLOBAL: - return "jni-global"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "jni-global")); case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS: - return "system-class"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "system-class")); case JVMTI_HEAP_REFERENCE_MONITOR: - return "monitor"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "monitor")); case JVMTI_HEAP_REFERENCE_STACK_LOCAL: - return "stack-local"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "stack-local")); case JVMTI_HEAP_REFERENCE_JNI_LOCAL: - return "jni-local"; + return std::unique_ptr<Elem>(new JNILocalElement(referrer, + referree, + size, + length, + reference_info)); case JVMTI_HEAP_REFERENCE_THREAD: - return "thread"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "thread")); case JVMTI_HEAP_REFERENCE_OTHER: - return "other"; + return std::unique_ptr<Elem>(new StringElement(referrer, + referree, + size, + length, + "other")); } - return "unknown"; + LOG(FATAL) << "Unknown kind"; + UNREACHABLE(); } - const std::vector<std::string>& GetLines() const { - return lines_; + static void DumpStacks() NO_THREAD_SAFETY_ANALYSIS { + auto dump_function = [](art::Thread* t, void* data ATTRIBUTE_UNUSED) { + std::string name; + t->GetThreadName(name); + LOG(ERROR) << name; + art::DumpNativeStack(LOG_STREAM(ERROR), t->GetTid()); + }; + art::Runtime::Current()->GetThreadList()->ForEach(dump_function, nullptr); } - private: jint counter_; const jint stop_after_; const jint follow_set_; - std::vector<std::string> lines_; + + std::vector<std::unique_ptr<Elem>> lines_; }; + jit::ScopedJitSuspend sjs; // Wait to avoid JIT influence (e.g., JNI globals). + // If jniRef isn't null, add a local and a global ref. ScopedLocalRef<jobject> jni_local_ref(env, nullptr); jobject jni_global_ref = nullptr; @@ -272,7 +438,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_followReferences(JNIEnv* env PrintIterationConfig config(stop_after, follow_set); Run(heap_filter, klass_filter, initial_object, &config); - const std::vector<std::string>& lines = config.GetLines(); + std::vector<std::string> lines = config.GetLines(); jobjectArray ret = CreateObjectArray(env, static_cast<jint>(lines.size()), "java/lang/String", @@ -299,12 +465,5 @@ jint OnLoad(JavaVM* vm, return 0; } -extern "C" JNIEXPORT void JNICALL Java_Main_waitForJitCompilation(JNIEnv*, jclass) { - jit::Jit* jit = Runtime::Current()->GetJit(); - if (jit != nullptr) { - jit->WaitForCompilationToFinish(Thread::Current()); - } -} - } // namespace Test913Heaps } // namespace art diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java index fc00ada916..a6ace9aeba 100644 --- a/test/913-heaps/src/Main.java +++ b/test/913-heaps/src/Main.java @@ -101,8 +101,6 @@ public class Main { private static void doFollowReferencesTestImpl(A root, int stopAfter, int followSet, Object asRoot, Verifier v, String additionalEnabled) { - waitForJitCompilation(); // Wait to avoid JIT influence (e.g., JNI globals). - String[] lines = followReferences(0, null, root, stopAfter, followSet, asRoot); @@ -388,6 +386,4 @@ public class Main { private static native String[] followReferences(int heapFilter, Class<?> klassFilter, Object initialObject, int stopAfter, int followSet, Object jniRef); - - private static native void waitForJitCompilation(); } diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt index 0a5caa157e..9b0932708e 100644 --- a/test/956-methodhandles/expected.txt +++ b/test/956-methodhandles/expected.txt @@ -7,3 +7,11 @@ Received exception: Expected (java.lang.String, java.lang.String)java.lang.Strin String constructors done. testReferenceReturnValueConversions done. testPrimitiveReturnValueConversions done. +Hi +Hi +Hi +Hi +Expect Hi here: Hi +Don't expect Hi now +[3, 2, 1] +[1, 2, 3] diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index aab9f50be7..ee9c43604a 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -19,16 +19,20 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.invoke.WrongMethodTypeException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; - import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class Main { public static class A { + public A() {} + public void foo() { System.out.println("foo_A"); } @@ -65,11 +69,13 @@ public class Main { testfindSpecial_invokeDirectBehaviour(); testExceptionDetailMessages(); testfindVirtual(); + testfindStatic(); testUnreflects(); testAsType(); testConstructors(); testStringConstructors(); testReturnValueConversions(); + testVariableArity(); } public static void testfindSpecial_invokeSuperBehaviour() throws Throwable { @@ -116,6 +122,19 @@ public class Main { System.out.println("findSpecial(A.class, foo, .. D.class) unexpectedly succeeded."); } catch (IllegalAccessException expected) { } + + // Check return type matches for find. + try { + B.lookup.findSpecial(A.class /* refC */, "foo", + MethodType.methodType(int.class), B.class /* specialCaller */); + fail(); + } catch (NoSuchMethodException e) {} + // Check constructors + try { + B.lookup.findSpecial(A.class /* refC */, "<init>", + MethodType.methodType(void.class), B.class /* specialCaller */); + fail(); + } catch (NoSuchMethodException e) {} } public static void testfindSpecial_invokeDirectBehaviour() throws Throwable { @@ -189,9 +208,20 @@ public class Main { return "bar"; } + public String add(int x, int y) { + return Arrays.toString(new int[] { x, y }); + } + private String privateMethod() { return "privateMethod"; } - public static String staticMethod() { return null; } + public static String staticMethod() { return staticString; } + + private static String staticString; + + { + // Static constructor + staticString = Long.toString(System.currentTimeMillis()); + } static final MethodHandles.Lookup lookup = MethodHandles.lookup(); } @@ -232,6 +262,21 @@ public class Main { System.out.println("Unexpected return value for BarImpl#foo: " + str); } + // Find virtual should check rtype. + try { + mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException e) {} + + // And ptypes + mh = MethodHandles.lookup().findVirtual( + BarImpl.class, "add", MethodType.methodType(String.class, int.class, int.class)); + try { + mh = MethodHandles.lookup().findVirtual( + BarImpl.class, "add", MethodType.methodType(String.class, Integer.class, int.class)); + } catch (NoSuchMethodException e) {} + // .. and their super-interfaces. mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar", MethodType.methodType(String.class)); @@ -272,6 +317,37 @@ public class Main { if (!"superPackageMethod".equals(str)) { System.out.println("Unexpected return value for BarImpl#superPackageMethod: " + str); } + + try { + MethodHandles.lookup().findVirtual(BarImpl.class, "<init>", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException e) {} + } + + public static void testfindStatic() throws Throwable { + MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod", + MethodType.methodType(String.class)); + try { + MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException e) {} + try { + MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod", + MethodType.methodType(String.class, int.class)); + fail(); + } catch (NoSuchMethodException e) {} + try { + MethodHandles.lookup().findStatic(BarImpl.class, "<clinit>", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException e) {} + try { + MethodHandles.lookup().findStatic(BarImpl.class, "<init>", + MethodType.methodType(void.class)); + fail(); + } catch (NoSuchMethodException e) {} } static class UnreflectTester { @@ -481,6 +557,34 @@ public class Main { } } + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("assertTrue value: " + value); + } + } + + public static void assertFalse(boolean value) { + if (value) { + throw new AssertionError("assertTrue value: " + value); + } + } + + public static void assertEquals(int i1, int i2) { + if (i1 == i2) { return; } + throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2); + } + + public static void assertEquals(long i1, long i2) { + if (i1 == i2) { return; } + throw new AssertionError("assertEquals l1: " + i1 + ", l2: " + i2); + } + + public static void assertEquals(Object o, Object p) { + if (o == p) { return; } + if (o != null && p != null && o.equals(p)) { return; } + throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); + } + public static void assertEquals(String s1, String s2) { if (s1 == s2) { return; @@ -886,4 +990,480 @@ public class Main { testReferenceReturnValueConversions(); testPrimitiveReturnValueConversions(); } + + public static class BaseVariableArityTester { + public String update(Float f0, Float... floats) { + return "base " + f0 + ", " + Arrays.toString(floats); + } + } + + public static class VariableArityTester extends BaseVariableArityTester { + private String lastResult; + + // Constructors + public VariableArityTester() {} + public VariableArityTester(boolean... booleans) { update(booleans); } + public VariableArityTester(byte... bytes) { update(bytes); } + public VariableArityTester(char... chars) { update(chars); } + public VariableArityTester(short... shorts) { update(shorts); } + public VariableArityTester(int... ints) { update(ints); } + public VariableArityTester(long... longs) { update(longs); } + public VariableArityTester(float... floats) { update(floats); } + public VariableArityTester(double... doubles) { update(doubles); } + public VariableArityTester(Float f0, Float... floats) { update(f0, floats); } + public VariableArityTester(String s0, String... strings) { update(s0, strings); } + public VariableArityTester(char c, Number... numbers) { update(c, numbers); } + @SafeVarargs + public VariableArityTester(ArrayList<Integer> l0, ArrayList<Integer>... lists) { + update(l0, lists); + } + public VariableArityTester(List l0, List... lists) { update(l0, lists); } + + // Methods + public String update(boolean... booleans) { return lastResult = tally(booleans); } + public String update(byte... bytes) { return lastResult = tally(bytes); } + public String update(char... chars) { return lastResult = tally(chars); } + public String update(short... shorts) { return lastResult = tally(shorts); } + public String update(int... ints) { + lastResult = tally(ints); + return lastResult; + } + public String update(long... longs) { return lastResult = tally(longs); } + public String update(float... floats) { return lastResult = tally(floats); } + public String update(double... doubles) { return lastResult = tally(doubles); } + @Override + public String update(Float f0, Float... floats) { return lastResult = tally(f0, floats); } + public String update(String s0, String... strings) { return lastResult = tally(s0, strings); } + public String update(char c, Number... numbers) { return lastResult = tally(c, numbers); } + @SafeVarargs + public final String update(ArrayList<Integer> l0, ArrayList<Integer>... lists) { + lastResult = tally(l0, lists); + return lastResult; + } + public String update(List l0, List... lists) { return lastResult = tally(l0, lists); } + + public String arrayMethod(Object[] o) { + return Arrays.deepToString(o); + } + + public String lastResult() { return lastResult; } + + // Static Methods + public static String tally(boolean... booleans) { return Arrays.toString(booleans); } + public static String tally(byte... bytes) { return Arrays.toString(bytes); } + public static String tally(char... chars) { return Arrays.toString(chars); } + public static String tally(short... shorts) { return Arrays.toString(shorts); } + public static String tally(int... ints) { return Arrays.toString(ints); } + public static String tally(long... longs) { return Arrays.toString(longs); } + public static String tally(float... floats) { return Arrays.toString(floats); } + public static String tally(double... doubles) { return Arrays.toString(doubles); } + public static String tally(Float f0, Float... floats) { + return f0 + ", " + Arrays.toString(floats); + } + public static String tally(String s0, String... strings) { + return s0 + ", " + Arrays.toString(strings); + } + public static String tally(char c, Number... numbers) { + return c + ", " + Arrays.toString(numbers); + } + @SafeVarargs + public static String tally(ArrayList<Integer> l0, ArrayList<Integer>... lists) { + return Arrays.toString(l0.toArray()) + ", " + Arrays.deepToString(lists); + } + public static String tally(List l0, List... lists) { + return Arrays.deepToString(l0.toArray()) + ", " + Arrays.deepToString(lists); + } + public static void foo(int... ints) { System.out.println(Arrays.toString(ints)); } + public static long sumToPrimitive(int... ints) { + long result = 0; + for (int i : ints) result += i; + return result; + } + public static Long sumToReference(int... ints) { + System.err.println("Hi"); + return new Long(sumToPrimitive(ints)); + } + public static MethodHandles.Lookup lookup() { + return MethodHandles.lookup(); + } + } + + // This method only exists to fool Jack's handling of types. See b/32536744. + public static Object getAsObject(String[] strings) { + return (Object) strings; + } + + public static void testVariableArity() throws Throwable { + MethodHandle mh; + VariableArityTester vat = new VariableArityTester(); + + assertEquals("[1]", vat.update(1)); + assertEquals("[1, 1]", vat.update(1, 1)); + assertEquals("[1, 1, 1]", vat.update(1, 1, 1)); + + // Methods - boolean + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, boolean[].class)); + assertTrue(mh.isVarargsCollector()); + assertFalse(mh.asFixedArity().isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[true, false, true]", mh.invoke(vat, true, false, true)); + assertEquals("[true, false, true]", mh.invoke(vat, new boolean[] { true, false, true})); + assertEquals("[false, true]", mh.invoke(vat, Boolean.valueOf(false), Boolean.valueOf(true))); + try { + mh.invoke(vat, true, true, 0); + fail(); + } catch (WrongMethodTypeException e) {} + try { + assertEquals("[false, true]", mh.invoke(vat, Boolean.valueOf(false), (Boolean) null)); + fail(); + } catch (NullPointerException e) {} + + // Methods - byte + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, byte[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[32, 64, 97]", mh.invoke(vat, (byte) 32, Byte.valueOf((byte) 64), (byte) 97)); + assertEquals("[32, 64, 97]", mh.invoke(vat, new byte[] {(byte) 32, (byte) 64, (byte) 97})); + try { + mh.invoke(vat, (byte) 1, Integer.valueOf(3), (byte) 0); + fail(); + } catch (WrongMethodTypeException e) {} + + // Methods - char + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, char[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[A, B, C]", mh.invoke(vat, 'A', Character.valueOf('B'), 'C')); + assertEquals("[W, X, Y, Z]", mh.invoke(vat, new char[] { 'W', 'X', 'Y', 'Z' })); + + // Methods - short + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, short[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[32767, -32768, 0]", + mh.invoke(vat, Short.MAX_VALUE, Short.MIN_VALUE, Short.valueOf((short) 0))); + assertEquals("[1, -1]", mh.invoke(vat, new short[] { (short) 1, (short) -1 })); + + // Methods - int + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, int[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[0, 2147483647, -2147483648, 0]", + mh.invoke(vat, Integer.valueOf(0), Integer.MAX_VALUE, Integer.MIN_VALUE, 0)); + assertEquals("[0, -1, 1, 0]", mh.invoke(vat, new int[] { 0, -1, 1, 0 })); + + assertEquals("[5, 4, 3, 2, 1]", (String) mh.invokeExact(vat, new int [] { 5, 4, 3, 2, 1 })); + try { + assertEquals("[5, 4, 3, 2, 1]", (String) mh.invokeExact(vat, 5, 4, 3, 2, 1)); + fail(); + } catch (WrongMethodTypeException e) {} + assertEquals("[5, 4, 3, 2, 1]", (String) mh.invoke(vat, 5, 4, 3, 2, 1)); + + // Methods - long + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, long[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[0, 9223372036854775807, -9223372036854775808]", + mh.invoke(vat, Long.valueOf(0), Long.MAX_VALUE, Long.MIN_VALUE)); + assertEquals("[0, -1, 1, 0]", mh.invoke(vat, new long[] { 0, -1, 1, 0 })); + + // Methods - float + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, float[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[0.0, 1.25, -1.25]", + mh.invoke(vat, 0.0f, Float.valueOf(1.25f), Float.valueOf(-1.25f))); + assertEquals("[0.0, -1.0, 1.0, 0.0]", + mh.invoke(vat, new float[] { 0.0f, -1.0f, 1.0f, 0.0f })); + + // Methods - double + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, double[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke(vat)); + assertEquals("[0.0, 1.25, -1.25]", + mh.invoke(vat, 0.0, Double.valueOf(1.25), Double.valueOf(-1.25))); + assertEquals("[0.0, -1.0, 1.0, 0.0]", + mh.invoke(vat, new double[] { 0.0, -1.0, 1.0, 0.0 })); + mh.invoke(vat, 0.3f, 1.33, 1.33); + + // Methods - String + mh = MethodHandles.lookup(). + findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, String.class, String[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("Echidna, []", mh.invoke(vat, "Echidna")); + assertEquals("Bongo, [Jerboa, Okapi]", + mh.invoke(vat, "Bongo", "Jerboa", "Okapi")); + + // Methods - Float + mh = MethodHandles.lookup(). + findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, Float.class, Float[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, Float.valueOf(9.99f), + new Float[] { Float.valueOf(0.0f), + Float.valueOf(0.1f), + Float.valueOf(1.1f) })); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, Float.valueOf(9.99f), Float.valueOf(0.0f), + Float.valueOf(0.1f), Float.valueOf(1.1f))); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f)); + try { + assertEquals("9.99, [77.0, 33.0, 64.0]", + (String) mh.invoke(vat, Float.valueOf(9.99f), 77, 33, 64)); + fail(); + } catch (WrongMethodTypeException e) {} + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invokeExact(vat, Float.valueOf(9.99f), + new Float[] { Float.valueOf(0.0f), + Float.valueOf(0.1f), + Float.valueOf(1.1f) })); + assertEquals("9.99, [0.0, null, 1.1]", + (String) mh.invokeExact(vat, Float.valueOf(9.99f), + new Float[] { Float.valueOf(0.0f), + null, + Float.valueOf(1.1f) })); + try { + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invokeExact(vat, Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f)); + fail(); + } catch (WrongMethodTypeException e) {} + + // Methods - Number + mh = MethodHandles.lookup(). + findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, char.class, Number[].class)); + assertTrue(mh.isVarargsCollector()); + assertFalse(mh.asFixedArity().isVarargsCollector()); + assertEquals("x, []", (String) mh.invoke(vat, 'x')); + assertEquals("x, [3.141]", (String) mh.invoke(vat, 'x', 3.141)); + assertEquals("x, [null, 3.131, 37]", + (String) mh.invoke(vat, 'x', null, 3.131, new Integer(37))); + try { + assertEquals("x, [null, 3.131, bad, 37]", + (String) mh.invoke(vat, 'x', null, 3.131, "bad", new Integer(37))); + assertTrue(false); + fail(); + } catch (ClassCastException e) {} + try { + assertEquals("x, [null, 3.131, bad, 37]", + (String) mh.invoke( + vat, 'x', (Process) null, 3.131, "bad", new Integer(37))); + assertTrue(false); + fail(); + } catch (ClassCastException e) {} + + // Methods - an array method that is not variable arity. + mh = MethodHandles.lookup().findVirtual( + VariableArityTester.class, "arrayMethod", + MethodType.methodType(String.class, Object[].class)); + assertFalse(mh.isVarargsCollector()); + mh.invoke(vat, new Object[] { "123" }); + try { + assertEquals("-", mh.invoke(vat, new Float(3), new Float(4))); + fail(); + } catch (WrongMethodTypeException e) {} + mh = mh.asVarargsCollector(Object[].class); + assertTrue(mh.isVarargsCollector()); + assertEquals("[3.0, 4.0]", (String) mh.invoke(vat, new Float(3), new Float(4))); + + // Constructors - default + mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, MethodType.methodType(void.class)); + assertFalse(mh.isVarargsCollector()); + + // Constructors - boolean + mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, MethodType.methodType(void.class, boolean[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[true, true, false]", + ((VariableArityTester) mh.invoke(new boolean[] {true, true, false})).lastResult()); + assertEquals("[true, true, false]", + ((VariableArityTester) mh.invoke(true, true, false)).lastResult()); + try { + assertEquals("[true, true, false]", + ((VariableArityTester) mh.invokeExact(true, true, false)).lastResult()); + fail(); + } catch (WrongMethodTypeException e) {} + + // Constructors - byte + mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, MethodType.methodType(void.class, byte[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("[55, 66, 60]", + ((VariableArityTester) + mh.invoke(new byte[] {(byte) 55, (byte) 66, (byte) 60})).lastResult()); + assertEquals("[55, 66, 60]", + ((VariableArityTester) mh.invoke( + (byte) 55, (byte) 66, (byte) 60)).lastResult()); + try { + assertEquals("[55, 66, 60]", + ((VariableArityTester) mh.invokeExact( + (byte) 55, (byte) 66, (byte) 60)).lastResult()); + fail(); + } catch (WrongMethodTypeException e) {} + try { + assertEquals("[3, 3]", + ((VariableArityTester) mh.invoke( + new Number[] { Byte.valueOf((byte) 3), (byte) 3})).lastResult()); + fail(); + } catch (WrongMethodTypeException e) {} + + // Constructors - String (have a different path than other reference types). + mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, MethodType.methodType(void.class, String.class, String[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("x, []", ((VariableArityTester) mh.invoke("x")).lastResult()); + assertEquals("x, [y]", ((VariableArityTester) mh.invoke("x", "y")).lastResult()); + assertEquals("x, [y, z]", + ((VariableArityTester) mh.invoke("x", new String[] { "y", "z" })).lastResult()); + try { + assertEquals("x, [y]", ((VariableArityTester) mh.invokeExact("x", "y")).lastResult()); + fail(); + } catch (WrongMethodTypeException e) {} + assertEquals("x, [null, z]", + ((VariableArityTester) mh.invoke("x", new String[] { null, "z" })).lastResult()); + + // Constructors - Number + mh = MethodHandles.lookup().findConstructor( + VariableArityTester.class, MethodType.methodType(void.class, char.class, Number[].class)); + assertTrue(mh.isVarargsCollector()); + assertFalse(mh.asFixedArity().isVarargsCollector()); + assertEquals("x, []", ((VariableArityTester) mh.invoke('x')).lastResult()); + assertEquals("x, [3.141]", ((VariableArityTester) mh.invoke('x', 3.141)).lastResult()); + assertEquals("x, [null, 3.131, 37]", + ((VariableArityTester) mh.invoke('x', null, 3.131, new Integer(37))).lastResult()); + try { + assertEquals("x, [null, 3.131, bad, 37]", + ((VariableArityTester) mh.invoke( + 'x', null, 3.131, "bad", new Integer(37))).lastResult()); + assertTrue(false); + fail(); + } catch (ClassCastException e) {} + try { + assertEquals("x, [null, 3.131, bad, 37]", + ((VariableArityTester) mh.invoke( + 'x', (Process) null, 3.131, "bad", new Integer(37))).lastResult()); + assertTrue(false); + fail(); + } catch (ClassCastException e) {} + + // Static Methods - Float + mh = MethodHandles.lookup(). + findStatic(VariableArityTester.class, "tally", + MethodType.methodType(String.class, Float.class, Float[].class)); + assertTrue(mh.isVarargsCollector()); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(Float.valueOf(9.99f), + new Float[] { Float.valueOf(0.0f), + Float.valueOf(0.1f), + Float.valueOf(1.1f) })); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(Float.valueOf(9.99f), Float.valueOf(0.0f), + Float.valueOf(0.1f), Float.valueOf(1.1f))); + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f)); + try { + assertEquals("9.99, [77.0, 33.0, 64.0]", + (String) mh.invoke(Float.valueOf(9.99f), 77, 33, 64)); + fail(); + } catch (WrongMethodTypeException e) {} + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invokeExact(Float.valueOf(9.99f), + new Float[] { Float.valueOf(0.0f), + Float.valueOf(0.1f), + Float.valueOf(1.1f) })); + assertEquals("9.99, [0.0, null, 1.1]", + (String) mh.invokeExact(Float.valueOf(9.99f), + new Float[] { Float.valueOf(0.0f), + null, + Float.valueOf(1.1f) })); + try { + assertEquals("9.99, [0.0, 0.1, 1.1]", + (String) mh.invokeExact(Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f)); + fail(); + } catch (WrongMethodTypeException e) {} + + // Special methods - Float + mh = VariableArityTester.lookup(). + findSpecial(BaseVariableArityTester.class, "update", + MethodType.methodType(String.class, Float.class, Float[].class), + VariableArityTester.class); + assertTrue(mh.isVarargsCollector()); + assertEquals("base 9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, + Float.valueOf(9.99f), + new Float[] { Float.valueOf(0.0f), + Float.valueOf(0.1f), + Float.valueOf(1.1f) })); + assertEquals("base 9.99, [0.0, 0.1, 1.1]", + (String) mh.invoke(vat, Float.valueOf(9.99f), Float.valueOf(0.0f), + Float.valueOf(0.1f), Float.valueOf(1.1f))); + + // Return value conversions. + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, int[].class)); + assertEquals("[1, 2, 3]", (String) mh.invoke(vat, 1, 2, 3)); + assertEquals("[1, 2, 3]", (Object) mh.invoke(vat, 1, 2, 3)); + try { + assertEquals("[1, 2, 3, 4]", (long) mh.invoke(vat, 1, 2, 3)); + fail(); + } catch (WrongMethodTypeException e) {} + assertEquals("[1, 2, 3]", vat.lastResult()); + mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "sumToPrimitive", + MethodType.methodType(long.class, int[].class)); + assertEquals(10l, (long) mh.invoke(1, 2, 3, 4)); + assertEquals(Long.valueOf(10l), (Long) mh.invoke(1, 2, 3, 4)); + mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "sumToReference", + MethodType.methodType(Long.class, int[].class)); + Object o = mh.invoke(1, 2, 3, 4); + long l = (long) mh.invoke(1, 2, 3, 4); + assertEquals(10l, (long) mh.invoke(1, 2, 3, 4)); + assertEquals(Long.valueOf(10l), (Long) mh.invoke(1, 2, 3, 4)); + try { + // WrongMethodTypeException should be raised before invoke here. + System.err.print("Expect Hi here: "); + assertEquals(Long.valueOf(10l), (Byte) mh.invoke(1, 2, 3, 4)); + fail(); + } catch (ClassCastException e) {} + try { + // WrongMethodTypeException should be raised before invoke here. + System.err.println("Don't expect Hi now"); + byte b = (byte) mh.invoke(1, 2, 3, 4); + fail(); + } catch (WrongMethodTypeException e) {} + + // Return void produces 0 / null. + mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "foo", + MethodType.methodType(void.class, int[].class)); + assertEquals(null, (Object) mh.invoke(3, 2, 1)); + assertEquals(0l, (long) mh.invoke(1, 2, 3)); + + // Combinators + mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update", + MethodType.methodType(String.class, boolean[].class)); + assertTrue(mh.isVarargsCollector()); + mh = mh.bindTo(vat); + assertFalse(mh.isVarargsCollector()); + mh = mh.asVarargsCollector(boolean[].class); + assertTrue(mh.isVarargsCollector()); + assertEquals("[]", mh.invoke()); + assertEquals("[true, false, true]", mh.invoke(true, false, true)); + assertEquals("[true, false, true]", mh.invoke(new boolean[] { true, false, true})); + assertEquals("[false, true]", mh.invoke(Boolean.valueOf(false), Boolean.valueOf(true))); + try { + mh.invoke(true, true, 0); + fail(); + } catch (WrongMethodTypeException e) {} + } } diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk index 845545c428..21b31b40b7 100644 --- a/test/Android.arm_vixl.mk +++ b/test/Android.arm_vixl.mk @@ -17,56 +17,35 @@ # Known broken tests for the ARM VIXL backend. TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 003-omnibus-opcodes \ - 004-ThreadStress \ - 028-array-write \ - 037-inherit \ + 020-string \ + 021-string2 \ 042-new-instance \ 044-proxy \ 080-oom-throw \ 082-inline-execute \ - 083-compiler-regressions \ 096-array-copy-concurrent-gc \ 099-vmdebug \ + 100-reflect2 \ 103-string-append \ 114-ParallelGC \ 122-npe \ - 123-inline-execute2 \ 129-ThreadGetId \ 137-cfi \ 144-static-field-sigquit \ - 201-built-in-except-detail-messages \ 412-new-array \ - 422-type-conversion \ - 437-inline \ 439-npe \ - 442-checker-constant-folding \ 450-checker-types \ - 458-checker-instruct-simplification \ - 458-long-to-fpu \ 488-checker-inline-recursive-calls \ - 510-checker-try-catch \ 515-dce-dominator \ 520-equivalent-phi \ 525-checker-arrays-fields1 \ 525-checker-arrays-fields2 \ 527-checker-array-access-split \ - 530-checker-loops2 \ - 530-checker-lse \ - 530-checker-lse2 \ - 535-regression-const-val \ - 536-checker-intrinsic-optimization \ 538-checker-embed-constants \ - 550-checker-multiply-accumulate \ - 552-checker-primitive-typeprop \ 552-checker-sharpening \ - 555-UnsafeGetLong-regression \ 562-checker-no-intermediate \ - 564-checker-negbitwise \ 570-checker-osr \ - 570-checker-select \ - 574-irreducible-and-constant-area \ - 580-checker-round \ - 594-checker-array-alias \ 602-deoptimizeable \ 700-LoadArgRegs \ 800-smali \ + diff --git a/test/Android.bp b/test/Android.bp index bdb7f80eb0..fe20f29015 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -266,10 +266,7 @@ art_cc_defaults { art_cc_test_library { name: "libtiagent", defaults: ["libtiagent-defaults"], - shared_libs: [ - "libart", - "libopenjdkjvmti", - ], + shared_libs: ["libart"], } art_cc_test_library { @@ -278,10 +275,7 @@ art_cc_test_library { "libtiagent-defaults", "art_debug_defaults", ], - shared_libs: [ - "libartd", - "libopenjdkjvmtid", - ], + shared_libs: ["libartd"], } cc_defaults { diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 60318a41d6..96b984d7e4 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -608,10 +608,7 @@ TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS := TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := # Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT). -# 484: Baker's fast path based read barrier compiler instrumentation generates code containing -# more parallel moves on x86, thus some Checker assertions may fail. -TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \ - 484-checker-register-hints +TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := # Tests that should fail in the read barrier configuration with JIT (Optimizing compiler). TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := @@ -791,6 +788,10 @@ ifdef TARGET_2ND_ARCH TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libnativebridgetest) endif +# Also need libopenjdkjvmti. +TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmti +TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmtid + # All tests require the host executables. The tests also depend on the core images, but on # specific version depending on the compiler. ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \ @@ -804,7 +805,9 @@ ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \ $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libnativebridgetest) \ $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \ $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdk$(ART_HOST_SHLIB_EXTENSION) \ - $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkd$(ART_HOST_SHLIB_EXTENSION) + $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkd$(ART_HOST_SHLIB_EXTENSION) \ + $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvmti$(ART_HOST_SHLIB_EXTENSION) \ + $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvmtid$(ART_HOST_SHLIB_EXTENSION) \ ifneq ($(HOST_PREFER_32_BIT),true) ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \ @@ -817,7 +820,10 @@ ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \ $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libnativebridgetest) \ $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \ $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdk$(ART_HOST_SHLIB_EXTENSION) \ - $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkd$(ART_HOST_SHLIB_EXTENSION) + $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkd$(ART_HOST_SHLIB_EXTENSION) \ + $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvmti$(ART_HOST_SHLIB_EXTENSION) \ + $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvmtid$(ART_HOST_SHLIB_EXTENSION) \ + endif # Create a rule to build and run a tests following the form: diff --git a/test/DexToDexDecompiler/Main.java b/test/DexToDexDecompiler/Main.java new file mode 100644 index 0000000000..8f5075ae2a --- /dev/null +++ b/test/DexToDexDecompiler/Main.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + Main() { + // Will be quickened with RETURN_VOID_NO_BARRIER. + } + + public static void main() { + Main m = new Main(); + Object o = m; + // The call and field accesses will be quickened. + m.foo(m.a); + + // The checkcast will be quickened. + m.foo(((Main)o).a); + } + + int a; + void foo(int a) {} +} diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java index 3cb1f23f88..c601e3e9bc 100644 --- a/test/MyClassNatives/MyClassNatives.java +++ b/test/MyClassNatives/MyClassNatives.java @@ -139,8 +139,8 @@ class MyClassNatives { float f9, int i10, float f10); // Normal native - native static void stackArgsSignExtendedMips64(int i1, int i2, int i3, int i4, int i5, int i6, - int i7, int i8); + native static long getStackArgSignExtendedMips64(int i1, int i2, int i3, int i4, int i5, int i6, + int stack_arg); // Normal native static native double logD(double d); @@ -273,8 +273,8 @@ class MyClassNatives { float f9, int i10, float f10); @FastNative - native static void stackArgsSignExtendedMips64_Fast(int i1, int i2, int i3, int i4, int i5, int i6, - int i7, int i8); + native static long getStackArgSignExtendedMips64_Fast(int i1, int i2, int i3, int i4, int i5, int i6, + int stack_arg); @FastNative static native double logD_Fast(double d); @@ -316,10 +316,6 @@ class MyClassNatives { float f9, int i10, float f10); @CriticalNative - native static void stackArgsSignExtendedMips64_Critical(int i1, int i2, int i3, int i4, int i5, int i6, - int i7, int i8); - - @CriticalNative static native double logD_Critical(double d); @CriticalNative static native float logF_Critical(float f); diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index c525b2b9bf..bb3a3ad714 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -58,6 +58,7 @@ INSTRUCTION_SET_FEATURES="" ARGS="" EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS. DRY_RUN="n" # if y prepare to run the test but don't run it. +TEST_VDEX="n" while true; do if [ "x$1" = "x--quiet" ]; then @@ -243,6 +244,9 @@ while true; do elif [ "x$1" = "x--dry-run" ]; then DRY_RUN="y" shift + elif [ "x$1" = "x--vdex" ]; then + TEST_VDEX="y" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 exit 1 @@ -444,6 +448,7 @@ if [ ${#VDEX_NAME} -gt $max_filename_size ]; then fi dex2oat_cmdline="true" +vdex_cmdline="true" mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA" strip_cmdline="true" @@ -473,6 +478,9 @@ if [ "$PREBUILD" = "y" ]; then # Use -k 1m to SIGKILL it a minute later if it hasn't ended. dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}" fi + if [ "$TEST_VDEX" = "y" ]; then + vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex" + fi fi if [ "$STRIP_DEX" = "y" ]; then @@ -513,6 +521,7 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ # Remove whitespace. dex2oat_cmdline=$(echo $dex2oat_cmdline) dalvikvm_cmdline=$(echo $dalvikvm_cmdline) +vdex_cmdline=$(echo $vdex_cmdline) if [ "$HOST" = "n" ]; then adb root > /dev/null @@ -553,6 +562,7 @@ if [ "$HOST" = "n" ]; then export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \ export PATH=$ANDROID_ROOT/bin:$PATH && \ $dex2oat_cmdline && \ + $vdex_cmdline && \ $strip_cmdline && \ $dalvikvm_cmdline" @@ -626,7 +636,7 @@ else fi if [ "$DEV_MODE" = "y" ]; then - echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $strip_cmdline && $cmdline" + echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $cmdline" fi cd $ANDROID_BUILD_TOP @@ -634,6 +644,7 @@ else rm -rf ${DEX_LOCATION}/dalvik-cache/ mkdir -p ${mkdir_locations} || exit 1 $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; } + $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; } $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; } # For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use diff --git a/test/run-test b/test/run-test index 37eefb34d3..ea9622aa69 100755 --- a/test/run-test +++ b/test/run-test @@ -351,6 +351,9 @@ while true; do elif [ "x$1" = "x--bisection-search" ]; then bisection_search="yes" shift + elif [ "x$1" = "x--vdex" ]; then + run_args="${run_args} --vdex" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 usage="yes" @@ -640,6 +643,7 @@ if [ "$usage" = "yes" ]; then echo " --pic-test Compile the test code position independent." echo " --quiet Don't print anything except failure messages" echo " --bisection-search Perform bisection bug search." + echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild." ) 1>&2 # Direct to stderr so usage is not printed if --quiet is set. exit 1 fi diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk index 27c20545f6..493eafb3c9 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -48,7 +48,7 @@ include $(BUILD_PREBUILT) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, test) LOCAL_JAR_MANIFEST := test/manifest.txt -LOCAL_STATIC_JAVA_LIBRARIES := ahat junit +LOCAL_STATIC_JAVA_LIBRARIES := ahat junit-host LOCAL_IS_HOST_MODULE := true LOCAL_MODULE_TAGS := tests LOCAL_MODULE := ahat-tests diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 12e03386d6..2d26b4858d 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -19,7 +19,17 @@ if [ ! -d art ]; then exit 1 fi -out_dir=${OUT_DIR-out} +# Logic for setting out_dir from build/make/core/envsetup.mk: +if [[ -z $OUT_DIR ]]; then + if [[ -z $OUT_DIR_COMMON_BASE ]]; then + out_dir=out + else + out_dir=${OUT_DIR_COMMON_BASE}/${PWD##*/} + fi +else + out_dir=${OUT_DIR} +fi + java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target ${out_dir}/host/linux-x86/bin/jack" mode="target" diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README index a0658ec449..c1cdf1e3f4 100644 --- a/tools/dexfuzz/README +++ b/tools/dexfuzz/README @@ -4,7 +4,7 @@ DexFuzz DexFuzz is primarily a tool for fuzzing DEX files. Fuzzing is the introduction of subtle changes ("mutations") to a file to produce a new test case. These test cases can be used to test the various modes of execution available to ART (Interpreter, -Quick compiler, Optimizing compiler) to check for bugs in these modes of execution. +Optimizing compiler) to check for bugs in these modes of execution. This is done by differential testing - each test file is executed with each mode of execution, and any differences between the resulting outputs may be an indication of a bug in one of the modes. @@ -53,17 +53,16 @@ You MUST specify one of the following ISAs: And also at least two of the following backends: --interpreter - --quick --optimizing Note that if you wanted to test both ARM and ARM64 on an ARM64 device, you can use --allarm. Also in this case only one backend is needed, if i.e., you wanted to test -ARM Quick Backend vs. ARM64 Quick Backend. +ARM Optimizing Backend vs. ARM64 Optimizing Backend. Some legal examples: - --arm --quick --optimizing - --x86 --quick --optimizing --interpreter - --allarm --quick + --arm --optimizing --interpreter + --x86 --optimizing --interpreter + --allarm --optimizing Add in --device=<device name, e.g. device:generic> if you want to specify a device. Add in --execute-dir=<dir on device> if you want to specify an execution directory. @@ -98,7 +97,6 @@ MutateFail - because mutation is a random process, and has attempt thresholds to those occurrences. Timed Out - mutated files that timed out for one or more backends. Current timeouts are: - Quick - 5 seconds Optimizing - 5 seconds Intepreter - 30 seconds (use --short-timeouts to set all backends to 2 seconds.) diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java index b442b221fa..7d5476da9a 100644 --- a/tools/dexfuzz/src/dexfuzz/Options.java +++ b/tools/dexfuzz/src/dexfuzz/Options.java @@ -61,7 +61,6 @@ public class Options { public static boolean executeOnHost; public static boolean noBootImage; public static boolean useInterpreter; - public static boolean useQuick; public static boolean useOptimizing; public static boolean useArchArm; public static boolean useArchArm64; @@ -101,7 +100,6 @@ public class Options { Log.always(" --execute-class=<c> : When executing, execute this class (default: Main)"); Log.always(""); Log.always(" --interpreter : Include the Interpreter in comparisons"); - Log.always(" --quick : Include the Quick Compiler in comparisons"); Log.always(" --optimizing : Include the Optimizing Compiler in comparisons"); Log.always(""); Log.always(" --arm : Include ARM backends in comparisons"); @@ -160,8 +158,6 @@ public class Options { skipHostVerify = true; } else if (flag.equals("interpreter")) { useInterpreter = true; - } else if (flag.equals("quick")) { - useQuick = true; } else if (flag.equals("optimizing")) { useOptimizing = true; } else if (flag.equals("arm")) { @@ -423,18 +419,15 @@ public class Options { if (useInterpreter) { backends++; } - if (useQuick) { - backends++; - } if (useOptimizing) { backends++; } if (useArchArm && useArchArm64) { - // Could just be comparing quick-ARM versus quick-ARM64? + // Could just be comparing optimizing-ARM versus optimizing-ARM64? backends++; } if (backends < 2) { - Log.error("Not enough backends specified! Try --quick --interpreter!"); + Log.error("Not enough backends specified! Try --optimizing --interpreter!"); return false; } } diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java index 72e36e86ea..84ed4c465f 100644 --- a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java +++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java @@ -29,6 +29,9 @@ public class Arm64OptimizingBackendExecutor extends Executor { protected String constructCommand(String programName) { StringBuilder commandBuilder = new StringBuilder(); commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing "); + // The -Xno-dex-file-fallback option ensures that the execution does not default to + // interpreter if compilations fails. + commandBuilder.append("-Xno-dex-file-fallback "); if (device.noBootImageAvailable()) { commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate "); } diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java deleted file mode 100644 index d9228ed08f..0000000000 --- a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dexfuzz.executors; - -import dexfuzz.listeners.BaseListener; - -public class Arm64QuickBackendExecutor extends Executor { - - public Arm64QuickBackendExecutor(BaseListener listener, Device device) { - super("ARM64 Quick Backend", 5, listener, Architecture.ARM64, device, - /*needsCleanCodeCache*/ true, /*isBisectable*/ false); - } - - @Override - protected String constructCommand(String programName) { - StringBuilder commandBuilder = new StringBuilder(); - commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick "); - if (device.noBootImageAvailable()) { - commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate "); - } - commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" "); - commandBuilder.append(executeClass); - return commandBuilder.toString(); - } -} diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java index ded8cf9914..26a5eeaf85 100644 --- a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java +++ b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java @@ -29,6 +29,9 @@ public class ArmOptimizingBackendExecutor extends Executor { protected String constructCommand(String programName) { StringBuilder commandBuilder = new StringBuilder(); commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing "); + // The -Xno-dex-file-fallback option ensures that the execution does not default to + // interpreter if compilations fails. + commandBuilder.append("-Xno-dex-file-fallback "); if (device.noBootImageAvailable()) { commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate "); } diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java deleted file mode 100644 index 0eb35f78c9..0000000000 --- a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dexfuzz.executors; - -import dexfuzz.listeners.BaseListener; - -public class ArmQuickBackendExecutor extends Executor { - - public ArmQuickBackendExecutor(BaseListener listener, Device device) { - super("ARM Quick Backend", 5, listener, Architecture.ARM, device, - /*needsCleanCodeCache*/ true, /*isBisectable*/ false); - } - - @Override - protected String constructCommand(String programName) { - StringBuilder commandBuilder = new StringBuilder(); - commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick "); - if (device.noBootImageAvailable()) { - commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate "); - } - commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" "); - commandBuilder.append(executeClass); - return commandBuilder.toString(); - } -} diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java index 72d43e732c..883ff2aa57 100644 --- a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java +++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java @@ -29,6 +29,9 @@ public class Mips64OptimizingBackendExecutor extends Executor { protected String constructCommand(String programName) { StringBuilder commandBuilder = new StringBuilder(); commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing "); + // The -Xno-dex-file-fallback option ensures that the execution does not default to + // interpreter if compilations fails. + commandBuilder.append("-Xno-dex-file-fallback "); commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" "); commandBuilder.append(executeClass); return commandBuilder.toString(); diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java deleted file mode 100644 index e7e5ff68e8..0000000000 --- a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dexfuzz.executors; - -import dexfuzz.listeners.BaseListener; - -public class Mips64QuickBackendExecutor extends Executor { - - public Mips64QuickBackendExecutor(BaseListener listener, Device device) { - super("MIPS64 Quick Backend", 5, listener, Architecture.MIPS64, device, - /*needsCleanCodeCache*/ true, /*isBisectable*/ false); - } - - @Override - protected String constructCommand(String programName) { - StringBuilder commandBuilder = new StringBuilder(); - commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick "); - commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" "); - commandBuilder.append(executeClass); - return commandBuilder.toString(); - } -} diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java index 63f6858cf5..b7babdc577 100644 --- a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java +++ b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java @@ -29,6 +29,9 @@ public class MipsOptimizingBackendExecutor extends Executor { protected String constructCommand(String programName) { StringBuilder commandBuilder = new StringBuilder(); commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing "); + // The -Xno-dex-file-fallback option ensures that the execution does not default to + // interpreter if compilations fails. + commandBuilder.append("-Xno-dex-file-fallback "); commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" "); commandBuilder.append(executeClass); return commandBuilder.toString(); diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java deleted file mode 100644 index b262090333..0000000000 --- a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dexfuzz.executors; - -import dexfuzz.listeners.BaseListener; - -public class MipsQuickBackendExecutor extends Executor { - - public MipsQuickBackendExecutor(BaseListener listener, Device device) { - super("MIPS Quick Backend", 5, listener, Architecture.MIPS, device, - /*needsCleanCodeCache*/ true, /*isBisectable*/ false); - } - - @Override - protected String constructCommand(String programName) { - StringBuilder commandBuilder = new StringBuilder(); - commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick "); - commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" "); - commandBuilder.append(executeClass); - return commandBuilder.toString(); - } -} diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java index 5908a8b961..1d62051f13 100644 --- a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java +++ b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java @@ -30,6 +30,9 @@ public class X86OptimizingBackendExecutor extends Executor { protected String constructCommand(String programName) { StringBuilder commandBuilder = new StringBuilder(); commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing "); + // The -Xno-dex-file-fallback option ensures that the execution does not default to + // interpreter if compilations fails. + commandBuilder.append("-Xno-dex-file-fallback "); if (Options.executeOnHost) { commandBuilder.append(device.getHostExecutionFlags()).append(" "); } diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java deleted file mode 100644 index 9e8039d67a..0000000000 --- a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dexfuzz.executors; - -import dexfuzz.Options; -import dexfuzz.listeners.BaseListener; - -public class X86QuickBackendExecutor extends Executor { - - public X86QuickBackendExecutor(BaseListener listener, Device device) { - super("x86 Quick Backend", 5, listener, Architecture.X86, device, - /*needsCleanCodeCache*/ true, /*isBisectable*/ false); - } - - @Override - protected String constructCommand(String programName) { - StringBuilder commandBuilder = new StringBuilder(); - commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick "); - if (Options.executeOnHost) { - commandBuilder.append(device.getHostExecutionFlags()).append(" "); - } - commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" "); - commandBuilder.append(executeClass); - return commandBuilder.toString(); - } -} diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java index 28ff1a506f..ad44259dbd 100644 --- a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java +++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java @@ -29,6 +29,9 @@ public class X86_64OptimizingBackendExecutor extends Executor { protected String constructCommand(String programName) { StringBuilder commandBuilder = new StringBuilder(); commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing "); + // The -Xno-dex-file-fallback option ensures that the execution does not default to + // interpreter if compilations fails. + commandBuilder.append("-Xno-dex-file-fallback "); commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" "); commandBuilder.append(executeClass); return commandBuilder.toString(); diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java deleted file mode 100644 index 22cafe2045..0000000000 --- a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dexfuzz.executors; - -import dexfuzz.listeners.BaseListener; - -public class X86_64QuickBackendExecutor extends Executor { - - public X86_64QuickBackendExecutor(BaseListener listener, Device device) { - super("x86_64 Quick Backend", 5, listener, Architecture.X86_64, device, - /*needsCleanCodeCache*/ true, /*isBisectable*/ false); - } - - @Override - protected String constructCommand(String programName) { - StringBuilder commandBuilder = new StringBuilder(); - commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick "); - commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" "); - commandBuilder.append(executeClass); - return commandBuilder.toString(); - } -} diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java index bc39d79a6c..1797d90141 100644 --- a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java +++ b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java @@ -22,24 +22,18 @@ import dexfuzz.Timer; import dexfuzz.executors.Architecture; import dexfuzz.executors.Arm64InterpreterExecutor; import dexfuzz.executors.Arm64OptimizingBackendExecutor; -import dexfuzz.executors.Arm64QuickBackendExecutor; import dexfuzz.executors.ArmInterpreterExecutor; import dexfuzz.executors.ArmOptimizingBackendExecutor; -import dexfuzz.executors.ArmQuickBackendExecutor; import dexfuzz.executors.Device; import dexfuzz.executors.Executor; import dexfuzz.executors.Mips64InterpreterExecutor; import dexfuzz.executors.Mips64OptimizingBackendExecutor; -import dexfuzz.executors.Mips64QuickBackendExecutor; import dexfuzz.executors.MipsInterpreterExecutor; import dexfuzz.executors.MipsOptimizingBackendExecutor; -import dexfuzz.executors.MipsQuickBackendExecutor; import dexfuzz.executors.X86InterpreterExecutor; import dexfuzz.executors.X86OptimizingBackendExecutor; -import dexfuzz.executors.X86QuickBackendExecutor; import dexfuzz.executors.X86_64InterpreterExecutor; import dexfuzz.executors.X86_64OptimizingBackendExecutor; -import dexfuzz.executors.X86_64QuickBackendExecutor; import dexfuzz.listeners.BaseListener; import dexfuzz.program.Mutation; import dexfuzz.program.Program; @@ -121,18 +115,13 @@ public abstract class Fuzzer { } } - private void addExecutorsForArchitecture(Device device, Class<? extends Executor> quick, - Class<? extends Executor> optimizing, Class<? extends Executor> interpreter) { - // NB: Currently QuickBackend MUST come immediately before same arch's Interpreter. + private void addExecutorsForArchitecture(Device device, Class<? extends Executor> optimizing, + Class<? extends Executor> interpreter) { + // NB: Currently OptimizingBackend MUST come immediately before same arch's Interpreter. // This is because intepreter execution relies on there being an OAT file already // created to produce correct debug information. Otherwise we will see // false-positive divergences. try { - if (Options.useQuick) { - Constructor<? extends Executor> constructor = - quick.getConstructor(BaseListener.class, Device.class); - executors.add(constructor.newInstance(listener, device)); - } if (Options.useOptimizing) { Constructor<? extends Executor> constructor = optimizing.getConstructor(BaseListener.class, Device.class); @@ -165,33 +154,33 @@ public abstract class Fuzzer { } if (Options.useArchArm64) { - addExecutorsForArchitecture(device, Arm64QuickBackendExecutor.class, - Arm64OptimizingBackendExecutor.class, Arm64InterpreterExecutor.class); + addExecutorsForArchitecture(device, Arm64OptimizingBackendExecutor.class, + Arm64InterpreterExecutor.class); } if (Options.useArchArm) { - addExecutorsForArchitecture(device, ArmQuickBackendExecutor.class, - ArmOptimizingBackendExecutor.class, ArmInterpreterExecutor.class); + addExecutorsForArchitecture(device, ArmOptimizingBackendExecutor.class, + ArmInterpreterExecutor.class); } if (Options.useArchX86_64) { - addExecutorsForArchitecture(device, X86_64QuickBackendExecutor.class, - X86_64OptimizingBackendExecutor.class, X86_64InterpreterExecutor.class); + addExecutorsForArchitecture(device, X86_64OptimizingBackendExecutor.class, + X86_64InterpreterExecutor.class); } if (Options.useArchX86) { - addExecutorsForArchitecture(device, X86QuickBackendExecutor.class, - X86OptimizingBackendExecutor.class, X86InterpreterExecutor.class); + addExecutorsForArchitecture(device, X86OptimizingBackendExecutor.class, + X86InterpreterExecutor.class); } if (Options.useArchMips64) { - addExecutorsForArchitecture(device, Mips64QuickBackendExecutor.class, - Mips64OptimizingBackendExecutor.class, Mips64InterpreterExecutor.class); + addExecutorsForArchitecture(device, Mips64OptimizingBackendExecutor.class, + Mips64InterpreterExecutor.class); } if (Options.useArchMips) { - addExecutorsForArchitecture(device, MipsQuickBackendExecutor.class, - MipsOptimizingBackendExecutor.class, MipsInterpreterExecutor.class); + addExecutorsForArchitecture(device, MipsOptimizingBackendExecutor.class, + MipsInterpreterExecutor.class); } // Add the first backend as the golden executor for self-divergence tests. |