Refactor DexViewBuilder

The code was previously pretty messy and hard to follow. This reworks some
things to make it more manageable. Among the changes:

* Pull out fragments of code into their own method, so the high level steps are
  more apparent in methods like DexViewBuilder::Start.
* Pull frequently used types into global constants in dex_layout_compiler.cc.
* Rework register handling to track liveness so a strict stack discipline is no
  longer needed.

Change-Id: Idb4b41f88c96a1ac4efb2c7b9bed05c2de0da999
This commit is contained in:
Eric Holk
2019-09-17 13:28:34 -07:00
parent 763cb4638a
commit 5c6a1a516b
5 changed files with 200 additions and 155 deletions

View File

@@ -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()});
@@ -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());
@@ -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{&register_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{&register_liveness_, register_liveness_.size() - 1};
}
Value MethodBuilder::MakeLabel() {
labels_.push_back({});
@@ -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;

View File

@@ -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
@@ -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...};
}
@@ -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...};
}
@@ -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...};
}
@@ -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},
@@ -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();
@@ -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
@@ -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;
}
@@ -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
@@ -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.
@@ -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());

View File

@@ -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()}} {}
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_));
}
void DexViewBuilder::BuildGetResources(Value dest) {
// dest = context.getResources();
auto get_resources =
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",
Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
method_->AddInstruction(
Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource));
}
void DexViewBuilder::BuildXmlNext() {
// xml_.next();
method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
}
void DexViewBuilder::Start() {
dex::DexBuilder* const dex = method_->dex_file();
BuildGetLayoutInflater(/*dest=*/inflater_);
BuildGetResources(/*dest=*/xml_);
BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_);
BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_);
// 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_));
// Resources res = context.getResources();
auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context");
auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources");
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"),
"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_));
// xml.next(); // start document
method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
// Advance past start document tag
BuildXmlNext();
}
void DexViewBuilder::Finish() {}
@@ -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), {}});
}
}
@@ -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_];
}
LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); }
void DexViewBuilder::ReleaseRegister() { top_register_--; }
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();
}

View File

@@ -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_;
};

View File

@@ -47,7 +47,7 @@ void GenerateSimpleTestCases(const string& outdir) {
// int return5() { return 5; }
auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})};
{
Value r{return5.MakeRegister()};
LiveRegister r{return5.AllocRegister()};
return5.BuildConst4(r, 5);
return5.BuildReturn(r);
}
@@ -57,9 +57,9 @@ void GenerateSimpleTestCases(const string& outdir) {
auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")};
auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})};
[&](MethodBuilder& method) {
Value five{method.MakeRegister()};
LiveRegister five{method.AllocRegister()};
method.BuildConst4(five, 5);
Value object{method.MakeRegister()};
LiveRegister object{method.AllocRegister()};
method.BuildNew(
object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five);
method.BuildReturn(object, /*is_object=*/true);
@@ -80,7 +80,7 @@ void GenerateSimpleTestCases(const string& outdir) {
auto returnStringLength{
cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})};
{
Value result = returnStringLength.MakeRegister();
LiveRegister result = returnStringLength.AllocRegister();
returnStringLength.AddInstruction(
Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
returnStringLength.BuildReturn(result);
@@ -91,7 +91,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder returnIfZero{cbuilder.CreateMethod(
"returnIfZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
{
Value resultIfZero{returnIfZero.MakeRegister()};
LiveRegister resultIfZero{returnIfZero.AllocRegister()};
Value else_target{returnIfZero.MakeLabel()};
returnIfZero.AddInstruction(Instruction::OpWithArgs(
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
@@ -112,7 +112,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder returnIfNotZero{cbuilder.CreateMethod(
"returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
{
Value resultIfNotZero{returnIfNotZero.MakeRegister()};
LiveRegister resultIfNotZero{returnIfNotZero.AllocRegister()};
Value else_target{returnIfNotZero.MakeLabel()};
returnIfNotZero.AddInstruction(Instruction::OpWithArgs(
Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target));
@@ -148,8 +148,8 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder backwardsBranch{
cbuilder.CreateMethod("backwardsBranch", Prototype{TypeDescriptor::Int()})};
[](MethodBuilder& method) {
Value zero = method.MakeRegister();
Value result = method.MakeRegister();
LiveRegister zero = method.AllocRegister();
LiveRegister result = method.AllocRegister();
Value labelA = method.MakeLabel();
Value labelB = method.MakeLabel();
method.BuildConst4(zero, 0);
@@ -177,7 +177,7 @@ void GenerateSimpleTestCases(const string& outdir) {
// public static String returnNull() { return null; }
MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
[](MethodBuilder& method) {
Value zero = method.MakeRegister();
LiveRegister zero = method.AllocRegister();
method.BuildConst4(zero, 0);
method.BuildReturn(zero, /*is_object=*/true);
}(returnNull);
@@ -188,7 +188,7 @@ void GenerateSimpleTestCases(const string& outdir) {
// public static String makeString() { return "Hello, World!"; }
MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
[](MethodBuilder& method) {
Value string = method.MakeRegister();
LiveRegister string = method.AllocRegister();
method.BuildConstString(string, "Hello, World!");
method.BuildReturn(string, /*is_object=*/true);
}(makeString);
@@ -200,7 +200,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder returnStringIfZeroAB{
cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
[&](MethodBuilder& method) {
Value resultIfZero{method.MakeRegister()};
LiveRegister resultIfZero{method.AllocRegister()};
Value else_target{method.MakeLabel()};
method.AddInstruction(Instruction::OpWithArgs(
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
@@ -220,7 +220,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder returnStringIfZeroBA{
cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
[&](MethodBuilder& method) {
Value resultIfZero{method.MakeRegister()};
LiveRegister resultIfZero{method.AllocRegister()};
Value else_target{method.MakeLabel()};
method.AddInstruction(Instruction::OpWithArgs(
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
@@ -244,7 +244,7 @@ void GenerateSimpleTestCases(const string& outdir) {
cbuilder.CreateMethod("invokeStaticReturnObject",
Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
[&](MethodBuilder& method) {
Value result{method.MakeRegister()};
LiveRegister result{method.AllocRegister()};
MethodDeclData to_string{dex_file.GetOrDeclareMethod(
TypeDescriptor::FromClassname("java.lang.Integer"),
"toString",
@@ -260,7 +260,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod(
"invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})};
[&](MethodBuilder& method) {
Value result{method.MakeRegister()};
LiveRegister result{method.AllocRegister()};
MethodDeclData substring{dex_file.GetOrDeclareMethod(
string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})};
method.AddInstruction(Instruction::InvokeVirtualObject(
@@ -291,7 +291,7 @@ void GenerateSimpleTestCases(const string& outdir) {
[&](MethodBuilder& method) {
const ir::FieldDecl* field =
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
Value result{method.MakeRegister()};
LiveRegister result{method.AllocRegister()};
method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
method.BuildReturn(result, /*is_object=*/false);
method.Encode();
@@ -304,7 +304,7 @@ void GenerateSimpleTestCases(const string& outdir) {
[&](MethodBuilder& method) {
const ir::FieldDecl* field =
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
Value number{method.MakeRegister()};
LiveRegister number{method.AllocRegister()};
method.BuildConst4(number, 7);
method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
method.BuildReturn();
@@ -318,7 +318,7 @@ void GenerateSimpleTestCases(const string& outdir) {
[&](MethodBuilder& method) {
const ir::FieldDecl* field =
dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
Value result{method.MakeRegister()};
LiveRegister result{method.AllocRegister()};
method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0)));
method.BuildReturn(result, /*is_object=*/false);
method.Encode();
@@ -331,7 +331,7 @@ void GenerateSimpleTestCases(const string& outdir) {
[&](MethodBuilder& method) {
const ir::FieldDecl* field =
dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
Value number{method.MakeRegister()};
LiveRegister number{method.AllocRegister()};
method.BuildConst4(number, 7);
method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
method.BuildReturn();