Loading startop/view_compiler/dex_builder.cc +17 −4 Original line number Diff line number Diff line Loading @@ -161,7 +161,7 @@ void WriteTestDexFile(const string& filename) { MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})}; Value result = method.MakeRegister(); LiveRegister result = method.AllocRegister(); MethodDeclData string_length = dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()}); Loading Loading @@ -314,7 +314,7 @@ ir::EncodedMethod* MethodBuilder::Encode() { CHECK(decl_->prototype != nullptr); size_t const num_args = decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0; code->registers = num_registers_ + num_args + kMaxScratchRegisters; code->registers = NumRegisters() + num_args + kMaxScratchRegisters; code->ins_count = num_args; EncodeInstructions(); code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size()); Loading @@ -327,7 +327,20 @@ ir::EncodedMethod* MethodBuilder::Encode() { return method; } Value MethodBuilder::MakeRegister() { return Value::Local(num_registers_++); } LiveRegister MethodBuilder::AllocRegister() { // Find a free register for (size_t i = 0; i < register_liveness_.size(); ++i) { if (!register_liveness_[i]) { register_liveness_[i] = true; return LiveRegister{®ister_liveness_, i}; } } // If we get here, all the registers are in use, so we have to allocate a new // one. register_liveness_.push_back(true); return LiveRegister{®ister_liveness_, register_liveness_.size() - 1}; } Value MethodBuilder::MakeLabel() { labels_.push_back({}); Loading Loading @@ -600,7 +613,7 @@ size_t MethodBuilder::RegisterValue(const Value& value) const { if (value.is_register()) { return value.value(); } else if (value.is_parameter()) { return value.value() + num_registers_ + kMaxScratchRegisters; return value.value() + NumRegisters() + kMaxScratchRegisters; } CHECK(false && "Must be either a parameter or a register"); return 0; Loading startop/view_compiler/dex_builder.h +40 −14 Original line number Diff line number Diff line Loading @@ -140,6 +140,29 @@ class Value { constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {} }; // Represents an allocated register returned by MethodBuilder::AllocRegister class LiveRegister { friend class MethodBuilder; public: LiveRegister(LiveRegister&& other) : liveness_{other.liveness_}, index_{other.index_} { other.index_ = {}; }; ~LiveRegister() { if (index_.has_value()) { (*liveness_)[*index_] = false; } }; operator const Value() const { return Value::Local(*index_); } private: LiveRegister(std::vector<bool>* liveness, size_t index) : liveness_{liveness}, index_{index} {} std::vector<bool>* const liveness_; std::optional<size_t> index_; }; // A virtual instruction. We convert these to real instructions in MethodBuilder::Encode. // Virtual instructions are needed to keep track of information that is not known until all of the // code is generated. This information includes things like how many local registers are created and Loading Loading @@ -178,7 +201,8 @@ class Instruction { } // For most instructions, which take some number of arguments and have an optional return value. template <typename... T> static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) { static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, const T&... args) { return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...}; } Loading @@ -199,14 +223,14 @@ class Instruction { template <typename... T> static inline Instruction InvokeVirtualObject(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { const T&... args) { return Instruction{ Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...}; } // For direct calls (basically, constructors). template <typename... T> static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { Value this_arg, const T&... args) { return Instruction{ Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...}; } Loading Loading @@ -234,7 +258,7 @@ class Instruction { // For static calls. template <typename... T> static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest, T... args) { const T&... args) { return Instruction{ Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...}; } Loading Loading @@ -277,7 +301,7 @@ class Instruction { template <typename... T> inline Instruction(Op opcode, size_t index_argument, bool result_is_object, std::optional<const Value> dest, T... args) std::optional<const Value> dest, const T&... args) : opcode_{opcode}, index_argument_{index_argument}, result_is_object_{result_is_object}, Loading Loading @@ -309,10 +333,8 @@ class MethodBuilder { // Encode the method into DEX format. ir::EncodedMethod* Encode(); // Create a new register to be used to storing values. Note that these are not SSA registers, like // might be expected in similar code generators. This does no liveness tracking or anything, so // it's up to the caller to reuse registers as appropriate. Value MakeRegister(); // Create a new register to be used to storing values. LiveRegister AllocRegister(); Value MakeLabel(); Loading @@ -329,7 +351,7 @@ class MethodBuilder { void BuildConst4(Value target, int value); void BuildConstString(Value target, const std::string& value); template <typename... T> void BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args); void BuildNew(Value target, TypeDescriptor type, Prototype constructor, const T&... args); // TODO: add builders for more instructions Loading Loading @@ -427,7 +449,7 @@ class MethodBuilder { static_assert(num_regs <= kMaxScratchRegisters); std::array<Value, num_regs> regs; for (size_t i = 0; i < num_regs; ++i) { regs[i] = std::move(Value::Local(num_registers_ + i)); regs[i] = std::move(Value::Local(NumRegisters() + i)); } return regs; } Loading Loading @@ -457,8 +479,9 @@ class MethodBuilder { // around to make legal DEX code. static constexpr size_t kMaxScratchRegisters = 5; // How many registers we've allocated size_t num_registers_{0}; size_t NumRegisters() const { return register_liveness_.size(); } // Stores information needed to back-patch a label once it is bound. We need to know the start of // the instruction that refers to the label, and the offset to where the actual label value should Loading @@ -478,6 +501,8 @@ class MethodBuilder { // During encoding, keep track of the largest number of arguments needed, so we can use it for our // outs count size_t max_args_{0}; std::vector<bool> register_liveness_; }; // A helper to build class definitions. Loading Loading @@ -576,7 +601,8 @@ class DexBuilder { }; template <typename... T> void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args) { void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, const T&... args) { MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)}; // allocate the object ir::Type* type_def = dex_->GetOrAddType(type.descriptor()); Loading startop/view_compiler/dex_layout_compiler.cc +106 −105 Original line number Diff line number Diff line Loading @@ -22,76 +22,94 @@ namespace startop { using android::base::StringPrintf; using dex::Instruction; using dex::LiveRegister; using dex::Prototype; using dex::TypeDescriptor; using dex::Value; namespace { // TODO: these are a bunch of static initializers, which we should avoid. See if // we can make them constexpr. const TypeDescriptor kAttributeSet = TypeDescriptor::FromClassname("android.util.AttributeSet"); const TypeDescriptor kContext = TypeDescriptor::FromClassname("android.content.Context"); const TypeDescriptor kLayoutInflater = TypeDescriptor::FromClassname("android.view.LayoutInflater"); const TypeDescriptor kResources = TypeDescriptor::FromClassname("android.content.res.Resources"); const TypeDescriptor kString = TypeDescriptor::FromClassname("java.lang.String"); const TypeDescriptor kView = TypeDescriptor::FromClassname("android.view.View"); const TypeDescriptor kViewGroup = TypeDescriptor::FromClassname("android.view.ViewGroup"); const TypeDescriptor kXmlResourceParser = TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"); } // namespace DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method) : method_{method}, context_{dex::Value::Parameter(0)}, resid_{dex::Value::Parameter(1)}, inflater_{method->MakeRegister()}, xml_{method->MakeRegister()}, attrs_{method->MakeRegister()}, classname_tmp_{method->MakeRegister()}, xml_next_{method->dex_file()->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next", dex::Prototype{dex::TypeDescriptor::Int()})}, context_{Value::Parameter(0)}, resid_{Value::Parameter(1)}, inflater_{method->AllocRegister()}, xml_{method->AllocRegister()}, attrs_{method->AllocRegister()}, classname_tmp_{method->AllocRegister()}, xml_next_{method->dex_file()->GetOrDeclareMethod(kXmlResourceParser, "next", Prototype{TypeDescriptor::Int()})}, try_create_view_{method->dex_file()->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView", dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"), dex::TypeDescriptor::FromClassname("android.view.View"), dex::TypeDescriptor::FromClassname("java.lang.String"), dex::TypeDescriptor::FromClassname("android.content.Context"), dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})}, kLayoutInflater, "tryCreateView", Prototype{kView, kView, kString, kContext, kAttributeSet})}, generate_layout_params_{method->dex_file()->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams", dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"), dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})}, kViewGroup, "generateLayoutParams", Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"), kAttributeSet})}, add_view_{method->dex_file()->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView", dex::Prototype{ dex::TypeDescriptor::Void(), dex::TypeDescriptor::FromClassname("android.view.View"), dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})}, // The register stack starts with one register, which will be null for the root view. register_stack_{{method->MakeRegister()}} {} void DexViewBuilder::Start() { dex::DexBuilder* const dex = method_->dex_file(); // LayoutInflater inflater = LayoutInflater.from(context); auto layout_inflater_from = dex->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "from", dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), dex::TypeDescriptor::FromClassname("android.content.Context")}); method_->AddInstruction( dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_)); kViewGroup, "addView", Prototype{TypeDescriptor::Void(), kView, TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {} void DexViewBuilder::BuildGetLayoutInflater(Value dest) { // dest = LayoutInflater.from(context); auto layout_inflater_from = method_->dex_file()->GetOrDeclareMethod( kLayoutInflater, "from", Prototype{kLayoutInflater, kContext}); method_->AddInstruction(Instruction::InvokeStaticObject(layout_inflater_from.id, dest, context_)); } // Resources res = context.getResources(); auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context"); auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources"); void DexViewBuilder::BuildGetResources(Value dest) { // dest = context.getResources(); auto get_resources = dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type}); method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_)); // XmlResourceParser xml = res.getLayout(resid); auto xml_resource_parser_type = dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"); auto get_layout = dex->GetOrDeclareMethod(resources_type, "getLayout", dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()}); method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_)); // AttributeSet attrs = Xml.asAttributeSet(xml); auto as_attribute_set = dex->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.util.Xml"), method_->dex_file()->GetOrDeclareMethod(kContext, "getResources", Prototype{kResources}); method_->AddInstruction(Instruction::InvokeVirtualObject(get_resources.id, dest, context_)); } void DexViewBuilder::BuildGetLayoutResource(Value dest, Value resources, Value resid) { // dest = resources.getLayout(resid); auto get_layout = method_->dex_file()->GetOrDeclareMethod( kResources, "getLayout", Prototype{kXmlResourceParser, TypeDescriptor::Int()}); method_->AddInstruction(Instruction::InvokeVirtualObject(get_layout.id, dest, resources, resid)); } void DexViewBuilder::BuildLayoutResourceToAttributeSet(dex::Value dest, dex::Value layout_resource) { // dest = Xml.asAttributeSet(layout_resource); auto as_attribute_set = method_->dex_file()->GetOrDeclareMethod( TypeDescriptor::FromClassname("android.util.Xml"), "asAttributeSet", dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"), dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")}); method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_)); Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")}); method_->AddInstruction( Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource)); } // xml.next(); // start document method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); void DexViewBuilder::BuildXmlNext() { // xml_.next(); method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_)); } void DexViewBuilder::Start() { BuildGetLayoutInflater(/*dest=*/inflater_); BuildGetResources(/*dest=*/xml_); BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_); BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_); // Advance past start document tag BuildXmlNext(); } void DexViewBuilder::Finish() {} Loading @@ -107,58 +125,57 @@ std::string ResolveName(const std::string& name) { } } // namespace void DexViewBuilder::BuildTryCreateView(Value dest, Value parent, Value classname) { // dest = inflater_.tryCreateView(parent, classname, context_, attrs_); method_->AddInstruction(Instruction::InvokeVirtualObject( try_create_view_.id, dest, inflater_, parent, classname, context_, attrs_)); } void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) { bool const is_root_view = view_stack_.empty(); // xml.next(); // start tag method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); // Advance to start tag BuildXmlNext(); dex::Value view = AcquireRegister(); LiveRegister view = AcquireRegister(); // try to create the view using the factories method_->BuildConstString(classname_tmp_, name); // TODO: the need to fully qualify the classname if (is_root_view) { dex::Value null = AcquireRegister(); LiveRegister null = AcquireRegister(); method_->BuildConst4(null, 0); method_->AddInstruction(dex::Instruction::InvokeVirtualObject( try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_)); ReleaseRegister(); BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_); } else { method_->AddInstruction(dex::Instruction::InvokeVirtualObject( try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_)); BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_); } auto label = method_->MakeLabel(); // branch if not null method_->AddInstruction( dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label)); Instruction::OpWithArgs(Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label)); // If null, create the class directly. method_->BuildNew(view, dex::TypeDescriptor::FromClassname(ResolveName(name)), dex::Prototype{dex::TypeDescriptor::Void(), dex::TypeDescriptor::FromClassname("android.content.Context"), dex::TypeDescriptor::FromClassname("android.util.AttributeSet")}, TypeDescriptor::FromClassname(ResolveName(name)), Prototype{TypeDescriptor::Void(), kContext, kAttributeSet}, context_, attrs_); method_->AddInstruction( dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label)); method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label)); if (is_viewgroup) { // Cast to a ViewGroup so we can add children later. const ir::Type* view_group_def = method_->dex_file()->GetOrAddType( dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor()); method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index))); const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(kViewGroup.descriptor()); method_->AddInstruction(Instruction::Cast(view, Value::Type(view_group_def->orig_index))); } if (!is_root_view) { // layout_params = parent.generateLayoutParams(attrs); dex::Value layout_params{AcquireRegister()}; method_->AddInstruction(dex::Instruction::InvokeVirtualObject( LiveRegister layout_params{AcquireRegister()}; method_->AddInstruction(Instruction::InvokeVirtualObject( generate_layout_params_.id, layout_params, GetCurrentView(), attrs_)); view_stack_.push_back({view, layout_params}); view_stack_.push_back({std::move(view), std::move(layout_params)}); } else { view_stack_.push_back({view, {}}); view_stack_.push_back({std::move(view), {}}); } } Loading @@ -167,40 +184,24 @@ void DexViewBuilder::FinishView() { method_->BuildReturn(GetCurrentView(), /*is_object=*/true); } else { // parent.add(view, layout_params) method_->AddInstruction(dex::Instruction::InvokeVirtual( method_->AddInstruction(Instruction::InvokeVirtual( add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams())); // xml.next(); // end tag method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_)); } PopViewStack(); } dex::Value DexViewBuilder::AcquireRegister() { top_register_++; if (register_stack_.size() == top_register_) { register_stack_.push_back(method_->MakeRegister()); } return register_stack_[top_register_]; } void DexViewBuilder::ReleaseRegister() { top_register_--; } LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); } dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; } dex::Value DexViewBuilder::GetCurrentLayoutParams() const { Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; } Value DexViewBuilder::GetCurrentLayoutParams() const { return view_stack_.back().layout_params.value(); } dex::Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; } Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; } void DexViewBuilder::PopViewStack() { const auto& top = view_stack_.back(); // release the layout params if we have them if (top.layout_params.has_value()) { ReleaseRegister(); } // Unconditionally release the view register. ReleaseRegister(); view_stack_.pop_back(); } Loading startop/view_compiler/dex_layout_compiler.h +18 −13 Original line number Diff line number Diff line Loading @@ -79,36 +79,41 @@ class DexViewBuilder { private: // Accessors for the stack of views that are under construction. dex::Value AcquireRegister(); void ReleaseRegister(); dex::LiveRegister AcquireRegister(); dex::Value GetCurrentView() const; dex::Value GetCurrentLayoutParams() const; dex::Value GetParentView() const; void PopViewStack(); // Methods to simplify building different code fragments. void BuildGetLayoutInflater(dex::Value dest); void BuildGetResources(dex::Value dest); void BuildGetLayoutResource(dex::Value dest, dex::Value resources, dex::Value resid); void BuildLayoutResourceToAttributeSet(dex::Value dest, dex::Value layout_resource); void BuildXmlNext(); void BuildTryCreateView(dex::Value dest, dex::Value parent, dex::Value classname); dex::MethodBuilder* method_; // Registers used for code generation // Parameters to the generated method dex::Value const context_; dex::Value const resid_; const dex::Value inflater_; const dex::Value xml_; const dex::Value attrs_; const dex::Value classname_tmp_; // Registers used for code generation const dex::LiveRegister inflater_; const dex::LiveRegister xml_; const dex::LiveRegister attrs_; const dex::LiveRegister classname_tmp_; const dex::MethodDeclData xml_next_; const dex::MethodDeclData try_create_view_; const dex::MethodDeclData generate_layout_params_; const dex::MethodDeclData add_view_; // used for keeping track of which registers are in use size_t top_register_{0}; std::vector<dex::Value> register_stack_; // Keep track of the views currently in progress. struct ViewEntry { dex::Value view; std::optional<dex::Value> layout_params; dex::LiveRegister view; std::optional<dex::LiveRegister> layout_params; }; std::vector<ViewEntry> view_stack_; }; Loading startop/view_compiler/dex_testcase_generator.cc +18 −18 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
startop/view_compiler/dex_builder.cc +17 −4 Original line number Diff line number Diff line Loading @@ -161,7 +161,7 @@ void WriteTestDexFile(const string& filename) { MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})}; Value result = method.MakeRegister(); LiveRegister result = method.AllocRegister(); MethodDeclData string_length = dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()}); Loading Loading @@ -314,7 +314,7 @@ ir::EncodedMethod* MethodBuilder::Encode() { CHECK(decl_->prototype != nullptr); size_t const num_args = decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0; code->registers = num_registers_ + num_args + kMaxScratchRegisters; code->registers = NumRegisters() + num_args + kMaxScratchRegisters; code->ins_count = num_args; EncodeInstructions(); code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size()); Loading @@ -327,7 +327,20 @@ ir::EncodedMethod* MethodBuilder::Encode() { return method; } Value MethodBuilder::MakeRegister() { return Value::Local(num_registers_++); } LiveRegister MethodBuilder::AllocRegister() { // Find a free register for (size_t i = 0; i < register_liveness_.size(); ++i) { if (!register_liveness_[i]) { register_liveness_[i] = true; return LiveRegister{®ister_liveness_, i}; } } // If we get here, all the registers are in use, so we have to allocate a new // one. register_liveness_.push_back(true); return LiveRegister{®ister_liveness_, register_liveness_.size() - 1}; } Value MethodBuilder::MakeLabel() { labels_.push_back({}); Loading Loading @@ -600,7 +613,7 @@ size_t MethodBuilder::RegisterValue(const Value& value) const { if (value.is_register()) { return value.value(); } else if (value.is_parameter()) { return value.value() + num_registers_ + kMaxScratchRegisters; return value.value() + NumRegisters() + kMaxScratchRegisters; } CHECK(false && "Must be either a parameter or a register"); return 0; Loading
startop/view_compiler/dex_builder.h +40 −14 Original line number Diff line number Diff line Loading @@ -140,6 +140,29 @@ class Value { constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {} }; // Represents an allocated register returned by MethodBuilder::AllocRegister class LiveRegister { friend class MethodBuilder; public: LiveRegister(LiveRegister&& other) : liveness_{other.liveness_}, index_{other.index_} { other.index_ = {}; }; ~LiveRegister() { if (index_.has_value()) { (*liveness_)[*index_] = false; } }; operator const Value() const { return Value::Local(*index_); } private: LiveRegister(std::vector<bool>* liveness, size_t index) : liveness_{liveness}, index_{index} {} std::vector<bool>* const liveness_; std::optional<size_t> index_; }; // A virtual instruction. We convert these to real instructions in MethodBuilder::Encode. // Virtual instructions are needed to keep track of information that is not known until all of the // code is generated. This information includes things like how many local registers are created and Loading Loading @@ -178,7 +201,8 @@ class Instruction { } // For most instructions, which take some number of arguments and have an optional return value. template <typename... T> static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) { static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, const T&... args) { return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...}; } Loading @@ -199,14 +223,14 @@ class Instruction { template <typename... T> static inline Instruction InvokeVirtualObject(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { const T&... args) { return Instruction{ Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...}; } // For direct calls (basically, constructors). template <typename... T> static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { Value this_arg, const T&... args) { return Instruction{ Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...}; } Loading Loading @@ -234,7 +258,7 @@ class Instruction { // For static calls. template <typename... T> static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest, T... args) { const T&... args) { return Instruction{ Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...}; } Loading Loading @@ -277,7 +301,7 @@ class Instruction { template <typename... T> inline Instruction(Op opcode, size_t index_argument, bool result_is_object, std::optional<const Value> dest, T... args) std::optional<const Value> dest, const T&... args) : opcode_{opcode}, index_argument_{index_argument}, result_is_object_{result_is_object}, Loading Loading @@ -309,10 +333,8 @@ class MethodBuilder { // Encode the method into DEX format. ir::EncodedMethod* Encode(); // Create a new register to be used to storing values. Note that these are not SSA registers, like // might be expected in similar code generators. This does no liveness tracking or anything, so // it's up to the caller to reuse registers as appropriate. Value MakeRegister(); // Create a new register to be used to storing values. LiveRegister AllocRegister(); Value MakeLabel(); Loading @@ -329,7 +351,7 @@ class MethodBuilder { void BuildConst4(Value target, int value); void BuildConstString(Value target, const std::string& value); template <typename... T> void BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args); void BuildNew(Value target, TypeDescriptor type, Prototype constructor, const T&... args); // TODO: add builders for more instructions Loading Loading @@ -427,7 +449,7 @@ class MethodBuilder { static_assert(num_regs <= kMaxScratchRegisters); std::array<Value, num_regs> regs; for (size_t i = 0; i < num_regs; ++i) { regs[i] = std::move(Value::Local(num_registers_ + i)); regs[i] = std::move(Value::Local(NumRegisters() + i)); } return regs; } Loading Loading @@ -457,8 +479,9 @@ class MethodBuilder { // around to make legal DEX code. static constexpr size_t kMaxScratchRegisters = 5; // How many registers we've allocated size_t num_registers_{0}; size_t NumRegisters() const { return register_liveness_.size(); } // Stores information needed to back-patch a label once it is bound. We need to know the start of // the instruction that refers to the label, and the offset to where the actual label value should Loading @@ -478,6 +501,8 @@ class MethodBuilder { // During encoding, keep track of the largest number of arguments needed, so we can use it for our // outs count size_t max_args_{0}; std::vector<bool> register_liveness_; }; // A helper to build class definitions. Loading Loading @@ -576,7 +601,8 @@ class DexBuilder { }; template <typename... T> void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args) { void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, const T&... args) { MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)}; // allocate the object ir::Type* type_def = dex_->GetOrAddType(type.descriptor()); Loading
startop/view_compiler/dex_layout_compiler.cc +106 −105 Original line number Diff line number Diff line Loading @@ -22,76 +22,94 @@ namespace startop { using android::base::StringPrintf; using dex::Instruction; using dex::LiveRegister; using dex::Prototype; using dex::TypeDescriptor; using dex::Value; namespace { // TODO: these are a bunch of static initializers, which we should avoid. See if // we can make them constexpr. const TypeDescriptor kAttributeSet = TypeDescriptor::FromClassname("android.util.AttributeSet"); const TypeDescriptor kContext = TypeDescriptor::FromClassname("android.content.Context"); const TypeDescriptor kLayoutInflater = TypeDescriptor::FromClassname("android.view.LayoutInflater"); const TypeDescriptor kResources = TypeDescriptor::FromClassname("android.content.res.Resources"); const TypeDescriptor kString = TypeDescriptor::FromClassname("java.lang.String"); const TypeDescriptor kView = TypeDescriptor::FromClassname("android.view.View"); const TypeDescriptor kViewGroup = TypeDescriptor::FromClassname("android.view.ViewGroup"); const TypeDescriptor kXmlResourceParser = TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"); } // namespace DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method) : method_{method}, context_{dex::Value::Parameter(0)}, resid_{dex::Value::Parameter(1)}, inflater_{method->MakeRegister()}, xml_{method->MakeRegister()}, attrs_{method->MakeRegister()}, classname_tmp_{method->MakeRegister()}, xml_next_{method->dex_file()->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next", dex::Prototype{dex::TypeDescriptor::Int()})}, context_{Value::Parameter(0)}, resid_{Value::Parameter(1)}, inflater_{method->AllocRegister()}, xml_{method->AllocRegister()}, attrs_{method->AllocRegister()}, classname_tmp_{method->AllocRegister()}, xml_next_{method->dex_file()->GetOrDeclareMethod(kXmlResourceParser, "next", Prototype{TypeDescriptor::Int()})}, try_create_view_{method->dex_file()->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView", dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"), dex::TypeDescriptor::FromClassname("android.view.View"), dex::TypeDescriptor::FromClassname("java.lang.String"), dex::TypeDescriptor::FromClassname("android.content.Context"), dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})}, kLayoutInflater, "tryCreateView", Prototype{kView, kView, kString, kContext, kAttributeSet})}, generate_layout_params_{method->dex_file()->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams", dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"), dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})}, kViewGroup, "generateLayoutParams", Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"), kAttributeSet})}, add_view_{method->dex_file()->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView", dex::Prototype{ dex::TypeDescriptor::Void(), dex::TypeDescriptor::FromClassname("android.view.View"), dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})}, // The register stack starts with one register, which will be null for the root view. register_stack_{{method->MakeRegister()}} {} void DexViewBuilder::Start() { dex::DexBuilder* const dex = method_->dex_file(); // LayoutInflater inflater = LayoutInflater.from(context); auto layout_inflater_from = dex->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "from", dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), dex::TypeDescriptor::FromClassname("android.content.Context")}); method_->AddInstruction( dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_)); kViewGroup, "addView", Prototype{TypeDescriptor::Void(), kView, TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {} void DexViewBuilder::BuildGetLayoutInflater(Value dest) { // dest = LayoutInflater.from(context); auto layout_inflater_from = method_->dex_file()->GetOrDeclareMethod( kLayoutInflater, "from", Prototype{kLayoutInflater, kContext}); method_->AddInstruction(Instruction::InvokeStaticObject(layout_inflater_from.id, dest, context_)); } // Resources res = context.getResources(); auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context"); auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources"); void DexViewBuilder::BuildGetResources(Value dest) { // dest = context.getResources(); auto get_resources = dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type}); method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_)); // XmlResourceParser xml = res.getLayout(resid); auto xml_resource_parser_type = dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"); auto get_layout = dex->GetOrDeclareMethod(resources_type, "getLayout", dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()}); method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_)); // AttributeSet attrs = Xml.asAttributeSet(xml); auto as_attribute_set = dex->GetOrDeclareMethod( dex::TypeDescriptor::FromClassname("android.util.Xml"), method_->dex_file()->GetOrDeclareMethod(kContext, "getResources", Prototype{kResources}); method_->AddInstruction(Instruction::InvokeVirtualObject(get_resources.id, dest, context_)); } void DexViewBuilder::BuildGetLayoutResource(Value dest, Value resources, Value resid) { // dest = resources.getLayout(resid); auto get_layout = method_->dex_file()->GetOrDeclareMethod( kResources, "getLayout", Prototype{kXmlResourceParser, TypeDescriptor::Int()}); method_->AddInstruction(Instruction::InvokeVirtualObject(get_layout.id, dest, resources, resid)); } void DexViewBuilder::BuildLayoutResourceToAttributeSet(dex::Value dest, dex::Value layout_resource) { // dest = Xml.asAttributeSet(layout_resource); auto as_attribute_set = method_->dex_file()->GetOrDeclareMethod( TypeDescriptor::FromClassname("android.util.Xml"), "asAttributeSet", dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"), dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")}); method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_)); Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")}); method_->AddInstruction( Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource)); } // xml.next(); // start document method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); void DexViewBuilder::BuildXmlNext() { // xml_.next(); method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_)); } void DexViewBuilder::Start() { BuildGetLayoutInflater(/*dest=*/inflater_); BuildGetResources(/*dest=*/xml_); BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_); BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_); // Advance past start document tag BuildXmlNext(); } void DexViewBuilder::Finish() {} Loading @@ -107,58 +125,57 @@ std::string ResolveName(const std::string& name) { } } // namespace void DexViewBuilder::BuildTryCreateView(Value dest, Value parent, Value classname) { // dest = inflater_.tryCreateView(parent, classname, context_, attrs_); method_->AddInstruction(Instruction::InvokeVirtualObject( try_create_view_.id, dest, inflater_, parent, classname, context_, attrs_)); } void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) { bool const is_root_view = view_stack_.empty(); // xml.next(); // start tag method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); // Advance to start tag BuildXmlNext(); dex::Value view = AcquireRegister(); LiveRegister view = AcquireRegister(); // try to create the view using the factories method_->BuildConstString(classname_tmp_, name); // TODO: the need to fully qualify the classname if (is_root_view) { dex::Value null = AcquireRegister(); LiveRegister null = AcquireRegister(); method_->BuildConst4(null, 0); method_->AddInstruction(dex::Instruction::InvokeVirtualObject( try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_)); ReleaseRegister(); BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_); } else { method_->AddInstruction(dex::Instruction::InvokeVirtualObject( try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_)); BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_); } auto label = method_->MakeLabel(); // branch if not null method_->AddInstruction( dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label)); Instruction::OpWithArgs(Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label)); // If null, create the class directly. method_->BuildNew(view, dex::TypeDescriptor::FromClassname(ResolveName(name)), dex::Prototype{dex::TypeDescriptor::Void(), dex::TypeDescriptor::FromClassname("android.content.Context"), dex::TypeDescriptor::FromClassname("android.util.AttributeSet")}, TypeDescriptor::FromClassname(ResolveName(name)), Prototype{TypeDescriptor::Void(), kContext, kAttributeSet}, context_, attrs_); method_->AddInstruction( dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label)); method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label)); if (is_viewgroup) { // Cast to a ViewGroup so we can add children later. const ir::Type* view_group_def = method_->dex_file()->GetOrAddType( dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor()); method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index))); const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(kViewGroup.descriptor()); method_->AddInstruction(Instruction::Cast(view, Value::Type(view_group_def->orig_index))); } if (!is_root_view) { // layout_params = parent.generateLayoutParams(attrs); dex::Value layout_params{AcquireRegister()}; method_->AddInstruction(dex::Instruction::InvokeVirtualObject( LiveRegister layout_params{AcquireRegister()}; method_->AddInstruction(Instruction::InvokeVirtualObject( generate_layout_params_.id, layout_params, GetCurrentView(), attrs_)); view_stack_.push_back({view, layout_params}); view_stack_.push_back({std::move(view), std::move(layout_params)}); } else { view_stack_.push_back({view, {}}); view_stack_.push_back({std::move(view), {}}); } } Loading @@ -167,40 +184,24 @@ void DexViewBuilder::FinishView() { method_->BuildReturn(GetCurrentView(), /*is_object=*/true); } else { // parent.add(view, layout_params) method_->AddInstruction(dex::Instruction::InvokeVirtual( method_->AddInstruction(Instruction::InvokeVirtual( add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams())); // xml.next(); // end tag method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_)); method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_)); } PopViewStack(); } dex::Value DexViewBuilder::AcquireRegister() { top_register_++; if (register_stack_.size() == top_register_) { register_stack_.push_back(method_->MakeRegister()); } return register_stack_[top_register_]; } void DexViewBuilder::ReleaseRegister() { top_register_--; } LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); } dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; } dex::Value DexViewBuilder::GetCurrentLayoutParams() const { Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; } Value DexViewBuilder::GetCurrentLayoutParams() const { return view_stack_.back().layout_params.value(); } dex::Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; } Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; } void DexViewBuilder::PopViewStack() { const auto& top = view_stack_.back(); // release the layout params if we have them if (top.layout_params.has_value()) { ReleaseRegister(); } // Unconditionally release the view register. ReleaseRegister(); view_stack_.pop_back(); } Loading
startop/view_compiler/dex_layout_compiler.h +18 −13 Original line number Diff line number Diff line Loading @@ -79,36 +79,41 @@ class DexViewBuilder { private: // Accessors for the stack of views that are under construction. dex::Value AcquireRegister(); void ReleaseRegister(); dex::LiveRegister AcquireRegister(); dex::Value GetCurrentView() const; dex::Value GetCurrentLayoutParams() const; dex::Value GetParentView() const; void PopViewStack(); // Methods to simplify building different code fragments. void BuildGetLayoutInflater(dex::Value dest); void BuildGetResources(dex::Value dest); void BuildGetLayoutResource(dex::Value dest, dex::Value resources, dex::Value resid); void BuildLayoutResourceToAttributeSet(dex::Value dest, dex::Value layout_resource); void BuildXmlNext(); void BuildTryCreateView(dex::Value dest, dex::Value parent, dex::Value classname); dex::MethodBuilder* method_; // Registers used for code generation // Parameters to the generated method dex::Value const context_; dex::Value const resid_; const dex::Value inflater_; const dex::Value xml_; const dex::Value attrs_; const dex::Value classname_tmp_; // Registers used for code generation const dex::LiveRegister inflater_; const dex::LiveRegister xml_; const dex::LiveRegister attrs_; const dex::LiveRegister classname_tmp_; const dex::MethodDeclData xml_next_; const dex::MethodDeclData try_create_view_; const dex::MethodDeclData generate_layout_params_; const dex::MethodDeclData add_view_; // used for keeping track of which registers are in use size_t top_register_{0}; std::vector<dex::Value> register_stack_; // Keep track of the views currently in progress. struct ViewEntry { dex::Value view; std::optional<dex::Value> layout_params; dex::LiveRegister view; std::optional<dex::LiveRegister> layout_params; }; std::vector<ViewEntry> view_stack_; }; Loading
startop/view_compiler/dex_testcase_generator.cc +18 −18 File changed.Preview size limit exceeded, changes collapsed. Show changes