Loading startop/view_compiler/dex_builder.cc +26 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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_) { Loading @@ -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: Loading @@ -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); } } Loading @@ -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); } Loading startop/view_compiler/dex_builder.h +12 −4 Original line number Diff line number Diff line Loading @@ -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_; Loading @@ -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 // Loading Loading @@ -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); Loading startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -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)); } } startop/view_compiler/dex_testcase_generator.cc +65 −0 Original line number Diff line number Diff line Loading @@ -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()); Loading Loading
startop/view_compiler/dex_builder.cc +26 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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_) { Loading @@ -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: Loading @@ -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); } } Loading @@ -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); } Loading
startop/view_compiler/dex_builder.h +12 −4 Original line number Diff line number Diff line Loading @@ -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_; Loading @@ -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 // Loading Loading @@ -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); Loading
startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -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)); } }
startop/view_compiler/dex_testcase_generator.cc +65 −0 Original line number Diff line number Diff line Loading @@ -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()); Loading