summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nicolas Geoffray <ngeoffray@google.com> 2016-11-17 10:58:36 +0000
committer Nicolas Geoffray <ngeoffray@google.com> 2016-11-18 10:44:15 +0000
commit01b70e8bca5e824ee3958ffbdd7727334e81eb0a (patch)
tree3e23fef88de183fd0c7945a6198a68050640b656
parent81cae78d1853893ff9c3ecea4b5100002a538eb7 (diff)
Add dequickening support.
Both vdex and BCI require full dequickening support. This change implements dequickening by using the existing encoded quickening, and adds the checkcast quickening to that encoding. bug: 30937355 bug: 32369913 Test: dex_to_dex_decompiler_test.cc test-art-host Change-Id: Ie95f46946d59b28157d6e47dcf4a859be032d1c3
-rw-r--r--build/Android.gtest.mk2
-rw-r--r--compiler/Android.bp2
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc2
-rw-r--r--compiler/dex/dex_to_dex_compiler.h2
-rw-r--r--compiler/dex/dex_to_dex_decompiler.cc198
-rw-r--r--compiler/dex/dex_to_dex_decompiler.h38
-rw-r--r--compiler/dex/dex_to_dex_decompiler_test.cc136
-rw-r--r--compiler/driver/compiler_driver.h1
-rw-r--r--compiler/driver/compiler_options.h1
-rw-r--r--compiler/optimizing/instruction_builder.cc5
-rw-r--r--runtime/dex_instruction.h12
-rw-r--r--test/DexToDexDecompiler/Main.java34
12 files changed, 430 insertions, 3 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c785befda0..4fce235b83 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -25,6 +25,7 @@ include art/build/Android.common_build.mk
GTEST_DEX_DIRECTORIES := \
AbstractMethod \
AllFields \
+ DexToDexDecompiler \
ExceptionHandle \
GetMethodSignature \
ImageLayoutA \
@@ -106,6 +107,7 @@ ART_GTEST_stub_test_DEX_DEPS := AllFields
ART_GTEST_transaction_test_DEX_DEPS := Transaction
ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps MultiDex
+ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler
# The elf writer test has dependencies on core.oat.
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_optimizing_no-pic_64) $(HOST_CORE_IMAGE_optimizing_no-pic_32)
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 6edb639a1a..e2a450d171 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -28,6 +28,7 @@ art_cc_defaults {
"compiled_method.cc",
"debug/elf_debug_writer.cc",
"dex/dex_to_dex_compiler.cc",
+ "dex/dex_to_dex_decompiler.cc",
"dex/verified_method.cc",
"dex/verification_results.cc",
"dex/quick_compiler_callbacks.cc",
@@ -312,6 +313,7 @@ art_cc_test {
srcs: [
"compiled_method_test.cc",
"debug/dwarf/dwarf_test.cc",
+ "dex/dex_to_dex_decompiler_test.cc",
"driver/compiled_method_storage_test.cc",
"driver/compiler_driver_test.cc",
"elf_writer_test.cc",
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 9c1d72b9be..cf69f469a0 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -233,6 +233,8 @@ Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) {
<< " by replacing it with 2 NOPs at dex pc "
<< StringPrintf("0x%x", dex_pc) << " in method "
<< GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
+ quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegA_21c()));
+ quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegB_21c()));
// We are modifying 4 consecutive bytes.
inst->SetOpcode(Instruction::NOP);
inst->SetVRegA_10x(0u); // keep compliant with verifier.
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index 3fad6d4c95..0a00d45297 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -17,8 +17,6 @@
#ifndef ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
#define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
-#include "jni.h"
-
#include "dex_file.h"
#include "invoke_type.h"
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc
new file mode 100644
index 0000000000..051125eeaa
--- /dev/null
+++ b/compiler/dex/dex_to_dex_decompiler.cc
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_to_dex_decompiler.h"
+
+#include "base/logging.h"
+#include "base/mutex.h"
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+#include "optimizing/bytecode_utils.h"
+
+namespace art {
+namespace optimizer {
+
+class DexDecompiler {
+ public:
+ DexDecompiler(const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info)
+ : code_item_(code_item),
+ quickened_info_ptr_(quickened_info.data()),
+ quickened_info_end_(quickened_info.data() + quickened_info.size()) {}
+
+ bool Decompile();
+
+ private:
+ void DecompileInstanceFieldAccess(Instruction* inst,
+ uint32_t dex_pc,
+ Instruction::Code new_opcode) {
+ uint16_t index = GetIndexAt(dex_pc);
+ inst->SetOpcode(new_opcode);
+ inst->SetVRegC_22c(index);
+ }
+
+ void DecompileInvokeVirtual(Instruction* inst,
+ uint32_t dex_pc,
+ Instruction::Code new_opcode,
+ bool is_range) {
+ uint16_t index = GetIndexAt(dex_pc);
+ inst->SetOpcode(new_opcode);
+ if (is_range) {
+ inst->SetVRegB_3rc(index);
+ } else {
+ inst->SetVRegB_35c(index);
+ }
+ }
+
+ void DecompileNop(Instruction* inst, uint32_t dex_pc) {
+ if (quickened_info_ptr_ == quickened_info_end_) {
+ return;
+ }
+ const uint8_t* temporary_pointer = quickened_info_ptr_;
+ uint32_t quickened_pc = DecodeUnsignedLeb128(&temporary_pointer);
+ if (quickened_pc != dex_pc) {
+ return;
+ }
+ uint16_t reference_index = GetIndexAt(dex_pc);
+ uint16_t type_index = GetIndexAt(dex_pc);
+ inst->SetOpcode(Instruction::CHECK_CAST);
+ inst->SetVRegA_21c(reference_index);
+ inst->SetVRegB_21c(type_index);
+ }
+
+ uint16_t GetIndexAt(uint32_t dex_pc) {
+ // Note that as a side effect, DecodeUnsignedLeb128 update the given pointer
+ // to the new position in the buffer.
+ DCHECK_LT(quickened_info_ptr_, quickened_info_end_);
+ uint32_t quickened_pc = DecodeUnsignedLeb128(&quickened_info_ptr_);
+ DCHECK_LT(quickened_info_ptr_, quickened_info_end_);
+ uint16_t index = DecodeUnsignedLeb128(&quickened_info_ptr_);
+ DCHECK_LE(quickened_info_ptr_, quickened_info_end_);
+ DCHECK_EQ(quickened_pc, dex_pc);
+ return index;
+ }
+
+ const DexFile::CodeItem& code_item_;
+ const uint8_t* quickened_info_ptr_;
+ const uint8_t* const quickened_info_end_;
+
+ DISALLOW_COPY_AND_ASSIGN(DexDecompiler);
+};
+
+bool DexDecompiler::Decompile() {
+ // We need to iterate over the code item, and not over the quickening data,
+ // because the RETURN_VOID quickening is not encoded in the quickening data. Because
+ // unquickening is a rare need and not performance sensitive, it is not worth the
+ // added storage to also add the RETURN_VOID quickening in the quickened data.
+ for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
+ uint32_t dex_pc = it.CurrentDexPc();
+ Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
+
+ switch (inst->Opcode()) {
+ case Instruction::RETURN_VOID_NO_BARRIER:
+ inst->SetOpcode(Instruction::RETURN_VOID);
+ break;
+
+ case Instruction::NOP:
+ DecompileNop(inst, dex_pc);
+ break;
+
+ case Instruction::IGET_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET);
+ break;
+
+ case Instruction::IGET_WIDE_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE);
+ break;
+
+ case Instruction::IGET_OBJECT_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT);
+ break;
+
+ case Instruction::IGET_BOOLEAN_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN);
+ break;
+
+ case Instruction::IGET_BYTE_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE);
+ break;
+
+ case Instruction::IGET_CHAR_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR);
+ break;
+
+ case Instruction::IGET_SHORT_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT);
+ break;
+
+ case Instruction::IPUT_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT);
+ break;
+
+ case Instruction::IPUT_BOOLEAN_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN);
+ break;
+
+ case Instruction::IPUT_BYTE_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE);
+ break;
+
+ case Instruction::IPUT_CHAR_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR);
+ break;
+
+ case Instruction::IPUT_SHORT_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT);
+ break;
+
+ case Instruction::IPUT_WIDE_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE);
+ break;
+
+ case Instruction::IPUT_OBJECT_QUICK:
+ DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT);
+ break;
+
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL, false);
+ break;
+
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+ DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE, true);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (quickened_info_ptr_ != quickened_info_end_) {
+ LOG(ERROR) << "Failed to use all values in quickening info."
+ << " Actual: " << std::hex << quickened_info_ptr_
+ << " Expected: " << quickened_info_end_;
+ return false;
+ }
+
+ return true;
+}
+
+bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
+ const ArrayRef<const uint8_t>& quickened_info) {
+ DexDecompiler decompiler(code_item, quickened_info);
+ return decompiler.Decompile();
+}
+
+} // namespace optimizer
+} // namespace art
diff --git a/compiler/dex/dex_to_dex_decompiler.h b/compiler/dex/dex_to_dex_decompiler.h
new file mode 100644
index 0000000000..5502ca2d92
--- /dev/null
+++ b/compiler/dex/dex_to_dex_decompiler.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
+#define ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
+
+#include "base/array_ref.h"
+#include "dex_file.h"
+
+namespace art {
+namespace optimizer {
+
+// "Decompile", that is unquicken, the code item provided, given the
+// associated quickening data.
+// TODO: code_item isn't really a const element, but changing it
+// to non-const has too many repercussions on the code base. We make it
+// consistent with DexToDexCompiler, but we should really change it to
+// DexFile::CodeItem*.
+bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
+ const ArrayRef<const uint8_t>& quickened_data);
+
+} // namespace optimizer
+} // namespace art
+
+#endif // ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
new file mode 100644
index 0000000000..ea6c7a2e09
--- /dev/null
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex/dex_to_dex_decompiler.h"
+
+#include "class_linker.h"
+#include "compiler/common_compiler_test.h"
+#include "compiler/compiled_method.h"
+#include "compiler/driver/compiler_options.h"
+#include "compiler/driver/compiler_driver.h"
+#include "compiler_callbacks.h"
+#include "dex_file.h"
+#include "handle_scope-inl.h"
+#include "verifier/method_verifier-inl.h"
+#include "mirror/class_loader.h"
+#include "runtime.h"
+#include "thread.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+
+class DexToDexDecompilerTest : public CommonCompilerTest {
+ public:
+ void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
+ TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
+ TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
+ compiler_options_->boot_image_ = false;
+ compiler_options_->SetCompilerFilter(CompilerFilter::kInterpretOnly);
+ compiler_driver_->CompileAll(class_loader,
+ GetDexFiles(class_loader),
+ /* verifier_deps */ nullptr,
+ &timings);
+ }
+
+ void RunTest(const char* dex_name) {
+ Thread* self = Thread::Current();
+ // First load the original dex file.
+ jobject original_class_loader;
+ {
+ ScopedObjectAccess soa(self);
+ original_class_loader = LoadDex(dex_name);
+ }
+ const DexFile* original_dex_file = GetDexFiles(original_class_loader)[0];
+
+ // Load the dex file again and make it writable to quicken them.
+ jobject class_loader;
+ const DexFile* updated_dex_file = nullptr;
+ {
+ ScopedObjectAccess soa(self);
+ class_loader = LoadDex(dex_name);
+ updated_dex_file = GetDexFiles(class_loader)[0];
+ Runtime::Current()->GetClassLinker()->RegisterDexFile(
+ *updated_dex_file, soa.Decode<mirror::ClassLoader>(class_loader).Ptr());
+ }
+ // The dex files should be identical.
+ int cmp = memcmp(original_dex_file->Begin(),
+ updated_dex_file->Begin(),
+ updated_dex_file->Size());
+ ASSERT_EQ(0, cmp);
+
+ updated_dex_file->EnableWrite();
+ CompileAll(class_loader);
+ // The dex files should be different after quickening.
+ cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size());
+ ASSERT_NE(0, cmp);
+
+ // Unquicken the dex file.
+ for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = updated_dex_file->GetClassDef(i);
+ const uint8_t* class_data = updated_dex_file->GetClassData(class_def);
+ if (class_data == nullptr) {
+ continue;
+ }
+ ClassDataItemIterator it(*updated_dex_file, class_data);
+ // Skip fields
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
+
+ // Unquicken each method.
+ while (it.HasNextDirectMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
+ ArrayRef<const uint8_t> table;
+ if (compiled_method != nullptr) {
+ table = compiled_method->GetVmapTable();
+ }
+ optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
+ ArrayRef<const uint8_t> table;
+ if (compiled_method != nullptr) {
+ table = compiled_method->GetVmapTable();
+ }
+ optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+
+ // Make sure after unquickening we go back to the same contents as the original dex file.
+ cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size());
+ ASSERT_EQ(0, cmp);
+ }
+};
+
+TEST_F(DexToDexDecompilerTest, VerifierDeps) {
+ RunTest("VerifierDeps");
+}
+
+TEST_F(DexToDexDecompilerTest, DexToDexDecompiler) {
+ RunTest("DexToDexDecompiler");
+}
+
+} // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index c8d6cb0d4d..1bd354658a 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -584,6 +584,7 @@ class CompilerDriver {
const BitVector* current_dex_to_dex_methods_;
friend class CompileClassVisitor;
+ friend class DexToDexDecompilerTest;
friend class verifier::VerifierDepsTest;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 56b632d6b4..9c62f80339 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -341,6 +341,7 @@ class CompilerOptions FINAL {
const std::vector<std::string>* passes_to_run_;
friend class Dex2Oat;
+ friend class DexToDexDecompilerTest;
friend class CommonCompilerTest;
friend class verifier::VerifierDepsTest;
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index c8c4ca76fd..b44137d138 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1723,7 +1723,10 @@ uint16_t HInstructionBuilder::LookupQuickenedInfo(uint32_t dex_pc) {
if (dex_pc_in_map == dex_pc) {
return value_in_map;
} else {
- skipped_interpreter_metadata_.Put(dex_pc_in_map, value_in_map);
+ // Overwrite and not Put, as quickened CHECK-CAST has two entries with
+ // the same dex_pc. This is OK, because the compiler does not care about those
+ // entries.
+ skipped_interpreter_metadata_.Overwrite(dex_pc_in_map, value_in_map);
}
}
}
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 8eb1a79420..99b9f9dd13 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -480,6 +480,18 @@ class Instruction {
insns[1] = val;
}
+ void SetVRegA_21c(uint8_t val) {
+ DCHECK(FormatOf(Opcode()) == k21c);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[0] = (val << 8) | (insns[0] & 0x00ff);
+ }
+
+ void SetVRegB_21c(uint16_t val) {
+ DCHECK(FormatOf(Opcode()) == k21c);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[1] = val;
+ }
+
// Returns the format of the given opcode.
static Format FormatOf(Code opcode) {
return kInstructionFormats[opcode];
diff --git a/test/DexToDexDecompiler/Main.java b/test/DexToDexDecompiler/Main.java
new file mode 100644
index 0000000000..8f5075ae2a
--- /dev/null
+++ b/test/DexToDexDecompiler/Main.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ Main() {
+ // Will be quickened with RETURN_VOID_NO_BARRIER.
+ }
+
+ public static void main() {
+ Main m = new Main();
+ Object o = m;
+ // The call and field accesses will be quickened.
+ m.foo(m.a);
+
+ // The checkcast will be quickened.
+ m.foo(((Main)o).a);
+ }
+
+ int a;
+ void foo(int a) {}
+}