Basic block optimization refactoring

Various optimization improvements for the common frontend.

Before applying basic block optimizations, split the graph
into extended basic blocks.  Replace existing pattern-match
range-check elimination with a scheme based on local value
numbering (which could also serve as the basis for some CSE
if the existing, but so-far untested, common frontend temp
register mechanism works as designed).

For the framework, this CL improves null check elimination
from 90.74% (statically) to 91.24% removed, and range
check elimination from 3.45% to 8.17%.

Change-Id: Ie1ce730cfe12a12fef665a30fe3814bad1993895
diff --git a/src/compiler/bb_opt.cc b/src/compiler/bb_opt.cc
new file mode 100644
index 0000000..3ad5821
--- /dev/null
+++ b/src/compiler/bb_opt.cc
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2012 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 "bb_opt.h"
+#include "dataflow.h"
+
+namespace art {
+
+
+uint16_t BBOpt::GetValueNumber(MIR* mir)
+{
+  uint16_t res = NO_VALUE;
+  uint16_t opcode = mir->dalvikInsn.opcode;
+  switch (opcode) {
+    case Instruction::NOP:
+    case Instruction::RETURN_VOID:
+    case Instruction::RETURN:
+    case Instruction::RETURN_OBJECT:
+    case Instruction::RETURN_WIDE:
+    case Instruction::MONITOR_ENTER:
+    case Instruction::MONITOR_EXIT:
+    case Instruction::GOTO:
+    case Instruction::GOTO_16:
+    case Instruction::GOTO_32:
+    case Instruction::CHECK_CAST:
+    case Instruction::THROW:
+    case Instruction::FILL_ARRAY_DATA:
+    case Instruction::FILLED_NEW_ARRAY:
+    case Instruction::FILLED_NEW_ARRAY_RANGE:
+    case Instruction::PACKED_SWITCH:
+    case Instruction::SPARSE_SWITCH:
+    case Instruction::IF_EQ:
+    case Instruction::IF_NE:
+    case Instruction::IF_LT:
+    case Instruction::IF_GE:
+    case Instruction::IF_GT:
+    case Instruction::IF_LE:
+    case Instruction::IF_EQZ:
+    case Instruction::IF_NEZ:
+    case Instruction::IF_LTZ:
+    case Instruction::IF_GEZ:
+    case Instruction::IF_GTZ:
+    case Instruction::IF_LEZ:
+    case Instruction::INVOKE_STATIC_RANGE:
+    case Instruction::INVOKE_STATIC:
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_DIRECT_RANGE:
+    case Instruction::INVOKE_VIRTUAL:
+    case Instruction::INVOKE_VIRTUAL_RANGE:
+    case Instruction::INVOKE_SUPER:
+    case Instruction::INVOKE_SUPER_RANGE:
+    case Instruction::INVOKE_INTERFACE:
+    case Instruction::INVOKE_INTERFACE_RANGE:
+    case kMirOpFusedCmplFloat:
+    case kMirOpFusedCmpgFloat:
+    case kMirOpFusedCmplDouble:
+    case kMirOpFusedCmpgDouble:
+    case kMirOpFusedCmpLong:
+      // Nothing defined - take no action.
+      break;
+
+    case Instruction::MOVE_EXCEPTION:
+    case Instruction::MOVE_RESULT:
+    case Instruction::MOVE_RESULT_OBJECT:
+    case Instruction::INSTANCE_OF:
+    case Instruction::NEW_INSTANCE:
+    case Instruction::CONST_STRING:
+    case Instruction::CONST_STRING_JUMBO:
+    case Instruction::CONST_CLASS:
+    case Instruction::NEW_ARRAY: {
+        // 1 result, treat as unique each time, use result s_reg - will be unique.
+        uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+    case Instruction::MOVE_RESULT_WIDE: {
+        // 1 wide result, treat as unique each time, use result s_reg - will be unique.
+        uint16_t res = GetOperandValueWide(mir->ssa_rep->defs[0]);
+        SetOperandValueWide(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case kMirOpPhi:
+      /*
+       * Because we'll only see phi nodes at the beginning of an extended basic block,
+       * we can ignore them.  Revisit if we shift to global value numbering.
+       */
+      break;
+
+    case Instruction::MOVE:
+    case Instruction::MOVE_OBJECT:
+    case Instruction::MOVE_16:
+    case Instruction::MOVE_OBJECT_16:
+    case Instruction::MOVE_FROM16:
+    case Instruction::MOVE_OBJECT_FROM16:
+    case kMirOpCopy: {
+        // Just copy value number of source to value number of resulit.
+        uint16_t res = GetOperandValue(mir->ssa_rep->uses[0]);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::MOVE_WIDE:
+    case Instruction::MOVE_WIDE_16:
+    case Instruction::MOVE_WIDE_FROM16: {
+        // Just copy value number of source to value number of result.
+        uint16_t res = GetOperandValueWide(mir->ssa_rep->uses[0]);
+        SetOperandValueWide(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::CONST:
+    case Instruction::CONST_4:
+    case Instruction::CONST_16: {
+        uint16_t res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB),
+                                   High16Bits(mir->dalvikInsn.vB >> 16), 0);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::CONST_HIGH16: {
+        uint16_t res = LookupValue(Instruction::CONST, 0, mir->dalvikInsn.vB, 0);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::CONST_WIDE_16:
+    case Instruction::CONST_WIDE_32: {
+        uint16_t low_res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB),
+                                       High16Bits(mir->dalvikInsn.vB >> 16), 1);
+        uint16_t high_res;
+        if (mir->dalvikInsn.vB & 0x80000000) {
+          high_res = LookupValue(Instruction::CONST, 0xffff, 0xffff, 2);
+        } else {
+          high_res = LookupValue(Instruction::CONST, 0, 0, 2);
+        }
+        uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::CONST_WIDE: {
+        uint32_t low_word = Low32Bits(mir->dalvikInsn.vB_wide);
+        uint32_t high_word = High32Bits(mir->dalvikInsn.vB_wide);
+        uint16_t low_res = LookupValue(Instruction::CONST, Low16Bits(low_word),
+                                       High16Bits(low_word), 1);
+        uint16_t high_res = LookupValue(Instruction::CONST, Low16Bits(high_word),
+                                       High16Bits(high_word), 2);
+        uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+        SetOperandValueWide(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::CONST_WIDE_HIGH16: {
+        uint16_t low_res = LookupValue(Instruction::CONST, 0, 0, 1);
+        uint16_t high_res = LookupValue(Instruction::CONST, 0, Low16Bits(mir->dalvikInsn.vB), 2);
+        uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+        SetOperandValueWide(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::ARRAY_LENGTH:
+    case Instruction::NEG_INT:
+    case Instruction::NOT_INT:
+    case Instruction::NEG_FLOAT:
+    case Instruction::INT_TO_BYTE:
+    case Instruction::INT_TO_SHORT:
+    case Instruction::INT_TO_CHAR:
+    case Instruction::INT_TO_FLOAT:
+    case Instruction::FLOAT_TO_INT: {
+        // res = op + 1 operand
+        uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+        uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::LONG_TO_FLOAT:
+    case Instruction::LONG_TO_INT:
+    case Instruction::DOUBLE_TO_FLOAT:
+    case Instruction::DOUBLE_TO_INT: {
+        // res = op + 1 wide operand
+        uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+        uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+
+    case Instruction::DOUBLE_TO_LONG:
+    case Instruction::LONG_TO_DOUBLE:
+    case Instruction::NEG_LONG:
+    case Instruction::NOT_LONG:
+    case Instruction::NEG_DOUBLE: {
+        // wide res = op + 1 wide operand
+        uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+        uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+        SetOperandValueWide(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::FLOAT_TO_DOUBLE:
+    case Instruction::FLOAT_TO_LONG:
+    case Instruction::INT_TO_DOUBLE:
+    case Instruction::INT_TO_LONG: {
+        // wide res = op + 1 operand
+        uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+        uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+        SetOperandValueWide(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::CMPL_DOUBLE:
+    case Instruction::CMPG_DOUBLE:
+    case Instruction::CMP_LONG: {
+        // res = op + 2 wide operands
+        uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+        uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
+        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::CMPG_FLOAT:
+    case Instruction::CMPL_FLOAT:
+    case Instruction::ADD_INT:
+    case Instruction::ADD_INT_2ADDR:
+    case Instruction::MUL_INT:
+    case Instruction::MUL_INT_2ADDR:
+    case Instruction::AND_INT:
+    case Instruction::AND_INT_2ADDR:
+    case Instruction::OR_INT:
+    case Instruction::OR_INT_2ADDR:
+    case Instruction::XOR_INT:
+    case Instruction::XOR_INT_2ADDR:
+    case Instruction::SUB_INT:
+    case Instruction::SUB_INT_2ADDR:
+    case Instruction::DIV_INT:
+    case Instruction::DIV_INT_2ADDR:
+    case Instruction::REM_INT:
+    case Instruction::REM_INT_2ADDR:
+    case Instruction::SHL_INT:
+    case Instruction::SHL_INT_2ADDR:
+    case Instruction::SHR_INT:
+    case Instruction::SHR_INT_2ADDR:
+    case Instruction::USHR_INT:
+    case Instruction::USHR_INT_2ADDR: {
+        // res = op + 2 operands
+        uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+        uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]);
+        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::ADD_LONG:
+    case Instruction::SUB_LONG:
+    case Instruction::MUL_LONG:
+    case Instruction::DIV_LONG:
+    case Instruction::REM_LONG:
+    case Instruction::AND_LONG:
+    case Instruction::OR_LONG:
+    case Instruction::XOR_LONG:
+    case Instruction::ADD_LONG_2ADDR:
+    case Instruction::SUB_LONG_2ADDR:
+    case Instruction::MUL_LONG_2ADDR:
+    case Instruction::DIV_LONG_2ADDR:
+    case Instruction::REM_LONG_2ADDR:
+    case Instruction::AND_LONG_2ADDR:
+    case Instruction::OR_LONG_2ADDR:
+    case Instruction::XOR_LONG_2ADDR:
+    case Instruction::ADD_DOUBLE:
+    case Instruction::SUB_DOUBLE:
+    case Instruction::MUL_DOUBLE:
+    case Instruction::DIV_DOUBLE:
+    case Instruction::REM_DOUBLE:
+    case Instruction::ADD_DOUBLE_2ADDR:
+    case Instruction::SUB_DOUBLE_2ADDR:
+    case Instruction::MUL_DOUBLE_2ADDR:
+    case Instruction::DIV_DOUBLE_2ADDR:
+    case Instruction::REM_DOUBLE_2ADDR: {
+        // wide res = op + 2 wide operands
+        uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+        uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
+        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        SetOperandValueWide(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::SHL_LONG:
+    case Instruction::SHR_LONG:
+    case Instruction::USHR_LONG:
+    case Instruction::SHL_LONG_2ADDR:
+    case Instruction::SHR_LONG_2ADDR:
+    case Instruction::USHR_LONG_2ADDR: {
+        // wide res = op + 1 wide operand + 1 operand
+        uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+        uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
+        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        SetOperandValueWide(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::ADD_FLOAT:
+    case Instruction::SUB_FLOAT:
+    case Instruction::MUL_FLOAT:
+    case Instruction::DIV_FLOAT:
+    case Instruction::REM_FLOAT:
+    case Instruction::ADD_FLOAT_2ADDR:
+    case Instruction::SUB_FLOAT_2ADDR:
+    case Instruction::MUL_FLOAT_2ADDR:
+    case Instruction::DIV_FLOAT_2ADDR:
+    case Instruction::REM_FLOAT_2ADDR: {
+        // res = op + 2 operands
+        uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+        uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]);
+        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::RSUB_INT:
+    case Instruction::ADD_INT_LIT16:
+    case Instruction::MUL_INT_LIT16:
+    case Instruction::DIV_INT_LIT16:
+    case Instruction::REM_INT_LIT16:
+    case Instruction::AND_INT_LIT16:
+    case Instruction::OR_INT_LIT16:
+    case Instruction::XOR_INT_LIT16:
+    case Instruction::ADD_INT_LIT8:
+    case Instruction::RSUB_INT_LIT8:
+    case Instruction::MUL_INT_LIT8:
+    case Instruction::DIV_INT_LIT8:
+    case Instruction::REM_INT_LIT8:
+    case Instruction::AND_INT_LIT8:
+    case Instruction::OR_INT_LIT8:
+    case Instruction::XOR_INT_LIT8:
+    case Instruction::SHL_INT_LIT8:
+    case Instruction::SHR_INT_LIT8:
+    case Instruction::USHR_INT_LIT8: {
+        // Same as res = op + 2 operands, except use vB as operand 2
+        uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+        uint16_t operand2 = LookupValue(Instruction::CONST, mir->dalvikInsn.vB, 0, 0);
+        uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+        SetOperandValue(mir->ssa_rep->defs[0], res);
+      }
+      break;
+
+    case Instruction::AGET_WIDE:
+    case Instruction::AGET:
+    case Instruction::AGET_OBJECT:
+    case Instruction::AGET_BOOLEAN:
+    case Instruction::AGET_BYTE:
+    case Instruction::AGET_CHAR:
+    case Instruction::AGET_SHORT: {
+        uint16_t array = GetOperandValue(mir->ssa_rep->uses[0]);
+        if (null_checked_.find(array) != null_checked_.end()) {
+          if (cu_->verbose) {
+            LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
+          }
+          mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+        } else {
+          null_checked_.insert(array);
+        }
+        uint16_t index = GetOperandValue(mir->ssa_rep->uses[1]);
+        if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
+          if (cu_->verbose) {
+            LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
+          }
+          mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
+        }
+        mir->meta.throw_insn->optimization_flags |= mir->optimization_flags;
+        // Use side effect to note range check completed.
+        (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+        // Establish value number for loaded register. Note use of memory version.
+        uint16_t memory_version = GetMemoryVersion(array, NO_VALUE);
+        uint16_t res = LookupValue(ARRAY_REF, array, index, memory_version);
+        if (opcode == Instruction::AGET_WIDE) {
+          SetOperandValueWide(mir->ssa_rep->defs[0], res);
+        } else {
+          SetOperandValue(mir->ssa_rep->defs[0], res);
+        }
+      }
+      break;
+
+    case Instruction::APUT_WIDE:
+    case Instruction::APUT:
+    case Instruction::APUT_OBJECT:
+    case Instruction::APUT_SHORT:
+    case Instruction::APUT_CHAR:
+    case Instruction::APUT_BYTE:
+    case Instruction::APUT_BOOLEAN: {
+        int array_idx = (opcode == Instruction::APUT_WIDE) ? 2 : 1;
+        int index_idx = array_idx + 1;
+        uint16_t array = GetOperandValue(mir->ssa_rep->uses[array_idx]);
+        if (null_checked_.find(array) != null_checked_.end()) {
+          if (cu_->verbose) {
+            LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
+          }
+          mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+        } else {
+          null_checked_.insert(array);
+        }
+        uint16_t index = GetOperandValue(mir->ssa_rep->uses[index_idx]);
+        if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
+          if (cu_->verbose) {
+            LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
+          }
+          mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
+        }
+        mir->meta.throw_insn->optimization_flags |= mir->optimization_flags;
+        // Use side effect to note range check completed.
+        (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+        // Rev the memory version
+        AdvanceMemoryVersion(array, NO_VALUE);
+      }
+      break;
+
+    case Instruction::IGET_OBJECT:
+    case Instruction::IGET_WIDE:
+    case Instruction::IGET:
+    case Instruction::IGET_CHAR:
+    case Instruction::IGET_SHORT:
+    case Instruction::IGET_BOOLEAN:
+    case Instruction::IGET_BYTE: {
+        uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]);
+        if (null_checked_.find(base) != null_checked_.end()) {
+          if (cu_->verbose) {
+            LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
+          }
+          mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+        } else {
+          null_checked_.insert(base);
+        }
+        mir->meta.throw_insn->optimization_flags |= mir->optimization_flags;
+        uint16_t field_ref = mir->dalvikInsn.vC;
+        uint16_t memory_version = GetMemoryVersion(base, field_ref);
+        if (opcode == Instruction::IGET_WIDE) {
+          uint16_t res = LookupValue(Instruction::IGET_WIDE, base, field_ref, memory_version);
+          SetOperandValueWide(mir->ssa_rep->defs[0], res);
+        } else {
+          uint16_t res = LookupValue(Instruction::IGET, base, field_ref, memory_version);
+          SetOperandValue(mir->ssa_rep->defs[0], res);
+        }
+      }
+      break;
+
+    case Instruction::IPUT_WIDE:
+    case Instruction::IPUT_OBJECT:
+    case Instruction::IPUT:
+    case Instruction::IPUT_BOOLEAN:
+    case Instruction::IPUT_BYTE:
+    case Instruction::IPUT_CHAR:
+    case Instruction::IPUT_SHORT: {
+        int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1;
+        uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]);
+        if (null_checked_.find(base) != null_checked_.end()) {
+          if (cu_->verbose) {
+            LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
+          }
+          mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+        } else {
+          null_checked_.insert(base);
+        }
+        mir->meta.throw_insn->optimization_flags |= mir->optimization_flags;
+        uint16_t field_ref = mir->dalvikInsn.vC;
+        AdvanceMemoryVersion(base, field_ref);
+      }
+      break;
+
+    case Instruction::SGET_OBJECT:
+    case Instruction::SGET:
+    case Instruction::SGET_BOOLEAN:
+    case Instruction::SGET_BYTE:
+    case Instruction::SGET_CHAR:
+    case Instruction::SGET_SHORT:
+    case Instruction::SGET_WIDE: {
+        uint16_t field_ref = mir->dalvikInsn.vB;
+        uint16_t memory_version = GetMemoryVersion(NO_VALUE, field_ref);
+        if (opcode == Instruction::SGET_WIDE) {
+          uint16_t res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_ref, memory_version);
+          SetOperandValueWide(mir->ssa_rep->defs[0], res);
+        } else {
+          uint16_t res = LookupValue(Instruction::SGET, NO_VALUE, field_ref, memory_version);
+          SetOperandValue(mir->ssa_rep->defs[0], res);
+        }
+      }
+      break;
+
+    case Instruction::SPUT_OBJECT:
+    case Instruction::SPUT:
+    case Instruction::SPUT_BOOLEAN:
+    case Instruction::SPUT_BYTE:
+    case Instruction::SPUT_CHAR:
+    case Instruction::SPUT_SHORT:
+    case Instruction::SPUT_WIDE: {
+        uint16_t field_ref = mir->dalvikInsn.vB;
+        AdvanceMemoryVersion(NO_VALUE, field_ref);
+      }
+      break;
+
+  }
+  return res;
+}
+
+}    // namespace art