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.code.MoveResult; 7 import com.android.tools.r8.code.MoveResultObject; 8 import com.android.tools.r8.code.MoveResultWide; 9 import com.android.tools.r8.dex.Constants; 10 import com.android.tools.r8.errors.Unreachable; 11 import com.android.tools.r8.graph.AppInfoWithSubtyping; 12 import com.android.tools.r8.graph.DexEncodedMethod; 13 import com.android.tools.r8.graph.DexItem; 14 import com.android.tools.r8.graph.DexMethod; 15 import com.android.tools.r8.graph.DexProto; 16 import com.android.tools.r8.graph.DexType; 17 import com.android.tools.r8.ir.conversion.DexBuilder; 18 import java.util.List; 19 20 public abstract class Invoke extends Instruction { 21 22 public enum Type { 23 DIRECT, 24 INTERFACE, 25 STATIC, 26 SUPER, 27 VIRTUAL, 28 NEW_ARRAY, 29 CUSTOM, 30 POLYMORPHIC 31 } 32 Invoke(Value result, List<Value> arguments)33 public Invoke(Value result, List<Value> arguments) { 34 super(result, arguments); 35 } 36 create( Type type, DexItem target, DexProto proto, Value result, List<Value> arguments)37 public static Invoke create( 38 Type type, DexItem target, DexProto proto, Value result, List<Value> arguments) { 39 switch (type) { 40 case DIRECT: 41 return new InvokeDirect((DexMethod) target, result, arguments); 42 case INTERFACE: 43 return new InvokeInterface((DexMethod) target, result, arguments); 44 case STATIC: 45 return new InvokeStatic((DexMethod) target, result, arguments); 46 case SUPER: 47 return new InvokeSuper((DexMethod) target, result, arguments); 48 case VIRTUAL: 49 return new InvokeVirtual((DexMethod) target, result, arguments); 50 case NEW_ARRAY: 51 return new InvokeNewArray((DexType) target, result, arguments); 52 case CUSTOM: 53 throw new Unreachable("Use InvokeCustom constructor instead"); 54 case POLYMORPHIC: 55 return new InvokePolymorphic((DexMethod) target, proto, result, arguments); 56 } 57 throw new Unreachable("Unknown invoke type: " + type); 58 } 59 createFromTemplate( Invoke template, Value outValue, List<Value> inValues)60 public static Instruction createFromTemplate( 61 Invoke template, Value outValue, List<Value> inValues) { 62 if (template.isInvokeMethod()) { 63 return create(template.getType(), 64 template.asInvokeMethod().getInvokedMethod(), 65 template.isInvokePolymorphic() ? template.asInvokePolymorphic().getProto() : null, 66 outValue, 67 inValues); 68 } 69 70 if (template.isInvokeNewArray()) { 71 return new InvokeNewArray(template.asInvokeNewArray().getArrayType(), outValue, inValues); 72 } 73 74 assert template.isInvokeCustom(); 75 InvokeCustom custom = template.asInvokeCustom(); 76 return new InvokeCustom(custom.getCallSite(), outValue, inValues); 77 } 78 getType()79 abstract public Type getType(); 80 arguments()81 public List<Value> arguments() { 82 return inValues; 83 } 84 requiredArgumentRegisters()85 public int requiredArgumentRegisters() { 86 int registers = 0; 87 for (Value inValue : inValues) { 88 registers += inValue.requiredRegisters(); 89 } 90 return registers; 91 } 92 argumentRegisterValue(int i, DexBuilder builder)93 protected int argumentRegisterValue(int i, DexBuilder builder) { 94 assert needsRangedInvoke(builder); 95 if (i < arguments().size()) { 96 // If argument values flow into ranged invokes, all the ranged invoke arguments 97 // are arguments to this method in order. Therefore, we use the incoming registers 98 // for the ranged invoke arguments. We know that arguments are always available there. 99 // If argument reuse is allowed there is no splitting and if argument reuse is disallowed 100 // the argument registers are never overwritten. 101 return builder.argumentOrAllocateRegister(arguments().get(i), getNumber()); 102 } 103 return 0; 104 } 105 fillArgumentRegisters(DexBuilder builder, int[] registers)106 protected int fillArgumentRegisters(DexBuilder builder, int[] registers) { 107 int i = 0; 108 for (Value value : arguments()) { 109 int register = builder.allocatedRegister(value, getNumber()); 110 for (int j = 0; j < value.requiredRegisters(); j++) { 111 assert i < 5; 112 registers[i++] = register++; 113 } 114 } 115 return i; 116 } 117 118 protected boolean hasHighArgumentRegister(DexBuilder builder) { 119 for (Value value : arguments()) { 120 if (builder.argumentValueUsesHighRegister(value, getNumber())) { 121 return true; 122 } 123 } 124 return false; 125 } 126 127 protected boolean argumentsConsecutive(DexBuilder builder) { 128 Value value = arguments().get(0); 129 int next = builder.argumentOrAllocateRegister(value, getNumber()) + value.requiredRegisters(); 130 for (int i = 1; i < arguments().size(); i++) { 131 value = arguments().get(i); 132 assert next == builder.argumentOrAllocateRegister(value, getNumber()); 133 next += value.requiredRegisters(); 134 } 135 return true; 136 } 137 138 protected void addInvokeAndMoveResult(com.android.tools.r8.code.Instruction instruction, DexBuilder builder) { 139 if (outValue != null && outValue.needsRegister()) { 140 int register = builder.allocatedRegister(outValue, getNumber()); 141 com.android.tools.r8.code.Instruction moveResult; 142 switch (outType()) { 143 case SINGLE: 144 moveResult = new MoveResult(register); 145 break; 146 case WIDE: 147 moveResult = new MoveResultWide(register); 148 break; 149 case OBJECT: 150 moveResult = new MoveResultObject(register); 151 break; 152 default: 153 throw new Unreachable("Unexpected result type " + outType()); 154 } 155 builder.add(this, new com.android.tools.r8.code.Instruction[]{instruction, moveResult}); 156 } else { 157 builder.add(this, instruction); 158 } 159 } 160 161 @Override 162 public boolean instructionTypeCanThrow() { 163 return true; 164 } 165 166 @Override 167 public int maxInValueRegister() { 168 if (requiredArgumentRegisters() > 5) { 169 return Constants.U16BIT_MAX; 170 } 171 if (argumentsAreConsecutiveInputArguments()) { 172 return Constants.U16BIT_MAX; 173 } 174 return Constants.U4BIT_MAX; 175 } 176 177 private boolean argumentsAreConsecutiveInputArguments() { 178 if (arguments().size() == 0) { 179 return false; 180 } 181 Value current = arguments().get(0); 182 if (!current.isArgument()) { 183 return false; 184 } 185 for (int i = 1; i < arguments().size(); i++) { 186 Value next = arguments().get(i); 187 if (current.getNextConsecutive() != next) { 188 return false; 189 } 190 current = next; 191 } 192 return true; 193 } 194 195 private boolean argumentsAreConsecutiveInputArgumentsWithHighRegisters( 196 DexBuilder builder) { 197 if (!argumentsAreConsecutiveInputArguments()) { 198 return false; 199 } 200 Value lastArgument = arguments().get(arguments().size() - 1); 201 return builder.argumentOrAllocateRegister(lastArgument, getNumber()) > Constants.U4BIT_MAX; 202 } 203 204 protected boolean needsRangedInvoke(DexBuilder builder) { 205 return requiredArgumentRegisters() > 5 206 || hasHighArgumentRegister(builder) 207 || argumentsAreConsecutiveInputArgumentsWithHighRegisters(builder); 208 } 209 210 @Override maxOutValueRegister()211 public int maxOutValueRegister() { 212 return Constants.U8BIT_MAX; 213 } 214 215 abstract protected String getTypeString(); 216 217 @Override getInstructionName()218 public String getInstructionName() { 219 return "Invoke-" + getTypeString(); 220 } 221 222 // This method is used for inlining. 223 // It returns the target method iff this invoke has only one target. 224 abstract public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo); 225 226 @Override isInvoke()227 public boolean isInvoke() { 228 return true; 229 } 230 231 @Override asInvoke()232 public Invoke asInvoke() { 233 return this; 234 } 235 } 236