| // Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 | package com.android.tools.r8.cf.code; | 
 |  | 
 | import com.android.tools.r8.cf.CfPrinter; | 
 | import com.android.tools.r8.errors.CompilationError; | 
 | import com.android.tools.r8.errors.Unreachable; | 
 | import com.android.tools.r8.graph.AppView; | 
 | import com.android.tools.r8.graph.DexType; | 
 | import com.android.tools.r8.graph.GraphLense; | 
 | import com.android.tools.r8.ir.code.ValueType; | 
 | import com.android.tools.r8.ir.conversion.CfSourceCode; | 
 | import com.android.tools.r8.ir.conversion.CfState; | 
 | import com.android.tools.r8.ir.conversion.CfState.Slot; | 
 | import com.android.tools.r8.ir.conversion.IRBuilder; | 
 | import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; | 
 | import com.android.tools.r8.ir.optimize.InliningConstraints; | 
 | import com.android.tools.r8.naming.NamingLens; | 
 | import org.objectweb.asm.MethodVisitor; | 
 | import org.objectweb.asm.Opcodes; | 
 |  | 
 | public class CfStackInstruction extends CfInstruction { | 
 |  | 
 |   public enum Opcode { | 
 |     Pop(Opcodes.POP), | 
 |     Pop2(Opcodes.POP2), | 
 |     Dup(Opcodes.DUP), | 
 |     DupX1(Opcodes.DUP_X1), | 
 |     DupX2(Opcodes.DUP_X2), | 
 |     Dup2(Opcodes.DUP2), | 
 |     Dup2X1(Opcodes.DUP2_X1), | 
 |     Dup2X2(Opcodes.DUP2_X2), | 
 |     Swap(Opcodes.SWAP); | 
 |  | 
 |     private final int opcode; | 
 |  | 
 |     Opcode(int opcode) { | 
 |       this.opcode = opcode; | 
 |     } | 
 |   } | 
 |  | 
 |   private final Opcode opcode; | 
 |  | 
 |   public static CfStackInstruction fromAsm(int opcode) { | 
 |     switch (opcode) { | 
 |       case Opcodes.POP: | 
 |         return new CfStackInstruction(Opcode.Pop); | 
 |       case Opcodes.POP2: | 
 |         return new CfStackInstruction(Opcode.Pop2); | 
 |       case Opcodes.DUP: | 
 |         return new CfStackInstruction(Opcode.Dup); | 
 |       case Opcodes.DUP_X1: | 
 |         return new CfStackInstruction(Opcode.DupX1); | 
 |       case Opcodes.DUP_X2: | 
 |         return new CfStackInstruction(Opcode.DupX2); | 
 |       case Opcodes.DUP2: | 
 |         return new CfStackInstruction(Opcode.Dup2); | 
 |       case Opcodes.DUP2_X1: | 
 |         return new CfStackInstruction(Opcode.Dup2X1); | 
 |       case Opcodes.DUP2_X2: | 
 |         return new CfStackInstruction(Opcode.Dup2X2); | 
 |       case Opcodes.SWAP: | 
 |         return new CfStackInstruction(Opcode.Swap); | 
 |       default: | 
 |         throw new Unreachable("Invalid opcode for CfStackInstruction"); | 
 |     } | 
 |   } | 
 |  | 
 |   public static CfStackInstruction popType(ValueType type) { | 
 |     return new CfStackInstruction(type.isWide() ? Opcode.Pop2 : Opcode.Pop); | 
 |   } | 
 |  | 
 |   public CfStackInstruction(Opcode opcode) { | 
 |     this.opcode = opcode; | 
 |   } | 
 |  | 
 |   @Override | 
 |   public void write(MethodVisitor visitor, NamingLens lens) { | 
 |     visitor.visitInsn(opcode.opcode); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public void print(CfPrinter printer) { | 
 |     printer.print(this); | 
 |   } | 
 |  | 
 |   public Opcode getOpcode() { | 
 |     return opcode; | 
 |   } | 
 |  | 
 |   @Override | 
 |   public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) { | 
 |     switch (opcode) { | 
 |       case Pop: | 
 |         { | 
 |           Slot pop = state.pop(); | 
 |           assert !pop.type.isWide(); | 
 |           break; | 
 |         } | 
 |       case Pop2: | 
 |         { | 
 |           Slot pop1 = state.pop(); | 
 |           if (!pop1.type.isWide()) { | 
 |             Slot pop2 = state.pop(); | 
 |             assert !pop2.type.isWide(); | 
 |           } | 
 |           break; | 
 |         } | 
 |       case Dup: | 
 |         { | 
 |           Slot dupValue = state.peek(); | 
 |           assert !dupValue.type.isWide(); | 
 |           builder.addMove(dupValue.type, state.push(dupValue).register, dupValue.register); | 
 |           break; | 
 |         } | 
 |       case DupX1: | 
 |         { | 
 |           Slot value1 = state.pop(); | 
 |           Slot value2 = state.pop(); | 
 |           assert !value1.type.isWide(); | 
 |           assert !value2.type.isWide(); | 
 |           dup1x1(builder, state, value1, value2); | 
 |           break; | 
 |         } | 
 |       case DupX2: | 
 |         { | 
 |           Slot value1 = state.pop(); | 
 |           Slot value2 = state.pop(); | 
 |           assert !value1.type.isWide(); | 
 |           if (value2.type.isWide()) { | 
 |             dup1x1(builder, state, value1, value2); | 
 |           } else { | 
 |             Slot value3 = state.pop(); | 
 |             assert !value3.type.isWide(); | 
 |             dup1x2(builder, state, value1, value2, value3); | 
 |           } | 
 |           break; | 
 |         } | 
 |       case Dup2: | 
 |         { | 
 |           Slot value1 = state.peek(); | 
 |           if (value1.type.isWide()) { | 
 |             builder.addMove(value1.type, state.push(value1).register, value1.register); | 
 |           } else { | 
 |             Slot value2 = state.peek(1); | 
 |             builder.addMove(value2.type, state.push(value2).register, value2.register); | 
 |             builder.addMove(value1.type, state.push(value1).register, value1.register); | 
 |           } | 
 |           break; | 
 |         } | 
 |       case Dup2X1: | 
 |         { | 
 |           Slot value1 = state.pop(); | 
 |           Slot value2 = state.pop(); | 
 |           assert !value2.type.isWide(); | 
 |           if (value1.type.isWide()) { | 
 |             dup1x1(builder, state, value1, value2); | 
 |           } else { | 
 |             Slot value3 = state.pop(); | 
 |             assert !value3.type.isWide(); | 
 |             dup2x1(builder, state, value1, value2, value3); | 
 |           } | 
 |           break; | 
 |         } | 
 |       case Dup2X2: | 
 |         { | 
 |           Slot value1 = state.pop(); | 
 |           Slot value2 = state.pop(); | 
 |           if (value1.type.isWide() && value2.type.isWide()) { | 
 |             // Input stack: ..., value2, value1 | 
 |             // Output stack: ..., value1, value2, value1 | 
 |             dup1x1(builder, state, value1, value2); | 
 |             break; | 
 |           } | 
 |           // Remaining valid cases are: | 
 |           // - single, single, wide | 
 |           // - wide, single, single | 
 |           // - single, single, single, single | 
 |           Slot value3 = state.pop(); | 
 |           if (value1.type.isWide()) { | 
 |             if (value3.type.isWide()) { | 
 |               throw new CompilationError("Invalid dup2x2 with types: wide, single, wide"); | 
 |             } | 
 |             // Input stack: ..., value3, value2, value1 | 
 |             // Output stack: ..., value1, value3, value2, value1 | 
 |             dup1x2(builder, state, value1, value2, value3); | 
 |             break; | 
 |           } | 
 |           if (value2.type.isWide()) { | 
 |             throw new CompilationError("Invalid dup2x2 with types: ..., wide, single"); | 
 |           } | 
 |           if (value3.type.isWide()) { | 
 |             dup2x1(builder, state, value1, value2, value3); | 
 |             break; | 
 |           } else { | 
 |             Slot value4 = state.pop(); | 
 |             if (value4.type.isWide()) { | 
 |               throw new CompilationError("Invalid dup2x2 with types: wide, single, single, single"); | 
 |             } | 
 |             // Input stack: ..., value4, value3, value2, value1 | 
 |             // Output stack: ..., value2, value1, value4, value3, value2, value1 | 
 |             dup2x2(builder, state, value1, value2, value3, value4); | 
 |             break; | 
 |           } | 
 |         } | 
 |       case Swap: | 
 |         { | 
 |           Slot value1 = state.pop(); | 
 |           Slot value2 = state.pop(); | 
 |           assert !value1.type.isWide(); | 
 |           assert !value2.type.isWide(); | 
 |           // Input stack: ..., value2, value1 | 
 |           dup1x1(builder, state, value1, value2); | 
 |           // Current stack: ..., value1, value2, value1copy | 
 |           state.pop(); | 
 |           // Output stack: ..., value1, value2 | 
 |           break; | 
 |         } | 
 |     } | 
 |   } | 
 |  | 
 |   // Dup top of stack across/X one other value (width independent). | 
 |   private void dup1x1(IRBuilder builder, CfState state, Slot inValue1, Slot inValue2) { | 
 |     // Input stack: ..., A:inValue2, B:inValue1 (values already popped) | 
 |     Slot outValue1 = state.push(inValue1); | 
 |     Slot outValue2 = state.push(inValue2); | 
 |     Slot outValue1Copy = state.push(inValue1); | 
 |     // Output stack: ..., A:outValue1, B:outValue2, C:outValue1Copy | 
 |     // Move C(outValue1Copy) <- B(inValue1) | 
 |     builder.addMove(inValue1.type, outValue1Copy.register, inValue1.register); | 
 |     // Move B(outValue2) <- A(inValue2) | 
 |     builder.addMove(inValue2.type, outValue2.register, inValue2.register); | 
 |     // Move A(outValue1) <- C(outValue1Copy) | 
 |     builder.addMove(outValue1Copy.type, outValue1.register, outValue1Copy.register); | 
 |   } | 
 |  | 
 |   // Dup top of stack across/X two other values. | 
 |   // Width independent, but the two cross values can only be single for valid CF inputs. | 
 |   private void dup1x2( | 
 |       IRBuilder builder, CfState state, Slot inValue1, Slot inValue2, Slot inValue3) { | 
 |     // Input stack: ..., A:inValue3, B:inValue2, C:inValue1 (values already popped) | 
 |     Slot outValue1 = state.push(inValue1); | 
 |     Slot outValue3 = state.push(inValue3); | 
 |     Slot outValue2 = state.push(inValue2); | 
 |     Slot outValue1Copy = state.push(inValue1); | 
 |     // Output stack: ..., A:outValue1, B:outValue3, C:outValue2, D:outValue1Copy | 
 |     // Move D(outValue1Copy) <- C(inValue1) | 
 |     builder.addMove(inValue1.type, outValue1Copy.register, inValue1.register); | 
 |     // Move C(outValue2) <- B(inValue2) | 
 |     builder.addMove(inValue2.type, outValue2.register, inValue2.register); | 
 |     // Move B(outValue3) <- A(inValue3) | 
 |     builder.addMove(inValue3.type, outValue3.register, inValue3.register); | 
 |     // Move A(outValue1) <- D(outValue1Copy) | 
 |     builder.addMove(outValue1Copy.type, outValue1.register, outValue1Copy.register); | 
 |   } | 
 |  | 
 |   // Dup top 2 elements of the stack across/X one other value (width independent). | 
 |   private void dup2x1( | 
 |       IRBuilder builder, CfState state, Slot inValue1, Slot inValue2, Slot inValue3) { | 
 |     // Input stack: ..., A:inValue3, B:inValue2, C:inValue1 (already popped). | 
 |     Slot outValue2 = state.push(inValue2); | 
 |     Slot outValue1 = state.push(inValue1); | 
 |     Slot outValue3 = state.push(inValue3); | 
 |     Slot outValue2Copy = state.push(inValue2); | 
 |     Slot outValue1Copy = state.push(inValue1); | 
 |     // Output: ..., A:outValue2, B:outValue1, C:outValue3, D:outValue2Copy, E:outValue1Copy | 
 |     // Move E(outValue1Copy) <- C(value1) | 
 |     builder.addMove(inValue1.type, outValue1Copy.register, inValue1.register); | 
 |     // Move D(outValue2Copy) <- B(value2) | 
 |     builder.addMove(inValue2.type, outValue2Copy.register, inValue2.register); | 
 |     // Move C(outValue3) <- A(value3) | 
 |     builder.addMove(inValue3.type, outValue3.register, inValue3.register); | 
 |     // Move B(outValue1) <- E(outValue1Copy) | 
 |     builder.addMove(inValue1.type, outValue1.register, outValue1Copy.register); | 
 |     // Move A(outValue2) <- D(outValue2Copy) | 
 |     builder.addMove(inValue2.type, outValue2.register, outValue2Copy.register); | 
 |   } | 
 |  | 
 |   // Dup top two elements of the stack across/X two other values. | 
 |   // Width independent, but the four values can only be single for valid CF inputs. | 
 |   private void dup2x2( | 
 |       IRBuilder builder, | 
 |       CfState state, | 
 |       Slot inValue1, | 
 |       Slot inValue2, | 
 |       Slot inValue3, | 
 |       Slot inValue4) { | 
 |     // Input stack: ..., A:inValue4, B:inValue3, C:inValue2, D:inValue1 (values already popped) | 
 |     Slot outValue2 = state.push(inValue2); | 
 |     Slot outValue1 = state.push(inValue1); | 
 |     Slot outValue4 = state.push(inValue4); | 
 |     Slot outValue3 = state.push(inValue3); | 
 |     Slot outValue2Copy = state.push(inValue2); | 
 |     Slot outValue1Copy = state.push(inValue1); | 
 |     // Output stack: | 
 |     // ..., A:outValue2, B:outValue1, C:outValue4, D:outValue3, E:outValue2Copy, F:outValue1Copy | 
 |     // Move F(outValue1Copy) <- D(inValue1) | 
 |     builder.addMove(inValue1.type, outValue1Copy.register, inValue1.register); | 
 |     // Move E(outValue2Copy) <- C(inValue2) | 
 |     builder.addMove(inValue2.type, outValue2Copy.register, inValue2.register); | 
 |     // Move D(outValue3) <- B(inValue3) | 
 |     builder.addMove(inValue3.type, outValue3.register, inValue3.register); | 
 |     // Move C(outValue4) <- A(inValue4) | 
 |     builder.addMove(inValue4.type, outValue4.register, inValue4.register); | 
 |     // Move B(outValue1) <- F(outValue1Copy) | 
 |     builder.addMove(outValue1Copy.type, outValue1.register, outValue1Copy.register); | 
 |     // Move A(outValue2) <- E(outValue2Copy) | 
 |     builder.addMove(outValue2Copy.type, outValue2.register, outValue2Copy.register); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public boolean emitsIR() { | 
 |     return false; | 
 |   } | 
 |  | 
 |   @Override | 
 |   public ConstraintWithTarget inliningConstraint( | 
 |       InliningConstraints inliningConstraints, | 
 |       DexType invocationContext, | 
 |       GraphLense graphLense, | 
 |       AppView<?> appView) { | 
 |     return ConstraintWithTarget.ALWAYS; | 
 |   } | 
 | } |