blob: fe9e5b2b6b4d34812b0aa4389c153a13d6ab55af [file] [log] [blame]
// Copyright (c) 2022, 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.lightir;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.ir.code.IRMetadata;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.lightir.LIRCode.PositionEntry;
import com.android.tools.r8.utils.ListUtils;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
import java.util.List;
/**
* Builder for constructing LIR code from IR.
*
* @param <V> Type of SSA values. This is abstract to ensure that value internals are not used in
* building.
*/
public class LIRBuilder<V> {
public interface ValueIndexGetter<V> {
int getValueIndex(V value);
}
private final ByteArrayWriter byteWriter = new ByteArrayWriter();
private final LIRWriter writer = new LIRWriter(byteWriter);
private final Reference2IntMap<DexItem> constants;
private final ValueIndexGetter<V> valueIndexGetter;
private final List<PositionEntry> positionTable;
private int argumentCount = 0;
private int instructionCount = 0;
private IRMetadata metadata = null;
private Position currentPosition;
private Position flushedPosition;
public LIRBuilder(DexMethod method, ValueIndexGetter<V> valueIndexGetter) {
constants = new Reference2IntOpenHashMap<>();
positionTable = new ArrayList<>();
this.valueIndexGetter = valueIndexGetter;
currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
flushedPosition = currentPosition;
}
public LIRBuilder<V> setCurrentPosition(Position position) {
assert position != null;
assert position != Position.none();
currentPosition = position;
return this;
}
private void setPositionIndex(int instructionIndex, Position position) {
assert positionTable.isEmpty()
|| ListUtils.last(positionTable).fromInstructionIndex < instructionIndex;
positionTable.add(new PositionEntry(instructionIndex, position));
}
private int getConstantIndex(DexItem item) {
int nextIndex = constants.size();
Integer oldIndex = constants.putIfAbsent(item, nextIndex);
return oldIndex != null ? oldIndex : nextIndex;
}
private int constantIndexSize(DexItem item) {
return 4;
}
private void writeConstantIndex(DexItem item) {
int index = getConstantIndex(item);
ByteUtils.writeEncodedInt(index, writer::writeOperand);
}
private int getValueIndex(V value) {
return valueIndexGetter.getValueIndex(value);
}
private int valueIndexSize(int index) {
return ByteUtils.intEncodingSize(index);
}
private void writeValueIndex(int index) {
ByteUtils.writeEncodedInt(index, writer::writeOperand);
}
public LIRBuilder<V> setMetadata(IRMetadata metadata) {
this.metadata = metadata;
return this;
}
public LIRBuilder<V> writeConstantReferencingInstruction(int opcode, DexItem item) {
writer.writeInstruction(opcode, constantIndexSize(item));
writeConstantIndex(item);
return this;
}
public LIRBuilder<V> addArgument(int index, boolean knownToBeBoolean) {
// Arguments are implicitly given by method descriptor and not an actual instruction.
assert argumentCount == index;
argumentCount++;
return this;
}
private void addInstruction() {
if (!currentPosition.equals(flushedPosition)) {
setPositionIndex(instructionCount, currentPosition);
flushedPosition = currentPosition;
}
++instructionCount;
}
public LIRBuilder<V> addConstNull() {
addInstruction();
writer.writeOneByteInstruction(LIROpcodes.ACONST_NULL);
return this;
}
public LIRBuilder<V> addConstInt(int value) {
addInstruction();
if (0 <= value && value <= 5) {
writer.writeOneByteInstruction(LIROpcodes.ICONST_0 + value);
} else {
writer.writeInstruction(LIROpcodes.ICONST, ByteUtils.intEncodingSize(value));
ByteUtils.writeEncodedInt(value, writer::writeOperand);
}
return this;
}
public LIRBuilder<V> addConstString(DexString string) {
addInstruction();
return writeConstantReferencingInstruction(LIROpcodes.LDC, string);
}
public LIRBuilder<V> addStaticGet(DexField field) {
addInstruction();
return writeConstantReferencingInstruction(LIROpcodes.GETSTATIC, field);
}
public LIRBuilder<V> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) {
addInstruction();
int argumentOprandSize = constantIndexSize(method);
int[] argumentIndexes = new int[arguments.size()];
int i = 0;
for (V argument : arguments) {
int argumentIndex = getValueIndex(argument);
argumentIndexes[i++] = argumentIndex;
argumentOprandSize += valueIndexSize(argumentIndex);
}
writer.writeInstruction(opcode, argumentOprandSize);
writeConstantIndex(method);
for (int argumentIndex : argumentIndexes) {
writeValueIndex(argumentIndex);
}
return this;
}
public LIRBuilder<V> addInvokeDirect(DexMethod method, List<V> arguments) {
return addInvokeInstruction(LIROpcodes.INVOKEDIRECT, method, arguments);
}
public LIRBuilder<V> addInvokeVirtual(DexMethod method, List<V> arguments) {
return addInvokeInstruction(LIROpcodes.INVOKEVIRTUAL, method, arguments);
}
public LIRBuilder<V> addReturn(V value) {
throw new Unimplemented();
}
public LIRBuilder<V> addReturnVoid() {
addInstruction();
writer.writeOneByteInstruction(LIROpcodes.RETURN);
return this;
}
public LIRBuilder<V> addDebugPosition(Position position) {
assert currentPosition == position;
addInstruction();
writer.writeOneByteInstruction(LIROpcodes.DEBUGPOS);
return this;
}
public LIRCode build() {
assert metadata != null;
int constantsCount = constants.size();
DexItem[] constantTable = new DexItem[constantsCount];
constants.forEach((item, index) -> constantTable[index] = item);
return new LIRCode(
metadata,
constantTable,
positionTable.toArray(new PositionEntry[positionTable.size()]),
argumentCount,
byteWriter.toByteArray(),
instructionCount);
}
}