Implement IR to CF conversion for the hello-world example program.
Change-Id: If83e89b992bd56559e92673bd0616e4de1493f5a
diff --git a/build.gradle b/build.gradle
index c97aa9d..61360dc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -170,6 +170,7 @@
compile group: 'org.ow2.asm', name: 'asm', version: '6.0'
compile group: 'org.ow2.asm', name: 'asm-commons', version: '6.0'
compile group: 'org.ow2.asm', name: 'asm-tree', version: '6.0'
+ compile group: 'org.ow2.asm', name: 'asm-analysis', version: '6.0'
compile group: 'org.ow2.asm', name: 'asm-util', version: '6.0'
testCompile sourceSets.examples.output
testCompile 'junit:junit:4.12'
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
new file mode 100644
index 0000000..62f1537
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, 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.graph.DexString;
+import org.objectweb.asm.MethodVisitor;
+
+public class CfConstString extends CfInstruction {
+
+ private final DexString string;
+
+ public CfConstString(DexString string) {
+ this.string = string;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitLdcInsn(string.toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
new file mode 100644
index 0000000..915a491
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2017, 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 org.objectweb.asm.MethodVisitor;
+
+public abstract class CfInstruction {
+
+ public abstract void write(MethodVisitor visitor);
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
new file mode 100644
index 0000000..0e927ac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2017, 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.graph.DexMethod;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class CfInvoke extends CfInstruction {
+
+ private final DexMethod method;
+ private final int opcode;
+
+ public CfInvoke(int opcode, DexMethod method) {
+ assert Opcodes.INVOKEVIRTUAL <= opcode && opcode <= Opcodes.INVOKEDYNAMIC;
+ this.opcode = opcode;
+ this.method = method;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ String owner = Type.getType(method.getHolder().toDescriptorString()).getInternalName();
+ String name = method.name.toString();
+ String desc = method.proto.toDescriptorString();
+ boolean iface = method.holder.isInterface();
+ visitor.visitMethodInsn(opcode, owner, name, desc, iface);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
new file mode 100644
index 0000000..0d63eaf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, 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.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.CfBuilder.LocalType;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfLoad extends CfInstruction {
+
+ private final int var;
+ private final LocalType type;
+
+ public CfLoad(LocalType type, int var) {
+ this.var = var;
+ this.type = type;
+ }
+
+ private int getLoadType() {
+ switch (type) {
+ case REFERENCE:
+ return Opcodes.ALOAD;
+ case INTEGER:
+ return Opcodes.ILOAD;
+ case FLOAT:
+ return Opcodes.FLOAD;
+ case LONG:
+ return Opcodes.LLOAD;
+ case DOUBLE:
+ return Opcodes.DLOAD;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitVarInsn(getLoadType(), var);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
new file mode 100644
index 0000000..4cf2908
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, 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 org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfReturn extends CfInstruction {
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitInsn(Opcodes.RETURN);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticGet.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticGet.java
new file mode 100644
index 0000000..6e71cb5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticGet.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2017, 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.graph.DexField;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class CfStaticGet extends CfInstruction {
+
+ private final DexField field;
+
+ public CfStaticGet(DexField field) {
+ this.field = field;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ String owner = Type.getType(field.getHolder().toDescriptorString()).getInternalName();
+ String name = field.name.toString();
+ String desc = field.type.toDescriptorString();
+ visitor.visitFieldInsn(Opcodes.GETSTATIC, owner, name, desc);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
new file mode 100644
index 0000000..c18ae55
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, 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.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.CfBuilder.LocalType;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfStore extends CfInstruction {
+
+ private final int var;
+ private final LocalType type;
+
+ public CfStore(LocalType type, int var) {
+ this.var = var;
+ this.type = type;
+ }
+
+ private int getStoreType() {
+ switch (type) {
+ case REFERENCE:
+ return Opcodes.ASTORE;
+ case INTEGER:
+ return Opcodes.ISTORE;
+ case FLOAT:
+ return Opcodes.FSTORE;
+ case LONG:
+ return Opcodes.LSTORE;
+ case DOUBLE:
+ return Opcodes.DSTORE;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ }
+
+ @Override
+ public void write(MethodVisitor visitor) {
+ visitor.visitVarInsn(getStoreType(), var);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
new file mode 100644
index 0000000..fa19a7c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2017, 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.graph;
+
+import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+import org.objectweb.asm.MethodVisitor;
+
+public class CfCode extends Code {
+
+ private final List<CfInstruction> instructions;
+
+ public CfCode(List<CfInstruction> instructions) {
+ this.instructions = instructions;
+ }
+
+ @Override
+ public boolean isCfCode() {
+ return true;
+ }
+
+ @Override
+ public CfCode asCfCode() {
+ return this;
+ }
+
+ public void write(MethodVisitor visitor) {
+ for (CfInstruction instruction : instructions) {
+ instruction.write(visitor);
+ }
+ visitor.visitEnd();
+ // TODO(zerny): Consider computing max-stack (and frames?) height as part of building Cf.
+ visitor.visitMaxs(0, 0); // Trigger computation of max stack (and frames).
+ }
+
+ @Override
+ protected int computeHashCode() {
+ throw new Unimplemented();
+ }
+
+ @Override
+ protected boolean computeEquals(Object other) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
+ throws ApiLevelException {
+ throw new Unimplemented("Converting Java class- file bytecode to IR not yet supported");
+ }
+
+ @Override
+ public void registerReachableDefinitions(UseRegistry registry) {
+ throw new Unimplemented("Inspecting Java class-file bytecode not yet supported");
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (CfInstruction instruction : instructions) {
+ builder.append(instruction.toString()).append('\n');
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public String toString(DexEncodedMethod method, ClassNameMapper naming) {
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 47bf267..b197e93 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -24,6 +24,10 @@
public abstract String toString(DexEncodedMethod method, ClassNameMapper naming);
+ public boolean isCfCode() {
+ return false;
+ }
+
public boolean isDexCode() {
return false;
}
@@ -40,6 +44,10 @@
return Integer.MAX_VALUE;
}
+ public CfCode asCfCode() {
+ throw new Unreachable(getClass().getCanonicalName() + ".asCfCode()");
+ }
+
public DexCode asDexCode() {
throw new Unreachable(getClass().getCanonicalName() + ".asDexCode()");
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 9c0c39c..8b5b0a6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -276,6 +276,10 @@
: code.asDexCode().buildIR(this, options, valueNumberGenerator);
}
+ public void setCode(Code code) {
+ this.code = code;
+ }
+
public void setCode(
IRCode ir,
RegisterAllocator registerAllocator,
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index 0cc3962..4da6eaf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.utils.InternalOptions;
@@ -76,4 +78,14 @@
public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
return Constraint.ALWAYS;
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
+ // Arguments are defined by locals so nothing to load or store.
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.addArgument(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index 9d869aa..fe90f84 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -3,8 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfConstString;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.LocalType;
+import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.conversion.DexBuilder;
public class ConstString extends ConstInstruction {
@@ -77,4 +81,14 @@
public ConstString asConstString() {
return this;
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
+ stack.storeOutValue(this, LocalType.REFERENCE, it);
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfConstString(value));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
index 48cc8e6..dbcf734 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.utils.InternalOptions;
@@ -62,4 +64,14 @@
public boolean canBeDeadCode(IRCode code, InternalOptions options) {
return false;
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
+ // Nothing to do for positions which are not actual instructions.
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ // Nothing so far...
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 54ff97f..6e5a893 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
@@ -93,6 +96,16 @@
}
}
+ public Value swapOutValue(Value newOutValue) {
+ Value oldOutValue = outValue;
+ outValue = null;
+ setOutValue(newOutValue);
+ if (oldOutValue != null) {
+ oldOutValue.definition = null;
+ }
+ return oldOutValue;
+ }
+
public void addDebugValue(Value value) {
assert value.hasLocalInfo();
if (debugValues == null) {
@@ -118,7 +131,13 @@
return outValue.outType();
}
- public abstract void buildDex(DexBuilder builder);
+ public void buildDex(DexBuilder builder) {
+ throw new Unreachable("Unexpected instruction when converting to DEX: " + getInstructionName());
+ }
+
+ public void buildCf(CfBuilder builder) {
+ throw new Unimplemented("No support for building CF instructions for: " + getInstructionName());
+ }
public void replaceValue(Value oldValue, Value newValue) {
for (int i = 0; i < inValues.size(); i++) {
@@ -903,4 +922,8 @@
// Returns the inlining constraint for this instruction.
public abstract Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder);
+
+ public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
+ throw new Unimplemented("Implment load/store insertion for: " + getInstructionName());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index a56f840..0b6494d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -3,14 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeDirectRange;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import java.util.List;
+import org.objectweb.asm.Opcodes;
public class InvokeDirect extends InvokeMethodWithReceiver {
@@ -91,4 +94,9 @@
DexEncodedMethod lookupTarget(AppInfo appInfo) {
return appInfo.lookupDirectTarget(getInvokedMethod());
}
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfInvoke(Opcodes.INVOKESPECIAL, getInvokedMethod()));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index cd6497d..75a449b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -9,9 +9,12 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder.LocalType;
+import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.ArrayList;
import java.util.List;
public abstract class InvokeMethod extends Invoke {
@@ -76,4 +79,28 @@
}
public abstract InlineAction computeInlining(InliningOracle decider);
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
+ List<LocalType> types;
+ if (isInvokeMethodWithReceiver()) {
+ types = new ArrayList<>(method.getArity() + 1);
+ types.add(LocalType.REFERENCE);
+ } else {
+ types = new ArrayList<>(method.getArity());
+ }
+ for (DexType type : method.proto.parameters.values) {
+ types.add(LocalType.fromDexType(type));
+ }
+ stack.loadInValues(this, inValues, types, it);
+ if (method.proto.returnType.isVoidType()) {
+ return;
+ }
+ if (outValue == null) {
+ stack.popOutValue(this, MoveType.fromDexType(method.proto.returnType), it);
+ } else {
+ LocalType returnType = LocalType.fromDexType(getInvokedMethod().proto.returnType);
+ stack.storeOutValue(this, returnType, it);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index c5cfd50..41418b3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -3,13 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeVirtualRange;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import java.util.List;
+import org.objectweb.asm.Opcodes;
public class InvokeVirtual extends InvokeMethodWithReceiver {
@@ -79,4 +82,9 @@
DexMethod method = getInvokedMethod();
return appInfo.lookupVirtualTarget(method.holder, method);
}
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, getInvokedMethod()));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java
new file mode 100644
index 0000000..e82149a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2017, 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.code;
+
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.LocalType;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
+
+public class Load extends Instruction {
+
+ private final LocalType type;
+
+ public Load(LocalType type, StackValue dest, Value src) {
+ super(dest, src);
+ this.type = type;
+ }
+
+ @Override
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return 0;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ Value value = inValues.get(0);
+ builder.add(new CfLoad(type, value.getNumber()));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java
new file mode 100644
index 0000000..61ab130
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2017, 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.code;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
+
+public class Pop extends Instruction {
+
+ public Pop(StackValue src) {
+ super(null, src);
+ }
+
+ @Override
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return 0;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
+ throw new Unreachable();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index e9311e3..e16cf95 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfReturn;
import com.android.tools.r8.code.ReturnObject;
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.code.ReturnWide;
@@ -10,6 +11,9 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.LocalType;
+import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
@@ -113,4 +117,25 @@
public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
return Constraint.ALWAYS;
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
+ if (isReturnVoid()) {
+ return;
+ }
+ Value oldValue = returnValue();
+ StackValue stackValue = new StackValue(returnType);
+ replaceValue(oldValue, stackValue);
+ Load load =
+ new Load(LocalType.fromDexType(stack.method.proto.returnType), stackValue, oldValue);
+ load.setBlock(getBlock());
+ it.previous();
+ it.add(load);
+ it.next();
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfReturn());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StackValue.java b/src/main/java/com/android/tools/r8/ir/code/StackValue.java
new file mode 100644
index 0000000..f743e9e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/StackValue.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2017, 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.code;
+
+public class StackValue extends Value {
+
+ public StackValue(MoveType type) {
+ super(Value.UNDEFINED_NUMBER, type, null);
+ }
+
+ @Override
+ public String toString() {
+ return "s";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 5075e7a..8338db5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfStaticGet;
import com.android.tools.r8.code.Sget;
import com.android.tools.r8.code.SgetBoolean;
import com.android.tools.r8.code.SgetByte;
@@ -16,6 +17,9 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.LocalType;
+import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.conversion.DexBuilder;
public class StaticGet extends FieldInstruction {
@@ -112,4 +116,14 @@
public StaticGet asStaticGet() {
return this;
}
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
+ stack.storeOutValue(this, LocalType.fromDexType(field.type), it);
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfStaticGet(field));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java
new file mode 100644
index 0000000..2124a40
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2017, 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.code;
+
+import com.android.tools.r8.cf.code.CfStore;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.CfBuilder.LocalType;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
+
+public class Store extends Instruction {
+
+ private final LocalType type;
+
+ public Store(LocalType type, Value dest, StackValue src) {
+ super(dest, src);
+ this.type = type;
+ }
+
+ @Override
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
+ return true;
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ return 0;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfStore(type, outValue.getNumber()));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index ea43cf8..4baf23f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -68,7 +68,9 @@
}
}
- public static final Value UNDEFINED = new Value(-1, MoveType.OBJECT, null);
+ public static final int UNDEFINED_NUMBER = -1;
+
+ public static final Value UNDEFINED = new Value(UNDEFINED_NUMBER, MoveType.OBJECT, null);
protected final int number;
protected final MoveType type;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
new file mode 100644
index 0000000..750003f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2017, 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.conversion;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Load;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Pop;
+import com.android.tools.r8.ir.code.StackValue;
+import com.android.tools.r8.ir.code.Store;
+import com.android.tools.r8.ir.code.Value;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CfBuilder {
+
+ public enum LocalType {
+ REFERENCE,
+ INTEGER,
+ FLOAT,
+ LONG,
+ DOUBLE;
+
+ public static LocalType fromDexType(DexType type) {
+ switch (type.toShorty()) {
+ case 'Z':
+ case 'B':
+ case 'C':
+ case 'S':
+ case 'I':
+ return INTEGER;
+ case 'F':
+ return FLOAT;
+ case 'J':
+ return LONG;
+ case 'D':
+ return DOUBLE;
+ case 'L':
+ return REFERENCE;
+ default:
+ throw new Unreachable("Unexpected type " + type);
+ }
+ }
+ }
+
+ private final DexEncodedMethod method;
+ private final IRCode code;
+ private List<CfInstruction> instructions;
+
+ public static class StackHelper {
+
+ public final DexMethod method;
+
+ public StackHelper(DexMethod method) {
+ this.method = method;
+ }
+
+ public void loadInValues(
+ Instruction instruction,
+ List<Value> values,
+ List<LocalType> types,
+ InstructionListIterator it) {
+ assert values.size() == types.size();
+ it.previous();
+ for (int i = 0; i < values.size(); i++) {
+ Value value = values.get(i);
+ LocalType type = types.get(i);
+ StackValue stackValue = new StackValue(value.outType());
+ instruction.replaceValue(value, stackValue);
+ add(new Load(type, stackValue, value), instruction, it);
+ }
+ it.next();
+ }
+
+ public void storeOutValue(Instruction instruction, LocalType type, InstructionListIterator it) {
+ StackValue newOutValue = new StackValue(instruction.outType());
+ Value oldOutValue = instruction.swapOutValue(newOutValue);
+ add(new Store(type, oldOutValue, newOutValue), instruction, it);
+ }
+
+ public void popOutValue(Instruction instruction, MoveType type, InstructionListIterator it) {
+ StackValue newOutValue = new StackValue(type);
+ instruction.swapOutValue(newOutValue);
+ add(new Pop(newOutValue), instruction, it);
+ }
+
+ private static void add(
+ Instruction newInstruction, Instruction existingInstruction, InstructionListIterator it) {
+ newInstruction.setBlock(existingInstruction.getBlock());
+ newInstruction.setPosition(existingInstruction.getPosition());
+ it.add(newInstruction);
+ }
+ }
+
+ public CfBuilder(DexEncodedMethod method, IRCode code) {
+ this.method = method;
+ this.code = code;
+ }
+
+ public Code build() {
+ try {
+ loadStoreInsertion();
+ // TODO(zerny): Optimize load/store patterns.
+ // TODO(zerny): Compute locals/register allocation.
+ // TODO(zerny): Compute debug info.
+ return buildCfCode();
+ } catch (Unimplemented e) {
+ System.out.println("Incomplete CF construction: " + e.getMessage());
+ return method.getCode().asJarCode();
+ }
+ }
+
+ private void loadStoreInsertion() {
+ StackHelper stack = new StackHelper(method.method);
+ for (BasicBlock block : code.blocks) {
+ InstructionListIterator it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction current = it.next();
+ current.insertLoadAndStores(it, stack);
+ }
+ }
+ }
+
+ private CfCode buildCfCode() {
+ instructions = new ArrayList<>();
+ InstructionIterator it = code.instructionIterator();
+ while (it.hasNext()) {
+ it.next().buildCf(this);
+ }
+ return new CfCode(instructions);
+ }
+
+ // Callbacks
+
+ public void add(CfInstruction instruction) {
+ instructions.add(instruction);
+ }
+
+ public void addArgument(Argument argument) {
+ // Nothing so far.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index da022fe..de6d86b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -587,7 +587,11 @@
private void finalizeToCf(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
assert method.getCode().isJarCode();
- // TODO(zerny): Actually convert IR back to Java bytecode.
+ CfBuilder builder = new CfBuilder(method, code);
+ // TODO(zerny): Change the return type of CfBuilder::build CfCode once complete.
+ Code result = builder.build();
+ assert result.isCfCode() || result.isJarCode();
+ method.setCode(result);
}
private void finalizeToDex(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 483327b..bd5ae63 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -3,19 +3,30 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.jar;
+import static org.objectweb.asm.Opcodes.ASM6;
+
import com.android.tools.r8.OutputSink;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
+import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.util.CheckClassAdapter;
+import org.objectweb.asm.util.Textifier;
+import org.objectweb.asm.util.TraceMethodVisitor;
public class CfApplicationWriter {
private final DexApplication application;
@@ -65,7 +76,11 @@
for (DexEncodedMethod method : clazz.directMethods()) {
writeMethod(method, writer);
}
- outputSink.writeClassFile(writer.toByteArray(), Collections.singleton(desc), desc);
+ writer.visitEnd();
+
+ byte[] result = writer.toByteArray();
+ assert verifyCf(result);
+ outputSink.writeClassFile(result, Collections.singleton(desc), desc);
}
private void writeMethod(DexEncodedMethod method, ClassWriter writer) {
@@ -75,10 +90,40 @@
String signature = null; // TODO(zerny): Support generic signatures.
String[] exceptions = null;
MethodVisitor visitor = writer.visitMethod(access, name, desc, signature, exceptions);
- method.getCode().asJarCode().writeTo(visitor);
+ writeCode(method.getCode(), visitor);
+ }
+
+ private void writeCode(Code code, MethodVisitor visitor) {
+ if (code.isJarCode()) {
+ code.asJarCode().writeTo(visitor);
+ } else {
+ assert code.isCfCode();
+ code.asCfCode().write(visitor);
+ }
}
private static String internalName(DexType type) {
return Type.getType(type.toDescriptorString()).getInternalName();
}
+
+ private String printCf(byte[] result) {
+ ClassReader reader = new ClassReader(result);
+ ClassNode node = new ClassNode(ASM6);
+ reader.accept(node, ASM6);
+ StringWriter writer = new StringWriter();
+ for (MethodNode method : node.methods) {
+ TraceMethodVisitor visitor = new TraceMethodVisitor(new Textifier());
+ method.accept(visitor);
+ visitor.p.print(new PrintWriter(writer));
+ writer.append('\n');
+ }
+ return writer.toString();
+ }
+
+ private static boolean verifyCf(byte[] result) {
+ ClassReader reader = new ClassReader(result);
+ PrintWriter pw = new PrintWriter(System.out);
+ CheckClassAdapter.verify(reader, false, pw);
+ return true;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 96d9e8b..d353d88 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -122,7 +122,8 @@
};
String[] javaBytecodeTests = {
- "hello.Hello",
+ "hello.Hello",
+ "arithmetic.Arithmetic",
};
List<String[]> fullTestList = new ArrayList<>(tests.length * 2);
@@ -148,6 +149,7 @@
makeTest(
Input.JAVAC_ALL, CompilerUnderTest.R8, CompilationMode.RELEASE, test, Output.CF));
}
+
return fullTestList;
}
@@ -264,6 +266,8 @@
ToolHelper.ProcessResult javaResult =
ToolHelper.runJava(ImmutableList.of(getOriginalJarFile("").toString()), mainClass);
if (javaResult.exitCode != 0) {
+ System.out.println(javaResult.stdout);
+ System.err.println(javaResult.stderr);
fail("JVM failed for: " + mainClass);
}