|  | // Copyright (c) 2016, 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.ir.optimize; | 
|  |  | 
|  | import com.android.tools.r8.ir.code.BasicBlock; | 
|  | import com.android.tools.r8.ir.code.CatchHandlers; | 
|  | import com.android.tools.r8.ir.code.IRCode; | 
|  | import com.android.tools.r8.ir.code.Instruction; | 
|  | import com.android.tools.r8.ir.code.Value; | 
|  | import com.android.tools.r8.ir.regalloc.RegisterAllocator; | 
|  | import com.google.common.base.Equivalence; | 
|  | import java.util.Arrays; | 
|  | import java.util.List; | 
|  |  | 
|  | class BasicBlockInstructionsEquivalence extends Equivalence<BasicBlock> { | 
|  | private static final int UNKNOW_HASH = -1; | 
|  | private static final int MAX_HASH_INSTRUCTIONS = 5; | 
|  | private final RegisterAllocator allocator; | 
|  | private final int[] hashes; | 
|  |  | 
|  | BasicBlockInstructionsEquivalence(IRCode code, RegisterAllocator allocator) { | 
|  | this.allocator = allocator; | 
|  | hashes = new int[code.getCurrentBlockNumber() + 1]; | 
|  | Arrays.fill(hashes, UNKNOW_HASH); | 
|  | } | 
|  |  | 
|  | private boolean hasIdenticalInstructions(BasicBlock first, BasicBlock second) { | 
|  | List<Instruction> instructions0 = first.getInstructions(); | 
|  | List<Instruction> instructions1 = second.getInstructions(); | 
|  | if (instructions0.size() != instructions1.size()) { | 
|  | return false; | 
|  | } | 
|  | for (int i = 0; i < instructions0.size(); i++) { | 
|  | Instruction i0 = instructions0.get(i); | 
|  | Instruction i1 = instructions1.get(i); | 
|  | if (!i0.identicalAfterRegisterAllocation(i1, allocator)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!allocator.hasEqualTypesAtEntry(first, second)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | CatchHandlers<BasicBlock> handlers0 = first.getCatchHandlers(); | 
|  | CatchHandlers<BasicBlock> handlers1 = second.getCatchHandlers(); | 
|  | if (!handlers0.equals(handlers1)) { | 
|  | return false; | 
|  | } | 
|  | // Normal successors are equal based on equality of the instruction stream. Verify that here. | 
|  | assert verifyAllSuccessors(first.getSuccessors(), second.getSuccessors()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private boolean verifyAllSuccessors(List<BasicBlock> successors0, List<BasicBlock> successors1) { | 
|  | if (successors0.size() != successors1.size()) { | 
|  | return false; | 
|  | } | 
|  | for (int i = 0; i < successors0.size(); i++) { | 
|  | if (successors0.get(i) != successors1.get(i)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected boolean doEquivalent(BasicBlock a, BasicBlock b) { | 
|  | return hasIdenticalInstructions(a, b); | 
|  | } | 
|  |  | 
|  | void clearComputedHash(BasicBlock basicBlock) { | 
|  | hashes[basicBlock.getNumber()] = UNKNOW_HASH; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected int doHash(BasicBlock basicBlock) { | 
|  | int hash = hashes[basicBlock.getNumber()]; | 
|  | if (hash != UNKNOW_HASH) { | 
|  | assert hash == computeHash(basicBlock); | 
|  | return hash; | 
|  | } | 
|  | hash = computeHash(basicBlock); | 
|  | hashes[basicBlock.getNumber()] = hash; | 
|  | return hash; | 
|  | } | 
|  |  | 
|  | private int computeHash(BasicBlock basicBlock) { | 
|  | List<Instruction> instructions = basicBlock.getInstructions(); | 
|  | int hash = instructions.size(); | 
|  | for (int i = 0; i < instructions.size() && i < MAX_HASH_INSTRUCTIONS; i++) { | 
|  | Instruction instruction = instructions.get(i); | 
|  | int hashPart = 0; | 
|  | if (instruction.outValue() != null && instruction.outValue().needsRegister()) { | 
|  | hashPart += allocator.getRegisterForValue(instruction.outValue(), instruction.getNumber()); | 
|  | } | 
|  | for (Value inValue : instruction.inValues()) { | 
|  | hashPart = hashPart << 4; | 
|  | if (inValue.needsRegister()) { | 
|  | hashPart += allocator.getRegisterForValue(inValue, instruction.getNumber()); | 
|  | } | 
|  | } | 
|  | hash = hash * 3 + hashPart; | 
|  | } | 
|  | return hash; | 
|  | } | 
|  | } |