Cache optimized code for OSR.

BUG=v8:2637
LOG=N
R=titzer@chromium.org, verwaest@chromium.org

Review URL: https://codereview.chromium.org/101853003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18410 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2013-12-23 14:42:42 +00:00
parent 2a4be7067c
commit 6ba6d0b4fe
10 changed files with 136 additions and 113 deletions

View File

@ -1194,9 +1194,14 @@ void CodeStubGraphBuilderBase::BuildInstallFromOptimizedCodeMap(
Label install_optimized;
HValue* first_context_slot = Add<HLoadNamedField>(optimized_map,
HObjectAccess::ForFirstContextSlot());
HValue* first_osr_ast_slot = Add<HLoadNamedField>(optimized_map,
HObjectAccess::ForFirstOsrAstIdSlot());
HValue* osr_ast_id_none = Add<HConstant>(BailoutId::None().ToInt());
IfBuilder already_in(this);
already_in.If<HCompareObjectEqAndBranch>(native_context,
first_context_slot);
already_in.AndIf<HCompareObjectEqAndBranch>(first_osr_ast_slot,
osr_ast_id_none);
already_in.Then();
{
HValue* code_object = Add<HLoadNamedField>(optimized_map,
@ -1213,7 +1218,7 @@ void CodeStubGraphBuilderBase::BuildInstallFromOptimizedCodeMap(
shared_function_entry_length);
HValue* array_length = Add<HLoadNamedField>(optimized_map,
HObjectAccess::ForFixedArrayLength());
HValue* key = loop_builder.BeginBody(array_length,
HValue* slot_iterator = loop_builder.BeginBody(array_length,
graph()->GetConstant0(),
Token::GT);
{
@ -1222,8 +1227,8 @@ void CodeStubGraphBuilderBase::BuildInstallFromOptimizedCodeMap(
HValue* second_entry_index =
Add<HConstant>(SharedFunctionInfo::kSecondEntryIndex);
IfBuilder restore_check(this);
restore_check.If<HCompareNumericAndBranch>(key, second_entry_index,
Token::EQ);
restore_check.If<HCompareNumericAndBranch>(
slot_iterator, second_entry_index, Token::EQ);
restore_check.Then();
{
// Store the unoptimized code
@ -1232,20 +1237,29 @@ void CodeStubGraphBuilderBase::BuildInstallFromOptimizedCodeMap(
}
restore_check.Else();
{
HValue* keyed_minus = AddUncasted<HSub>(
key, shared_function_entry_length);
HInstruction* keyed_lookup = Add<HLoadKeyed>(optimized_map,
keyed_minus, static_cast<HValue*>(NULL), FAST_ELEMENTS);
STATIC_ASSERT(SharedFunctionInfo::kContextOffset == 0);
STATIC_ASSERT(SharedFunctionInfo::kEntryLength -
SharedFunctionInfo::kOsrAstIdOffset == 1);
HValue* native_context_slot = AddUncasted<HSub>(
slot_iterator, shared_function_entry_length);
HValue* osr_ast_id_slot = AddUncasted<HSub>(
slot_iterator, graph()->GetConstant1());
HInstruction* native_context_entry = Add<HLoadKeyed>(optimized_map,
native_context_slot, static_cast<HValue*>(NULL), FAST_ELEMENTS);
HInstruction* osr_ast_id_entry = Add<HLoadKeyed>(optimized_map,
osr_ast_id_slot, static_cast<HValue*>(NULL), FAST_ELEMENTS);
IfBuilder done_check(this);
done_check.If<HCompareObjectEqAndBranch>(native_context,
keyed_lookup);
native_context_entry);
done_check.AndIf<HCompareObjectEqAndBranch>(osr_ast_id_entry,
osr_ast_id_none);
done_check.Then();
{
// Hit: fetch the optimized code.
HValue* keyed_plus = AddUncasted<HAdd>(
keyed_minus, graph()->GetConstant1());
HValue* code_slot = AddUncasted<HAdd>(
native_context_slot, graph()->GetConstant1());
HValue* code_object = Add<HLoadKeyed>(optimized_map,
keyed_plus, static_cast<HValue*>(NULL), FAST_ELEMENTS);
code_slot, static_cast<HValue*>(NULL), FAST_ELEMENTS);
BuildInstallOptimizedCode(js_function, native_context, code_object);
// Fall out of the loop

View File

@ -1028,16 +1028,20 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal,
}
static Handle<Code> GetCodeFromOptimizedCodeMap(Handle<JSFunction> function) {
static Handle<Code> GetCodeFromOptimizedCodeMap(Handle<JSFunction> function,
BailoutId osr_ast_id) {
if (FLAG_cache_optimized_code) {
Handle<SharedFunctionInfo> shared(function->shared());
DisallowHeapAllocation no_gc;
int index = shared->SearchOptimizedCodeMap(
function->context()->native_context());
function->context()->native_context(), osr_ast_id);
if (index > 0) {
if (FLAG_trace_opt) {
PrintF("[found optimized code for ");
function->ShortPrint();
if (!osr_ast_id.IsNone()) {
PrintF(" at OSR AST id %d", osr_ast_id.ToInt());
}
PrintF("]\n");
}
FixedArray* literals = shared->GetLiteralsFromOptimizedCodeMap(index);
@ -1053,14 +1057,14 @@ static void InsertCodeIntoOptimizedCodeMap(CompilationInfo* info) {
Handle<Code> code = info->code();
if (code->kind() != Code::OPTIMIZED_FUNCTION) return; // Nothing to do.
// Cache non-OSR optimized code.
if (FLAG_cache_optimized_code && !info->is_osr()) {
// Cache optimized code.
if (FLAG_cache_optimized_code) {
Handle<JSFunction> function = info->closure();
Handle<SharedFunctionInfo> shared(function->shared());
Handle<FixedArray> literals(function->literals());
Handle<Context> native_context(function->context()->native_context());
SharedFunctionInfo::AddToOptimizedCodeMap(
shared, native_context, code, literals);
shared, native_context, code, literals, info->osr_ast_id());
}
}
@ -1137,10 +1141,8 @@ Handle<Code> Compiler::GetOptimizedCode(Handle<JSFunction> function,
Handle<Code> current_code,
ConcurrencyMode mode,
BailoutId osr_ast_id) {
if (osr_ast_id.IsNone()) { // No cache for OSR.
Handle<Code> cached_code = GetCodeFromOptimizedCodeMap(function);
if (!cached_code.is_null()) return cached_code;
}
Handle<Code> cached_code = GetCodeFromOptimizedCodeMap(function, osr_ast_id);
if (!cached_code.is_null()) return cached_code;
SmartPointer<CompilationInfo> info(new CompilationInfoWithZone(function));
Isolate* isolate = info->isolate();
@ -1210,7 +1212,7 @@ Handle<Code> Compiler::GetConcurrentlyOptimizedCode(OptimizedCompileJob* job) {
Compiler::RecordFunctionCompilation(
Logger::LAZY_COMPILE_TAG, info.get(), shared);
if (info->shared_info()->SearchOptimizedCodeMap(
info->context()->native_context()) == -1) {
info->context()->native_context(), info->osr_ast_id()) == -1) {
InsertCodeIntoOptimizedCodeMap(info.get());
}

View File

@ -924,7 +924,8 @@ Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo(
result->set_context(*context);
int index = function_info->SearchOptimizedCodeMap(context->native_context());
int index = function_info->SearchOptimizedCodeMap(context->native_context(),
BailoutId::None());
if (!function_info->bound() && index < 0) {
int number_of_literals = function_info->num_literals();
Handle<FixedArray> literals = NewFixedArray(number_of_literals, pretenure);

View File

@ -6028,6 +6028,10 @@ class HObjectAccess V8_FINAL {
return HObjectAccess(kInobject, SharedFunctionInfo::kFirstContextSlot);
}
static HObjectAccess ForFirstOsrAstIdSlot() {
return HObjectAccess(kInobject, SharedFunctionInfo::kFirstOsrAstIdSlot);
}
static HObjectAccess ForOptimizedCodeMap() {
return HObjectAccess(kInobject,
SharedFunctionInfo::kOptimizedCodeMapOffset);

View File

@ -1065,55 +1065,40 @@ void CodeFlusher::ProcessSharedFunctionInfoCandidates() {
void CodeFlusher::ProcessOptimizedCodeMaps() {
static const int kEntriesStart = SharedFunctionInfo::kEntriesStart;
static const int kEntryLength = SharedFunctionInfo::kEntryLength;
static const int kContextOffset = 0;
static const int kCodeOffset = 1;
static const int kLiteralsOffset = 2;
STATIC_ASSERT(kEntryLength == 3);
STATIC_ASSERT(SharedFunctionInfo::kEntryLength == 4);
SharedFunctionInfo* holder = optimized_code_map_holder_head_;
SharedFunctionInfo* next_holder;
while (holder != NULL) {
next_holder = GetNextCodeMap(holder);
ClearNextCodeMap(holder);
FixedArray* code_map = FixedArray::cast(holder->optimized_code_map());
int new_length = kEntriesStart;
int new_length = SharedFunctionInfo::kEntriesStart;
int old_length = code_map->length();
for (int i = kEntriesStart; i < old_length; i += kEntryLength) {
Code* code = Code::cast(code_map->get(i + kCodeOffset));
MarkBit code_mark = Marking::MarkBitFrom(code);
if (!code_mark.Get()) {
continue;
for (int i = SharedFunctionInfo::kEntriesStart;
i < old_length;
i += SharedFunctionInfo::kEntryLength) {
Code* code =
Code::cast(code_map->get(i + SharedFunctionInfo::kCachedCodeOffset));
if (!Marking::MarkBitFrom(code).Get()) continue;
// Move every slot in the entry.
for (int j = 0; j < SharedFunctionInfo::kEntryLength; j++) {
int dst_index = new_length++;
Object** slot = code_map->RawFieldOfElementAt(dst_index);
Object* object = code_map->get(i + j);
code_map->set(dst_index, object);
if (j == SharedFunctionInfo::kOsrAstIdOffset) {
ASSERT(object->IsSmi());
} else {
ASSERT(Marking::IsBlack(
Marking::MarkBitFrom(HeapObject::cast(*slot))));
isolate_->heap()->mark_compact_collector()->
RecordSlot(slot, slot, *slot);
}
}
// Update and record the context slot in the optimized code map.
Object** context_slot = HeapObject::RawField(code_map,
FixedArray::OffsetOfElementAt(new_length));
code_map->set(new_length++, code_map->get(i + kContextOffset));
ASSERT(Marking::IsBlack(
Marking::MarkBitFrom(HeapObject::cast(*context_slot))));
isolate_->heap()->mark_compact_collector()->
RecordSlot(context_slot, context_slot, *context_slot);
// Update and record the code slot in the optimized code map.
Object** code_slot = HeapObject::RawField(code_map,
FixedArray::OffsetOfElementAt(new_length));
code_map->set(new_length++, code_map->get(i + kCodeOffset));
ASSERT(Marking::IsBlack(
Marking::MarkBitFrom(HeapObject::cast(*code_slot))));
isolate_->heap()->mark_compact_collector()->
RecordSlot(code_slot, code_slot, *code_slot);
// Update and record the literals slot in the optimized code map.
Object** literals_slot = HeapObject::RawField(code_map,
FixedArray::OffsetOfElementAt(new_length));
code_map->set(new_length++, code_map->get(i + kLiteralsOffset));
ASSERT(Marking::IsBlack(
Marking::MarkBitFrom(HeapObject::cast(*literals_slot))));
isolate_->heap()->mark_compact_collector()->
RecordSlot(literals_slot, literals_slot, *literals_slot);
}
// Trim the optimized code map if entries have been removed.
@ -2608,9 +2593,7 @@ void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
cached_map,
SKIP_WRITE_BARRIER);
}
Object** slot =
HeapObject::RawField(prototype_transitions,
FixedArray::OffsetOfElementAt(proto_index));
Object** slot = prototype_transitions->RawFieldOfElementAt(proto_index);
RecordSlot(slot, slot, prototype);
new_number_of_transitions++;
}
@ -2715,12 +2698,10 @@ void MarkCompactCollector::ProcessWeakCollections() {
for (int i = 0; i < table->Capacity(); i++) {
if (MarkCompactCollector::IsMarked(HeapObject::cast(table->KeyAt(i)))) {
Object** key_slot =
HeapObject::RawField(table, FixedArray::OffsetOfElementAt(
ObjectHashTable::EntryToIndex(i)));
table->RawFieldOfElementAt(ObjectHashTable::EntryToIndex(i));
RecordSlot(anchor, key_slot, *key_slot);
Object** value_slot =
HeapObject::RawField(table, FixedArray::OffsetOfElementAt(
ObjectHashTable::EntryToValueIndex(i)));
table->RawFieldOfElementAt(ObjectHashTable::EntryToValueIndex(i));
MarkCompactMarkingVisitor::MarkObjectByPointer(
this, anchor, value_slot);
}

View File

@ -2364,9 +2364,7 @@ void Map::LookupTransition(JSObject* holder,
Object** DescriptorArray::GetKeySlot(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
return HeapObject::RawField(
reinterpret_cast<HeapObject*>(this),
OffsetOfElementAt(ToKeyIndex(descriptor_number)));
return RawFieldOfElementAt(ToKeyIndex(descriptor_number));
}
@ -2421,9 +2419,7 @@ void DescriptorArray::InitializeRepresentations(Representation representation) {
Object** DescriptorArray::GetValueSlot(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
return HeapObject::RawField(
reinterpret_cast<HeapObject*>(this),
OffsetOfElementAt(ToValueIndex(descriptor_number)));
return RawFieldOfElementAt(ToValueIndex(descriptor_number));
}
@ -3224,7 +3220,7 @@ void JSFunctionResultCache::MakeZeroSize() {
void JSFunctionResultCache::Clear() {
int cache_size = size();
Object** entries_start = RawField(this, OffsetOfElementAt(kEntriesIndex));
Object** entries_start = RawFieldOfElementAt(kEntriesIndex);
MemsetPointer(entries_start,
GetHeap()->the_hole_value(),
cache_size - kEntriesIndex);
@ -3830,8 +3826,7 @@ Object* DependentCode::object_at(int i) {
Object** DependentCode::slot_at(int i) {
return HeapObject::RawField(
this, FixedArray::OffsetOfElementAt(kCodesStartIndex + i));
return RawFieldOfElementAt(kCodesStartIndex + i);
}

View File

@ -331,8 +331,7 @@ void StaticMarkingVisitor<StaticVisitor>::VisitNativeContext(
for (int idx = Context::FIRST_WEAK_SLOT;
idx < Context::NATIVE_CONTEXT_SLOTS;
++idx) {
Object** slot =
HeapObject::RawField(object, FixedArray::OffsetOfElementAt(idx));
Object** slot = Context::cast(object)->RawFieldOfElementAt(idx);
collector->RecordSlot(slot, slot, *slot);
}
}

View File

@ -9526,42 +9526,48 @@ void SharedFunctionInfo::AddToOptimizedCodeMap(
Handle<SharedFunctionInfo> shared,
Handle<Context> native_context,
Handle<Code> code,
Handle<FixedArray> literals) {
Handle<FixedArray> literals,
BailoutId osr_ast_id) {
CALL_HEAP_FUNCTION_VOID(
shared->GetIsolate(),
shared->AddToOptimizedCodeMap(*native_context, *code, *literals));
shared->AddToOptimizedCodeMap(
*native_context, *code, *literals, osr_ast_id));
}
MaybeObject* SharedFunctionInfo::AddToOptimizedCodeMap(Context* native_context,
Code* code,
FixedArray* literals) {
FixedArray* literals,
BailoutId osr_ast_id) {
ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);
ASSERT(native_context->IsNativeContext());
STATIC_ASSERT(kEntryLength == 3);
STATIC_ASSERT(kEntryLength == 4);
Heap* heap = GetHeap();
FixedArray* new_code_map;
Object* value = optimized_code_map();
Smi* osr_ast_id_smi = Smi::FromInt(osr_ast_id.ToInt());
if (value->IsSmi()) {
// No optimized code map.
ASSERT_EQ(0, Smi::cast(value)->value());
// Create 3 entries per context {context, code, literals}.
MaybeObject* maybe = heap->AllocateFixedArray(kInitialLength);
if (!maybe->To(&new_code_map)) return maybe;
new_code_map->set(kEntriesStart + 0, native_context);
new_code_map->set(kEntriesStart + 1, code);
new_code_map->set(kEntriesStart + 2, literals);
new_code_map->set(kEntriesStart + kContextOffset, native_context);
new_code_map->set(kEntriesStart + kCachedCodeOffset, code);
new_code_map->set(kEntriesStart + kLiteralsOffset, literals);
new_code_map->set(kEntriesStart + kOsrAstIdOffset, osr_ast_id_smi);
} else {
// Copy old map and append one new entry.
FixedArray* old_code_map = FixedArray::cast(value);
ASSERT_EQ(-1, SearchOptimizedCodeMap(native_context));
ASSERT_EQ(-1, SearchOptimizedCodeMap(native_context, osr_ast_id));
int old_length = old_code_map->length();
int new_length = old_length + kEntryLength;
MaybeObject* maybe = old_code_map->CopySize(new_length);
if (!maybe->To(&new_code_map)) return maybe;
new_code_map->set(old_length + 0, native_context);
new_code_map->set(old_length + 1, code);
new_code_map->set(old_length + 2, literals);
new_code_map->set(old_length + kContextOffset, native_context);
new_code_map->set(old_length + kCachedCodeOffset, code);
new_code_map->set(old_length + kLiteralsOffset, literals);
new_code_map->set(old_length + kOsrAstIdOffset, osr_ast_id_smi);
// Zap the old map for the sake of the heap verifier.
if (Heap::ShouldZapGarbage()) {
Object** data = old_code_map->data_start();
@ -9570,11 +9576,12 @@ MaybeObject* SharedFunctionInfo::AddToOptimizedCodeMap(Context* native_context,
}
#ifdef DEBUG
for (int i = kEntriesStart; i < new_code_map->length(); i += kEntryLength) {
ASSERT(new_code_map->get(i)->IsNativeContext());
ASSERT(new_code_map->get(i + 1)->IsCode());
ASSERT(Code::cast(new_code_map->get(i + 1))->kind() ==
ASSERT(new_code_map->get(i + kContextOffset)->IsNativeContext());
ASSERT(new_code_map->get(i + kCachedCodeOffset)->IsCode());
ASSERT(Code::cast(new_code_map->get(i + kCachedCodeOffset))->kind() ==
Code::OPTIMIZED_FUNCTION);
ASSERT(new_code_map->get(i + 2)->IsFixedArray());
ASSERT(new_code_map->get(i + kLiteralsOffset)->IsFixedArray());
ASSERT(new_code_map->get(i + kOsrAstIdOffset)->IsSmi());
}
#endif
set_optimized_code_map(new_code_map);
@ -9594,7 +9601,6 @@ FixedArray* SharedFunctionInfo::GetLiteralsFromOptimizedCodeMap(int index) {
}
Code* SharedFunctionInfo::GetCodeFromOptimizedCodeMap(int index) {
ASSERT(index > kEntriesStart);
FixedArray* code_map = FixedArray::cast(optimized_code_map());
@ -9639,9 +9645,14 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code,
}
}
while (i < (code_map->length() - kEntryLength)) {
code_map->set(i, code_map->get(i + kEntryLength));
code_map->set(i + 1, code_map->get(i + 1 + kEntryLength));
code_map->set(i + 2, code_map->get(i + 2 + kEntryLength));
code_map->set(i + kContextOffset,
code_map->get(i + kContextOffset + kEntryLength));
code_map->set(i + kCachedCodeOffset,
code_map->get(i + kCachedCodeOffset + kEntryLength));
code_map->set(i + kLiteralsOffset,
code_map->get(i + kLiteralsOffset + kEntryLength));
code_map->set(i + kOsrAstIdOffset,
code_map->get(i + kOsrAstIdOffset + kEntryLength));
i += kEntryLength;
}
if (removed_entry) {
@ -10203,16 +10214,19 @@ void SharedFunctionInfo::CompleteInobjectSlackTracking() {
}
int SharedFunctionInfo::SearchOptimizedCodeMap(Context* native_context) {
int SharedFunctionInfo::SearchOptimizedCodeMap(Context* native_context,
BailoutId osr_ast_id) {
ASSERT(native_context->IsNativeContext());
if (!FLAG_cache_optimized_code) return -1;
Object* value = optimized_code_map();
if (!value->IsSmi()) {
FixedArray* optimized_code_map = FixedArray::cast(value);
int length = optimized_code_map->length();
Smi* osr_ast_id_smi = Smi::FromInt(osr_ast_id.ToInt());
for (int i = kEntriesStart; i < length; i += kEntryLength) {
if (optimized_code_map->get(i) == native_context) {
return i + 1;
if (optimized_code_map->get(i + kContextOffset) == native_context &&
optimized_code_map->get(i + kOsrAstIdOffset) == osr_ast_id_smi) {
return i + kCachedCodeOffset;
}
}
if (FLAG_trace_opt) {

View File

@ -2957,6 +2957,11 @@ class FixedArray: public FixedArrayBase {
// Code Generation support.
static int OffsetOfElementAt(int index) { return SizeFor(index); }
// Garbage collection support.
Object** RawFieldOfElementAt(int index) {
return HeapObject::RawField(this, OffsetOfElementAt(index));
}
// Casting.
static inline FixedArray* cast(Object* obj);
@ -6536,10 +6541,10 @@ class SharedFunctionInfo: public HeapObject {
// and a shared literals array or Smi(0) if none.
DECL_ACCESSORS(optimized_code_map, Object)
// Returns index i of the entry with the specified context. At position
// i - 1 is the context, position i the code, and i + 1 the literals array.
// Returns -1 when no matching entry is found.
int SearchOptimizedCodeMap(Context* native_context);
// Returns index i of the entry with the specified context and OSR entry.
// At position i - 1 is the context, position i the code, and i + 1 the
// literals array. Returns -1 when no matching entry is found.
int SearchOptimizedCodeMap(Context* native_context, BailoutId osr_ast_id);
// Installs optimized code from the code map on the given closure. The
// index has to be consistent with a search result as defined above.
@ -6559,18 +6564,28 @@ class SharedFunctionInfo: public HeapObject {
// Add a new entry to the optimized code map.
MUST_USE_RESULT MaybeObject* AddToOptimizedCodeMap(Context* native_context,
Code* code,
FixedArray* literals);
FixedArray* literals,
BailoutId osr_ast_id);
static void AddToOptimizedCodeMap(Handle<SharedFunctionInfo> shared,
Handle<Context> native_context,
Handle<Code> code,
Handle<FixedArray> literals);
Handle<FixedArray> literals,
BailoutId osr_ast_id);
// Layout description of the optimized code map.
static const int kNextMapIndex = 0;
static const int kEntriesStart = 1;
static const int kEntryLength = 3;
static const int kFirstContextSlot = FixedArray::kHeaderSize + kPointerSize;
static const int kFirstCodeSlot = FixedArray::kHeaderSize + 2 * kPointerSize;
static const int kContextOffset = 0;
static const int kCachedCodeOffset = 1;
static const int kLiteralsOffset = 2;
static const int kOsrAstIdOffset = 3;
static const int kEntryLength = 4;
static const int kFirstContextSlot = FixedArray::kHeaderSize +
(kEntriesStart + kContextOffset) * kPointerSize;
static const int kFirstCodeSlot = FixedArray::kHeaderSize +
(kEntriesStart + kCachedCodeOffset) * kPointerSize;
static const int kFirstOsrAstIdSlot = FixedArray::kHeaderSize +
(kEntriesStart + kOsrAstIdOffset) * kPointerSize;
static const int kSecondEntryIndex = kEntryLength + kEntriesStart;
static const int kInitialLength = kEntriesStart + kEntryLength;

View File

@ -115,9 +115,7 @@ Object** TransitionArray::GetPrototypeTransitionsSlot() {
Object** TransitionArray::GetKeySlot(int transition_number) {
ASSERT(!IsSimpleTransition());
ASSERT(transition_number < number_of_transitions());
return HeapObject::RawField(
reinterpret_cast<HeapObject*>(this),
OffsetOfElementAt(ToKeyIndex(transition_number)));
return RawFieldOfElementAt(ToKeyIndex(transition_number));
}