|  | // 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.Unimplemented; | 
|  | import com.android.tools.r8.errors.Unreachable; | 
|  | 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.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 value = state.pop(); | 
|  | if (!value.type.isWide()) { | 
|  | state.pop(); | 
|  | throw new Unimplemented("Building IR for Pop2 of narrow value not supported"); | 
|  | } | 
|  | 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(); | 
|  | dupX1(builder, state, value1, value2); | 
|  | break; | 
|  | } | 
|  | case DupX2: | 
|  | { | 
|  | Slot value1 = state.pop(); | 
|  | Slot value2 = state.pop(); | 
|  | assert !value1.type.isWide(); | 
|  | if (value2.type.isWide()) { | 
|  | dupX1(builder, state, value1, value2); | 
|  | throw new Unimplemented("Building IR for DupX2 of wide value not supported"); | 
|  | } else { | 
|  | Slot value3 = state.pop(); | 
|  | assert !value3.type.isWide(); | 
|  | // Input stack: ..., A:value3, B:value2, C:value1 | 
|  | Slot outValue1 = state.push(value1); | 
|  | Slot outValue3 = state.push(value3); | 
|  | Slot outValue2 = state.push(value2); | 
|  | Slot outValue1Copy = state.push(value1); | 
|  | // Output stack: ..., A:outValue1, B:outValue3, C:outValue2, D:outValue1Copy | 
|  | // Move D(outValue1Copy) <- C(value1) | 
|  | builder.addMove(value1.type, outValue1Copy.register, value1.register); | 
|  | // Move C(outValue2) <- B(value2) | 
|  | builder.addMove(value2.type, outValue2.register, value2.register); | 
|  | // Move B(outValue3) <- A(value3) | 
|  | builder.addMove(value3.type, outValue3.register, value3.register); | 
|  | // Move A(outValue1) <- D(outValue1Copy) | 
|  | builder.addMove(value1.type, outValue1.register, outValue1Copy.register); | 
|  | } | 
|  | 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()) { | 
|  | dupX1(builder, state, value1, value2); | 
|  | } else { | 
|  | // Input stack: ..., A:value3, B:value2, C:value1 | 
|  | Slot value3 = state.pop(); | 
|  | assert !value3.type.isWide(); | 
|  | Slot outValue2 = state.push(value2); | 
|  | Slot outValue1 = state.push(value1); | 
|  | Slot outValue3 = state.push(value3); | 
|  | Slot outValue2Copy = state.push(value2); | 
|  | Slot outValue1Copy = state.push(value1); | 
|  | // Output: ..., A:outValue2, B:outValue1, C:outValue3, D:outValue2Copy, E:outValue1Copy | 
|  | // Move E(outValue1Copy) <- C(value1) | 
|  | builder.addMove(value1.type, outValue1Copy.register, value1.register); | 
|  | // Move D(outValue2Copy) <- B(value2) | 
|  | builder.addMove(value2.type, outValue2Copy.register, value2.register); | 
|  | // Move C(outValue3) <- A(value3) | 
|  | builder.addMove(value3.type, outValue3.register, value3.register); | 
|  | // Move B(outValue1) <- E(outValue1Copy) | 
|  | builder.addMove(value1.type, outValue1.register, outValue1Copy.register); | 
|  | // Move A(outValue2) <- D(outValue2Copy) | 
|  | builder.addMove(value2.type, outValue2.register, outValue2Copy.register); | 
|  | throw new Unimplemented("Building IR for Dup2X1 narrow not supported"); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case Dup2X2: | 
|  | { | 
|  | // Input stack: | 
|  | Slot value1 = state.pop(); | 
|  | Slot value2 = state.pop(); | 
|  | if (value1.type.isWide() && value2.type.isWide()) { | 
|  | // Input stack: ..., value2, value1 | 
|  | // Output stack: ..., value1, value2, value1 | 
|  | dupX1(builder, state, value1, value2); | 
|  | } else if (value1.type.isWide()) { | 
|  | throw new Unimplemented("Building IR for Dup2X2 narrow,narrow,wide not supported"); | 
|  | } else if (value2.type.isWide()) { | 
|  | throw new Unimplemented("Building IR for Dup2X2 wide,narrow,narrow not supported"); | 
|  | } else { | 
|  | throw new Unimplemented( | 
|  | "Building IR for Dup2X2 narrow,narrow,narrow,narrow not supported"); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case Swap: | 
|  | { | 
|  | Slot value1 = state.pop(); | 
|  | Slot value2 = state.pop(); | 
|  | assert !value1.type.isWide(); | 
|  | assert !value2.type.isWide(); | 
|  | // Input stack: ..., value2, value1 | 
|  | dupX1(builder, state, value1, value2); | 
|  | // Current stack: ..., value1, value2, value1copy | 
|  | state.pop(); | 
|  | // Output stack: ..., value1, value2 | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void dupX1(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); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean emitsIR() { | 
|  | return false; | 
|  | } | 
|  | } |