1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 package com.android.tools.r8.ir.code; 5 6 import com.android.tools.r8.dex.Constants; 7 import com.android.tools.r8.graph.DebugLocalInfo; 8 import com.android.tools.r8.ir.regalloc.LiveIntervals; 9 import com.android.tools.r8.utils.InternalOptions; 10 import com.android.tools.r8.utils.LongInterval; 11 import com.google.common.collect.ImmutableSet; 12 import java.util.ArrayList; 13 import java.util.Collections; 14 import java.util.HashSet; 15 import java.util.LinkedList; 16 import java.util.List; 17 import java.util.Set; 18 19 public class Value { 20 21 /** 22 * Immutable view of the debug info associated with an SSA value. 23 * 24 * Used during IR building and to construct replacement values. 25 */ 26 public static class DebugInfo { 27 private final DebugLocalInfo local; 28 private final Value previousLocalValue; 29 DebugInfo(DebugLocalInfo local, Value previousLocalValue)30 public DebugInfo(DebugLocalInfo local, Value previousLocalValue) { 31 assert local != null; 32 this.local = local; 33 this.previousLocalValue = previousLocalValue; 34 } 35 } 36 37 // Actual internal data for the debug information of locals. 38 // This is wrapped in a class to avoid multiple pointers in the value structure. 39 private static class DebugData { 40 final DebugLocalInfo local; 41 Value previousLocalValue; 42 Set<Instruction> debugUsers = new HashSet<>(); 43 List<Instruction> localStarts = new ArrayList<>(); 44 List<Instruction> localEnds = new ArrayList<>(); 45 DebugData(DebugInfo info)46 DebugData(DebugInfo info) { 47 this(info.local, info.previousLocalValue); 48 } 49 DebugData(DebugLocalInfo local, Value previousLocalValue)50 DebugData(DebugLocalInfo local, Value previousLocalValue) { 51 assert previousLocalValue == null || !previousLocalValue.isUninitializedLocal(); 52 this.local = local; 53 this.previousLocalValue = previousLocalValue; 54 } 55 } 56 57 public static final Value UNDEFINED = new Value(-1, MoveType.OBJECT, null); 58 59 protected final int number; 60 protected final MoveType type; 61 public Instruction definition = null; 62 private LinkedList<Instruction> users = new LinkedList<>(); 63 private Set<Instruction> uniqueUsers = null; 64 private LinkedList<Phi> phiUsers = new LinkedList<>(); 65 private Set<Phi> uniquePhiUsers = null; 66 private Value nextConsecutive = null; 67 private Value previousConsecutive = null; 68 private LiveIntervals liveIntervals; 69 private int needsRegister = -1; 70 private boolean neverNull = false; 71 private boolean isThis = false; 72 private boolean isArgument = false; 73 private LongInterval valueRange; 74 private final DebugData debugData; 75 Value(int number, MoveType type, DebugInfo debugInfo)76 public Value(int number, MoveType type, DebugInfo debugInfo) { 77 this.number = number; 78 this.type = type; 79 this.debugData = debugInfo == null ? null : new DebugData(debugInfo); 80 } 81 isFixedRegisterValue()82 public boolean isFixedRegisterValue() { 83 return false; 84 } 85 asFixedRegisterValue()86 public FixedRegisterValue asFixedRegisterValue() { 87 return null; 88 } 89 getNumber()90 public int getNumber() { 91 return number; 92 } 93 requiredRegisters()94 public int requiredRegisters() { 95 return type.requiredRegisters(); 96 } 97 getDebugInfo()98 public DebugInfo getDebugInfo() { 99 return debugData == null ? null : new DebugInfo(debugData.local, debugData.previousLocalValue); 100 } 101 getLocalInfo()102 public DebugLocalInfo getLocalInfo() { 103 return debugData == null ? null : debugData.local; 104 } 105 getPreviousLocalValue()106 public Value getPreviousLocalValue() { 107 return debugData == null ? null : debugData.previousLocalValue; 108 } 109 replacePreviousLocalValue(Value value)110 public void replacePreviousLocalValue(Value value) { 111 if (value == null || value.isUninitializedLocal()) { 112 debugData.previousLocalValue = null; 113 } else { 114 debugData.previousLocalValue = value; 115 } 116 } 117 getDebugLocalStarts()118 public List<Instruction> getDebugLocalStarts() { 119 return debugData.localStarts; 120 } 121 getDebugLocalEnds()122 public List<Instruction> getDebugLocalEnds() { 123 return debugData.localEnds; 124 } 125 addDebugLocalStart(Instruction start)126 public void addDebugLocalStart(Instruction start) { 127 assert start != null; 128 debugData.localStarts.add(start); 129 } 130 addDebugLocalEnd(Instruction end)131 public void addDebugLocalEnd(Instruction end) { 132 assert end != null; 133 debugData.localEnds.add(end); 134 } 135 linkTo(Value other)136 public void linkTo(Value other) { 137 assert nextConsecutive == null || nextConsecutive == other; 138 assert other.previousConsecutive == null || other.previousConsecutive == this; 139 other.previousConsecutive = this; 140 nextConsecutive = other; 141 } 142 replaceLink(Value newArgument)143 public void replaceLink(Value newArgument) { 144 assert isLinked(); 145 if (previousConsecutive != null) { 146 previousConsecutive.nextConsecutive = newArgument; 147 newArgument.previousConsecutive = previousConsecutive; 148 previousConsecutive = null; 149 } 150 if (nextConsecutive != null) { 151 nextConsecutive.previousConsecutive = newArgument; 152 newArgument.nextConsecutive = nextConsecutive; 153 nextConsecutive = null; 154 } 155 } 156 isLinked()157 public boolean isLinked() { 158 return nextConsecutive != null || previousConsecutive != null; 159 } 160 getStartOfConsecutive()161 public Value getStartOfConsecutive() { 162 Value current = this; 163 while (current.getPreviousConsecutive() != null) { 164 current = current.getPreviousConsecutive(); 165 } 166 return current; 167 } 168 getNextConsecutive()169 public Value getNextConsecutive() { 170 return nextConsecutive; 171 } 172 getPreviousConsecutive()173 public Value getPreviousConsecutive() { 174 return previousConsecutive; 175 } 176 uniqueUsers()177 public Set<Instruction> uniqueUsers() { 178 if (uniqueUsers != null) { 179 return uniqueUsers; 180 } 181 return uniqueUsers = ImmutableSet.copyOf(users); 182 } 183 uniquePhiUsers()184 public Set<Phi> uniquePhiUsers() { 185 if (uniquePhiUsers != null) { 186 return uniquePhiUsers; 187 } 188 return uniquePhiUsers = ImmutableSet.copyOf(phiUsers); 189 } 190 debugUsers()191 public Set<Instruction> debugUsers() { 192 if (debugData == null) { 193 return null; 194 } 195 return Collections.unmodifiableSet(debugData.debugUsers); 196 } 197 numberOfUsers()198 public int numberOfUsers() { 199 int size = users.size(); 200 if (size <= 1) { 201 return size; 202 } 203 return uniqueUsers().size(); 204 } 205 numberOfPhiUsers()206 public int numberOfPhiUsers() { 207 int size = phiUsers.size(); 208 if (size <= 1) { 209 return size; 210 } 211 return uniquePhiUsers().size(); 212 } 213 numberOfDebugUsers()214 public int numberOfDebugUsers() { 215 return debugData == null ? 0 : debugData.debugUsers.size(); 216 } 217 numberOfAllUsers()218 public int numberOfAllUsers() { 219 return numberOfUsers() + numberOfPhiUsers() + numberOfDebugUsers(); 220 } 221 addUser(Instruction user)222 public void addUser(Instruction user) { 223 users.add(user); 224 uniqueUsers = null; 225 } 226 removeUser(Instruction user)227 public void removeUser(Instruction user) { 228 users.remove(user); 229 uniqueUsers = null; 230 } 231 clearUsers()232 public void clearUsers() { 233 users.clear(); 234 uniqueUsers = null; 235 phiUsers.clear(); 236 uniquePhiUsers = null; 237 if (debugData != null) { 238 debugData.debugUsers.clear(); 239 } 240 } 241 addPhiUser(Phi user)242 public void addPhiUser(Phi user) { 243 phiUsers.add(user); 244 uniquePhiUsers = null; 245 } 246 removePhiUser(Phi user)247 public void removePhiUser(Phi user) { 248 phiUsers.remove(user); 249 uniquePhiUsers = null; 250 } 251 addDebugUser(Instruction user)252 public void addDebugUser(Instruction user) { 253 if (isUninitializedLocal()) { 254 return; 255 } 256 debugData.debugUsers.add(user); 257 } 258 isUninitializedLocal()259 public boolean isUninitializedLocal() { 260 return definition != null && definition.isDebugLocalUninitialized(); 261 } 262 isInitializedLocal()263 public boolean isInitializedLocal() { 264 return !isUninitializedLocal(); 265 } 266 removeDebugUser(Instruction user)267 public void removeDebugUser(Instruction user) { 268 debugData.debugUsers.remove(user); 269 } 270 hasUsersInfo()271 public boolean hasUsersInfo() { 272 return users != null; 273 } 274 clearUsersInfo()275 public void clearUsersInfo() { 276 users = null; 277 uniqueUsers = null; 278 phiUsers = null; 279 uniquePhiUsers = null; 280 if (debugData != null) { 281 debugData.debugUsers = null; 282 } 283 } 284 replaceUsers(Value newValue)285 public void replaceUsers(Value newValue) { 286 if (this == newValue) { 287 return; 288 } 289 for (Instruction user : uniqueUsers()) { 290 user.inValues.replaceAll(v -> { 291 if (v == this) { 292 newValue.addUser(user); 293 return newValue; 294 } 295 return v; 296 }); 297 } 298 for (Phi user : uniquePhiUsers()) { 299 user.getOperands().replaceAll(v -> { 300 if (v == this) { 301 newValue.addPhiUser(user); 302 return newValue; 303 } 304 return v; 305 }); 306 } 307 if (debugData != null) { 308 for (Instruction user : debugUsers()) { 309 user.getDebugValues().replaceAll(v -> { 310 if (v == this) { 311 newValue.addDebugUser(user); 312 return newValue; 313 } 314 return v; 315 }); 316 if (user.getPreviousLocalValue() == this) { 317 newValue.addDebugUser(user); 318 user.replacePreviousLocalValue(newValue); 319 } 320 } 321 } 322 clearUsers(); 323 } 324 setLiveIntervals(LiveIntervals intervals)325 public void setLiveIntervals(LiveIntervals intervals) { 326 assert liveIntervals == null; 327 liveIntervals = intervals; 328 } 329 getLiveIntervals()330 public LiveIntervals getLiveIntervals() { 331 return liveIntervals; 332 } 333 needsRegister()334 public boolean needsRegister() { 335 assert needsRegister >= 0; 336 assert !hasUsersInfo() || (needsRegister > 0) == internalComputeNeedsRegister(); 337 return needsRegister > 0; 338 } 339 setNeedsRegister(boolean value)340 public void setNeedsRegister(boolean value) { 341 assert needsRegister == -1 || (needsRegister > 0) == value; 342 needsRegister = value ? 1 : 0; 343 } 344 computeNeedsRegister()345 public void computeNeedsRegister() { 346 assert needsRegister < 0; 347 setNeedsRegister(internalComputeNeedsRegister()); 348 } 349 350 public boolean internalComputeNeedsRegister() { 351 if (!isConstant()) { 352 return true; 353 } 354 if (numberOfPhiUsers() > 0) { 355 return true; 356 } 357 for (Instruction user : uniqueUsers()) { 358 if (user.needsValueInRegister(this)) { 359 return true; 360 } 361 } 362 return false; 363 } 364 hasRegisterConstraint()365 public boolean hasRegisterConstraint() { 366 for (Instruction instruction : uniqueUsers()) { 367 if (instruction.maxInValueRegister() != Constants.U16BIT_MAX) { 368 return true; 369 } 370 } 371 return false; 372 } 373 374 @Override hashCode()375 public int hashCode() { 376 return number; 377 } 378 379 @Override toString()380 public String toString() { 381 StringBuilder builder = new StringBuilder(); 382 builder.append("v"); 383 builder.append(number); 384 boolean isConstant = definition != null && definition.isConstNumber(); 385 boolean hasLocalInfo = getLocalInfo() != null; 386 if (isConstant || hasLocalInfo) { 387 builder.append("("); 388 if (isConstant) { 389 ConstNumber constNumber = getConstInstruction().asConstNumber(); 390 if (constNumber.outType() == MoveType.SINGLE) { 391 builder.append((int) constNumber.getRawValue()); 392 } else { 393 builder.append(constNumber.getRawValue()); 394 } 395 } 396 if (isConstant && hasLocalInfo) { 397 builder.append(", "); 398 } 399 if (hasLocalInfo) { 400 builder.append(getLocalInfo()); 401 } 402 builder.append(")"); 403 } 404 if (valueRange != null) { 405 builder.append(valueRange); 406 } 407 return builder.toString(); 408 } 409 outType()410 public MoveType outType() { 411 return type; 412 } 413 getConstInstruction()414 public ConstInstruction getConstInstruction() { 415 assert isConstant(); 416 return definition.getOutConstantConstInstruction(); 417 } 418 isConstant()419 public boolean isConstant() { 420 return definition.isOutConstant() && getLocalInfo() == null; 421 } 422 isPhi()423 public boolean isPhi() { 424 return false; 425 } 426 asPhi()427 public Phi asPhi() { 428 return null; 429 } 430 markNeverNull()431 public void markNeverNull() { 432 assert !neverNull; 433 neverNull = true; 434 } 435 436 /** 437 * Returns whether this value is know to never be <code>null</code>. 438 */ isNeverNull()439 public boolean isNeverNull() { 440 return neverNull; 441 } 442 canBeNull()443 public boolean canBeNull() { 444 return !neverNull; 445 } 446 markAsArgument()447 public void markAsArgument() { 448 assert !isArgument; 449 assert !isThis; 450 isArgument = true; 451 } 452 isArgument()453 public boolean isArgument() { 454 return isArgument; 455 } 456 markAsThis()457 public void markAsThis() { 458 assert isArgument; 459 assert !isThis; 460 isThis = true; 461 markNeverNull(); 462 } 463 464 /** 465 * Returns whether this value is known to be the receiver (this argument) in a method body. 466 * <p> 467 * For a receiver value {@link #isNeverNull()} is guarenteed to be <code>true</code> as well. 468 */ isThis()469 public boolean isThis() { 470 return isThis; 471 } 472 setValueRange(LongInterval range)473 public void setValueRange(LongInterval range) { 474 valueRange = range; 475 } 476 hasValueRange()477 public boolean hasValueRange() { 478 return valueRange != null || isConstant(); 479 } 480 isValueInRange(int value)481 public boolean isValueInRange(int value) { 482 if (isConstant()) { 483 return value == getConstInstruction().asConstNumber().getIntValue(); 484 } else { 485 return valueRange != null && valueRange.containsValue(value); 486 } 487 } 488 getValueRange()489 public LongInterval getValueRange() { 490 if (isConstant()) { 491 if (type == MoveType.SINGLE) { 492 int value = getConstInstruction().asConstNumber().getIntValue(); 493 return new LongInterval(value, value); 494 } else { 495 assert type == MoveType.WIDE; 496 long value = getConstInstruction().asConstNumber().getLongValue(); 497 return new LongInterval(value, value); 498 } 499 } else { 500 return valueRange; 501 } 502 } 503 isDead(InternalOptions options)504 public boolean isDead(InternalOptions options) { 505 // Totally unused values are trivially dead. 506 return numberOfAllUsers() == 0 || isDead(new HashSet<>(), options); 507 } 508 isDead(Set<Value> active, InternalOptions options)509 protected boolean isDead(Set<Value> active, InternalOptions options) { 510 // If the value has debug users we cannot eliminate it since it represents a value in a local 511 // variable that should be visible in the debugger. 512 if (numberOfDebugUsers() != 0) { 513 return false; 514 } 515 // This is a candidate for a dead value. Guard against looping by adding it to the set of 516 // currently active values. 517 active.add(this); 518 for (Instruction instruction : uniqueUsers()) { 519 if (!instruction.canBeDeadCode(null, options)) { 520 return false; 521 } 522 Value outValue = instruction.outValue(); 523 // Instructions with no out value cannot be dead code by the current definition 524 // (unused out value). They typically side-effect input values or deals with control-flow. 525 assert outValue != null; 526 if (!active.contains(outValue) && !outValue.isDead(active, options)) { 527 return false; 528 } 529 } 530 for (Phi phi : uniquePhiUsers()) { 531 if (!active.contains(phi) && !phi.isDead(active, options)) { 532 return false; 533 } 534 } 535 return true; 536 } 537 } 538