blob: f539cd4b6bcaf436938202cfed3cf2b5a1da8446 [file] [log] [blame]
// Copyright (c) 2024, 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.optimize.compose;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.And;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.LinearFlowInstructionListIterator;
import com.android.tools.r8.ir.code.Or;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Shl;
import com.android.tools.r8.ir.code.Shr;
public class ComposeUtils {
/**
* Checks if the given code object executes the following instructions.
*
* <pre>
* private const val changedLowBitMask = 0b001_001_001_001_001_001_001_001_001_001_0
* private const val changedHighBitMask = changedLowBitMask shl 1
* private const val changedMask = (changedLowBitMask or changedHighBitMask).inv()
*
* internal fun updateChangedFlags(flags: Int): Int {
* val lowBits = flags and changedLowBitMask
* val highBits = flags and changedHighBitMask
* return ((flags and changedMask) or
* (lowBits or (highBits shr 1)) or ((lowBits shl 1) and highBits))
* }
* </pre>
*/
public static boolean isUpdateChangedFlags(IRCode code, DexItemFactory factory) {
ProgramMethod method = code.context();
if (!method.getAccessFlags().isStatic()
|| method.getArity() != 1
|| method.getParameter(0).isNotIdenticalTo(factory.intType)
|| method.getReturnType().isNotIdenticalTo(factory.intType)) {
return false;
}
LinearFlowInstructionListIterator instructionIterator =
new LinearFlowInstructionListIterator(code);
Argument argument = instructionIterator.next().asArgument();
assert argument != null;
ConstNumber changedLowBitMask =
instructionIterator.next().asConstNumber(0b001_001_001_001_001_001_001_001_001_001_0);
if (changedLowBitMask == null) {
return false;
}
And lowBits =
instructionIterator.next().asAnd(argument.outValue(), changedLowBitMask.outValue());
if (lowBits == null) {
return false;
}
ConstNumber changedHighBitMask =
instructionIterator.next().asConstNumber(0b010_010_010_010_010_010_010_010_010_010_0);
if (changedHighBitMask == null) {
return false;
}
And highBits =
instructionIterator.next().asAnd(argument.outValue(), changedHighBitMask.outValue());
if (highBits == null) {
return false;
}
ConstNumber changedMask =
instructionIterator.next().asConstNumber(0b1_100_100_100_100_100_100_100_100_100_100_1);
if (changedMask == null) {
return false;
}
And changedBits = instructionIterator.next().asAnd(argument.outValue(), changedMask.outValue());
if (changedBits == null) {
return false;
}
ConstNumber one = instructionIterator.next().asConstNumber(1);
if (one == null) {
return false;
}
Shr highBitsShrOne = instructionIterator.next().asShr(highBits.outValue(), one.outValue());
if (highBitsShrOne == null) {
return false;
}
Or lowBitsOrHighBitsShrOne =
instructionIterator.next().asOr(lowBits.outValue(), highBitsShrOne.outValue());
if (lowBitsOrHighBitsShrOne == null) {
return false;
}
Or changedBitsOrLowBitsOrHighBitsShrOne =
instructionIterator.next().asOr(changedBits.outValue(), lowBitsOrHighBitsShrOne.outValue());
if (changedBitsOrLowBitsOrHighBitsShrOne == null) {
return false;
}
ConstNumber oneAgain = instructionIterator.next().asConstNumber(1);
if (oneAgain == null) {
return false;
}
Shl lowBitsShlOne = instructionIterator.next().asShl(lowBits.outValue(), oneAgain.outValue());
if (lowBitsShlOne == null) {
return false;
}
And lowBitsShlOneAndHighBits =
instructionIterator.next().asAnd(lowBitsShlOne.outValue(), highBits.outValue());
if (lowBitsShlOneAndHighBits == null) {
return false;
}
Or result =
instructionIterator
.next()
.asOr(
changedBitsOrLowBitsOrHighBitsShrOne.outValue(),
lowBitsShlOneAndHighBits.outValue());
if (result == null) {
return false;
}
Return theReturn = instructionIterator.next().asReturn();
return theReturn != null && theReturn.returnValue() == result.outValue();
}
}