summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--startop/view_compiler/dex_builder.cc33
-rw-r--r--startop/view_compiler/dex_builder.h16
-rw-r--r--startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java34
-rw-r--r--startop/view_compiler/dex_testcase_generator.cc65
4 files changed, 137 insertions, 11 deletions
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 33df6f9c37d7..85357bb79bc0 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -49,6 +49,9 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
case Instruction::Op::kReturn:
out << "kReturn";
return out;
+ case Instruction::Op::kReturnObject:
+ out << "kReturnObject";
+ return out;
case Instruction::Op::kMove:
out << "kMove";
return out;
@@ -137,6 +140,9 @@ ir::String* DexBuilder::GetOrAddString(const std::string& string) {
entry = Alloc<ir::String>();
// +1 for null terminator
entry->data = slicer::MemView{buffer.get(), header_length + string.size() + 1};
+ ::dex::u4 const new_index = dex_file_->strings_indexes.AllocateIndex();
+ dex_file_->strings_map[new_index] = entry;
+ entry->orig_index = new_index;
string_data_.push_back(std::move(buffer));
}
return entry;
@@ -240,8 +246,9 @@ void MethodBuilder::AddInstruction(Instruction instruction) {
void MethodBuilder::BuildReturn() { AddInstruction(Instruction::OpNoArgs(Op::kReturn)); }
-void MethodBuilder::BuildReturn(Value src) {
- AddInstruction(Instruction::OpWithArgs(Op::kReturn, /*destination=*/{}, src));
+void MethodBuilder::BuildReturn(Value src, bool is_object) {
+ AddInstruction(Instruction::OpWithArgs(
+ is_object ? Op::kReturnObject : Op::kReturn, /*destination=*/{}, src));
}
void MethodBuilder::BuildConst4(Value target, int value) {
@@ -249,6 +256,11 @@ void MethodBuilder::BuildConst4(Value target, int value) {
AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::Immediate(value)));
}
+void MethodBuilder::BuildConstString(Value target, const std::string& value) {
+ const ir::String* const dex_string = dex_->GetOrAddString(value);
+ AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::String(dex_string->orig_index)));
+}
+
void MethodBuilder::EncodeInstructions() {
buffer_.clear();
for (const auto& instruction : instructions_) {
@@ -259,7 +271,9 @@ void MethodBuilder::EncodeInstructions() {
void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
switch (instruction.opcode()) {
case Instruction::Op::kReturn:
- return EncodeReturn(instruction);
+ return EncodeReturn(instruction, ::art::Instruction::RETURN);
+ case Instruction::Op::kReturnObject:
+ return EncodeReturn(instruction, ::art::Instruction::RETURN_OBJECT);
case Instruction::Op::kMove:
return EncodeMove(instruction);
case Instruction::Op::kInvokeVirtual:
@@ -271,15 +285,14 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
}
}
-void MethodBuilder::EncodeReturn(const Instruction& instruction) {
- DCHECK_EQ(Instruction::Op::kReturn, instruction.opcode());
+void MethodBuilder::EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode) {
DCHECK(!instruction.dest().has_value());
if (instruction.args().size() == 0) {
buffer_.push_back(art::Instruction::RETURN_VOID);
} else {
- DCHECK(instruction.args().size() == 1);
+ DCHECK_EQ(1, instruction.args().size());
size_t source = RegisterValue(instruction.args()[0]);
- buffer_.push_back(art::Instruction::RETURN | source << 8);
+ buffer_.push_back(opcode | source << 8);
}
}
@@ -297,6 +310,12 @@ void MethodBuilder::EncodeMove(const Instruction& instruction) {
DCHECK_LT(source.value(), 16);
buffer_.push_back(art::Instruction::CONST_4 | (source.value() << 12) |
(RegisterValue(*instruction.dest()) << 8));
+ } else if (source.is_string()) {
+ constexpr size_t kMaxRegisters = 256;
+ DCHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters);
+ DCHECK_LT(source.value(), 65536); // make sure we don't need a jumbo string
+ buffer_.push_back(::art::Instruction::CONST_STRING | (RegisterValue(*instruction.dest()) << 8));
+ buffer_.push_back(source.value());
} else {
UNIMPLEMENTED(FATAL);
}
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 07441518ea32..31414c82e510 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -110,18 +110,20 @@ class Value {
static constexpr Value Local(size_t id) { return Value{id, Kind::kLocalRegister}; }
static constexpr Value Parameter(size_t id) { return Value{id, Kind::kParameter}; }
static constexpr Value Immediate(size_t value) { return Value{value, Kind::kImmediate}; }
+ static constexpr Value String(size_t value) { return Value{value, Kind::kString}; }
static constexpr Value Label(size_t id) { return Value{id, Kind::kLabel}; }
bool is_register() const { return kind_ == Kind::kLocalRegister; }
bool is_parameter() const { return kind_ == Kind::kParameter; }
bool is_variable() const { return is_register() || is_parameter(); }
bool is_immediate() const { return kind_ == Kind::kImmediate; }
+ bool is_string() const { return kind_ == Kind::kString; }
bool is_label() const { return kind_ == Kind::kLabel; }
size_t value() const { return value_; }
private:
- enum class Kind { kLocalRegister, kParameter, kImmediate, kLabel };
+ enum class Kind { kLocalRegister, kParameter, kImmediate, kString, kLabel };
const size_t value_;
const Kind kind_;
@@ -137,7 +139,7 @@ class Instruction {
public:
// The operation performed by this instruction. These are virtual instructions that do not
// correspond exactly to DEX instructions.
- enum class Op { kReturn, kMove, kInvokeVirtual, kBindLabel, kBranchEqz };
+ enum class Op { kReturn, kReturnObject, kMove, kInvokeVirtual, kBindLabel, kBranchEqz };
////////////////////////
// Named Constructors //
@@ -210,16 +212,22 @@ class MethodBuilder {
// return-void
void BuildReturn();
- void BuildReturn(Value src);
+ void BuildReturn(Value src, bool is_object = false);
// const/4
void BuildConst4(Value target, int value);
+ void BuildConstString(Value target, const std::string& value);
// TODO: add builders for more instructions
private:
void EncodeInstructions();
void EncodeInstruction(const Instruction& instruction);
- void EncodeReturn(const Instruction& instruction);
+
+ // Encodes a return instruction. For instructions with no return value, the opcode field is
+ // ignored. Otherwise, this specifies which return instruction will be used (return,
+ // return-object, etc.)
+ void EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode);
+
void EncodeMove(const Instruction& instruction);
void EncodeInvokeVirtual(const Instruction& instruction);
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 169c63374cb7..2ccdc6d5b4bf 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -82,4 +82,38 @@ public class DexBuilderTest {
Method method = clazz.getMethod("backwardsBranch");
Assert.assertEquals(2, method.invoke(null));
}
+
+ @Test
+ public void returnNull() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("returnNull");
+ Assert.assertEquals(null, method.invoke(null));
+ }
+
+ @Test
+ public void makeString() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("makeString");
+ Assert.assertEquals("Hello, World!", method.invoke(null));
+ }
+
+ @Test
+ public void returnStringIfZeroAB() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("returnStringIfZeroAB", int.class);
+ Assert.assertEquals("a", method.invoke(null, 0));
+ Assert.assertEquals("b", method.invoke(null, 1));
+ }
+
+ @Test
+ public void returnStringIfZeroBA() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("returnStringIfZeroBA", int.class);
+ Assert.assertEquals("b", method.invoke(null, 0));
+ Assert.assertEquals("a", method.invoke(null, 1));
+ }
}
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index c521bf2b8ccf..063a0cf6df7e 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -138,6 +138,71 @@ void GenerateSimpleTestCases(const string& outdir) {
}(backwardsBranch);
backwardsBranch.Encode();
+ // Test that we can make a null value. Basically:
+ //
+ // public static String returnNull() { return null; }
+ MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
+ [](MethodBuilder& method) {
+ Value zero = method.MakeRegister();
+ method.BuildConst4(zero, 0);
+ method.BuildReturn(zero, /*is_object=*/true);
+ }(returnNull);
+ returnNull.Encode();
+
+ // Test that we can make String literals. Basically:
+ //
+ // public static String makeString() { return "Hello, World!"; }
+ MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
+ [](MethodBuilder& method) {
+ Value string = method.MakeRegister();
+ method.BuildConstString(string, "Hello, World!");
+ method.BuildReturn(string, /*is_object=*/true);
+ }(makeString);
+ makeString.Encode();
+
+ // Make sure strings are sorted correctly.
+ //
+ // int returnStringIfZeroAB(int x) { if (x == 0) { return "a"; } else { return "b"; } }
+ MethodBuilder returnStringIfZeroAB{
+ cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
+ [&](MethodBuilder& method) {
+ Value resultIfZero{method.MakeRegister()};
+ Value else_target{method.MakeLabel()};
+ method.AddInstruction(Instruction::OpWithArgs(
+ Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
+ // else branch
+ method.BuildConstString(resultIfZero, "b");
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+ // then branch
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
+ method.BuildConstString(resultIfZero, "a");
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+ method.Encode();
+ }(returnStringIfZeroAB);
+ // int returnStringIfZeroAB(int x) { if (x == 0) { return "b"; } else { return "a"; } }
+ MethodBuilder returnStringIfZeroBA{
+ cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
+ [&](MethodBuilder& method) {
+ Value resultIfZero{method.MakeRegister()};
+ Value else_target{method.MakeLabel()};
+ method.AddInstruction(Instruction::OpWithArgs(
+ Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
+ // else branch
+ method.BuildConstString(resultIfZero, "a");
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+ // then branch
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
+ method.BuildConstString(resultIfZero, "b");
+ method.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+ method.Encode();
+ }(returnStringIfZeroBA);
+
slicer::MemView image{dex_file.CreateImage()};
std::ofstream out_file(outdir + "/simple.dex");
out_file.write(image.ptr<const char>(), image.size());