• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017, 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.optimize;
5 
6 import com.android.tools.r8.errors.CompilationError;
7 import com.android.tools.r8.graph.AppInfo;
8 import com.android.tools.r8.graph.DexClass;
9 import com.android.tools.r8.graph.DexEncodedField;
10 import com.android.tools.r8.graph.DexEncodedMethod;
11 import com.android.tools.r8.graph.DexField;
12 import com.android.tools.r8.graph.DexItem;
13 import com.android.tools.r8.graph.DexMethod;
14 import com.android.tools.r8.graph.DexType;
15 import com.android.tools.r8.ir.code.ConstNumber;
16 import com.android.tools.r8.ir.code.ConstType;
17 import com.android.tools.r8.ir.code.IRCode;
18 import com.android.tools.r8.ir.code.InstancePut;
19 import com.android.tools.r8.ir.code.Instruction;
20 import com.android.tools.r8.ir.code.InstructionIterator;
21 import com.android.tools.r8.ir.code.InvokeMethod;
22 import com.android.tools.r8.ir.code.MoveType;
23 import com.android.tools.r8.ir.code.StaticGet;
24 import com.android.tools.r8.ir.code.StaticPut;
25 import com.android.tools.r8.ir.code.Value;
26 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
27 import com.android.tools.r8.shaking.ProguardMemberRule;
28 
29 public class MemberValuePropagation {
30 
31   private final AppInfo appInfo;
32   private final AppInfoWithLiveness liveSet;
33 
34   private enum RuleType {
35     NONE,
36     ASSUME_NO_SIDE_EFFECTS,
37     ASSUME_VALUES
38   }
39 
40   private class ProguardMemberRuleLookup {
41 
42     final RuleType type;
43     final ProguardMemberRule rule;
44 
ProguardMemberRuleLookup(RuleType type, ProguardMemberRule rule)45     ProguardMemberRuleLookup(RuleType type, ProguardMemberRule rule) {
46       this.type = type;
47       this.rule = rule;
48     }
49   }
50 
MemberValuePropagation(AppInfo appInfo)51   public MemberValuePropagation(AppInfo appInfo) {
52     this.appInfo = appInfo;
53     this.liveSet = appInfo.withLiveness();
54   }
55 
lookupMemberRule(DexItem item)56   private ProguardMemberRuleLookup lookupMemberRule(DexItem item) {
57     if (liveSet == null) {
58       return null;
59     }
60     ProguardMemberRule rule = liveSet.noSideEffects.get(item);
61     if (rule != null) {
62       return new ProguardMemberRuleLookup(RuleType.ASSUME_NO_SIDE_EFFECTS, rule);
63     }
64     rule = liveSet.assumedValues.get(item);
65     if (rule != null) {
66       return new ProguardMemberRuleLookup(RuleType.ASSUME_VALUES, rule);
67     }
68     return null;
69   }
70 
constantReplacementFromProguardRule( ProguardMemberRule rule, IRCode code, Instruction instruction)71   private Instruction constantReplacementFromProguardRule(
72       ProguardMemberRule rule, IRCode code, Instruction instruction) {
73     // Check if this value can be assumed constant.
74     Instruction replacement = null;
75     MoveType moveType = instruction.outValue().outType();
76     if (rule != null && rule.hasReturnValue() && rule.getReturnValue().isSingleValue()) {
77       assert moveType != MoveType.OBJECT;
78       Value value = code.createValue(moveType, instruction.getDebugInfo());
79       replacement = new ConstNumber(
80           ConstType.fromMoveType(moveType), value, rule.getReturnValue().getSingleValue());
81     }
82     if (replacement == null &&
83         rule != null && rule.hasReturnValue() && rule.getReturnValue().isField()) {
84       DexField field = rule.getReturnValue().getField();
85       DexEncodedField staticField = appInfo.lookupStaticTarget(field.clazz, field);
86       if (staticField != null) {
87         Value value = code.createValue(moveType, instruction.getDebugInfo());
88         replacement = staticField.staticValue.asConstInstruction(false, value);
89       } else {
90         throw new CompilationError(field.clazz.toSourceString() + "." + field.name.toString() +
91             " used in assumevalues rule does not exist.");
92       }
93     }
94     return replacement;
95   }
96 
setValueRangeFromProguardRule(ProguardMemberRule rule, Value value)97   private void setValueRangeFromProguardRule(ProguardMemberRule rule, Value value) {
98     if (rule.hasReturnValue() && rule.getReturnValue().isValueRange()) {
99       assert !rule.getReturnValue().isSingleValue();
100       value.setValueRange(rule.getReturnValue().getValueRange());
101     }
102   }
103 
replaceInstructionFromProguardRule(RuleType ruleType, InstructionIterator iterator, Instruction current, Instruction replacement)104   private void replaceInstructionFromProguardRule(RuleType ruleType, InstructionIterator iterator,
105       Instruction current, Instruction replacement) {
106     if (ruleType == RuleType.ASSUME_NO_SIDE_EFFECTS) {
107       iterator.replaceCurrentInstruction(replacement);
108     } else {
109       if (current.outValue() != null) {
110         assert replacement.outValue() != null;
111         current.outValue().replaceUsers(replacement.outValue());
112       }
113       iterator.add(replacement);
114     }
115   }
116 
117   /**
118    * Replace invoke targets and field accesses with constant values where possible.
119    * <p>
120    * Also assigns value ranges to values where possible.
121    */
rewriteWithConstantValues(IRCode code)122   public void rewriteWithConstantValues(IRCode code) {
123     InstructionIterator iterator = code.instructionIterator();
124     while (iterator.hasNext()) {
125       Instruction current = iterator.next();
126       if (current.isInvokeMethod()) {
127         InvokeMethod invoke = current.asInvokeMethod();
128         DexMethod invokedMethod = invoke.getInvokedMethod();
129         DexType invokedHolder = invokedMethod.getHolder();
130         if (!invokedHolder.isClassType()) {
131           continue;
132         }
133         DexEncodedMethod definition = appInfo.lookup(invoke.getType(), invokedMethod);
134 
135         // Process invokes marked as having no side effects.
136         boolean invokeReplaced = false;
137         ProguardMemberRuleLookup lookup = lookupMemberRule(definition);
138         if (lookup != null) {
139           if (lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS
140               && (invoke.outValue() == null || invoke.outValue().numberOfAllUsers() == 0)) {
141             iterator.remove();
142             invokeReplaced = true;
143           } else {
144             // Check to see if a constant value can be assumed.
145             Instruction replacement =
146                 constantReplacementFromProguardRule(lookup.rule, code, invoke);
147             if (replacement != null) {
148               replaceInstructionFromProguardRule(lookup.type, iterator, current, replacement);
149               invokeReplaced = true;
150             } else {
151               // Check to see if a value range can be assumed.
152               setValueRangeFromProguardRule(lookup.rule, current.outValue());
153             }
154           }
155         }
156 
157         // If no Proguard rule could replace the instruction check for knowledge about the
158         // return value.
159         if (!invokeReplaced && liveSet != null && invoke.outValue() != null) {
160           DexEncodedMethod target = invoke.computeSingleTarget(liveSet);
161           if (target != null) {
162             if (target.getOptimizationInfo().neverReturnsNull()) {
163               invoke.outValue().markNeverNull();
164             }
165             if (target.getOptimizationInfo().returnsConstant()) {
166               long constant = target.getOptimizationInfo().getReturnedConstant();
167               MoveType moveType = invoke.outType();
168               if (moveType == MoveType.OBJECT) {
169                 assert constant == 0;
170                 moveType = MoveType.SINGLE;
171               }
172               Value value = code.createValue(moveType);
173               // TODO(ager): Attempt to get a more precise const type from the method analysis?
174               Instruction knownConstReturn =
175                   new ConstNumber(ConstType.fromMoveType(moveType), value, constant);
176               invoke.outValue().replaceUsers(value);
177               iterator.add(knownConstReturn);
178             }
179           }
180         }
181       } else if (current.isInstancePut()) {
182         InstancePut instancePut = current.asInstancePut();
183         DexField field = instancePut.getField();
184         DexEncodedField target = appInfo.lookupInstanceTarget(field.getHolder(), field);
185         if (target != null) {
186           // Remove writes to dead (i.e. never read) fields.
187           if (!isFieldRead(target, false) && instancePut.object().isNeverNull()) {
188             iterator.remove();
189           }
190         }
191       } else if (current.isStaticGet()) {
192         StaticGet staticGet = current.asStaticGet();
193         DexField field = staticGet.getField();
194         Instruction replacement = null;
195         DexEncodedField target = appInfo.lookupStaticTarget(field.getHolder(), field);
196         ProguardMemberRuleLookup lookup = null;
197         if (target != null) {
198           // Check if a this value is known const.
199           replacement = target.valueAsConstInstruction(appInfo, staticGet.dest());
200           if (replacement == null) {
201             lookup = lookupMemberRule(target);
202             if (lookup != null) {
203               replacement = constantReplacementFromProguardRule(lookup.rule, code, staticGet);
204             }
205           }
206           if (replacement == null) {
207             // If no const replacement was found, at least store the range information.
208             if (lookup != null) {
209               setValueRangeFromProguardRule(lookup.rule, staticGet.dest());
210             }
211           }
212           if (replacement != null) {
213             // Ignore assumenosideeffects for fields.
214             if (lookup != null && lookup.type == RuleType.ASSUME_VALUES) {
215               replaceInstructionFromProguardRule(lookup.type, iterator, current, replacement);
216             } else {
217               iterator.replaceCurrentInstruction(replacement);
218             }
219           }
220         }
221       } else if (current.isStaticPut()) {
222         StaticPut staticPut = current.asStaticPut();
223         DexField field = staticPut.getField();
224         DexEncodedField target = appInfo.lookupStaticTarget(field.getHolder(), field);
225         if (target != null) {
226           // Remove writes to dead (i.e. never read) fields.
227           if (!isFieldRead(target, true)) {
228             iterator.remove();
229           }
230         }
231       }
232     }
233     assert code.isConsistentSSA();
234   }
235 
isFieldRead(DexEncodedField field, boolean isStatic)236   private boolean isFieldRead(DexEncodedField field, boolean isStatic) {
237     // Without live set information we cannot tell and assume true.
238     if (liveSet == null
239         || liveSet.fieldsRead.contains(field.field)
240         || liveSet.pinnedItems.contains(field)) {
241       return true;
242     }
243     // For library classes we don't know whether a field is read.
244     DexClass holder = appInfo.definitionFor(field.field.clazz);
245     return holder == null || holder.isLibraryClass();
246   }
247 }
248