Move mapping table and vmap table offsets to OatMethodHeader.
This change has a libcore/ companion CL
"Remove ArtMethod's quick fields mapping table and vmap table."
https://android-review.googlesource.com/91254
Bug: 11767815
Change-Id: I46ce2067e1ecd915da3890606498e31ffc332813
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index fdf09a5..8bba84a 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -132,34 +132,24 @@
class CommonCompilerTest : public CommonRuntimeTest {
public:
- static void MakeExecutable(const std::vector<uint8_t>& code) {
- CHECK_NE(code.size(), 0U);
- MakeExecutable(&code[0], code.size());
- }
-
// Create an OatMethod based on pointers (for unit tests).
OatFile::OatMethod CreateOatMethod(const void* code,
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- const uint8_t* mapping_table,
- const uint8_t* vmap_table,
const uint8_t* gc_map) {
+ CHECK(code != nullptr);
const byte* base;
- uint32_t code_offset, mapping_table_offset, vmap_table_offset, gc_map_offset;
- if (mapping_table == nullptr && vmap_table == nullptr && gc_map == nullptr) {
+ uint32_t code_offset, gc_map_offset;
+ if (gc_map == nullptr) {
base = reinterpret_cast<const byte*>(code); // Base of data points at code.
base -= kPointerSize; // Move backward so that code_offset != 0.
code_offset = kPointerSize;
- mapping_table_offset = 0;
- vmap_table_offset = 0;
gc_map_offset = 0;
} else {
// TODO: 64bit support.
base = nullptr; // Base of data in oat file, ie 0.
code_offset = PointerToLowMemUInt32(code);
- mapping_table_offset = PointerToLowMemUInt32(mapping_table);
- vmap_table_offset = PointerToLowMemUInt32(vmap_table);
gc_map_offset = PointerToLowMemUInt32(gc_map);
}
return OatFile::OatMethod(base,
@@ -167,8 +157,6 @@
frame_size_in_bytes,
core_spill_mask,
fp_spill_mask,
- mapping_table_offset,
- vmap_table_offset,
gc_map_offset);
}
@@ -185,19 +173,44 @@
}
if (compiled_method != nullptr) {
const std::vector<uint8_t>* code = compiled_method->GetQuickCode();
- if (code == nullptr) {
+ const void* code_ptr;
+ if (code != nullptr) {
+ uint32_t code_size = code->size();
+ CHECK_NE(0u, code_size);
+ const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
+ uint32_t vmap_table_offset = vmap_table.empty() ? 0u
+ : sizeof(OatMethodHeader) + vmap_table.size();
+ const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
+ uint32_t mapping_table_offset = mapping_table.empty() ? 0u
+ : sizeof(OatMethodHeader) + vmap_table.size() + mapping_table.size();
+ OatMethodHeader method_header(vmap_table_offset, mapping_table_offset, code_size);
+
+ header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
+ std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
+ size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size();
+ size_t code_offset = compiled_method->AlignCode(size - code_size);
+ size_t padding = code_offset - (size - code_size);
+ chunk->reserve(padding + size);
+ chunk->resize(sizeof(method_header));
+ memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
+ chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
+ chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end());
+ chunk->insert(chunk->begin(), padding, 0);
+ chunk->insert(chunk->end(), code->begin(), code->end());
+ CHECK_EQ(padding + size, chunk->size());
+ code_ptr = &(*chunk)[code_offset];
+ } else {
code = compiled_method->GetPortableCode();
+ code_ptr = &(*code)[0];
}
- MakeExecutable(*code);
- const void* method_code = CompiledMethod::CodePointer(&(*code)[0],
+ MakeExecutable(code_ptr, code->size());
+ const void* method_code = CompiledMethod::CodePointer(code_ptr,
compiled_method->GetInstructionSet());
LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code;
OatFile::OatMethod oat_method = CreateOatMethod(method_code,
compiled_method->GetFrameSizeInBytes(),
compiled_method->GetCoreSpillMask(),
compiled_method->GetFpSpillMask(),
- &compiled_method->GetMappingTable()[0],
- &compiled_method->GetVmapTable()[0],
nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
@@ -211,8 +224,6 @@
kStackAlignment,
0,
0,
- nullptr,
- nullptr,
nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
@@ -230,8 +241,6 @@
sirt_size,
callee_save_method->GetCoreSpillMask(),
callee_save_method->GetFpSpillMask(),
- nullptr,
- nullptr,
nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
@@ -436,6 +445,9 @@
private:
UniquePtr<MemMap> image_reservation_;
+
+ // Chunks must not move their storage after being created - use the node-based std::list.
+ std::list<std::vector<uint8_t> > header_code_and_maps_chunks_;
};
} // namespace art
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 3400b01..c35d400 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -680,14 +680,6 @@
copy->SetNativeMethod<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_));
} else {
// Normal (non-abstract non-native) methods have various tables to relocate.
- uint32_t mapping_table_off = orig->GetOatMappingTableOffset();
- const byte* mapping_table = GetOatAddress(mapping_table_off);
- copy->SetMappingTable<kVerifyNone>(mapping_table);
-
- uint32_t vmap_table_offset = orig->GetOatVmapTableOffset();
- const byte* vmap_table = GetOatAddress(vmap_table_offset);
- copy->SetVmapTable<kVerifyNone>(vmap_table);
-
uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset();
const byte* native_gc_map = GetOatAddress(native_gc_map_offset);
copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map));
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 766ef7b..b5d3923 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -176,7 +176,8 @@
// If this test is failing and you have to update these constants,
// it is time to update OatHeader::kOatVersion
EXPECT_EQ(80U, sizeof(OatHeader));
- EXPECT_EQ(28U, sizeof(OatMethodOffsets));
+ EXPECT_EQ(20U, sizeof(OatMethodOffsets));
+ EXPECT_EQ(12U, sizeof(OatMethodHeader));
}
TEST_F(OatTest, OatHeaderIsValid) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 2114fe9..bbc9c3e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -155,12 +155,15 @@
}
static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
- return oat_class->method_offsets_[method_offsets_index].mapping_table_offset_;
+ uint32_t offset = oat_class->method_headers_[method_offsets_index].mapping_table_offset_;
+ return offset == 0u ? 0u :
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
}
static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
ALWAYS_INLINE {
- oat_class->method_offsets_[method_offsets_index].mapping_table_offset_ = offset;
+ oat_class->method_headers_[method_offsets_index].mapping_table_offset_ =
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
}
static const char* Name() ALWAYS_INLINE {
@@ -174,12 +177,15 @@
}
static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
- return oat_class->method_offsets_[method_offsets_index].vmap_table_offset_;
+ uint32_t offset = oat_class->method_headers_[method_offsets_index].vmap_table_offset_;
+ return offset == 0u ? 0u :
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
}
static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
ALWAYS_INLINE {
- oat_class->method_offsets_[method_offsets_index].vmap_table_offset_ = offset;
+ oat_class->method_headers_[method_offsets_index].vmap_table_offset_ =
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
}
static const char* Name() ALWAYS_INLINE {
@@ -368,17 +374,22 @@
}
}
+ DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
+ OatMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
+ method_header->code_size_ = code_size;
+
// Deduplicate code arrays.
- auto code_iter = dedupe_map_.find(quick_code);
+ auto code_iter = dedupe_map_.find(compiled_method);
if (code_iter != dedupe_map_.end()) {
quick_code_offset = code_iter->second;
+ FixupMethodHeader(method_header, quick_code_offset - thumb_offset);
} else {
- dedupe_map_.Put(quick_code, quick_code_offset);
- OatMethodHeader method_header(code_size);
- offset_ += sizeof(method_header); // Method header is prepended before code.
- writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header));
- offset_ += code_size;
+ dedupe_map_.Put(compiled_method, quick_code_offset);
+ FixupMethodHeader(method_header, quick_code_offset - thumb_offset);
+ writer_->oat_header_->UpdateChecksum(method_header, sizeof(*method_header));
+ offset_ += sizeof(*method_header); // Method header is prepended before code.
writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
+ offset_ += code_size;
}
}
frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
@@ -420,9 +431,22 @@
}
private:
+ static void FixupMethodHeader(OatMethodHeader* method_header, uint32_t code_offset) {
+ // The code offset was 0 when the mapping/vmap table offset was set, so it's set
+ // to 0-offset and we need to adjust it by code_offset.
+ if (method_header->mapping_table_offset_ != 0u) {
+ method_header->mapping_table_offset_ += code_offset;
+ DCHECK_LT(method_header->mapping_table_offset_, code_offset);
+ }
+ if (method_header->vmap_table_offset_ != 0u) {
+ method_header->vmap_table_offset_ += code_offset;
+ DCHECK_LT(method_header->vmap_table_offset_, code_offset);
+ }
+ }
+
// Deduplication is already done on a pointer basis by the compiler driver,
// so we can simply compare the pointers to find out if things are duplicated.
- SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_;
+ SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
};
template <typename DataAccess>
@@ -477,7 +501,7 @@
OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
- OatMethodOffsets offsets(0u, kStackAlignment, 0u, 0u, 0u, 0u, 0u);
+ OatMethodOffsets offsets(0u, kStackAlignment, 0u, 0u, 0u);
if (compiled_method != nullptr) {
DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
offsets = oat_class->method_offsets_[method_offsets_index_];
@@ -511,8 +535,6 @@
offsets.frame_size_in_bytes_ = callee_save_method->GetFrameSizeInBytes() + sirt_size;
offsets.core_spill_mask_ = callee_save_method->GetCoreSpillMask();
offsets.fp_spill_mask_ = callee_save_method->GetFpSpillMask();
- DCHECK_EQ(offsets.mapping_table_offset_, 0u);
- DCHECK_EQ(offsets.vmap_table_offset_, 0u);
DCHECK_EQ(offsets.gc_map_offset_, 0u);
}
@@ -528,10 +550,8 @@
method->SetFrameSizeInBytes(offsets.frame_size_in_bytes_);
method->SetCoreSpillMask(offsets.core_spill_mask_);
method->SetFpSpillMask(offsets.fp_spill_mask_);
- method->SetOatMappingTableOffset(offsets.mapping_table_offset_);
// Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
method->SetQuickOatCodeOffset(offsets.code_offset_);
- method->SetOatVmapTableOffset(offsets.vmap_table_offset_);
method->SetOatNativeGcMapOffset(offsets.gc_map_offset_);
return true;
@@ -584,7 +604,7 @@
offset_ + sizeof(OatMethodHeader) + compiled_method->CodeDelta())
<< PrettyMethod(it.GetMemberIndex(), *dex_file_);
if (method_offsets.code_offset_ >= offset_) {
- OatMethodHeader method_header(code_size);
+ const OatMethodHeader& method_header = oat_class->method_headers_[method_offsets_index_];
if (!out->WriteFully(&method_header, sizeof(method_header))) {
ReportWriteFailure("method header", it);
return false;
@@ -1153,6 +1173,7 @@
status_ = status;
method_offsets_.resize(num_non_null_compiled_methods);
+ method_headers_.resize(num_non_null_compiled_methods);
uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
if (type_ == kOatClassSomeCompiled) {
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 1abacd8..7cdd532 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -225,12 +225,13 @@
// not is kOatClassBitmap, the bitmap will be NULL.
BitVector* method_bitmap_;
- // OatMethodOffsets for each CompiledMethod present in the
- // OatClass. Note that some may be missing if
+ // OatMethodOffsets and OatMethodHeaders for each CompiledMethod
+ // present in the OatClass. Note that some may be missing if
// OatClass::compiled_methods_ contains NULL values (and
// oat_method_offsets_offsets_from_oat_class_ should contain 0
// values in this case).
std::vector<OatMethodOffsets> method_offsets_;
+ std::vector<OatMethodHeader> method_headers_;
private:
DISALLOW_COPY_AND_ASSIGN(OatClass);
@@ -299,6 +300,22 @@
uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
+ struct CodeOffsetsKeyComparator {
+ bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
+ if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
+ return lhs->GetQuickCode() < rhs->GetQuickCode();
+ }
+ // If the code is the same, all other fields are likely to be the same as well.
+ if (UNLIKELY(&lhs->GetMappingTable() != &rhs->GetMappingTable())) {
+ return &lhs->GetMappingTable() < &rhs->GetMappingTable();
+ }
+ if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) {
+ return &lhs->GetVmapTable() < &rhs->GetVmapTable();
+ }
+ return false;
+ }
+ };
+
DISALLOW_COPY_AND_ASSIGN(OatWriter);
};
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 5054f96..1a67952 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -44,6 +44,7 @@
#include "mirror/object_array-inl.h"
#include "noop_compiler_callbacks.h"
#include "oat.h"
+#include "oat_file-inl.h"
#include "object_utils.h"
#include "os.h"
#include "runtime.h"
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 58b82f0..338133c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -185,7 +185,8 @@
quick_resolution_trampoline_(nullptr),
portable_imt_conflict_trampoline_(nullptr),
quick_imt_conflict_trampoline_(nullptr),
- quick_generic_jni_trampoline_(nullptr) {
+ quick_generic_jni_trampoline_(nullptr),
+ quick_to_interpreter_bridge_trampoline_(nullptr) {
CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
}
@@ -1002,6 +1003,7 @@
portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline();
quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
+ quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge();
mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
mirror::ObjectArray<mirror::DexCache>* dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>();
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a14d1d1..9771318 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -369,6 +369,10 @@
return quick_imt_conflict_trampoline_;
}
+ const void* GetQuickToInterpreterBridgeTrampoline() const {
+ return quick_to_interpreter_bridge_trampoline_;
+ }
+
InternTable* GetInternTable() const {
return intern_table_;
}
@@ -658,6 +662,7 @@
const void* portable_imt_conflict_trampoline_;
const void* quick_imt_conflict_trampoline_;
const void* quick_generic_jni_trampoline_;
+ const void* quick_to_interpreter_bridge_trampoline_;
friend class ImageWriter; // for GetClassRoots
FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 5b72a44..1218357 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -485,8 +485,6 @@
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_portable_compiled_code_), "entryPointFromPortableCompiledCode"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_quick_compiled_code_), "entryPointFromQuickCompiledCode"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, gc_map_), "gcMap"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, quick_mapping_table_), "quickMappingTable"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, quick_vmap_table_), "quickVmapTable"));
// alphabetical 32-bit
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, access_flags_), "accessFlags"));
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8b48b36..05912bf 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -761,6 +761,10 @@
return class_linker->GetQuickGenericJniTrampoline();
}
+static inline const void* GetQuickToInterpreterBridgeTrampoline(ClassLinker* class_linker) {
+ return class_linker->GetQuickToInterpreterBridgeTrampoline();
+}
+
extern "C" void art_portable_proxy_invoke_handler();
static inline const void* GetPortableProxyInvokeHandler() {
return reinterpret_cast<void*>(art_portable_proxy_invoke_handler);
diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
index 633f580..60c5377 100644
--- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
@@ -32,6 +32,7 @@
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
const void* result = instrumentation->GetQuickCodeFor(method);
+ DCHECK(result != GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()));
bool interpreter_entry = (result == GetQuickToInterpreterBridge());
instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? NULL : this_object,
method, lr, interpreter_entry);
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 208eb74..97a8367 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -49,10 +49,6 @@
dex_ = my_klass_->GetDexCache()->GetDexFile();
uint32_t code_size = 12;
- fake_code_.push_back((code_size >> 24) & 0xFF);
- fake_code_.push_back((code_size >> 16) & 0xFF);
- fake_code_.push_back((code_size >> 8) & 0xFF);
- fake_code_.push_back((code_size >> 0) & 0xFF);
for (size_t i = 0 ; i < code_size; i++) {
fake_code_.push_back(0x70 | i);
}
@@ -74,20 +70,35 @@
fake_gc_map_.push_back(0); // 0 entries.
fake_gc_map_.push_back(0);
+ const std::vector<uint8_t>& fake_vmap_table_data = fake_vmap_table_data_.GetData();
+ const std::vector<uint8_t>& fake_mapping_data = fake_mapping_data_.GetData();
+ uint32_t vmap_table_offset = sizeof(OatMethodHeader) + fake_vmap_table_data.size();
+ uint32_t mapping_table_offset = vmap_table_offset + fake_mapping_data.size();
+ OatMethodHeader method_header(vmap_table_offset, mapping_table_offset, code_size);
+ fake_header_code_and_maps_.resize(sizeof(method_header));
+ memcpy(&fake_header_code_and_maps_[0], &method_header, sizeof(method_header));
+ fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(),
+ fake_vmap_table_data.begin(), fake_vmap_table_data.end());
+ fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(),
+ fake_mapping_data.begin(), fake_mapping_data.end());
+ fake_header_code_and_maps_.insert(fake_header_code_and_maps_.end(),
+ fake_code_.begin(), fake_code_.end());
+
+ // NOTE: Don't align the code (it will not be executed) but check that the Thumb2
+ // adjustment will be a NOP, see ArtMethod::EntryPointToCodePointer().
+ CHECK_EQ(mapping_table_offset & 1u, 0u);
+ const uint8_t* code_ptr = &fake_header_code_and_maps_[mapping_table_offset];
+
method_f_ = my_klass_->FindVirtualMethod("f", "()I");
ASSERT_TRUE(method_f_ != NULL);
method_f_->SetFrameSizeInBytes(4 * kPointerSize);
- method_f_->SetEntryPointFromQuickCompiledCode(&fake_code_[sizeof(code_size)]);
- method_f_->SetMappingTable(&fake_mapping_data_.GetData()[0]);
- method_f_->SetVmapTable(&fake_vmap_table_data_.GetData()[0]);
+ method_f_->SetEntryPointFromQuickCompiledCode(code_ptr);
method_f_->SetNativeGcMap(&fake_gc_map_[0]);
method_g_ = my_klass_->FindVirtualMethod("g", "(I)V");
ASSERT_TRUE(method_g_ != NULL);
method_g_->SetFrameSizeInBytes(4 * kPointerSize);
- method_g_->SetEntryPointFromQuickCompiledCode(&fake_code_[sizeof(code_size)]);
- method_g_->SetMappingTable(&fake_mapping_data_.GetData()[0]);
- method_g_->SetVmapTable(&fake_vmap_table_data_.GetData()[0]);
+ method_g_->SetEntryPointFromQuickCompiledCode(code_ptr);
method_g_->SetNativeGcMap(&fake_gc_map_[0]);
}
@@ -97,6 +108,7 @@
Leb128EncodingVector fake_mapping_data_;
Leb128EncodingVector fake_vmap_table_data_;
std::vector<uint8_t> fake_gc_map_;
+ std::vector<uint8_t> fake_header_code_and_maps_;
mirror::ArtMethod* method_f_;
mirror::ArtMethod* method_g_;
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 2cd7f49..77d29dd 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -94,6 +94,7 @@
}
if (!method->IsResolutionMethod()) {
if (quick_code == GetQuickToInterpreterBridge() ||
+ quick_code == GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()) ||
(quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker()) &&
Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly()
&& !method->IsNative() && !method->IsProxyMethod())) {
@@ -147,6 +148,7 @@
// Do not overwrite interpreter to prevent from posting method entry/exit events twice.
new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
new_quick_code = class_linker->GetQuickOatCodeFor(method);
+ DCHECK(new_quick_code != GetQuickToInterpreterBridgeTrampoline(class_linker));
if (entry_exit_stubs_installed_ && new_quick_code != GetQuickToInterpreterBridge()) {
DCHECK(new_portable_code != GetPortableToInterpreterBridge());
new_portable_code = GetPortableToInterpreterBridge();
@@ -562,7 +564,8 @@
new_quick_code = GetQuickToInterpreterBridge();
new_have_portable_code = false;
} else if (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker()) ||
- quick_code == GetQuickToInterpreterBridge()) {
+ quick_code == GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()) ||
+ quick_code == GetQuickToInterpreterBridge()) {
DCHECK((portable_code == GetPortableResolutionTrampoline(Runtime::Current()->GetClassLinker())) ||
(portable_code == GetPortableToInterpreterBridge()));
new_portable_code = portable_code;
@@ -709,9 +712,10 @@
Runtime* runtime = Runtime::Current();
if (LIKELY(!instrumentation_stubs_installed_)) {
const void* code = method->GetEntryPointFromQuickCompiledCode();
- DCHECK(code != NULL);
- if (LIKELY(code != GetQuickResolutionTrampoline(runtime->GetClassLinker()) &&
- code != GetQuickToInterpreterBridge())) {
+ DCHECK(code != nullptr);
+ if (LIKELY(code != GetQuickResolutionTrampoline(runtime->GetClassLinker())) &&
+ LIKELY(code != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker())) &&
+ LIKELY(code != GetQuickToInterpreterBridge())) {
return code;
}
}
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 5d62b88..6e1f062 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -78,13 +78,11 @@
inline uint32_t ArtMethod::GetCodeSize() {
DCHECK(!IsRuntimeMethod() && !IsProxyMethod()) << PrettyMethod(this);
- uintptr_t code = reinterpret_cast<uintptr_t>(GetEntryPointFromQuickCompiledCode());
- if (code == 0) {
- return 0;
+ const void* code = EntryPointToCodePointer(GetEntryPointFromQuickCompiledCode());
+ if (code == nullptr) {
+ return 0u;
}
- // TODO: make this Thumb2 specific
- code &= ~0x1;
- return reinterpret_cast<OatMethodHeader*>(code)[-1].code_size_;
+ return reinterpret_cast<const OatMethodHeader*>(code)[-1].code_size_;
}
inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) {
@@ -124,7 +122,8 @@
return;
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (code == GetQuickResolutionTrampoline(class_linker)) {
+ if (code == GetQuickResolutionTrampoline(class_linker) ||
+ code == GetQuickToInterpreterBridgeTrampoline(class_linker)) {
return;
}
DCHECK(IsWithinQuickCode(pc))
@@ -154,26 +153,6 @@
SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(code_offset));
}
-inline uint32_t ArtMethod::GetOatMappingTableOffset() {
- DCHECK(!Runtime::Current()->IsStarted());
- return PointerToLowMemUInt32(GetMappingTable());
-}
-
-inline void ArtMethod::SetOatMappingTableOffset(uint32_t mapping_table_offset) {
- DCHECK(!Runtime::Current()->IsStarted());
- SetMappingTable(reinterpret_cast<const uint8_t*>(mapping_table_offset));
-}
-
-inline uint32_t ArtMethod::GetOatVmapTableOffset() {
- DCHECK(!Runtime::Current()->IsStarted());
- return PointerToLowMemUInt32(GetVmapTable());
-}
-
-inline void ArtMethod::SetOatVmapTableOffset(uint32_t vmap_table_offset) {
- DCHECK(!Runtime::Current()->IsStarted());
- SetVmapTable(reinterpret_cast<uint8_t*>(vmap_table_offset));
-}
-
inline void ArtMethod::SetOatNativeGcMapOffset(uint32_t gc_map_offset) {
DCHECK(!Runtime::Current()->IsStarted());
SetNativeGcMap(reinterpret_cast<uint8_t*>(gc_map_offset));
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index ee5a0a4..8da57bd 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -374,5 +374,43 @@
RegisterNative(self, GetJniDlsymLookupStub(), false);
}
+const void* ArtMethod::GetOatCodePointer() {
+ if (IsPortableCompiled() || IsNative() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
+ return nullptr;
+ }
+ Runtime* runtime = Runtime::Current();
+ const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this);
+ // On failure, instead of nullptr we get the quick-to-interpreter-bridge (but not the trampoline).
+ DCHECK(entry_point != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker()));
+ if (entry_point == GetQuickToInterpreterBridge()) {
+ return nullptr;
+ }
+ return EntryPointToCodePointer(entry_point);
+}
+
+const uint8_t* ArtMethod::GetMappingTable() {
+ const void* code = GetOatCodePointer();
+ if (code == nullptr) {
+ return nullptr;
+ }
+ uint32_t offset = reinterpret_cast<const OatMethodHeader*>(code)[-1].mapping_table_offset_;
+ if (UNLIKELY(offset == 0u)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const uint8_t*>(code) - offset;
+}
+
+const uint8_t* ArtMethod::GetVmapTable() {
+ const void* code = GetOatCodePointer();
+ if (code == nullptr) {
+ return nullptr;
+ }
+ uint32_t offset = reinterpret_cast<const OatMethodHeader*>(code)[-1].vmap_table_offset_;
+ if (UNLIKELY(offset == 0u)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const uint8_t*>(code) - offset;
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index ee23c40..8d2c39c 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -21,6 +21,7 @@
#include "dex_file.h"
#include "invoke_type.h"
#include "modifiers.h"
+#include "oat.h"
#include "object.h"
#include "object_callbacks.h"
@@ -261,7 +262,6 @@
EntryPointFromQuickCompiledCodeOffset(), entry_point_from_quick_compiled_code, false);
}
-
uint32_t GetCodeSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -286,37 +286,20 @@
void SetQuickOatCodeOffset(uint32_t code_offset);
void SetPortableOatCodeOffset(uint32_t code_offset);
+ static const void* EntryPointToCodePointer(const void* entry_point) ALWAYS_INLINE {
+ uintptr_t code = reinterpret_cast<uintptr_t>(entry_point);
+ code &= ~0x1; // TODO: Make this Thumb2 specific.
+ return reinterpret_cast<const void*>(code);
+ }
+
+ // Actual pointer to compiled oat code or nullptr.
+ const void* GetOatCodePointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Callers should wrap the uint8_t* in a MappingTable instance for convenient access.
- const uint8_t* GetMappingTable() {
- return GetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_),
- false);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetMappingTable(const uint8_t* mapping_table) {
- SetFieldPtr<false, true, kVerifyFlags>(
- OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_), mapping_table, false);
- }
-
- uint32_t GetOatMappingTableOffset();
-
- void SetOatMappingTableOffset(uint32_t mapping_table_offset);
+ const uint8_t* GetMappingTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Callers should wrap the uint8_t* in a VmapTable instance for convenient access.
- const uint8_t* GetVmapTable() {
- return GetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_),
- false);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetVmapTable(const uint8_t* vmap_table) {
- SetFieldPtr<false, true, kVerifyFlags>(
- OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, false);
- }
-
- uint32_t GetOatVmapTableOffset();
-
- void SetOatVmapTableOffset(uint32_t vmap_table_offset);
+ const uint8_t* GetVmapTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const uint8_t* GetNativeGcMap() {
return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), false);
@@ -468,20 +451,6 @@
// offsets for the quick compiler and dex PCs for the portable.
uint64_t gc_map_;
- // --- Quick compiler meta-data. ---
- // TODO: merge and place in native heap, such as done with the code size.
-
- // Pointer to a data structure created by the quick compiler to map between dex PCs and native
- // PCs, and vice-versa.
- uint64_t quick_mapping_table_;
-
- // When a register is promoted into a register, the spill mask holds which registers hold dex
- // registers. The first promoted register's corresponding dex register is vmap_table_[1], the Nth
- // is vmap_table_[N]. vmap_table_[0] holds the length of the table.
- uint64_t quick_vmap_table_;
-
- // --- End of quick compiler meta-data. ---
-
// Access flags; low 16 bits are defined by spec.
uint32_t access_flags_;
diff --git a/runtime/oat.cc b/runtime/oat.cc
index d01dc72..c1a48e9 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,7 +22,7 @@
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '2', '1', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '2', '2', '\0' };
OatHeader::OatHeader() {
memset(this, 0, sizeof(*this));
@@ -348,8 +348,6 @@
frame_size_in_bytes_(0),
core_spill_mask_(0),
fp_spill_mask_(0),
- mapping_table_offset_(0),
- vmap_table_offset_(0),
gc_map_offset_(0)
{}
@@ -357,27 +355,28 @@
uint32_t frame_size_in_bytes,
uint32_t core_spill_mask,
uint32_t fp_spill_mask,
- uint32_t mapping_table_offset,
- uint32_t vmap_table_offset,
uint32_t gc_map_offset
)
: code_offset_(code_offset),
frame_size_in_bytes_(frame_size_in_bytes),
core_spill_mask_(core_spill_mask),
fp_spill_mask_(fp_spill_mask),
- mapping_table_offset_(mapping_table_offset),
- vmap_table_offset_(vmap_table_offset),
gc_map_offset_(gc_map_offset)
{}
OatMethodOffsets::~OatMethodOffsets() {}
OatMethodHeader::OatMethodHeader()
- : code_size_(0)
+ : mapping_table_offset_(0),
+ vmap_table_offset_(0),
+ code_size_(0)
{}
-OatMethodHeader::OatMethodHeader(uint32_t code_size)
- : code_size_(code_size)
+OatMethodHeader::OatMethodHeader(uint32_t vmap_table_offset, uint32_t mapping_table_offset,
+ uint32_t code_size)
+ : mapping_table_offset_(mapping_table_offset),
+ vmap_table_offset_(vmap_table_offset),
+ code_size_(code_size)
{}
OatMethodHeader::~OatMethodHeader() {}
diff --git a/runtime/oat.h b/runtime/oat.h
index 035aba1..e9dfae9 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -119,9 +119,9 @@
DISALLOW_COPY_AND_ASSIGN(OatHeader);
};
-// OatMethodOffsets are currently 7x32-bits=224-bits long, so if we can
+// OatMethodOffsets are currently 5x32-bits=160-bits long, so if we can
// save even one OatMethodOffsets struct, the more complicated encoding
-// using a bitmap pays for itself since few classes will have 224
+// using a bitmap pays for itself since few classes will have 160
// methods.
enum OatClassType {
kOatClassAllCompiled = 0, // OatClass is followed by an OatMethodOffsets for each method.
@@ -140,8 +140,6 @@
uint32_t frame_size_in_bytes,
uint32_t core_spill_mask,
uint32_t fp_spill_mask,
- uint32_t mapping_table_offset,
- uint32_t vmap_table_offset,
uint32_t gc_map_offset);
~OatMethodOffsets();
@@ -150,8 +148,6 @@
uint32_t frame_size_in_bytes_;
uint32_t core_spill_mask_;
uint32_t fp_spill_mask_;
- uint32_t mapping_table_offset_;
- uint32_t vmap_table_offset_;
uint32_t gc_map_offset_;
};
@@ -160,10 +156,15 @@
public:
OatMethodHeader();
- explicit OatMethodHeader(uint32_t code_size);
+ explicit OatMethodHeader(uint32_t mapping_table_offset, uint32_t vmap_table_offset,
+ uint32_t code_size);
~OatMethodHeader();
+ // The offset in bytes from the start of the mapping table to the end of the header.
+ uint32_t mapping_table_offset_;
+ // The offset in bytes from the start of the vmap table to the end of the header.
+ uint32_t vmap_table_offset_;
// The code size in bytes.
uint32_t code_size_;
};
diff --git a/runtime/oat_file-inl.h b/runtime/oat_file-inl.h
new file mode 100644
index 0000000..00ae797
--- /dev/null
+++ b/runtime/oat_file-inl.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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_RUNTIME_OAT_FILE_INL_H_
+#define ART_RUNTIME_OAT_FILE_INL_H_
+
+#include "oat_file.h"
+
+namespace art {
+
+inline uint32_t OatFile::OatMethod::GetMappingTableOffset() const {
+ const uint8_t* mapping_table = GetMappingTable();
+ return static_cast<uint32_t>(mapping_table != nullptr ? mapping_table - begin_ : 0u);
+}
+
+inline uint32_t OatFile::OatMethod::GetVmapTableOffset() const {
+ const uint8_t* vmap_table = GetVmapTable();
+ return static_cast<uint32_t>(vmap_table != nullptr ? vmap_table - begin_ : 0u);
+}
+
+inline const uint8_t* OatFile::OatMethod::GetMappingTable() const {
+ const void* code = mirror::ArtMethod::EntryPointToCodePointer(GetQuickCode());
+ if (code == nullptr) {
+ return nullptr;
+ }
+ uint32_t offset = reinterpret_cast<const OatMethodHeader*>(code)[-1].mapping_table_offset_;
+ if (UNLIKELY(offset == 0u)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const uint8_t*>(code) - offset;
+}
+
+inline const uint8_t* OatFile::OatMethod::GetVmapTable() const {
+ const void* code = mirror::ArtMethod::EntryPointToCodePointer(GetQuickCode());
+ if (code == nullptr) {
+ return nullptr;
+ }
+ uint32_t offset = reinterpret_cast<const OatMethodHeader*>(code)[-1].vmap_table_offset_;
+ if (UNLIKELY(offset == 0u)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const uint8_t*>(code) - offset;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_OAT_FILE_INL_H_
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 0aff8c3..56e1f05 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -464,7 +464,7 @@
// NOTE: We don't keep the number of methods and cannot do a bounds check for method_index.
if (methods_pointer_ == NULL) {
CHECK_EQ(kOatClassNoneCompiled, type_);
- return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0);
+ return OatMethod(NULL, 0, 0, 0, 0, 0);
}
size_t methods_pointer_index;
if (bitmap_ == NULL) {
@@ -473,7 +473,7 @@
} else {
CHECK_EQ(kOatClassSomeCompiled, type_);
if (!BitVector::IsBitSet(bitmap_, method_index)) {
- return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0);
+ return OatMethod(NULL, 0, 0, 0, 0, 0);
}
size_t num_set_bits = BitVector::NumSetBits(bitmap_, method_index);
methods_pointer_index = num_set_bits;
@@ -485,8 +485,6 @@
oat_method_offsets.frame_size_in_bytes_,
oat_method_offsets.core_spill_mask_,
oat_method_offsets.fp_spill_mask_,
- oat_method_offsets.mapping_table_offset_,
- oat_method_offsets.vmap_table_offset_,
oat_method_offsets.gc_map_offset_);
}
@@ -495,32 +493,13 @@
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- const uint32_t mapping_table_offset,
- const uint32_t vmap_table_offset,
const uint32_t gc_map_offset)
: begin_(base),
code_offset_(code_offset),
frame_size_in_bytes_(frame_size_in_bytes),
core_spill_mask_(core_spill_mask),
fp_spill_mask_(fp_spill_mask),
- mapping_table_offset_(mapping_table_offset),
- vmap_table_offset_(vmap_table_offset),
native_gc_map_offset_(gc_map_offset) {
- if (kIsDebugBuild) {
- if (mapping_table_offset_ != 0) { // implies non-native, non-stub code
- if (vmap_table_offset_ == 0) {
- CHECK_EQ(0U, static_cast<uint32_t>(__builtin_popcount(core_spill_mask_) +
- __builtin_popcount(fp_spill_mask_)));
- } else {
- VmapTable vmap_table(reinterpret_cast<const uint8_t*>(begin_ + vmap_table_offset_));
-
- CHECK_EQ(vmap_table.Size(), static_cast<uint32_t>(__builtin_popcount(core_spill_mask_) +
- __builtin_popcount(fp_spill_mask_)));
- }
- } else {
- CHECK_EQ(vmap_table_offset_, 0U);
- }
- }
}
OatFile::OatMethod::~OatMethod() {}
@@ -543,8 +522,6 @@
method->SetFrameSizeInBytes(frame_size_in_bytes_);
method->SetCoreSpillMask(core_spill_mask_);
method->SetFpSpillMask(fp_spill_mask_);
- method->SetMappingTable(GetMappingTable());
- method->SetVmapTable(GetVmapTable());
method->SetNativeGcMap(GetNativeGcMap()); // Used by native methods in work around JNI mode.
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 10f64cc..5f6cb1e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -87,12 +87,6 @@
uint32_t GetFpSpillMask() const {
return fp_spill_mask_;
}
- uint32_t GetMappingTableOffset() const {
- return mapping_table_offset_;
- }
- uint32_t GetVmapTableOffset() const {
- return vmap_table_offset_;
- }
uint32_t GetNativeGcMapOffset() const {
return native_gc_map_offset_;
}
@@ -122,16 +116,15 @@
}
uint32_t GetQuickCodeSize() const;
- const uint8_t* GetMappingTable() const {
- return GetOatPointer<const uint8_t*>(mapping_table_offset_);
- }
- const uint8_t* GetVmapTable() const {
- return GetOatPointer<const uint8_t*>(vmap_table_offset_);
- }
const uint8_t* GetNativeGcMap() const {
return GetOatPointer<const uint8_t*>(native_gc_map_offset_);
}
+ uint32_t GetMappingTableOffset() const;
+ uint32_t GetVmapTableOffset() const;
+ const uint8_t* GetMappingTable() const;
+ const uint8_t* GetVmapTable() const;
+
~OatMethod();
// Create an OatMethod with offsets relative to the given base address
@@ -140,8 +133,6 @@
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- const uint32_t mapping_table_offset,
- const uint32_t vmap_table_offset,
const uint32_t gc_map_offset);
private:
@@ -159,8 +150,6 @@
size_t frame_size_in_bytes_;
uint32_t core_spill_mask_;
uint32_t fp_spill_mask_;
- uint32_t mapping_table_offset_;
- uint32_t vmap_table_offset_;
uint32_t native_gc_map_offset_;
friend class OatClass;