changeset 379:0afdd88556bc

tagu: first experiment with 64-bit tagged unions
author jrose
date Tue, 27 Sep 2011 19:02:18 -0700
parents 1bdc6f420130
children 47f0b00a6005
files series tagu.patch tagu.txt
diffstat 3 files changed, 3435 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/series	Tue Sep 27 18:34:51 2011 +0200
+++ b/series	Tue Sep 27 19:02:18 2011 -0700
@@ -11,6 +11,7 @@
 anonk.proj.patch                #-/anonk #+projects
 
 # Keep these separate, for debugging and review:
+tagu.patch      #+tagu          #-/tagu #+5a3c2bc614ca #-buildable
 annot.patch     #+annot         #-/annot #+d1605aabd0a1 #+jdk7-b30 #-testable
 inti.patch      #+inti          #-/inti #+d1605aabd0a1 #+jdk7-b30 #-buildable
 callcc_old.patch #+callcc_old   #-/callcc_old #+d6d1af32c5f9 #-testable
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tagu.patch	Tue Sep 27 19:02:18 2011 -0700
@@ -0,0 +1,3215 @@
+tagged unions in the JVM
+Good public write-up of a related design space:
+  http://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations
+
+Done:
+ - add EnableTaggedUnions, T_UNION, TaggedUnion, utos, etc.
+ - support fields of type "U" in the class file parser
+ - support T_UNION fields in the GC (instances, mirrors, interpreter oop maps)
+To do:
+ - account for type "[U" in metadata (unionArray{Oop,Klass} or special objArray subtype)
+ - write interpreter paths for get/put/invoke
+ - support JNI calls and write the needed intrinsics
+ - tell the compilers about T_UNION values
+ - finish vframe support
+ - port to zero (cppInterp), x86_64, sparc
+Keep in mind:
+ - the same paths will need even more generalization for tuples
+
+diff --git a/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp b/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp
+--- a/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp
++++ b/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp
+@@ -53,6 +53,8 @@
+       opr = as_long_opr(reg);
+     } else if (type == T_OBJECT || type == T_ARRAY) {
+       opr = as_oop_opr(reg);
++    } else if (type == T_UNION) {
++      Unimplemented(); //FIXME
+     } else {
+       opr = as_opr(reg);
+     }
+diff --git a/src/cpu/x86/vm/interp_masm_x86_32.cpp b/src/cpu/x86/vm/interp_masm_x86_32.cpp
+--- a/src/cpu/x86/vm/interp_masm_x86_32.cpp
++++ b/src/cpu/x86/vm/interp_masm_x86_32.cpp
+@@ -167,6 +167,9 @@
+     case itos: movl(rax, val_addr);                   break;
+     case ftos: fld_s(val_addr);                       break;
+     case dtos: fld_d(val_addr);                       break;
++    case utos: movl(rdx, val_addr1);
++               movl(rax, val_addr);
++               verify_oop(rax, state);                break;
+     case vtos: /* nothing to do */                    break;
+     default  : ShouldNotReachHere();
+   }
+@@ -342,6 +345,7 @@
+     case ltos: pop_l(rax, rdx);                              break;
+     case ftos: pop_f();                                      break;
+     case dtos: pop_d();                                      break;
++    case utos: pop_l(rax, rdx);                              break;
+     case vtos: /* nothing to do */                           break;
+     default  : ShouldNotReachHere();
+   }
+@@ -385,6 +389,7 @@
+     case ltos: push_l(rax, rdx);                               break;
+     case ftos: push_f();                                       break;
+     case dtos: push_d(rax);                                    break;
++    case utos: push_l(rax, rdx);                               break;
+     case vtos: /* nothing to do */                             break;
+     default  : ShouldNotReachHere();
+   }
+@@ -1326,6 +1331,26 @@
+ 
+ void InterpreterMacroAssembler::verify_oop(Register reg, TosState state) {
+   if (state == atos) MacroAssembler::verify_oop(reg);
++  if (state == utos && EnableTaggedUnions && VerifyOops) {
++    Label done, is_prim;
++    assert(reg == rax, "must be rdx:rax pair");
++    testl(rdx, rdx);
++    assert(TaggedUnion::tag_is_ref(0), "testing high half for zero here");
++    jcc(Assembler::notZero, is_prim);
++    MacroAssembler::verify_oop(reg);
++    jmp(done);
++
++    // If not an oop, the decoded primitive value must be in (0x8000..0x7ff0) << 48.
++    bind(is_prim);
++    const jlong decoding_bias = TaggedUnion::decode_prim(0);
++    push(rdx);
++    addl(rdx, high(decoding_bias));  // temporarily decode in place
++    cmpl(rdx, high(max_junion_long));
++    pop(rdx);  // pop preserves flags from cmpl!
++    jcc(Assembler::lessEqual, done);
++    stop("verify_oop: rdx:rax: broken tagged primitive");
++    bind(done);
++  }
+ }
+ 
+ 
+diff --git a/src/cpu/x86/vm/interp_masm_x86_64.cpp b/src/cpu/x86/vm/interp_masm_x86_64.cpp
+--- a/src/cpu/x86/vm/interp_masm_x86_64.cpp
++++ b/src/cpu/x86/vm/interp_masm_x86_64.cpp
+@@ -165,6 +165,8 @@
+     case itos: movl(rax, val_addr);                 break;
+     case ftos: movflt(xmm0, val_addr);              break;
+     case dtos: movdbl(xmm0, val_addr);              break;
++    case utos: movptr(rax, val_addr);
++               verify_oop(rax, state);              break;
+     case vtos: /* nothing to do */                  break;
+     default  : ShouldNotReachHere();
+   }
+@@ -367,6 +369,7 @@
+   case ltos: pop_l();                   break;
+   case ftos: pop_f();                   break;
+   case dtos: pop_d();                   break;
++  case utos: pop_l();                   break;
+   case vtos: /* nothing to do */        break;
+   default:   ShouldNotReachHere();
+   }
+@@ -384,6 +387,7 @@
+   case ltos: push_l();                  break;
+   case ftos: push_f();                  break;
+   case dtos: push_d();                  break;
++  case utos: push_l();                  break;
+   case vtos: /* nothing to do */        break;
+   default  : ShouldNotReachHere();
+   }
+@@ -1394,6 +1398,9 @@
+   if (state == atos) {
+     MacroAssembler::verify_oop(reg);
+   }
++  if (state == utos && EnableTaggedUnions && VerifyOops) {
++    Unimplemented();  // adapt from 32-bit version
++  }
+ }
+ 
+ void InterpreterMacroAssembler::verify_FPU(int stack_depth, TosState state) {
+diff --git a/src/cpu/x86/vm/templateInterpreter_x86_32.cpp b/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
+--- a/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
++++ b/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
+@@ -1847,11 +1847,12 @@
+ //------------------------------------------------------------------------------------------------------------------------
+ // Helper for vtos entry point generation
+ 
+-void TemplateInterpreterGenerator::set_vtos_entry_points (Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep) {
++void TemplateInterpreterGenerator::set_vtos_entry_points (Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& uep, address& vep) {
+   assert(t->is_valid() && t->tos_in() == vtos, "illegal template");
+   Label L;
+   fep = __ pc(); __ push(ftos); __ jmp(L);
+   dep = __ pc(); __ push(dtos); __ jmp(L);
++  uep =                         // fall through
+   lep = __ pc(); __ push(ltos); __ jmp(L);
+   aep = __ pc(); __ push(atos); __ jmp(L);
+   bep = cep = sep =             // fall through
+diff --git a/src/cpu/x86/vm/templateInterpreter_x86_64.cpp b/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
+--- a/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
++++ b/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
+@@ -1875,12 +1875,14 @@
+                                                          address& lep,
+                                                          address& fep,
+                                                          address& dep,
++                                                         address& uep,
+                                                          address& vep) {
+   assert(t->is_valid() && t->tos_in() == vtos, "illegal template");
+   Label L;
+   aep = __ pc();  __ push_ptr();  __ jmp(L);
+   fep = __ pc();  __ push_f();    __ jmp(L);
+   dep = __ pc();  __ push_d();    __ jmp(L);
++  uep =
+   lep = __ pc();  __ push_l();    __ jmp(L);
+   bep = cep = sep =
+   iep = __ pc();  __ push_i();
+diff --git a/src/share/vm/ci/ciObjectFactory.cpp b/src/share/vm/ci/ciObjectFactory.cpp
+--- a/src/share/vm/ci/ciObjectFactory.cpp
++++ b/src/share/vm/ci/ciObjectFactory.cpp
+@@ -146,8 +146,9 @@
+ 
+   _ci_objects = new (_arena) GrowableArray<ciObject*>(_arena, 64, 0, NULL);
+ 
+-  for (int i = T_BOOLEAN; i <= T_CONFLICT; i++) {
++  for (int i = T_MIN_VALUE; i <= T_CONFLICT; i++) {
+     BasicType t = (BasicType)i;
++    if (!EnableTaggedUnions && t == T_UNION)  continue;
+     if (type2name(t) != NULL && t != T_OBJECT && t != T_ARRAY && t != T_NARROWOOP) {
+       ciType::_basic_types[t] = new (_arena) ciType(t);
+       init_ident_of(ciType::_basic_types[t]);
+diff --git a/src/share/vm/classfile/classFileParser.cpp b/src/share/vm/classfile/classFileParser.cpp
+--- a/src/share/vm/classfile/classFileParser.cpp
++++ b/src/share/vm/classfile/classFileParser.cpp
+@@ -993,11 +993,13 @@
+   STATIC_SHORT,         // shorts
+   STATIC_WORD,          // ints
+   STATIC_DOUBLE,        // aligned long or double
++  STATIC_UNION,         // aligned long/double/ref union
+   NONSTATIC_OOP,
+   NONSTATIC_BYTE,
+   NONSTATIC_SHORT,
+   NONSTATIC_WORD,
+   NONSTATIC_DOUBLE,
++  NONSTATIC_UNION,
+   MAX_FIELD_ALLOCATION_TYPE,
+   BAD_ALLOCATION_TYPE = -1
+ };
+@@ -1006,7 +1008,7 @@
+   BAD_ALLOCATION_TYPE, // 0
+   BAD_ALLOCATION_TYPE, // 1
+   BAD_ALLOCATION_TYPE, // 2
+-  BAD_ALLOCATION_TYPE, // 3
++  NONSTATIC_UNION,     // T_UNION    =  3,
+   NONSTATIC_BYTE ,     // T_BOOLEAN  =  4,
+   NONSTATIC_SHORT,     // T_CHAR     =  5,
+   NONSTATIC_WORD,      // T_FLOAT    =  6,
+@@ -1024,7 +1026,7 @@
+   BAD_ALLOCATION_TYPE, // 0
+   BAD_ALLOCATION_TYPE, // 1
+   BAD_ALLOCATION_TYPE, // 2
+-  BAD_ALLOCATION_TYPE, // 3
++  STATIC_UNION,        // T_UNION    =  3,
+   STATIC_BYTE ,        // T_BOOLEAN  =  4,
+   STATIC_SHORT,        // T_CHAR     =  5,
+   STATIC_WORD,          // T_FLOAT    =  6,
+@@ -2949,33 +2951,44 @@
+ #endif
+     int static_field_size = 0;
+     int next_static_oop_offset;
++    int next_static_union_offset;
+     int next_static_double_offset;
+     int next_static_word_offset;
+     int next_static_short_offset;
+     int next_static_byte_offset;
+     int next_static_type_offset;
+     int next_nonstatic_oop_offset;
++    int next_nonstatic_union_offset;
+     int next_nonstatic_double_offset;
+     int next_nonstatic_word_offset;
+     int next_nonstatic_short_offset;
+     int next_nonstatic_byte_offset;
+     int next_nonstatic_type_offset;
+-    int first_nonstatic_oop_offset;
+     int first_nonstatic_field_offset;
+     int next_nonstatic_field_offset;
+ 
++    unsigned int static_double_count = fac.count[STATIC_DOUBLE];
++    unsigned int static_union_count  = fac.count[STATIC_UNION];
++
++    // For allocation sizing, group T_UNION with T_DOUBLE and T_LONG:
++    if (EnableTaggedUnions) {
++      static_double_count += static_union_count;
++    } else {
++      assert(static_union_count == 0, "!EnableTaggedUnions");
++    }
++
+     // Calculate the starting byte offsets
+     next_static_oop_offset      = instanceMirrorKlass::offset_of_static_fields();
+     next_static_double_offset   = next_static_oop_offset +
+                                   (fac.count[STATIC_OOP] * heapOopSize);
+-    if ( fac.count[STATIC_DOUBLE] &&
++    if ( static_double_count &&
+          (Universe::field_type_should_be_aligned(T_DOUBLE) ||
++          Universe::field_type_should_be_aligned(T_UNION) ||
+           Universe::field_type_should_be_aligned(T_LONG)) ) {
+       next_static_double_offset = align_size_up(next_static_double_offset, BytesPerLong);
+     }
+-
+     next_static_word_offset     = next_static_double_offset +
+-                                  (fac.count[STATIC_DOUBLE] * BytesPerLong);
++                                  (static_double_count * BytesPerLong);
+     next_static_short_offset    = next_static_word_offset +
+                                   (fac.count[STATIC_WORD] * BytesPerInt);
+     next_static_byte_offset     = next_static_short_offset +
+@@ -2985,16 +2998,28 @@
+     static_field_size           = (next_static_type_offset -
+                                   next_static_oop_offset) / wordSize;
+ 
++    // static unions are a subsequence of static doubles
++    next_static_union_offset    = next_static_double_offset;
++    next_static_double_offset  += (static_union_count * BytesPerLong);
++
+     first_nonstatic_field_offset = instanceOopDesc::base_offset_in_bytes() +
+                                    nonstatic_field_size * heapOopSize;
+     next_nonstatic_field_offset = first_nonstatic_field_offset;
+ 
+     unsigned int nonstatic_double_count = fac.count[NONSTATIC_DOUBLE];
++    unsigned int nonstatic_union_count  = fac.count[NONSTATIC_UNION];
+     unsigned int nonstatic_word_count   = fac.count[NONSTATIC_WORD];
+     unsigned int nonstatic_short_count  = fac.count[NONSTATIC_SHORT];
+     unsigned int nonstatic_byte_count   = fac.count[NONSTATIC_BYTE];
+     unsigned int nonstatic_oop_count    = fac.count[NONSTATIC_OOP];
+ 
++    // Treat unions as doubles for allocation.
++    if (EnableTaggedUnions) {
++      nonstatic_double_count += nonstatic_union_count;
++    } else {
++      assert(nonstatic_union_count == 0, "!EnableTaggedUnions");
++    }
++
+     bool super_has_nonstatic_fields =
+             (super_klass() != NULL && super_klass->has_nonstatic_fields());
+     bool has_nonstatic_fields  =  super_has_nonstatic_fields ||
+@@ -3004,16 +3029,21 @@
+ 
+ 
+     // Prepare list of oops for oop map generation.
+-    int* nonstatic_oop_offsets;
+-    unsigned int* nonstatic_oop_counts;
++    int* nonstatic_oop_offsets = NEW_RESOURCE_ARRAY_IN_THREAD(
++              THREAD, int, nonstatic_oop_count + 1);
++    unsigned int* nonstatic_oop_counts = NEW_RESOURCE_ARRAY_IN_THREAD(
++              THREAD, unsigned int, nonstatic_oop_count + 1);
+     unsigned int nonstatic_oop_map_count = 0;
+-
+-    nonstatic_oop_offsets = NEW_RESOURCE_ARRAY_IN_THREAD(
+-              THREAD, int, nonstatic_oop_count + 1);
+-    nonstatic_oop_counts  = NEW_RESOURCE_ARRAY_IN_THREAD(
+-              THREAD, unsigned int, nonstatic_oop_count + 1);
+-
+-    first_nonstatic_oop_offset = 0; // will be set for first oop field
++    int first_nonstatic_oop_offset = 0; // will be set for first oop field
++
++    // Prepare separate lists for unions (tagged references).
++    // These need special processing by the GC to avoid non-oops.
++    int* nonstatic_union_offsets = NEW_RESOURCE_ARRAY_IN_THREAD(
++              THREAD, int, nonstatic_union_count + 1);
++    unsigned int* nonstatic_union_counts = NEW_RESOURCE_ARRAY_IN_THREAD(
++              THREAD, unsigned int, nonstatic_union_count + 1);
++    unsigned int nonstatic_union_map_count = 0;
++    int first_nonstatic_union_offset = 0; // will be set for first union field if any
+ 
+ #ifndef PRODUCT
+     if( PrintCompactFieldsSavings ) {
+@@ -3066,12 +3096,12 @@
+     }
+ 
+     if( allocation_style == 0 ) {
+-      // Fields order: oops, longs/doubles, ints, shorts/chars, bytes
++      // Fields order: oops, unions, longs/doubles, ints, shorts/chars, bytes
+       next_nonstatic_oop_offset    = next_nonstatic_field_offset;
+       next_nonstatic_double_offset = next_nonstatic_oop_offset +
+                                       (nonstatic_oop_count * heapOopSize);
+     } else if( allocation_style == 1 ) {
+-      // Fields order: longs/doubles, ints, shorts/chars, bytes, oops
++      // Fields order: unions, longs/doubles, ints, shorts/chars, bytes, oops
+       next_nonstatic_double_offset = next_nonstatic_field_offset;
+     } else if( allocation_style == 2 ) {
+       // Fields allocation: oops fields in super and sub classes are together.
+@@ -3096,6 +3126,10 @@
+       ShouldNotReachHere();
+     }
+ 
++    // nonstatic unions are a subsequence of nonstatic doubles
++    next_nonstatic_union_offset   = next_static_double_offset;
++    next_nonstatic_double_offset += (nonstatic_union_count * BytesPerLong);
++
+     int nonstatic_oop_space_count   = 0;
+     int nonstatic_word_space_count  = 0;
+     int nonstatic_short_space_count = 0;
+@@ -3192,6 +3226,19 @@
+           real_offset = next_static_double_offset;
+           next_static_double_offset += BytesPerLong;
+           break;
++        case STATIC_UNION:
++          real_offset = next_static_union_offset;
++          next_static_union_offset += BytesPerLong;
++          nonstatic_union_map_count =
++            update_oop_maps(real_offset,
++                            nonstatic_union_map_count,
++                            nonstatic_union_offsets,
++                            nonstatic_union_counts,
++                            BytesPerLong);
++          if (first_nonstatic_union_offset == 0) { // Undefined
++            first_nonstatic_union_offset = real_offset;
++          }
++          break;
+         case NONSTATIC_OOP:
+           if( nonstatic_oop_space_count > 0 ) {
+             real_offset = nonstatic_oop_space_offset;
+@@ -3201,22 +3248,14 @@
+             real_offset = next_nonstatic_oop_offset;
+             next_nonstatic_oop_offset += heapOopSize;
+           }
+-          // Update oop maps
+-          if( nonstatic_oop_map_count > 0 &&
+-              nonstatic_oop_offsets[nonstatic_oop_map_count - 1] ==
+-              real_offset -
+-              int(nonstatic_oop_counts[nonstatic_oop_map_count - 1]) *
+-              heapOopSize ) {
+-            // Extend current oop map
+-            nonstatic_oop_counts[nonstatic_oop_map_count - 1] += 1;
+-          } else {
+-            // Create new oop map
+-            nonstatic_oop_offsets[nonstatic_oop_map_count] = real_offset;
+-            nonstatic_oop_counts [nonstatic_oop_map_count] = 1;
+-            nonstatic_oop_map_count += 1;
+-            if( first_nonstatic_oop_offset == 0 ) { // Undefined
+-              first_nonstatic_oop_offset = real_offset;
+-            }
++          nonstatic_oop_map_count =
++            update_oop_maps(real_offset,
++                            nonstatic_oop_map_count,
++                            nonstatic_oop_offsets,
++                            nonstatic_oop_counts,
++                            heapOopSize);
++          if (first_nonstatic_oop_offset == 0) { // Undefined
++            first_nonstatic_oop_offset = real_offset;
+           }
+           break;
+         case NONSTATIC_BYTE:
+@@ -3253,6 +3292,10 @@
+           real_offset = next_nonstatic_double_offset;
+           next_nonstatic_double_offset += BytesPerLong;
+           break;
++        case NONSTATIC_UNION:
++          real_offset = next_nonstatic_union_offset;
++          next_nonstatic_union_offset += BytesPerLong;
++          break;
+         default:
+           ShouldNotReachHere();
+       }
+@@ -3269,8 +3312,11 @@
+ 
+     // Number of non-static oop map blocks allocated at end of klass.
+     const unsigned int total_oop_map_count =
+-      compute_oop_map_count(super_klass, nonstatic_oop_map_count,
++      compute_oop_map_count(super_klass, T_OBJECT, nonstatic_oop_map_count,
+                             first_nonstatic_oop_offset);
++    const unsigned int total_union_map_count =
++      compute_oop_map_count(super_klass, T_UNION, nonstatic_union_map_count,
++                            first_nonstatic_union_offset);
+ 
+     // Compute reference type
+     ReferenceType rt;
+@@ -3284,12 +3330,15 @@
+     klassOop ik = oopFactory::new_instanceKlass(name, vtable_size, itable_size,
+                                                 static_field_size,
+                                                 total_oop_map_count,
++                                                total_union_map_count,
+                                                 rt, CHECK_(nullHandle));
+     instanceKlassHandle this_klass (THREAD, ik);
+ 
+     assert(this_klass->static_field_size() == static_field_size, "sanity");
+     assert(this_klass->nonstatic_oop_map_count() == total_oop_map_count,
+            "sanity");
++    assert(this_klass->nonstatic_union_map_count() == total_union_map_count,
++           "sanity");
+ 
+     // Fill in information already parsed
+     this_klass->set_access_flags(access_flags);
+@@ -3304,6 +3353,7 @@
+     this_klass->set_nonstatic_field_size(nonstatic_field_size);
+     this_klass->set_has_nonstatic_fields(has_nonstatic_fields);
+     this_klass->set_static_oop_field_count(fac.count[STATIC_OOP]);
++    this_klass->set_static_union_field_count(fac.count[STATIC_UNION]);
+     cp->set_pool_holder(this_klass());
+     error_handler.set_in_error(false);   // turn off error handler for cp
+     this_klass->set_constants(cp());
+@@ -3375,7 +3425,12 @@
+     klassItable::setup_itable_offset_table(this_klass);
+ 
+     // Do final class setup
+-    fill_oop_maps(this_klass, nonstatic_oop_map_count, nonstatic_oop_offsets, nonstatic_oop_counts);
++    fill_oop_maps(this_klass, T_OBJECT, nonstatic_oop_map_count, nonstatic_oop_offsets, nonstatic_oop_counts);
++    if (EnableTaggedUnions) {
++      fill_oop_maps(this_klass, T_UNION, nonstatic_union_map_count, nonstatic_union_offsets, nonstatic_union_counts);
++    } else {
++      assert(total_union_map_count == 0, "union signatures must have been rejected");
++    }
+ 
+     set_precomputed_flags(this_klass);
+ 
+@@ -3471,12 +3526,39 @@
+ }
+ 
+ 
++unsigned int ClassFileParser::update_oop_maps(int real_offset,
++                                              unsigned int nonstatic_oop_map_count,
++                                              int* nonstatic_oop_offsets,
++                                              unsigned int* nonstatic_oop_counts,
++                                              int element_size) {
++  // Update oop maps
++  if (nonstatic_oop_map_count > 0 &&
++      nonstatic_oop_offsets[nonstatic_oop_map_count - 1] ==
++      real_offset - (int(nonstatic_oop_counts[nonstatic_oop_map_count - 1]) *
++                     element_size)) {
++    // Extend current oop map
++    nonstatic_oop_counts[nonstatic_oop_map_count - 1] += 1;
++  } else {
++    // Create new oop map
++    nonstatic_oop_offsets[nonstatic_oop_map_count] = real_offset;
++    nonstatic_oop_counts [nonstatic_oop_map_count] = 1;
++    nonstatic_oop_map_count += 1;
++  }
++  return nonstatic_oop_map_count;
++}
++
++
+ unsigned int
+ ClassFileParser::compute_oop_map_count(instanceKlassHandle super,
++                                       BasicType map_type,
+                                        unsigned int nonstatic_oop_map_count,
+                                        int first_nonstatic_oop_offset) {
++  bool umaps = (map_type == T_UNION);
++  assert(map_type == T_UNION || map_type == T_OBJECT, "");
+   unsigned int map_count =
+-    super.is_null() ? 0 : super->nonstatic_oop_map_count();
++    super.is_null() ? 0 : (umaps
++                           ? super->nonstatic_union_map_count()
++                           : super->nonstatic_oop_map_count());
+   if (nonstatic_oop_map_count > 0) {
+     // We have oops to add to map
+     if (map_count == 0) {
+@@ -3484,7 +3566,9 @@
+     } else {
+       // Check whether we should add a new map block or whether the last one can
+       // be extended
+-      OopMapBlock* const first_map = super->start_of_nonstatic_oop_maps();
++      OopMapBlock* const first_map = (umaps
++                                      ? super->start_of_nonstatic_union_maps()
++                                      : super->start_of_nonstatic_oop_maps());
+       OopMapBlock* const last_map = first_map + map_count - 1;
+ 
+       int next_offset = last_map->offset() + last_map->count() * heapOopSize;
+@@ -3504,22 +3588,45 @@
+ 
+ 
+ void ClassFileParser::fill_oop_maps(instanceKlassHandle k,
++                                    BasicType map_type,
+                                     unsigned int nonstatic_oop_map_count,
+                                     int* nonstatic_oop_offsets,
+                                     unsigned int* nonstatic_oop_counts) {
+-  OopMapBlock* this_oop_map = k->start_of_nonstatic_oop_maps();
++  bool umaps = (map_type == T_UNION);
++  assert(map_type == T_UNION || map_type == T_OBJECT, "");
++  unsigned int this_count = (umaps
++                             ? k->nonstatic_union_map_count()
++                             : k->nonstatic_oop_map_count());
++  OopMapBlock* this_oop_map = (umaps
++                               ? k->start_of_nonstatic_union_maps()
++                               : k->start_of_nonstatic_oop_maps());
++#ifdef ASSERT
++  // Record the values which k actually advertises.
++  OopMapBlock* stored_this_oop_map = this_oop_map;
++  unsigned int stored_this_count   = this_count;
++#endif //ASSERT
++
+   const instanceKlass* const super = k->superklass();
+-  const unsigned int super_count = super ? super->nonstatic_oop_map_count() : 0;
++  unsigned int super_count = 0;
++  OopMapBlock* super_oop_map = NULL;
++  if (super != NULL) {
++    super_count = (umaps
++                   ? super->nonstatic_union_map_count()
++                   : super->nonstatic_oop_map_count());
++    if (super_count > 0)
++      super_oop_map = (umaps
++                       ? super->start_of_nonstatic_union_maps()
++                       : super->start_of_nonstatic_oop_maps());
++  }
+   if (super_count > 0) {
+     // Copy maps from superklass
+-    OopMapBlock* super_oop_map = super->start_of_nonstatic_oop_maps();
+     for (unsigned int i = 0; i < super_count; ++i) {
+       *this_oop_map++ = *super_oop_map++;
+     }
+   }
+ 
+   if (nonstatic_oop_map_count > 0) {
+-    if (super_count + nonstatic_oop_map_count > k->nonstatic_oop_map_count()) {
++    if (super_count + nonstatic_oop_map_count > this_count) {
+       // The counts differ because there is no gap between superklass's last oop
+       // field and the first local oop field.  Extend the last oop map copied
+       // from the superklass instead of creating new one.
+@@ -3530,14 +3637,18 @@
+       this_oop_map++;
+     }
+ 
++    // Make sure the advertised count is correct:
++    assert(super_count + nonstatic_oop_map_count == stored_this_count, "fixed");
++
+     // Add new map blocks, fill them
+     while (nonstatic_oop_map_count-- > 0) {
+       this_oop_map->set_offset(*nonstatic_oop_offsets++);
+       this_oop_map->set_count(*nonstatic_oop_counts++);
+       this_oop_map++;
+     }
+-    assert(k->start_of_nonstatic_oop_maps() + k->nonstatic_oop_map_count() ==
+-           this_oop_map, "sanity");
++
++    // Make sure we put the oop map into the advertised place:
++    assert(stored_this_oop_map + stored_this_count == this_oop_map, "sanity");
+   }
+ }
+ 
+@@ -4159,7 +4270,7 @@
+     nextp = skip_over_field_signature(p, false, length, CHECK_0);
+     while ((length > 0) && (nextp != NULL)) {
+       args_size++;
+-      if (p[0] == 'J' || p[0] == 'D') {
++      if (p[0] == 'J' || p[0] == 'D' || (p[0] == 'U' && EnableTaggedUnions)) {
+         args_size++;
+       }
+       length -= nextp - p;
+@@ -4330,6 +4441,11 @@
+       case JVM_SIGNATURE_LONG:
+       case JVM_SIGNATURE_DOUBLE:
+         return signature + 1;
++      case JVM_SIGNATURE_UNION:
++        if (!EnableTaggedUnions) {
++          classfile_parse_error("Class name contains illegal character 'U' in descriptor in class file %s", CHECK_0);
++        }
++        return signature + 1;
+       case JVM_SIGNATURE_CLASS: {
+         if (_major_version < JAVA_1_5_VERSION) {
+           // Skip over the class name if one is there
+diff --git a/src/share/vm/classfile/classFileParser.hpp b/src/share/vm/classfile/classFileParser.hpp
+--- a/src/share/vm/classfile/classFileParser.hpp
++++ b/src/share/vm/classfile/classFileParser.hpp
+@@ -144,10 +144,17 @@
+                                        int runtime_invisible_annotations_length, TRAPS);
+ 
+   // Final setup
++  unsigned int update_oop_maps(int real_offset,
++                               unsigned int nonstatic_oop_map_count,
++                               int* nonstatic_oop_offsets,
++                               unsigned int* nonstatic_oop_counts,
++                               int element_size);
+   unsigned int compute_oop_map_count(instanceKlassHandle super,
++                                     BasicType map_type,  // T_OBJECT or T_UNION
+                                      unsigned int nonstatic_oop_count,
+                                      int first_nonstatic_oop_offset);
+   void fill_oop_maps(instanceKlassHandle k,
++                     BasicType map_type,  // T_OBJECT or T_UNION
+                      unsigned int nonstatic_oop_map_count,
+                      int* nonstatic_oop_offsets,
+                      unsigned int* nonstatic_oop_counts);
+diff --git a/src/share/vm/classfile/javaClasses.cpp b/src/share/vm/classfile/javaClasses.cpp
+--- a/src/share/vm/classfile/javaClasses.cpp
++++ b/src/share/vm/classfile/javaClasses.cpp
+@@ -510,6 +510,7 @@
+     instanceMirrorKlass* mk = instanceMirrorKlass::cast(mirror->klass());
+     java_lang_Class::set_oop_size(mirror(), mk->instance_size(k));
+     java_lang_Class::set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror()));
++    java_lang_Class::set_static_union_field_count(mirror(), mk->compute_static_union_field_count(mirror()));
+ 
+     // It might also have a component mirror.  This mirror must already exist.
+     if (k->oop_is_javaArray()) {
+@@ -561,6 +562,14 @@
+   assert(_static_oop_field_count_offset != 0, "must be set");
+   java_class->int_field_put(_static_oop_field_count_offset, size);
+ }
++int  java_lang_Class::static_union_field_count(oop java_class) {
++  assert(_static_union_field_count_offset != 0, "must be set");
++  return java_class->int_field(_static_union_field_count_offset);
++}
++void java_lang_Class::set_static_union_field_count(oop java_class, int size) {
++  assert(_static_union_field_count_offset != 0, "must be set");
++  java_class->int_field_put(_static_union_field_count_offset, size);
++}
+ 
+ oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS) {
+   // This should be improved by adding a field at the Java level or by
+@@ -574,6 +583,7 @@
+   instanceMirrorKlass* mk = instanceMirrorKlass::cast(SystemDictionary::Class_klass());
+   java_lang_Class::set_oop_size(java_class, mk->instance_size(oop(NULL)));
+   java_lang_Class::set_static_oop_field_count(java_class, 0);
++  java_lang_Class::set_static_union_field_count(java_class, 0);
+   return java_class;
+ }
+ 
+@@ -2832,6 +2842,7 @@
+ int java_lang_Class::_resolved_constructor_offset;
+ int java_lang_Class::_oop_size_offset;
+ int java_lang_Class::_static_oop_field_count_offset;
++int java_lang_Class::_static_union_field_count_offset;
+ int java_lang_Throwable::backtrace_offset;
+ int java_lang_Throwable::detailMessage_offset;
+ int java_lang_Throwable::cause_offset;
+diff --git a/src/share/vm/classfile/javaClasses.hpp b/src/share/vm/classfile/javaClasses.hpp
+--- a/src/share/vm/classfile/javaClasses.hpp
++++ b/src/share/vm/classfile/javaClasses.hpp
+@@ -161,7 +161,8 @@
+   macro(java_lang_Class, resolved_constructor,   object_signature,  false) \
+   macro(java_lang_Class, array_klass,            object_signature,  false) \
+   macro(java_lang_Class, oop_size,               int_signature,     false) \
+-  macro(java_lang_Class, static_oop_field_count, int_signature,     false)
++  macro(java_lang_Class, static_oop_field_count, int_signature,     false) \
++  macro(java_lang_Class, static_union_field_count, int_signature,   false)
+ 
+ class java_lang_Class : AllStatic {
+   friend class VMStructs;
+@@ -175,6 +176,7 @@
+ 
+   static int _oop_size_offset;
+   static int _static_oop_field_count_offset;
++  static int _static_union_field_count_offset;
+ 
+   static bool offsets_computed;
+   static int classRedefinedCount_offset;
+@@ -222,6 +224,8 @@
+   static void set_oop_size(oop java_class, int size);
+   static int static_oop_field_count(oop java_class);
+   static void set_static_oop_field_count(oop java_class, int size);
++  static int static_union_field_count(oop java_class);
++  static void set_static_union_field_count(oop java_class, int size);
+ 
+   // Debugging
+   friend class JavaClasses;
+diff --git a/src/share/vm/classfile/systemDictionary.cpp b/src/share/vm/classfile/systemDictionary.cpp
+--- a/src/share/vm/classfile/systemDictionary.cpp
++++ b/src/share/vm/classfile/systemDictionary.cpp
+@@ -2020,7 +2020,7 @@
+ // If so, returns the basic type it holds.  If not, returns T_OBJECT.
+ BasicType SystemDictionary::box_klass_type(klassOop k) {
+   assert(k != NULL, "");
+-  for (int i = T_BOOLEAN; i < T_VOID+1; i++) {
++  for (int i = T_MIN_VALUE; i < T_VOID+1; i++) {
+     if (_box_klasses[i] == k)
+       return (BasicType)i;
+   }
+diff --git a/src/share/vm/classfile/vmSymbols.cpp b/src/share/vm/classfile/vmSymbols.cpp
+--- a/src/share/vm/classfile/vmSymbols.cpp
++++ b/src/share/vm/classfile/vmSymbols.cpp
+@@ -190,7 +190,7 @@
+ 
+ BasicType vmSymbols::signature_type(Symbol* s) {
+   assert(s != NULL, "checking");
+-  for (int i = T_BOOLEAN; i < T_VOID+1; i++) {
++  for (int i = T_MIN_VALUE; i < T_VOID+1; i++) {
+     if (s == _type_signatures[i]) {
+       return (BasicType)i;
+     }
+diff --git a/src/share/vm/classfile/vmSymbols.hpp b/src/share/vm/classfile/vmSymbols.hpp
+--- a/src/share/vm/classfile/vmSymbols.hpp
++++ b/src/share/vm/classfile/vmSymbols.hpp
+@@ -370,6 +370,7 @@
+   template(array_klass_name,                          "array_klass")                              \
+   template(oop_size_name,                             "oop_size")                                 \
+   template(static_oop_field_count_name,               "static_oop_field_count")                   \
++  template(static_union_field_count_name,             "static_union_field_count")                 \
+                                                                                                   \
+   /* non-intrinsic name/signature pairs: */                                                       \
+   template(register_method_name,                      "register")                                 \
+diff --git a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp
+--- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp
++++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp
+@@ -60,6 +60,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { MarkRefsIntoClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { MarkRefsIntoClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+   bool do_header() { return true; }
+   Prefetch::style prefetch_style() {
+     return Prefetch::do_read;
+@@ -82,6 +83,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { MarkRefsIntoVerifyClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { MarkRefsIntoVerifyClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+   bool do_header() { return true; }
+   Prefetch::style prefetch_style() {
+     return Prefetch::do_read;
+@@ -147,6 +149,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { PushAndMarkClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { PushAndMarkClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+   bool do_header() { return true; }
+   Prefetch::style prefetch_style() {
+     return Prefetch::do_read;
+@@ -183,6 +186,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { Par_PushAndMarkClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { Par_PushAndMarkClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+   bool do_header() { return true; }
+   Prefetch::style prefetch_style() {
+     return Prefetch::do_read;
+@@ -223,6 +227,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { MarkRefsIntoAndScanClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { MarkRefsIntoAndScanClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+   bool do_header() { return true; }
+   Prefetch::style prefetch_style() {
+     return Prefetch::do_read;
+@@ -267,6 +272,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { Par_MarkRefsIntoAndScanClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { Par_MarkRefsIntoAndScanClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+   bool do_header() { return true; }
+   // When ScanMarkedObjectsAgainClosure is used,
+   // it passes [Par_]MarkRefsIntoAndScanClosure to oop_oop_iterate(),
+@@ -309,6 +315,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { PushOrMarkClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { PushOrMarkClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+   // In support of class unloading
+   virtual const bool should_remember_mdo() const {
+     return false;
+@@ -353,6 +360,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { Par_PushOrMarkClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { Par_PushOrMarkClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+   // In support of class unloading
+   virtual const bool should_remember_mdo() const {
+     return false;
+@@ -389,6 +397,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { CMSKeepAliveClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { CMSKeepAliveClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+ };
+ 
+ class CMSInnerParMarkAndPushClosure: public Par_KlassRememberingOopClosure {
+@@ -407,6 +416,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { CMSInnerParMarkAndPushClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { CMSInnerParMarkAndPushClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+ };
+ 
+ // A parallel (MT) version of the above, used when
+@@ -431,6 +441,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { CMSParKeepAliveClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { CMSParKeepAliveClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+ };
+ 
+ #endif // SHARE_VM_GC_IMPLEMENTATION_CONCURRENTMARKSWEEP_CMSOOPCLOSURES_HPP
+diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
+--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
++++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
+@@ -1830,6 +1830,7 @@
+ #ifdef ASSERT
+   bool verify_ref(narrowOop* ref) const;
+   bool verify_ref(oop* ref) const;
++  bool verify_ref(TaggedUnion* ref) const { return verify_ref(ref->ref_addr()); }
+   bool verify_task(StarTask ref) const;
+ #endif // ASSERT
+ 
+diff --git a/src/share/vm/gc_implementation/parNew/parOopClosures.hpp b/src/share/vm/gc_implementation/parNew/parOopClosures.hpp
+--- a/src/share/vm/gc_implementation/parNew/parOopClosures.hpp
++++ b/src/share/vm/gc_implementation/parNew/parOopClosures.hpp
+@@ -57,6 +57,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p);
+   inline void do_oop_nv(narrowOop* p);
++  inline void do_oop_nv(TaggedUnion* p);
+ };
+ 
+ class ParScanWithoutBarrierClosure: public ParScanClosure {
+@@ -68,6 +69,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p);
+   inline void do_oop_nv(narrowOop* p);
++  inline void do_oop_nv(TaggedUnion* p);
+ };
+ 
+ class ParRootScanWithBarrierTwoGensClosure: public ParScanClosure {
+@@ -99,6 +101,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p);
+   inline void do_oop_nv(narrowOop* p);
++  inline void do_oop_nv(TaggedUnion* p);
+ };
+ 
+ class ParEvacuateFollowersClosure: public VoidClosure {
+diff --git a/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp b/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp
+--- a/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp
++++ b/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp
+@@ -53,6 +53,7 @@
+ 
+ inline void ParScanWeakRefClosure::do_oop_nv(oop* p)       { ParScanWeakRefClosure::do_oop_work(p); }
+ inline void ParScanWeakRefClosure::do_oop_nv(narrowOop* p) { ParScanWeakRefClosure::do_oop_work(p); }
++inline void ParScanWeakRefClosure::do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+ 
+ template <class T> inline void ParScanClosure::par_do_barrier(T* p) {
+   assert(generation()->is_in_reserved(p), "expected ref in generation");
+@@ -127,8 +128,10 @@
+ 
+ inline void ParScanWithBarrierClosure::do_oop_nv(oop* p)       { ParScanClosure::do_oop_work(p, true, false); }
+ inline void ParScanWithBarrierClosure::do_oop_nv(narrowOop* p) { ParScanClosure::do_oop_work(p, true, false); }
++inline void ParScanWithBarrierClosure::do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+ 
+ inline void ParScanWithoutBarrierClosure::do_oop_nv(oop* p)       { ParScanClosure::do_oop_work(p, false, false); }
+ inline void ParScanWithoutBarrierClosure::do_oop_nv(narrowOop* p) { ParScanClosure::do_oop_work(p, false, false); }
++inline void ParScanWithoutBarrierClosure::do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+ 
+ #endif // SHARE_VM_GC_IMPLEMENTATION_PARNEW_PAROOPCLOSURES_INLINE_HPP
+diff --git a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp
+--- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp
++++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp
+@@ -1192,6 +1192,7 @@
+ 
+   static inline void adjust_pointer(oop* p)       { adjust_pointer(p, false); }
+   static inline void adjust_pointer(narrowOop* p) { adjust_pointer(p, false); }
++  static inline void adjust_pointer(TaggedUnion* p) { if (p->is_ref())  adjust_pointer(p->ref_addr()); }
+ 
+   // Reference Processing
+   static ReferenceProcessor* const ref_processor() { return _ref_processor; }
+diff --git a/src/share/vm/gc_implementation/shared/markSweep.hpp b/src/share/vm/gc_implementation/shared/markSweep.hpp
+--- a/src/share/vm/gc_implementation/shared/markSweep.hpp
++++ b/src/share/vm/gc_implementation/shared/markSweep.hpp
+@@ -214,6 +214,7 @@
+   static void adjust_root_pointer(oop* p)  { adjust_pointer(p, true); }
+   static void adjust_pointer(oop* p)       { adjust_pointer(p, false); }
+   static void adjust_pointer(narrowOop* p) { adjust_pointer(p, false); }
++  static void adjust_pointer(TaggedUnion* p) { if (p->is_ref())  adjust_pointer(p->ref_addr()); }
+ 
+ #ifdef VALIDATE_MARK_SWEEP
+   static void track_adjusted_pointer(void* p, bool isroot);
+diff --git a/src/share/vm/interpreter/oopMapCache.cpp b/src/share/vm/interpreter/oopMapCache.cpp
+--- a/src/share/vm/interpreter/oopMapCache.cpp
++++ b/src/share/vm/interpreter/oopMapCache.cpp
+@@ -155,7 +155,7 @@
+ 
+  public:
+   VerifyClosure(OopMapCacheEntry* entry)         { _entry = entry; _failed = false; }
+-  void offset_do(int offset)                     { if (!_entry->is_oop(offset)) _failed = true; }
++  void offset_do(int offset, BasicType type)     { if (!_entry->is_oop(offset)) _failed = true; }
+   bool failed() const                            { return _failed; }
+ };
+ 
+@@ -222,7 +222,8 @@
+       mask = 1;
+     }
+     // test for oop
+-    if ((value & (mask << oop_bit_number)) != 0) oop_closure->offset_do(i);
++    if ((value & (mask <<   oop_bit_number)) != 0) oop_closure->offset_do(i, T_OBJECT);
++    if ((value & (mask << union_bit_number)) != 0) oop_closure->offset_do(i, T_UNION);
+   }
+ }
+ 
+@@ -249,9 +250,10 @@
+       mask = 1;
+     }
+     // test for dead values  & oops, and for live values
+-         if ((value & (mask << dead_bit_number)) != 0)  dead_closure->offset_do(i); // call this for all dead values or oops
+-    else if ((value & (mask <<  oop_bit_number)) != 0)   oop_closure->offset_do(i); // call this for all live oops
+-    else                                               value_closure->offset_do(i); // call this for all live values
++         if ((value & (mask << dead_bit_number)) != 0)  dead_closure->offset_do(i, T_CONFLICT); // call this for all dead values or oops
++    else if ((value & (mask <<  oop_bit_number)) != 0)   oop_closure->offset_do(i, T_OBJECT); // call this for all live oops
++    else if ((value & (mask << union_bit_number)) != 0)  oop_closure->offset_do(i, T_UNION); // call this for all live unions
++    else                                               value_closure->offset_do(i, T_INT); // call this for all live values
+   }
+ }
+ 
+@@ -278,10 +280,10 @@
+   uintptr_t * _mask;                             // the bit mask to be filled
+   int         _size;                             // the mask size in bits
+ 
+-  void set_one(int i) {
++  void set_bit(int which_bit, int i) {
+     i *= InterpreterOopMap::bits_per_entry;
+     assert(0 <= i && i < _size, "offset out of bounds");
+-    _mask[i / BitsPerWord] |= (((uintptr_t) 1 << InterpreterOopMap::oop_bit_number) << (i % BitsPerWord));
++    _mask[i / BitsPerWord] |= (((uintptr_t) 1 << which_bit) << (i % BitsPerWord));
+   }
+ 
+  public:
+@@ -289,7 +291,8 @@
+   void pass_long()                               { /* ignore */ }
+   void pass_float()                              { /* ignore */ }
+   void pass_double()                             { /* ignore */ }
+-  void pass_object()                             { set_one(offset()); }
++  void pass_object()                             { set_bit(InterpreterOopMap::oop_bit_number, offset()); }
++  void pass_union()                              { set_bit(InterpreterOopMap::union_bit_number, offset()); }
+ 
+   MaskFillerForNative(methodHandle method, uintptr_t* mask, int size) : NativeSignatureIterator(method) {
+     _mask   = mask;
+@@ -400,6 +403,7 @@
+   set_mask_size(n_entries * bits_per_entry);
+   allocate_bit_mask();
+   set_expression_stack_size(stack_top);
++  assert(is_power_of_2(bits_per_entry), "otherwise check mask==0 below");
+ 
+   // compute bits
+   int word_index = 0;
+@@ -425,6 +429,10 @@
+       value |= (mask << oop_bit_number );
+     }
+ 
++    if (cell->is_union()) {
++      value |= (mask << union_bit_number );
++    }
++
+   #ifdef ENABLE_ZAP_DEAD_LOCALS
+     // set dead bit
+     if (!cell->is_live()) {
+diff --git a/src/share/vm/interpreter/oopMapCache.hpp b/src/share/vm/interpreter/oopMapCache.hpp
+--- a/src/share/vm/interpreter/oopMapCache.hpp
++++ b/src/share/vm/interpreter/oopMapCache.hpp
+@@ -57,7 +57,7 @@
+ 
+ class OffsetClosure  {
+  public:
+-  virtual void offset_do(int offset) = 0;
++  virtual void offset_do(int offset, BasicType type) = 0;
+ };
+ 
+ 
+@@ -74,11 +74,12 @@
+                                          // for testing bit_mask allocation
+ 
+ #ifdef ENABLE_ZAP_DEAD_LOCALS
++    bits_per_entry   = 4,
++    dead_bit_number  = 2,
++#else
+     bits_per_entry   = 2,
+-    dead_bit_number  = 1,
+-#else
+-    bits_per_entry   = 1,
+ #endif
++    union_bit_number = 1,
+     oop_bit_number   = 0
+   };
+ 
+@@ -148,6 +149,17 @@
+   void print();
+ 
+   bool is_oop  (int offset)                      { return (entry_at(offset) & (1 << oop_bit_number )) != 0; }
++  bool is_union(int offset)                      { return (entry_at(offset) & (1 << union_bit_number)) != 0; }
++
++  BasicType type_at(int offset) {
++    int e = entry_at(offset);
++    if ((e & (1 <<   oop_bit_number)) != 0)  return T_OBJECT;
++    if ((e & (1 << union_bit_number)) != 0)  return T_UNION;
++#ifdef ENABLE_ZAP_DEAD_LOCALS
++    if ((e & (1 <<  dead_bit_number)) != 0)  return T_CONFLICT;
++#endif
++    return T_INT;
++  }
+ 
+   int expression_stack_size()                    { return _expression_stack_size; }
+ 
+diff --git a/src/share/vm/interpreter/templateInterpreter.cpp b/src/share/vm/interpreter/templateInterpreter.cpp
+--- a/src/share/vm/interpreter/templateInterpreter.cpp
++++ b/src/share/vm/interpreter/templateInterpreter.cpp
+@@ -61,7 +61,7 @@
+ // Implementation of EntryPoint
+ 
+ EntryPoint::EntryPoint() {
+-  assert(number_of_states == 9, "check the code below");
++  assert(number_of_states == 10, "check the code below");
+   _entry[btos] = NULL;
+   _entry[ctos] = NULL;
+   _entry[stos] = NULL;
+@@ -70,12 +70,13 @@
+   _entry[ltos] = NULL;
+   _entry[ftos] = NULL;
+   _entry[dtos] = NULL;
++  _entry[utos] = NULL;
+   _entry[vtos] = NULL;
+ }
+ 
+ 
+-EntryPoint::EntryPoint(address bentry, address centry, address sentry, address aentry, address ientry, address lentry, address fentry, address dentry, address ventry) {
+-  assert(number_of_states == 9, "check the code below");
++EntryPoint::EntryPoint(address bentry, address centry, address sentry, address aentry, address ientry, address lentry, address fentry, address dentry, address uentry, address ventry) {
++  assert(number_of_states == 10, "check the code below");
+   _entry[btos] = bentry;
+   _entry[ctos] = centry;
+   _entry[stos] = sentry;
+@@ -84,12 +85,14 @@
+   _entry[ltos] = lentry;
+   _entry[ftos] = fentry;
+   _entry[dtos] = dentry;
++  _entry[utos] = uentry;
+   _entry[vtos] = ventry;
+ }
+ 
+ 
+ void EntryPoint::set_entry(TosState state, address entry) {
+   assert(0 <= state && state < number_of_states, "state out of bounds");
++  assert(int(sizeof(_entry) / sizeof(_entry[0])) == int(number_of_states), "");
+   _entry[state] = entry;
+ }
+ 
+@@ -134,6 +137,7 @@
+       _table[ltos][i],
+       _table[ftos][i],
+       _table[dtos][i],
++      _table[utos][i],
+       _table[vtos][i]
+     );
+ }
+@@ -141,7 +145,7 @@
+ 
+ void DispatchTable::set_entry(int i, EntryPoint& entry) {
+   assert(0 <= i && i < length, "index out of bounds");
+-  assert(number_of_states == 9, "check the code below");
++  assert(number_of_states == 10, "check the code below");
+   _table[btos][i] = entry.entry(btos);
+   _table[ctos][i] = entry.entry(ctos);
+   _table[stos][i] = entry.entry(stos);
+@@ -150,6 +154,7 @@
+   _table[ltos][i] = entry.entry(ltos);
+   _table[ftos][i] = entry.entry(ftos);
+   _table[dtos][i] = entry.entry(dtos);
++  _table[utos][i] = entry.entry(utos);
+   _table[vtos][i] = entry.entry(vtos);
+ }
+ 
+@@ -231,6 +236,7 @@
+         generate_trace_code(ltos),
+         generate_trace_code(ftos),
+         generate_trace_code(dtos),
++        generate_trace_code(utos),
+         generate_trace_code(vtos)
+       );
+   }
+@@ -248,6 +254,7 @@
+           generate_return_entry_for(ltos, i),
+           generate_return_entry_for(ftos, i),
+           generate_return_entry_for(dtos, i),
++          generate_return_entry_for(utos, i),
+           generate_return_entry_for(vtos, i)
+         );
+     }
+@@ -264,6 +271,7 @@
+         generate_earlyret_entry_for(ltos),
+         generate_earlyret_entry_for(ftos),
+         generate_earlyret_entry_for(dtos),
++        generate_earlyret_entry_for(utos),
+         generate_earlyret_entry_for(vtos)
+       );
+   }
+@@ -280,6 +288,7 @@
+           generate_deopt_entry_for(ltos, i),
+           generate_deopt_entry_for(ftos, i),
+           generate_deopt_entry_for(dtos, i),
++          generate_deopt_entry_for(utos, i),
+           generate_deopt_entry_for(vtos, i)
+         );
+     }
+@@ -299,7 +308,8 @@
+   }
+ 
+   for (int j = 0; j < number_of_states; j++) {
+-    const TosState states[] = {btos, ctos, stos, itos, ltos, ftos, dtos, atos, vtos};
++    const TosState states[] = {btos, ctos, stos, itos, ltos, ftos, dtos, atos, utos, vtos};
++    assert(states[j] == (TosState) j, "watch this brittle array");
+     int index = Interpreter::TosState_as_index(states[j]);
+     Interpreter::_return_3_addrs_by_index[index] = Interpreter::return_entry(states[j], 3);
+     Interpreter::_return_5_addrs_by_index[index] = Interpreter::return_entry(states[j], 5);
+@@ -316,6 +326,7 @@
+         generate_continuation_for(ltos),
+         generate_continuation_for(ftos),
+         generate_continuation_for(dtos),
++        generate_continuation_for(utos),
+         generate_continuation_for(vtos)
+       );
+   }
+@@ -331,6 +342,7 @@
+         generate_safept_entry_for(ltos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
+         generate_safept_entry_for(ftos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
+         generate_safept_entry_for(dtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
++        generate_safept_entry_for(utos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
+         generate_safept_entry_for(vtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint))
+       );
+   }
+@@ -418,7 +430,7 @@
+ 
+ void TemplateInterpreterGenerator::set_unimplemented(int i) {
+   address e = _unimplemented_bytecode;
+-  EntryPoint entry(e, e, e, e, e, e, e, e, e);
++  EntryPoint entry(e, e, e, e, e, e, e, e, e, e);
+   Interpreter::_normal_table.set_entry(i, entry);
+   Interpreter::_wentry_point[i] = _unimplemented_bytecode;
+ }
+@@ -437,13 +449,14 @@
+   address lep = _illegal_bytecode_sequence;
+   address fep = _illegal_bytecode_sequence;
+   address dep = _illegal_bytecode_sequence;
++  address uep = _illegal_bytecode_sequence;
+   address vep = _unimplemented_bytecode;
+   address wep = _unimplemented_bytecode;
+   // code for short & wide version of bytecode
+   if (Bytecodes::is_defined(code)) {
+     Template* t = TemplateTable::template_for(code);
+     assert(t->is_valid(), "just checking");
+-    set_short_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep);
++    set_short_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, uep, vep);
+   }
+   if (Bytecodes::wide_is_defined(code)) {
+     Template* t = TemplateTable::template_for_wide(code);
+@@ -451,7 +464,7 @@
+     set_wide_entry_point(t, wep);
+   }
+   // set entry points
+-  EntryPoint entry(bep, cep, sep, aep, iep, lep, fep, dep, vep);
++  EntryPoint entry(bep, cep, sep, aep, iep, lep, fep, dep, uep, vep);
+   Interpreter::_normal_table.set_entry(code, entry);
+   Interpreter::_wentry_point[code] = wep;
+ }
+@@ -464,7 +477,7 @@
+ }
+ 
+ 
+-void TemplateInterpreterGenerator::set_short_entry_points(Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep) {
++void TemplateInterpreterGenerator::set_short_entry_points(Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& uep, address& vep) {
+   assert(t->is_valid(), "template must exist");
+   switch (t->tos_in()) {
+     case btos:
+@@ -477,7 +490,8 @@
+     case ltos: vep = __ pc(); __ pop(ltos); lep = __ pc(); generate_and_dispatch(t); break;
+     case ftos: vep = __ pc(); __ pop(ftos); fep = __ pc(); generate_and_dispatch(t); break;
+     case dtos: vep = __ pc(); __ pop(dtos); dep = __ pc(); generate_and_dispatch(t); break;
+-    case vtos: set_vtos_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep);     break;
++    case utos: vep = __ pc(); __ pop(utos); uep = __ pc(); generate_and_dispatch(t); break;
++    case vtos: set_vtos_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, uep, vep);     break;
+     default  : ShouldNotReachHere();                                                 break;
+   }
+ }
+diff --git a/src/share/vm/interpreter/templateInterpreter.hpp b/src/share/vm/interpreter/templateInterpreter.hpp
+--- a/src/share/vm/interpreter/templateInterpreter.hpp
++++ b/src/share/vm/interpreter/templateInterpreter.hpp
+@@ -44,7 +44,7 @@
+  public:
+   // Construction
+   EntryPoint();
+-  EntryPoint(address bentry, address centry, address sentry, address aentry, address ientry, address lentry, address fentry, address dentry, address ventry);
++  EntryPoint(address bentry, address centry, address sentry, address aentry, address ientry, address lentry, address fentry, address dentry, address uentry, address ventry);
+ 
+   // Attributes
+   address entry(TosState state) const;                // return target address for a given tosca state
+diff --git a/src/share/vm/interpreter/templateInterpreterGenerator.hpp b/src/share/vm/interpreter/templateInterpreterGenerator.hpp
+--- a/src/share/vm/interpreter/templateInterpreterGenerator.hpp
++++ b/src/share/vm/interpreter/templateInterpreterGenerator.hpp
+@@ -64,8 +64,8 @@
+ 
+   // Instruction generation
+   void generate_and_dispatch (Template* t, TosState tos_out = ilgl);
+-  void set_vtos_entry_points (Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep);
+-  void set_short_entry_points(Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep);
++  void set_vtos_entry_points (Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& uep, address& vep);
++  void set_short_entry_points(Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& uep, address& vep);
+   void set_wide_entry_point  (Template* t, address& wep);
+ 
+   void set_entry_points(Bytecodes::Code code);
+diff --git a/src/share/vm/memory/genOopClosures.hpp b/src/share/vm/memory/genOopClosures.hpp
+--- a/src/share/vm/memory/genOopClosures.hpp
++++ b/src/share/vm/memory/genOopClosures.hpp
+@@ -100,6 +100,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p);
+   inline void do_oop_nv(narrowOop* p);
++  inline void do_oop_nv(TaggedUnion* p);
+   bool do_header() { return false; }
+   Prefetch::style prefetch_style() {
+     return Prefetch::do_write;
+@@ -123,6 +124,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p);
+   inline void do_oop_nv(narrowOop* p);
++  inline void do_oop_nv(TaggedUnion* p);
+   bool do_header() { return false; }
+   Prefetch::style prefetch_style() {
+     return Prefetch::do_write;
+@@ -151,6 +153,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p)       { FilteringClosure::do_oop_work(p); }
+   inline void do_oop_nv(narrowOop* p) { FilteringClosure::do_oop_work(p); }
++  inline void do_oop_nv(TaggedUnion* p) { if (p->is_ref())  do_oop_nv(p->ref_addr()); }
+   bool do_header() { return false; }
+ };
+ 
+@@ -169,6 +172,7 @@
+   virtual void do_oop(narrowOop* p);
+   inline void do_oop_nv(oop* p);
+   inline void do_oop_nv(narrowOop* p);
++  inline void do_oop_nv(TaggedUnion* p);
+ };
+ 
+ class VerifyOopClosure: public OopClosure {
+diff --git a/src/share/vm/memory/genOopClosures.inline.hpp b/src/share/vm/memory/genOopClosures.inline.hpp
+--- a/src/share/vm/memory/genOopClosures.inline.hpp
++++ b/src/share/vm/memory/genOopClosures.inline.hpp
+@@ -94,6 +94,7 @@
+ 
+ inline void ScanClosure::do_oop_nv(oop* p)       { ScanClosure::do_oop_work(p); }
+ inline void ScanClosure::do_oop_nv(narrowOop* p) { ScanClosure::do_oop_work(p); }
++inline void ScanClosure::do_oop_nv(TaggedUnion* p) { if (p->is_ref()) do_oop_nv(p->ref_addr()); }
+ 
+ // NOTE! Any changes made here should also be made
+ // in ScanClosure::do_oop_work()
+@@ -117,6 +118,7 @@
+ 
+ inline void FastScanClosure::do_oop_nv(oop* p)       { FastScanClosure::do_oop_work(p); }
+ inline void FastScanClosure::do_oop_nv(narrowOop* p) { FastScanClosure::do_oop_work(p); }
++inline void FastScanClosure::do_oop_nv(TaggedUnion* p) { if (p->is_ref()) do_oop_nv(p->ref_addr()); }
+ 
+ // Note similarity to ScanClosure; the difference is that
+ // the barrier set is taken care of outside this closure.
+@@ -134,5 +136,6 @@
+ 
+ inline void ScanWeakRefClosure::do_oop_nv(oop* p)       { ScanWeakRefClosure::do_oop_work(p); }
+ inline void ScanWeakRefClosure::do_oop_nv(narrowOop* p) { ScanWeakRefClosure::do_oop_work(p); }
++inline void ScanWeakRefClosure::do_oop_nv(TaggedUnion* p) { if (p->is_ref()) do_oop_nv(p->ref_addr()); }
+ 
+ #endif // SHARE_VM_MEMORY_GENOOPCLOSURES_INLINE_HPP
+diff --git a/src/share/vm/memory/iterator.hpp b/src/share/vm/memory/iterator.hpp
+--- a/src/share/vm/memory/iterator.hpp
++++ b/src/share/vm/memory/iterator.hpp
+@@ -28,6 +28,7 @@
+ #include "memory/allocation.hpp"
+ #include "memory/memRegion.hpp"
+ #include "runtime/prefetch.hpp"
++#include "utilities/taggedUnion.hpp"
+ #include "utilities/top.hpp"
+ 
+ // The following classes are C++ `closures` for iterating over objects, roots and spaces
+@@ -62,6 +63,10 @@
+   virtual void do_oop_v(oop* o) { do_oop(o); }
+   virtual void do_oop(narrowOop* o) = 0;
+   virtual void do_oop_v(narrowOop* o) { do_oop(o); }
++  virtual void do_oop(TaggedUnion* o) {
++    if (o->is_ref())  do_oop(o->ref_addr());
++  }
++  virtual void do_oop_v(TaggedUnion* o) { do_oop(o); }
+ 
+   // In support of post-processing of weak links of KlassKlass objects;
+   // see KlassKlass::oop_oop_iterate().
+diff --git a/src/share/vm/memory/oopFactory.cpp b/src/share/vm/memory/oopFactory.cpp
+--- a/src/share/vm/memory/oopFactory.cpp
++++ b/src/share/vm/memory/oopFactory.cpp
+@@ -120,9 +120,10 @@
+ klassOop oopFactory::new_instanceKlass(Symbol* name, int vtable_len, int itable_len,
+                                        int static_field_size,
+                                        unsigned int nonstatic_oop_map_count,
++                                       unsigned int nonstatic_union_map_count,
+                                        ReferenceType rt, TRAPS) {
+   instanceKlassKlass* ikk = instanceKlassKlass::cast(Universe::instanceKlassKlassObj());
+-  return ikk->allocate_instance_klass(name, vtable_len, itable_len, static_field_size, nonstatic_oop_map_count, rt, CHECK_NULL);
++  return ikk->allocate_instance_klass(name, vtable_len, itable_len, static_field_size, nonstatic_oop_map_count, nonstatic_union_map_count, rt, CHECK_NULL);
+ }
+ 
+ 
+diff --git a/src/share/vm/memory/oopFactory.hpp b/src/share/vm/memory/oopFactory.hpp
+--- a/src/share/vm/memory/oopFactory.hpp
++++ b/src/share/vm/memory/oopFactory.hpp
+@@ -76,6 +76,7 @@
+                                            int vtable_len, int itable_len,
+                                            int static_field_size,
+                                            unsigned int nonstatic_oop_map_count,
++                                           unsigned int nonstatic_union_map_count,
+                                            ReferenceType rt, TRAPS);
+ 
+   // Methods
+diff --git a/src/share/vm/memory/universe.cpp b/src/share/vm/memory/universe.cpp
+--- a/src/share/vm/memory/universe.cpp
++++ b/src/share/vm/memory/universe.cpp
+@@ -109,6 +109,7 @@
+ klassOop Universe::_longArrayKlassObj                 = NULL;
+ klassOop Universe::_singleArrayKlassObj               = NULL;
+ klassOop Universe::_doubleArrayKlassObj               = NULL;
++klassOop Universe::_unionArrayKlassObj                = NULL;
+ klassOop Universe::_typeArrayKlassObjs[T_VOID+1]      = { NULL /*, NULL...*/ };
+ klassOop Universe::_objectArrayKlassObj               = NULL;
+ klassOop Universe::_methodKlassObj                    = NULL;
+@@ -132,6 +133,7 @@
+ oop Universe::_long_mirror                            = NULL;
+ oop Universe::_short_mirror                           = NULL;
+ oop Universe::_void_mirror                            = NULL;
++oop Universe::_union_mirror                           = NULL;
+ oop Universe::_mirrors[T_VOID+1]                      = { NULL /*, NULL...*/ };
+ oop Universe::_main_thread_group                      = NULL;
+ oop Universe::_system_thread_group                    = NULL;
+@@ -179,6 +181,8 @@
+ 
+ 
+ void Universe::basic_type_classes_do(void f(klassOop)) {
++  if (EnableTaggedUnions)
++    f(unionArrayKlassObj());
+   f(boolArrayKlassObj());
+   f(byteArrayKlassObj());
+   f(charArrayKlassObj());
+@@ -214,10 +218,13 @@
+   f->do_oop((oop*) &_long_mirror);
+   f->do_oop((oop*) &_short_mirror);
+   f->do_oop((oop*) &_void_mirror);
++  if (EnableTaggedUnions)
++    f->do_oop((oop*) &_union_mirror);
+ 
+   // It's important to iterate over these guys even if they are null,
+   // since that's how shared heaps are restored.
+-  for (int i = T_BOOLEAN; i < T_VOID+1; i++) {
++  for (int i = T_MIN_VALUE; i < T_VOID+1; i++) {
++    if (!EnableTaggedUnions && i == T_UNION)  continue;
+     f->do_oop((oop*) &_mirrors[i]);
+   }
+   assert(_mirrors[0] == NULL && _mirrors[T_BOOLEAN - 1] == NULL, "checking");
+@@ -231,6 +238,8 @@
+   f->do_oop((oop*)&_longArrayKlassObj);
+   f->do_oop((oop*)&_singleArrayKlassObj);
+   f->do_oop((oop*)&_doubleArrayKlassObj);
++  if (EnableTaggedUnions)
++    f->do_oop((oop*)&_unionArrayKlassObj);
+   f->do_oop((oop*)&_objectArrayKlassObj);
+   {
+     for (int i = 0; i < T_VOID+1; i++) {
+@@ -318,6 +327,8 @@
+         _shortArrayKlassObj     = typeArrayKlass::create_klass(T_SHORT,   sizeof(jshort),   CHECK);
+         _intArrayKlassObj       = typeArrayKlass::create_klass(T_INT,     sizeof(jint),     CHECK);
+         _longArrayKlassObj      = typeArrayKlass::create_klass(T_LONG,    sizeof(jlong),    CHECK);
++        if (EnableTaggedUnions)  // FIXME: should be like unionArrayKlass::create_klass
++          _unionArrayKlassObj   = objArrayKlassKlass::cast(objArrayKlassKlassObj())->allocate_system_objArray_klass(CHECK);
+ 
+         _typeArrayKlassObjs[T_BOOLEAN] = _boolArrayKlassObj;
+         _typeArrayKlassObjs[T_CHAR]    = _charArrayKlassObj;
+@@ -327,6 +338,7 @@
+         _typeArrayKlassObjs[T_SHORT]   = _shortArrayKlassObj;
+         _typeArrayKlassObjs[T_INT]     = _intArrayKlassObj;
+         _typeArrayKlassObjs[T_LONG]    = _longArrayKlassObj;
++        _typeArrayKlassObjs[T_UNION]   = _unionArrayKlassObj;
+ 
+         _methodKlassObj             = methodKlass::create_klass(CHECK);
+         _constMethodKlassObj        = constMethodKlass::create_klass(CHECK);
+@@ -375,6 +387,8 @@
+       assert(Klass::cast(shortArrayKlassObj()    )->super() == ok, "u3");
+       assert(Klass::cast(intArrayKlassObj()      )->super() == ok, "u3");
+       assert(Klass::cast(longArrayKlassObj()     )->super() == ok, "u3");
++      if (EnableTaggedUnions)
++        assert(Klass::cast(unionArrayKlassObj()  )->super() == ok, "u3");
+       assert(Klass::cast(constantPoolKlassObj()  )->super() == ok, "u3");
+       assert(Klass::cast(systemObjArrayKlassObj())->super() == ok, "u3");
+     } else {
+@@ -395,6 +409,8 @@
+       Klass::cast(shortArrayKlassObj()    )->initialize_supers(ok, CHECK);
+       Klass::cast(intArrayKlassObj()      )->initialize_supers(ok, CHECK);
+       Klass::cast(longArrayKlassObj()     )->initialize_supers(ok, CHECK);
++      if (EnableTaggedUnions)
++        Klass::cast(unionArrayKlassObj()  )->initialize_supers(ok, CHECK);
+       Klass::cast(constantPoolKlassObj()  )->initialize_supers(ok, CHECK);
+       Klass::cast(systemObjArrayKlassObj())->initialize_supers(ok, CHECK);
+       Klass::cast(boolArrayKlassObj()     )->set_super(ok);
+@@ -405,6 +421,8 @@
+       Klass::cast(shortArrayKlassObj()    )->set_super(ok);
+       Klass::cast(intArrayKlassObj()      )->set_super(ok);
+       Klass::cast(longArrayKlassObj()     )->set_super(ok);
++      if (EnableTaggedUnions)
++        Klass::cast(unionArrayKlassObj()  )->set_super(ok);
+       Klass::cast(constantPoolKlassObj()  )->set_super(ok);
+       Klass::cast(systemObjArrayKlassObj())->set_super(ok);
+     }
+@@ -417,6 +435,8 @@
+     Klass::cast(shortArrayKlassObj()    )->append_to_sibling_list();
+     Klass::cast(intArrayKlassObj()      )->append_to_sibling_list();
+     Klass::cast(longArrayKlassObj()     )->append_to_sibling_list();
++    if (EnableTaggedUnions)
++      Klass::cast(unionArrayKlassObj()  )->append_to_sibling_list();
+     Klass::cast(constantPoolKlassObj()  )->append_to_sibling_list();
+     Klass::cast(systemObjArrayKlassObj())->append_to_sibling_list();
+   } // end of core bootstrapping
+@@ -589,6 +609,9 @@
+       java_lang_Class::create_basic_type_mirror("short",  T_SHORT,   CHECK);
+     _void_mirror    =
+       java_lang_Class::create_basic_type_mirror("void",   T_VOID, CHECK);
++    if (EnableTaggedUnions)
++      _union_mirror =
++      java_lang_Class::create_basic_type_mirror("union",  T_UNION, CHECK);
+ 
+     _mirrors[T_INT]     = _int_mirror;
+     _mirrors[T_FLOAT]   = _float_mirror;
+@@ -599,6 +622,7 @@
+     _mirrors[T_LONG]    = _long_mirror;
+     _mirrors[T_SHORT]   = _short_mirror;
+     _mirrors[T_VOID]    = _void_mirror;
++    _mirrors[T_UNION]   = _union_mirror;
+     //_mirrors[T_OBJECT]  = instanceKlass::cast(_object_klass)->java_mirror();
+     //_mirrors[T_ARRAY]   = instanceKlass::cast(_object_klass)->java_mirror();
+   }
+diff --git a/src/share/vm/memory/universe.hpp b/src/share/vm/memory/universe.hpp
+--- a/src/share/vm/memory/universe.hpp
++++ b/src/share/vm/memory/universe.hpp
+@@ -143,6 +143,7 @@
+   static klassOop _longArrayKlassObj;
+   static klassOop _singleArrayKlassObj;
+   static klassOop _doubleArrayKlassObj;
++  static klassOop _unionArrayKlassObj;
+   static klassOop _typeArrayKlassObjs[T_VOID+1];
+ 
+   static klassOop _objectArrayKlassObj;
+@@ -172,6 +173,7 @@
+   static oop _long_mirror;
+   static oop _short_mirror;
+   static oop _void_mirror;
++  static oop _union_mirror;
+ 
+   static oop          _main_thread_group;             // Reference to the main thread group object
+   static oop          _system_thread_group;           // Reference to the system thread group object
+@@ -267,6 +269,7 @@
+   static klassOop longArrayKlassObj()                 { return _longArrayKlassObj;   }
+   static klassOop singleArrayKlassObj()               { return _singleArrayKlassObj; }
+   static klassOop doubleArrayKlassObj()               { return _doubleArrayKlassObj; }
++  static klassOop unionArrayKlassObj()                { return _unionArrayKlassObj;  }
+ 
+   static klassOop objectArrayKlassObj() {
+     return _objectArrayKlassObj;
+@@ -301,6 +304,7 @@
+   static oop long_mirror()                  { return check_mirror(_long_mirror); }
+   static oop short_mirror()                 { return check_mirror(_short_mirror); }
+   static oop void_mirror()                  { return check_mirror(_void_mirror); }
++  static oop union_mirror()                 { return check_mirror(_union_mirror); }
+ 
+   // table of same
+   static oop _mirrors[T_VOID+1];
+@@ -348,6 +352,7 @@
+   static klassOop* longArrayKlassObj_addr()           { return &_longArrayKlassObj;   }
+   static klassOop* singleArrayKlassObj_addr()         { return &_singleArrayKlassObj; }
+   static klassOop* doubleArrayKlassObj_addr()         { return &_doubleArrayKlassObj; }
++  static klassOop* unionArrayKlassObj_addr()          { return &_unionArrayKlassObj;  }
+   static klassOop* systemObjArrayKlassObj_addr()      { return &_systemObjArrayKlassObj; }
+ 
+   // The particular choice of collected heap.
+diff --git a/src/share/vm/memory/universe.inline.hpp b/src/share/vm/memory/universe.inline.hpp
+--- a/src/share/vm/memory/universe.inline.hpp
++++ b/src/share/vm/memory/universe.inline.hpp
+@@ -32,13 +32,13 @@
+ // strongly.
+ 
+ inline bool Universe::element_type_should_be_aligned(BasicType type) {
+-  return type == T_DOUBLE || type == T_LONG;
++  return type == T_DOUBLE || type == T_LONG || type == T_UNION;
+ }
+ 
+ // Check whether an object field (static/non-static) of the given type must be aligned 0 mod 8.
+ 
+ inline bool Universe::field_type_should_be_aligned(BasicType type) {
+-  return type == T_DOUBLE || type == T_LONG;
++  return type == T_DOUBLE || type == T_LONG || type == T_UNION;
+ }
+ 
+ #endif // SHARE_VM_MEMORY_UNIVERSE_INLINE_HPP
+diff --git a/src/share/vm/oops/generateOopMap.cpp b/src/share/vm/oops/generateOopMap.cpp
+--- a/src/share/vm/oops/generateOopMap.cpp
++++ b/src/share/vm/oops/generateOopMap.cpp
+@@ -100,6 +100,7 @@
+ // Specialization of SignatureIterator - compute the effects of a call
+ //
+ class ComputeCallStack : public SignatureIterator {
++protected:
+   CellTypeState *_effect;
+   int _idx;
+ 
+@@ -114,13 +115,17 @@
+   virtual void do_short ()              { set(CellTypeState::value); };
+   virtual void do_int   ()              { set(CellTypeState::value); };
+   virtual void do_void  ()              { set(CellTypeState::bottom);};
+-  virtual void do_object(int begin, int end)  { set(CellTypeState::ref); };
+-  virtual void do_array (int begin, int end)  { set(CellTypeState::ref); };
++  virtual void do_object(int begin, int end) { set(make_ref()); };
++  virtual void do_array (int begin, int end) { set(make_ref()); };
+ 
+   void do_double()                      { set(CellTypeState::value);
+                                           set(CellTypeState::value); }
+   void do_long  ()                      { set(CellTypeState::value);
+-                                           set(CellTypeState::value); }
++                                          set(CellTypeState::value); }
++  void do_union ()                      { set(CellTypeState::tagu);  // push union cell first
++                                          set(CellTypeState::value); }
++
++  virtual CellTypeState make_ref()      {  return CellTypeState::ref; }
+ 
+ public:
+   ComputeCallStack(Symbol* signature) : SignatureIterator(signature) {};
+@@ -131,7 +136,7 @@
+     _effect = effect;
+ 
+     if (!is_static)
+-      effect[_idx++] = CellTypeState::ref;
++      set(make_ref());
+ 
+     iterate_parameters();
+ 
+@@ -151,55 +156,13 @@
+ //=========================================================================================
+ // ComputeEntryStack
+ //
+-// Specialization of SignatureIterator - in order to set up first stack frame
++// Specialization of ComputeCallStack - in order to set up first stack frame
+ //
+-class ComputeEntryStack : public SignatureIterator {
+-  CellTypeState *_effect;
+-  int _idx;
+-
+-  void setup();
+-  void set(CellTypeState state)         { _effect[_idx++] = state; }
+-  int  length()                         { return _idx; };
+-
+-  virtual void do_bool  ()              { set(CellTypeState::value); };
+-  virtual void do_char  ()              { set(CellTypeState::value); };
+-  virtual void do_float ()              { set(CellTypeState::value); };
+-  virtual void do_byte  ()              { set(CellTypeState::value); };
+-  virtual void do_short ()              { set(CellTypeState::value); };
+-  virtual void do_int   ()              { set(CellTypeState::value); };
+-  virtual void do_void  ()              { set(CellTypeState::bottom);};
+-  virtual void do_object(int begin, int end)  { set(CellTypeState::make_slot_ref(_idx)); }
+-  virtual void do_array (int begin, int end)  { set(CellTypeState::make_slot_ref(_idx)); }
+-
+-  void do_double()                      { set(CellTypeState::value);
+-                                          set(CellTypeState::value); }
+-  void do_long  ()                      { set(CellTypeState::value);
+-                                          set(CellTypeState::value); }
++class ComputeEntryStack : public ComputeCallStack {
++  virtual CellTypeState make_ref()      {  return CellTypeState::make_slot_ref(_idx); }
+ 
+ public:
+-  ComputeEntryStack(Symbol* signature) : SignatureIterator(signature) {};
+-
+-  // Compute methods
+-  int compute_for_parameters(bool is_static, CellTypeState *effect) {
+-    _idx    = 0;
+-    _effect = effect;
+-
+-    if (!is_static)
+-      effect[_idx++] = CellTypeState::make_slot_ref(0);
+-
+-    iterate_parameters();
+-
+-    return length();
+-  };
+-
+-  int compute_for_returntype(CellTypeState *effect) {
+-    _idx    = 0;
+-    _effect = effect;
+-    iterate_returntype();
+-    set(CellTypeState::bottom);  // Always terminate with a bottom state, so ppush works
+-
+-    return length();
+-  }
++  ComputeEntryStack(Symbol* signature) : ComputeCallStack(signature) {};
+ };
+ 
+ //=====================================================================================
+@@ -282,7 +245,8 @@
+ CellTypeState CellTypeState::bottom      = CellTypeState::make_bottom();
+ CellTypeState CellTypeState::uninit      = CellTypeState::make_any(uninit_value);
+ CellTypeState CellTypeState::ref         = CellTypeState::make_any(ref_conflict);
+-CellTypeState CellTypeState::value       = CellTypeState::make_any(val_value);
++CellTypeState CellTypeState::value       = CellTypeState::make_any(val_conflict);
++CellTypeState CellTypeState::tagu        = CellTypeState::make_any(tagu_conflict);
+ CellTypeState CellTypeState::refUninit   = CellTypeState::make_any(ref_conflict | uninit_value);
+ CellTypeState CellTypeState::top         = CellTypeState::make_top();
+ CellTypeState CellTypeState::addr        = CellTypeState::make_any(addr_conflict);
+@@ -296,6 +260,8 @@
+ static CellTypeState   rrCTS[3] = { CellTypeState::ref,   CellTypeState::ref,   CellTypeState::bottom };
+ static CellTypeState   vrCTS[3] = { CellTypeState::value, CellTypeState::ref,   CellTypeState::bottom };
+ static CellTypeState   vvCTS[3] = { CellTypeState::value, CellTypeState::value, CellTypeState::bottom };
++static CellTypeState   uvCTS[3] = { CellTypeState::tagu,  CellTypeState::value, CellTypeState::bottom };
++static CellTypeState   vuCTS[3] = { CellTypeState::value, CellTypeState::tagu,  CellTypeState::bottom };
+ static CellTypeState  rvrCTS[4] = { CellTypeState::ref,   CellTypeState::value, CellTypeState::ref,   CellTypeState::bottom };
+ static CellTypeState  vvrCTS[4] = { CellTypeState::value, CellTypeState::value, CellTypeState::ref,   CellTypeState::bottom };
+ static CellTypeState  vvvCTS[4] = { CellTypeState::value, CellTypeState::value, CellTypeState::value, CellTypeState::bottom };
+@@ -310,6 +276,8 @@
+       return 'r';
+   } else if (can_be_value())
+     return 'v';
++  else if (can_be_union())
++    return 'u';
+   else if (can_be_address())
+     return 'p';
+   else if (can_be_uninit())
+@@ -1455,7 +1423,7 @@
+     case Bytecodes::_bastore:
+     case Bytecodes::_castore:
+     case Bytecodes::_sastore:           ppop(vvrCTS);               break;
+-    case Bytecodes::_lastore:
++    case Bytecodes::_lastore:           ppop(vvvrCTS);              break;
+     case Bytecodes::_dastore:           ppop(vvvrCTS);              break;
+     case Bytecodes::_aastore:           ppop(rvrCTS);               break;
+ 
+@@ -1899,8 +1867,10 @@
+   assert(signature->utf8_length() > 0, "field signatures cannot have zero length");
+   // The signature is UFT8 encoded, but the first char is always ASCII for signatures.
+   char sigch = (char)*(signature->base());
++  debug_only(int sigsz = type2size[char2type(sigch)]);
+   CellTypeState temp[4];
+-  CellTypeState *eff  = sigchar_to_effect(sigch, bci, temp);
++  CellTypeState *eff  = sigchar_to_effect(sigch, bci, is_get, temp);
++  assert(eff[sigsz].is_bottom(), "correct size");
+ 
+   CellTypeState in[4];
+   CellTypeState *out;
+@@ -1956,7 +1926,7 @@
+ }
+ 
+ // This is used to parse the signature for fields, since they are very simple...
+-CellTypeState *GenerateOopMap::sigchar_to_effect(char sigch, int bci, CellTypeState *out) {
++CellTypeState *GenerateOopMap::sigchar_to_effect(char sigch, int bci, bool for_push, CellTypeState *out) {
+   // Object and array
+   if (sigch=='L' || sigch=='[') {
+     out[0] = CellTypeState::make_line_ref(bci);
+@@ -1965,6 +1935,7 @@
+   }
+   if (sigch == 'J' || sigch == 'D' ) return vvCTS;  // Long and Double
+   if (sigch == 'V' ) return epsilonCTS;             // Void
++  if (sigch == 'U' ) return (for_push ? uvCTS : vuCTS);
+   return vCTS;                                      // Otherwise
+ }
+ 
+@@ -2077,6 +2048,8 @@
+     }
+   }
+ 
++  debug_only(CellTypeState::verify_bit_assignments());
++
+   // if no code - do nothing
+   // compiler needs info
+   if (method()->code_size() == 0 || _max_locals + method()->max_stack() == 0) {
+diff --git a/src/share/vm/oops/generateOopMap.hpp b/src/share/vm/oops/generateOopMap.hpp
+--- a/src/share/vm/oops/generateOopMap.hpp
++++ b/src/share/vm/oops/generateOopMap.hpp
+@@ -92,7 +92,7 @@
+   unsigned int _state;
+ 
+   // Masks for separating the BITS and INFO portions of a CellTypeState
+-  enum { info_mask            = right_n_bits(28),
++  enum { info_mask            = right_n_bits(27),
+          bits_mask            = (int)(~info_mask) };
+ 
+   // These constant are used for manipulating the BITS portion of a
+@@ -101,21 +101,23 @@
+          ref_bit              = nth_bit(30),
+          val_bit              = nth_bit(29),
+          addr_bit             = nth_bit(28),
++         tagu_bit             = nth_bit(27),  // TaggedUnion
++         low_type_bit         = tagu_bit,
+          live_bits_mask       = (int)(bits_mask & ~uninit_bit) };
+ 
+   // These constants are used for manipulating the INFO portion of a
+   // CellTypeState
+-  enum { top_info_bit         = nth_bit(27),
+-         not_bottom_info_bit  = nth_bit(26),
+-         info_data_mask       = right_n_bits(26),
++  enum { top_info_bit         = (low_type_bit >> 1),
++         not_bottom_info_bit  = (top_info_bit >> 1),
++         info_data_mask       = (not_bottom_info_bit - 1),
+          info_conflict        = info_mask };
+ 
+   // Within the INFO data, these values are used to distinguish different
+   // kinds of references.
+-  enum { ref_not_lock_bit     = nth_bit(25),  // 0 if this reference is locked as a monitor
+-         ref_slot_bit         = nth_bit(24),  // 1 if this reference is a "slot" reference,
+-                                              // 0 if it is a "line" reference.
+-         ref_data_mask        = right_n_bits(24) };
++  enum { ref_not_lock_bit     = (not_bottom_info_bit >> 1),  // 0 if this reference is locked as a monitor
++         ref_slot_bit         = (ref_not_lock_bit    >> 1),  // 1 if this reference is a "slot" reference,
++                                                             // 0 if it is a "line" reference.
++         ref_data_mask        = (ref_slot_bit - 1) };
+ 
+ 
+   // These values are used to initialize commonly used CellTypeState
+@@ -124,7 +126,8 @@
+          uninit_value         = (int)(uninit_bit | info_conflict),
+          ref_value            = ref_bit,
+          ref_conflict         = ref_bit | info_conflict,
+-         val_value            = val_bit | info_conflict,
++         val_conflict         = val_bit | info_conflict,
++         tagu_conflict        = tagu_bit | info_conflict,
+          addr_value           = addr_bit,
+          addr_conflict        = addr_bit | info_conflict };
+ 
+@@ -181,7 +184,7 @@
+   bool is_live() const                  { return ((_state & live_bits_mask) != 0); }
+   bool is_valid_state() const {
+     // Uninitialized and value cells must contain no data in their info field:
+-    if ((can_be_uninit() || can_be_value()) && !is_info_top()) {
++    if ((can_be_uninit() || can_be_value() || can_be_union()) && !is_info_top()) {
+       return false;
+     }
+     // The top bit is only set when all info bits are set:
+@@ -198,11 +201,13 @@
+   bool is_address() const               { return ((_state & bits_mask) == addr_bit); }
+   bool is_reference() const             { return ((_state & bits_mask) == ref_bit); }
+   bool is_value() const                 { return ((_state & bits_mask) == val_bit); }
++  bool is_union() const                 { return ((_state & bits_mask) == tagu_bit); }
+   bool is_uninit() const                { return ((_state & bits_mask) == (uint)uninit_bit); }
+ 
+   bool can_be_address() const           { return ((_state & addr_bit) != 0); }
+   bool can_be_reference() const         { return ((_state & ref_bit) != 0); }
+   bool can_be_value() const             { return ((_state & val_bit) != 0); }
++  bool can_be_union() const             { return ((_state & tagu_bit) != 0); }
+   bool can_be_uninit() const            { return ((_state & uninit_bit) != 0); }
+ 
+   bool is_info_bottom() const           { return ((_state & not_bottom_info_bit) == 0); }
+@@ -234,13 +239,23 @@
+   // Debugging output
+   void print(outputStream *os);
+ 
++  // Verification
++#ifdef ASSERT
++  static void verify_bit_assignments() {
++    assert((info_mask & bits_mask) == 0, "");
++    assert((info_mask | bits_mask) == -1, "");
++    assert((low_type_bit & info_mask) == 0, "");
++    assert((top_info_bit & info_mask) != 0, "");
++  }
++#endif
++
+   // Default values of common values
+   static CellTypeState bottom;
+   static CellTypeState uninit;
+   static CellTypeState ref;
+   static CellTypeState value;
++  static CellTypeState tagu;
+   static CellTypeState refUninit;
+-  static CellTypeState varUninit;
+   static CellTypeState top;
+   static CellTypeState addr;
+ };
+@@ -409,7 +424,7 @@
+   void  do_monitorexit                      (int bci);
+   void  do_return_monitor_check             ();
+   void  do_checkcast                        ();
+-  CellTypeState *sigchar_to_effect          (char sigch, int bci, CellTypeState *out);
++  CellTypeState *sigchar_to_effect          (char sigch, int bci, bool for_push, CellTypeState *out);
+   int copy_cts                              (CellTypeState *dst, CellTypeState *src);
+ 
+   // Error handling
+diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp
+--- a/src/share/vm/oops/instanceKlass.cpp
++++ b/src/share/vm/oops/instanceKlass.cpp
+@@ -1526,9 +1526,10 @@
+ //
+ // Macros that iterate over areas of oops which are specialized on type of
+ // oop pointer either narrow or wide, depending on UseCompressedOops
++// They also iterate over TaggedUnion cells, if any are present.
+ //
+ // Parameters are:
+-//   T         - type of oop to point to (either oop or narrowOop)
++//   T         - type of oop pointed to (oop, narrowOop, TaggedUnion)
+ //   start_p   - starting pointer for region to iterate over
+ //   count     - number of oops or narrowOops to iterate over
+ //   do_oop    - action to perform on each oop (it's arbitrary C code which
+@@ -1536,49 +1537,80 @@
+ //               it a template function)
+ //   assert_fn - assert function which is template function because performance
+ //               doesn't matter when enabled.
++// Optional parameters:
++//   low, high - bounds used additionally to filter p_, (as low <= p_ < high)
++//   P         - type of oop to process (either oop or narrowOop), defaults to T
++//   p_filter  - expression which filters p_ for validity, defaults to true
++//   p_expr    - expression which converts p_ from T to P, defaults to p_
+ #define InstanceKlass_SPECIALIZED_OOP_ITERATE( \
+   T, start_p, count, do_oop,                \
+   assert_fn)                                \
++  InstanceKlass_SPECIALIZED2_OOP_ITERATE(   \
++    T, start_p, count, T, true, p_, do_oop, assert_fn)
++#define InstanceKlass_SPECIALIZED2_OOP_ITERATE( \
++  T, start_p, count, P, p_filter, p_expr, do_oop, \
++  assert_fn)                                \
+ {                                           \
+-  T* p         = (T*)(start_p);             \
+-  T* const end = p + (count);               \
+-  while (p < end) {                         \
+-    (assert_fn)(p);                         \
+-    do_oop;                                 \
+-    ++p;                                    \
++  T* p_        = (T*)(start_p);             \
++  T* const end = p_ + (count);              \
++  while (p_ < end) {                        \
++    if (p_filter) {                         \
++      P* const p = (p_expr);                \
++      (assert_fn)(p);                       \
++      do_oop;                               \
++    }                                       \
++    ++p_;                                   \
+   }                                         \
+ }
+ 
+ #define InstanceKlass_SPECIALIZED_OOP_REVERSE_ITERATE( \
+   T, start_p, count, do_oop,                \
+   assert_fn)                                \
++  InstanceKlass_SPECIALIZED2_OOP_REVERSE_ITERATE(   \
++    T, start_p, count, T, true, p_, do_oop, assert_fn)
++#define InstanceKlass_SPECIALIZED2_OOP_REVERSE_ITERATE( \
++  T, start_p, count, P, p_filter, p_expr, do_oop, \
++  assert_fn)                                \
+ {                                           \
+   T* const start = (T*)(start_p);           \
+-  T*       p     = start + (count);         \
+-  while (start < p) {                       \
+-    --p;                                    \
+-    (assert_fn)(p);                         \
+-    do_oop;                                 \
++  T*       p_    = start + (count);         \
++  while (start < p_) {                      \
++    --p_;                                   \
++    if (p_filter) {                         \
++      P* const p = (p_expr);                \
++      (assert_fn)(p);                       \
++      do_oop;                               \
++    }                                       \
+   }                                         \
+ }
+ 
+ #define InstanceKlass_SPECIALIZED_BOUNDED_OOP_ITERATE( \
+   T, start_p, count, low, high,             \
+   do_oop, assert_fn)                        \
++  InstanceKlass_SPECIALIZED2_BOUNDED_OOP_ITERATE(   \
++    T, start_p, count, low, high,           \
++    T, true, p_, do_oop, assert_fn)
++#define InstanceKlass_SPECIALIZED2_BOUNDED_OOP_ITERATE( \
++  T, start_p, count, low, high,             \
++  P, p_filter, p_expr,                      \
++  do_oop, assert_fn)                        \
+ {                                           \
+   T* const l = (T*)(low);                   \
+   T* const h = (T*)(high);                  \
+   assert(mask_bits((intptr_t)l, sizeof(T)-1) == 0 && \
+          mask_bits((intptr_t)h, sizeof(T)-1) == 0,   \
+          "bounded region must be properly aligned"); \
+-  T* p       = (T*)(start_p);               \
+-  T* end     = p + (count);                 \
+-  if (p < l) p = l;                         \
++  T* p_      = (T*)(start_p);               \
++  T* end     = p_ + (count);                \
++  if (p_ < l) p_ = l;                       \
+   if (end > h) end = h;                     \
+-  while (p < end) {                         \
+-    (assert_fn)(p);                         \
+-    do_oop;                                 \
+-    ++p;                                    \
++  while (p_ < end) {                        \
++    if (p_filter) {                         \
++      P* const p = (p_expr);                \
++      (assert_fn)(p);                       \
++      do_oop;                               \
++    }                                       \
++    ++p_;                                   \
+   }                                         \
+ }
+ 
+@@ -1607,6 +1639,19 @@
+       ++map;                                                             \
+     }                                                                    \
+   }                                                                      \
++  const int umap_count  = nonstatic_union_map_count();                   \
++  if (umap_count > 0) {                                                  \
++    debug_only(const char* const map = "do not use me";)                 \
++    OopMapBlock* umap           = start_of_nonstatic_union_maps();       \
++    OopMapBlock* const end_umap = umap + umap_count;                     \
++    while (umap < end_umap) {                                            \
++      InstanceKlass_SPECIALIZED2_OOP_ITERATE(TaggedUnion,                \
++        obj->obj_field_addr<TaggedUnion>(umap->offset()), umap->count(), \
++        oop, p_->is_ref(), p_->ref_addr(),                               \
++        do_oop, assert_fn)                                               \
++      ++umap;                                                            \
++    }                                                                    \
++  }                                                                      \
+ }
+ 
+ #define InstanceKlass_OOP_MAP_REVERSE_ITERATE(obj, do_oop, assert_fn)    \
+@@ -1628,6 +1673,19 @@
+         do_oop, assert_fn)                                               \
+     }                                                                    \
+   }                                                                      \
++  const int umap_count  = nonstatic_union_map_count();                   \
++  if (umap_count > 0) {                                                  \
++    debug_only(const char* const map = "do not use me";)                 \
++    OopMapBlock* const start_umap = start_of_nonstatic_union_maps();     \
++    OopMapBlock* umap             = umap + umap_count;                   \
++    while (start_umap < umap) {                                          \
++      --umap;                                                            \
++      InstanceKlass_SPECIALIZED2_OOP_REVERSE_ITERATE(TaggedUnion,        \
++        obj->obj_field_addr<TaggedUnion>(umap->offset()), umap->count(), \
++        oop, p_->is_ref(), p_->ref_addr(),                               \
++        do_oop, assert_fn)                                               \
++    }                                                                    \
++  }                                                                      \
+ }
+ 
+ #define InstanceKlass_BOUNDED_OOP_MAP_ITERATE(obj, low, high, do_oop,    \
+@@ -1656,6 +1714,20 @@
+       ++map;                                                             \
+     }                                                                    \
+   }                                                                      \
++  const int umap_count  = nonstatic_union_map_count();                   \
++  if (umap_count > 0) {                                                  \
++    debug_only(const char* const map = "do not use me";)                 \
++    OopMapBlock* umap           = start_of_nonstatic_union_maps();       \
++    OopMapBlock* const end_umap = umap + umap_count;                     \
++    while (umap < end_umap) {                                            \
++      InstanceKlass_SPECIALIZED2_BOUNDED_OOP_ITERATE(TaggedUnion,        \
++        obj->obj_field_addr<TaggedUnion>(umap->offset()), umap->count(), \
++        low, high,                                                       \
++        oop, p_->is_ref(), p_->ref_addr(),                               \
++        do_oop, assert_fn)                                               \
++      ++umap;                                                            \
++    }                                                                    \
++  }                                                                      \
+ }
+ 
+ void instanceKlass::oop_follow_contents(oop obj) {
+@@ -2309,6 +2381,8 @@
+     st->cr();
+     st->print_cr(BULLET"fake entry for oop_size: %d", java_lang_Class::oop_size(obj));
+     st->print_cr(BULLET"fake entry for static_oop_field_count: %d", java_lang_Class::static_oop_field_count(obj));
++    if (java_lang_Class::static_union_field_count(obj) != 0)
++      st->print_cr(BULLET"fake entry for static_union_field_count: %d", java_lang_Class::static_union_field_count(obj));
+     klassOop real_klass = java_lang_Class::as_klassOop(obj);
+     if (real_klass != NULL && real_klass->klass_part()->oop_is_instance()) {
+       instanceKlass::cast(real_klass)->do_local_static_fields(&print_field);
+diff --git a/src/share/vm/oops/instanceKlass.hpp b/src/share/vm/oops/instanceKlass.hpp
+--- a/src/share/vm/oops/instanceKlass.hpp
++++ b/src/share/vm/oops/instanceKlass.hpp
+@@ -228,7 +228,9 @@
+   int             _nonstatic_field_size;
+   int             _static_field_size;    // number words used by static fields (oop and non-oop) in this klass
+   int             _static_oop_field_count;// number of static oop fields in this klass
++  int             _static_union_field_count;// number of static tagged union fields in this klass
+   int             _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks
++  int             _nonstatic_union_map_size;// size in words of oop map blocks for unions
+   int             _java_fields_count;    // The number of declared Java fields
+   bool            _is_marked_dependent;  // used for marking during flushing and deoptimization
+   bool            _rewritten;            // methods rewritten.
+@@ -282,6 +284,9 @@
+   int static_oop_field_count() const        { return _static_oop_field_count; }
+   void set_static_oop_field_count(int size) { _static_oop_field_count = size; }
+ 
++  int static_union_field_count() const      { return _static_union_field_count; }
++  void set_static_union_field_count(int size) { _static_union_field_count = size; }
++
+   // Java vtable
+   int  vtable_length() const               { return _vtable_len; }
+   void set_vtable_length(int len)          { _vtable_len = len; }
+@@ -486,6 +491,18 @@
+     _nonstatic_oop_map_size = words;
+   }
+ 
++  // same thing for unions
++  static int nonstatic_union_map_size(unsigned int union_map_count) {
++    return union_map_count * OopMapBlock::size_in_words();
++  }
++  unsigned int nonstatic_union_map_count() const {
++    return _nonstatic_union_map_size / OopMapBlock::size_in_words();
++  }
++  int nonstatic_union_map_size() const { return _nonstatic_union_map_size; }
++  void set_nonstatic_union_map_size(int words) {
++    _nonstatic_union_map_size = words;
++  }
++
+   // RedefineClasses() support for previous versions:
+   void add_previous_version(instanceKlassHandle ikh, BitMap *emcp_methods,
+          int emcp_method_count);
+@@ -666,7 +683,7 @@
+ 
+   // Sizing (in words)
+   static int header_size()            { return align_object_offset(oopDesc::header_size() + sizeof(instanceKlass)/HeapWordSize); }
+-  int object_size() const             { return object_size(align_object_offset(vtable_length()) + align_object_offset(itable_length()) + nonstatic_oop_map_size()); }
++  int object_size() const             { return object_size(align_object_offset(vtable_length()) + align_object_offset(itable_length()) + nonstatic_oop_map_size() + nonstatic_union_map_size()); }
+   static int vtable_start_offset()    { return header_size(); }
+   static int vtable_length_offset()   { return oopDesc::header_size() + offset_of(instanceKlass, _vtable_len) / HeapWordSize; }
+   static int object_size(int extra)   { return align_object_size(header_size() + extra); }
+@@ -682,6 +699,9 @@
+   OopMapBlock* start_of_nonstatic_oop_maps() const {
+     return (OopMapBlock*)(start_of_itable() + align_object_offset(itable_length()));
+   }
++  OopMapBlock* start_of_nonstatic_union_maps() const {
++    return start_of_nonstatic_oop_maps() + nonstatic_oop_map_count();
++  }
+ 
+   // Allocation profiling support
+   juint alloc_size() const            { return _alloc_count * size_helper(); }
+diff --git a/src/share/vm/oops/instanceKlassKlass.cpp b/src/share/vm/oops/instanceKlassKlass.cpp
+--- a/src/share/vm/oops/instanceKlassKlass.cpp
++++ b/src/share/vm/oops/instanceKlassKlass.cpp
+@@ -342,11 +342,19 @@
+ instanceKlassKlass::allocate_instance_klass(Symbol* name, int vtable_len, int itable_len,
+                                             int static_field_size,
+                                             unsigned nonstatic_oop_map_count,
++                                            unsigned nonstatic_union_map_count,
+                                             ReferenceType rt, TRAPS) {
+ 
+   const int nonstatic_oop_map_size =
+     instanceKlass::nonstatic_oop_map_size(nonstatic_oop_map_count);
+-  int size = instanceKlass::object_size(align_object_offset(vtable_len) + align_object_offset(itable_len) + nonstatic_oop_map_size);
++  const int nonstatic_union_map_size =
++    instanceKlass::nonstatic_oop_map_size(nonstatic_union_map_count);
++  assert(EnableTaggedUnions || nonstatic_union_map_size == 0, "");
++
++  int size = instanceKlass::object_size(align_object_offset(vtable_len) +
++                                        align_object_offset(itable_len) +
++                                        nonstatic_oop_map_size +
++                                        nonstatic_union_map_size);
+ 
+   // Allocation
+   KlassHandle h_this_klass(THREAD, as_klassOop());
+@@ -378,6 +386,7 @@
+     ik->set_itable_length(itable_len);
+     ik->set_static_field_size(static_field_size);
+     ik->set_nonstatic_oop_map_size(nonstatic_oop_map_size);
++    ik->set_nonstatic_union_map_size(nonstatic_union_map_size);
+     assert(k()->size() == size, "wrong size for object");
+ 
+     ik->set_array_klasses(NULL);
+@@ -398,6 +407,7 @@
+     ik->set_array_name(NULL);
+     ik->set_inner_classes(NULL);
+     ik->set_static_oop_field_count(0);
++    ik->set_static_union_field_count(0);
+     ik->set_nonstatic_field_size(0);
+     ik->set_is_marked_dependent(false);
+     ik->set_init_state(instanceKlass::allocated);
+@@ -554,6 +564,17 @@
+     map++;
+   }
+   st->cr();
++
++  if (EnableTaggedUnions && ik->nonstatic_union_map_count() > 0) {
++    st->print(BULLET"non-static union maps: ");
++    OopMapBlock* map     = ik->start_of_nonstatic_union_maps();
++    OopMapBlock* end_map = map + ik->nonstatic_union_map_count();
++    while (map < end_map) {
++      st->print("%d-%d ", map->offset(), map->offset() + heapOopSize*(map->count() - 1));
++      map++;
++    }
++    st->cr();
++  }
+ }
+ 
+ #endif //PRODUCT
+diff --git a/src/share/vm/oops/instanceKlassKlass.hpp b/src/share/vm/oops/instanceKlassKlass.hpp
+--- a/src/share/vm/oops/instanceKlassKlass.hpp
++++ b/src/share/vm/oops/instanceKlassKlass.hpp
+@@ -46,6 +46,7 @@
+                                    int itable_len,
+                                    int static_field_size,
+                                    unsigned int nonstatic_oop_map_count,
++                                   unsigned int nonstatic_union_map_count,
+                                    ReferenceType rt,
+                                    TRAPS);
+ 
+diff --git a/src/share/vm/oops/instanceMirrorKlass.cpp b/src/share/vm/oops/instanceMirrorKlass.cpp
+--- a/src/share/vm/oops/instanceMirrorKlass.cpp
++++ b/src/share/vm/oops/instanceMirrorKlass.cpp
+@@ -83,40 +83,60 @@
+ #define InstanceMirrorKlass_SPECIALIZED_OOP_ITERATE( \
+   T, start_p, count, do_oop,                         \
+   assert_fn)                                         \
++  InstanceMirrorKlass_SPECIALIZED2_OOP_ITERATE(      \
++    T, start_p, count, T, true, p_, do_oop, assert_fn)
++#define InstanceMirrorKlass_SPECIALIZED2_OOP_ITERATE( \
++  T, start_p, count, P, p_filter, p_expr, do_oop,    \
++  assert_fn)                                         \
+ {                                                    \
+-  T* p         = (T*)(start_p);                      \
+-  T* const end = p + (count);                        \
+-  while (p < end) {                                  \
+-    (assert_fn)(p);                                  \
+-    do_oop;                                          \
+-    ++p;                                             \
++  T* p_         = (T*)(start_p);                     \
++  T* const end = p_ + (count);                       \
++  while (p_ < end) {                                 \
++    if (p_filter) {                                  \
++      P* const p = (p_expr);                         \
++      (assert_fn)(p);                                \
++      do_oop;                                        \
++    }                                                \
++    ++p_;                                            \
+   }                                                  \
+ }
+ 
+ #define InstanceMirrorKlass_SPECIALIZED_BOUNDED_OOP_ITERATE( \
+   T, start_p, count, low, high,                              \
+   do_oop, assert_fn)                                         \
++  InstanceMirrorKlass_SPECIALIZED2_BOUNDED_OOP_ITERATE(      \
++    T, start_p, count, low, high,                            \
++    T, true, p_, do_oop, assert_fn)
++#define InstanceMirrorKlass_SPECIALIZED2_BOUNDED_OOP_ITERATE( \
++  T, start_p, count, low, high,             \
++  P, p_filter, p_expr,                      \
++  do_oop, assert_fn)                        \
+ {                                                            \
+   T* const l = (T*)(low);                                    \
+   T* const h = (T*)(high);                                   \
+   assert(mask_bits((intptr_t)l, sizeof(T)-1) == 0 &&         \
+          mask_bits((intptr_t)h, sizeof(T)-1) == 0,           \
+          "bounded region must be properly aligned");         \
+-  T* p       = (T*)(start_p);                                \
+-  T* end     = p + (count);                                  \
+-  if (p < l) p = l;                                          \
++  T* p_       = (T*)(start_p);                               \
++  T* end     = p_ + (count);                                 \
++  if (p_ < l) p_ = l;                                        \
+   if (end > h) end = h;                                      \
+-  while (p < end) {                                          \
+-    (assert_fn)(p);                                          \
+-    do_oop;                                                  \
+-    ++p;                                                     \
++  while (p_ < end) {                                         \
++    if (p_filter) {                                          \
++      P* const p = (p_expr);                                 \
++      (assert_fn)(p);                                        \
++      do_oop;                                                \
++    }                                                        \
++    ++p_;                                                    \
+   }                                                          \
+ }
+ 
+ 
+-#define InstanceMirrorKlass_OOP_ITERATE(start_p, count,    \
++#define InstanceMirrorKlass_OOP_ITERATE(obj,               \
+                                   do_oop, assert_fn)       \
+ {                                                          \
++  HeapWord* start_p = start_of_static_oop_fields(obj);     \
++  int count = java_lang_Class::static_oop_field_count(obj); \
+   if (UseCompressedOops) {                                 \
+     InstanceMirrorKlass_SPECIALIZED_OOP_ITERATE(narrowOop, \
+       start_p, count,                                      \
+@@ -126,14 +146,25 @@
+       start_p, count,                                      \
+       do_oop, assert_fn)                                   \
+   }                                                        \
++  int ucount = java_lang_Class::static_union_field_count(obj); \
++  if (ucount > 0) {                                        \
++    HeapWord* union_p = start_of_static_union_fields(obj); \
++    InstanceMirrorKlass_SPECIALIZED2_OOP_ITERATE(TaggedUnion, \
++      union_p, ucount,                                     \
++      oop, p_->is_ref(), p_->ref_addr(),                   \
++      do_oop, assert_fn)                                   \
++  }                                                        \
+ }
+ 
+ // The following macros call specialized macros, passing either oop or
+ // narrowOop as the specialization type.  These test the UseCompressedOops
+ // flag.
+-#define InstanceMirrorKlass_BOUNDED_OOP_ITERATE(start_p, count, low, high, \
++// They also iterate over TaggedUnion cells, if any are present.
++#define InstanceMirrorKlass_BOUNDED_OOP_ITERATE(obj, low, high,            \
+                                           do_oop, assert_fn)               \
+ {                                                                          \
++  HeapWord* start_p = start_of_static_oop_fields(obj);                     \
++  int count = java_lang_Class::static_oop_field_count(obj);                \
+   if (UseCompressedOops) {                                                 \
+     InstanceMirrorKlass_SPECIALIZED_BOUNDED_OOP_ITERATE(narrowOop,         \
+       start_p, count,                                                      \
+@@ -145,13 +176,21 @@
+       low, high,                                                           \
+       do_oop, assert_fn)                                                   \
+   }                                                                        \
++  int ucount = java_lang_Class::static_union_field_count(obj);             \
++  if (ucount > 0) {                                                        \
++    HeapWord* union_p = start_of_static_union_fields(obj);                 \
++    InstanceMirrorKlass_SPECIALIZED2_BOUNDED_OOP_ITERATE(TaggedUnion,      \
++      union_p, ucount,                                                     \
++      low, high,                                                           \
++      oop, p_->is_ref(), p_->ref_addr(),                                   \
++      do_oop, assert_fn)                                                   \
++  }                                                                        \
+ }
+ 
+-
+ void instanceMirrorKlass::oop_follow_contents(oop obj) {
+   instanceKlass::oop_follow_contents(obj);
+   InstanceMirrorKlass_OOP_ITERATE(                                                    \
+-    start_of_static_fields(obj), java_lang_Class::static_oop_field_count(obj),        \
++    obj,                                                                              \
+     MarkSweep::mark_and_push(p),                                                      \
+     assert_is_in_closed_subset)
+ }
+@@ -161,7 +200,7 @@
+                                               oop obj) {
+   instanceKlass::oop_follow_contents(cm, obj);
+   InstanceMirrorKlass_OOP_ITERATE(                                                    \
+-    start_of_static_fields(obj), java_lang_Class::static_oop_field_count(obj),        \
++    obj,                                                                              \
+     PSParallelCompact::mark_and_push(cm, p),                                          \
+     assert_is_in)
+ }
+@@ -171,22 +210,22 @@
+   int size = oop_size(obj);
+   instanceKlass::oop_adjust_pointers(obj);
+   InstanceMirrorKlass_OOP_ITERATE(                                                    \
+-    start_of_static_fields(obj), java_lang_Class::static_oop_field_count(obj),        \
++    obj,                                                                              \
+     MarkSweep::adjust_pointer(p),                                                     \
+     assert_nothing)
+   return size;
+ }
+ 
+-#define InstanceMirrorKlass_SPECIALIZED_OOP_ITERATE_DEFN(T, nv_suffix)                \
++#define InstanceMirrorKlass_OOP_ITERATE_DEFN(nv_suffix)                               \
+   InstanceMirrorKlass_OOP_ITERATE(                                                    \
+-    start_of_static_fields(obj), java_lang_Class::static_oop_field_count(obj),        \
++    obj,                                                                              \
+       (closure)->do_oop##nv_suffix(p),                                                \
+     assert_is_in_closed_subset)                                                       \
+   return oop_size(obj);                                                               \
+ 
+-#define InstanceMirrorKlass_BOUNDED_SPECIALIZED_OOP_ITERATE(T, nv_suffix, mr)         \
++#define InstanceMirrorKlass_BOUNDED_OOP_ITERATE_DEFN(nv_suffix, mr)                   \
+   InstanceMirrorKlass_BOUNDED_OOP_ITERATE(                                            \
+-    start_of_static_fields(obj), java_lang_Class::static_oop_field_count(obj),        \
++    obj,                                                                              \
+     mr.start(), mr.end(),                                                             \
+       (closure)->do_oop##nv_suffix(p),                                                \
+     assert_is_in_closed_subset)                                                       \
+@@ -204,12 +243,7 @@
+   SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::irk);      \
+                                                                                       \
+   instanceKlass::oop_oop_iterate##nv_suffix(obj, closure);                            \
+-                                                                                      \
+-  if (UseCompressedOops) {                                                            \
+-    InstanceMirrorKlass_SPECIALIZED_OOP_ITERATE_DEFN(narrowOop, nv_suffix);           \
+-  } else {                                                                            \
+-    InstanceMirrorKlass_SPECIALIZED_OOP_ITERATE_DEFN(oop, nv_suffix);                 \
+-  }                                                                                   \
++  InstanceMirrorKlass_OOP_ITERATE_DEFN(nv_suffix);                                    \
+ }
+ 
+ #ifndef SERIALGC
+@@ -221,12 +255,7 @@
+   SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::irk);      \
+                                                                                       \
+   instanceKlass::oop_oop_iterate_backwards##nv_suffix(obj, closure);                  \
+-                                                                                      \
+-  if (UseCompressedOops) {                                                            \
+-    InstanceMirrorKlass_SPECIALIZED_OOP_ITERATE_DEFN(narrowOop, nv_suffix);           \
+-  } else {                                                                            \
+-    InstanceMirrorKlass_SPECIALIZED_OOP_ITERATE_DEFN(oop, nv_suffix);                 \
+-  }                                                                                   \
++  InstanceMirrorKlass_OOP_ITERATE_DEFN(nv_suffix);                                    \
+ }
+ #endif // !SERIALGC
+ 
+@@ -240,11 +269,7 @@
+   SpecializationStats::record_iterate_call##nv_suffix(SpecializationStats::irk);      \
+                                                                                       \
+   instanceKlass::oop_oop_iterate##nv_suffix##_m(obj, closure, mr);                    \
+-  if (UseCompressedOops) {                                                            \
+-    InstanceMirrorKlass_BOUNDED_SPECIALIZED_OOP_ITERATE(narrowOop, nv_suffix, mr);    \
+-  } else {                                                                            \
+-    InstanceMirrorKlass_BOUNDED_SPECIALIZED_OOP_ITERATE(oop, nv_suffix, mr);          \
+-  }                                                                                   \
++  InstanceMirrorKlass_BOUNDED_OOP_ITERATE_DEFN(nv_suffix, mr);                        \
+ }
+ 
+ ALL_OOP_OOP_ITERATE_CLOSURES_1(InstanceMirrorKlass_OOP_OOP_ITERATE_DEFN)
+@@ -259,19 +284,19 @@
+ #ifndef SERIALGC
+ void instanceMirrorKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
+   instanceKlass::oop_push_contents(pm, obj);
+-  InstanceMirrorKlass_OOP_ITERATE(                                            \
+-    start_of_static_fields(obj), java_lang_Class::static_oop_field_count(obj),\
+-    if (PSScavenge::should_scavenge(p)) {                                     \
+-      pm->claim_or_forward_depth(p);                                          \
+-    },                                                                        \
++  InstanceMirrorKlass_OOP_ITERATE(                                                \
++    obj,                                                                          \
++    if (PSScavenge::should_scavenge(p)) {                                         \
++      pm->claim_or_forward_depth(p);                                              \
++    },                                                                            \
+     assert_nothing )
+ }
+ 
+ int instanceMirrorKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) {
+   instanceKlass::oop_update_pointers(cm, obj);
+-  InstanceMirrorKlass_OOP_ITERATE(                                            \
+-    start_of_static_fields(obj), java_lang_Class::static_oop_field_count(obj),\
+-    PSParallelCompact::adjust_pointer(p),                                     \
++  InstanceMirrorKlass_OOP_ITERATE(                                                \
++    obj,                                                                          \
++    PSParallelCompact::adjust_pointer(p),                                         \
+     assert_nothing)
+   return oop_size(obj);
+ }
+@@ -311,3 +336,11 @@
+   }
+   return 0;
+ }
++
++int instanceMirrorKlass::compute_static_union_field_count(oop obj) {
++  klassOop k = java_lang_Class::as_klassOop(obj);
++  if (k != NULL && k->klass_part()->oop_is_instance()) {
++    return instanceKlass::cast(k)->static_union_field_count();
++  }
++  return 0;
++}
+diff --git a/src/share/vm/oops/instanceMirrorKlass.hpp b/src/share/vm/oops/instanceMirrorKlass.hpp
+--- a/src/share/vm/oops/instanceMirrorKlass.hpp
++++ b/src/share/vm/oops/instanceMirrorKlass.hpp
+@@ -56,7 +56,7 @@
+ 
+   // Static field offset is an offset into the Heap, should be converted by
+   // based on UseCompressedOop for traversal
+-  static HeapWord* start_of_static_fields(oop obj) {
++  static HeapWord* start_of_static_oop_fields(oop obj) {
+     return (HeapWord*)((intptr_t)obj + offset_of_static_fields());
+   }
+ 
+@@ -70,7 +70,24 @@
+     return _offset_of_static_fields;
+   }
+ 
++  HeapWord* start_of_static_union_fields(oop obj) {
++    return (HeapWord*)((intptr_t)obj + offset_of_static_union_fields(obj));
++  }
++
++  int offset_of_static_union_fields(oop obj) {
++    int offset = offset_of_static_fields();
++    if (java_lang_Class::static_union_field_count(obj) != 0) {
++      int num_oops = java_lang_Class::static_oop_field_count(obj);
++      offset += (num_oops * heapOopSize);
++      if (Universe::field_type_should_be_aligned(T_UNION)) {
++        offset = align_size_up(offset, BytesPerLong);
++      }
++    }
++    return offset;
++  }
++
+   int compute_static_oop_field_count(oop obj);
++  int compute_static_union_field_count(oop obj);
+ 
+   // Given a Klass return the size of the instance
+   int instance_size(KlassHandle k);
+diff --git a/src/share/vm/oops/oop.hpp b/src/share/vm/oops/oop.hpp
+--- a/src/share/vm/oops/oop.hpp
++++ b/src/share/vm/oops/oop.hpp
+@@ -164,14 +164,17 @@
+ 
+   static bool is_null(oop obj);
+   static bool is_null(narrowOop obj);
++  static bool is_null(TaggedUnion obj);
+ 
+   // Decode an oop pointer from a narrowOop if compressed.
+   // These are overloaded for oop and narrowOop as are the other functions
+   // below so that they can be called in template functions.
+   static oop decode_heap_oop_not_null(oop v);
+   static oop decode_heap_oop_not_null(narrowOop v);
++  static oop decode_heap_oop_not_null(TaggedUnion v);
+   static oop decode_heap_oop(oop v);
+   static oop decode_heap_oop(narrowOop v);
++  static oop decode_heap_oop(TaggedUnion v);
+ 
+   // Encode an oop pointer to a narrow oop.  The or_null versions accept
+   // null oop pointer, others do not in order to eliminate the
+@@ -182,14 +185,18 @@
+   // Load an oop out of the Java heap
+   static narrowOop load_heap_oop(narrowOop* p);
+   static oop       load_heap_oop(oop* p);
++  static TaggedUnion load_heap_oop(TaggedUnion* p);
+ 
+   // Load an oop out of Java heap and decode it to an uncompressed oop.
+   static oop load_decode_heap_oop_not_null(narrowOop* p);
+   static oop load_decode_heap_oop_not_null(oop* p);
++  static oop load_decode_heap_oop_not_null(TaggedUnion* p);
+   static oop load_decode_heap_oop(narrowOop* p);
+   static oop load_decode_heap_oop(oop* p);
++  static oop load_decode_heap_oop(TaggedUnion* p);
+ 
+   // Store an oop into the heap.
++  //@@??static void store_heap_oop(TaggedUnion* p, TaggedUnion v);
+   static void store_heap_oop(narrowOop* p, narrowOop v);
+   static void store_heap_oop(oop* p, oop v);
+ 
+diff --git a/src/share/vm/oops/oop.inline.hpp b/src/share/vm/oops/oop.inline.hpp
+--- a/src/share/vm/oops/oop.inline.hpp
++++ b/src/share/vm/oops/oop.inline.hpp
+@@ -178,6 +178,7 @@
+ 
+ inline bool oopDesc::is_null(oop obj)       { return obj == NULL; }
+ inline bool oopDesc::is_null(narrowOop obj) { return obj == 0; }
++inline bool oopDesc::is_null(TaggedUnion obj) { return obj.as_ref_or_null() == NULL; }
+ 
+ // Algorithm for encoding and decoding oops from 64 bit pointers to 32 bit
+ // offset from the heap base.  Saving the check for null can save instructions
+@@ -221,22 +222,32 @@
+ inline oop oopDesc::decode_heap_oop_not_null(oop v) { return v; }
+ inline oop oopDesc::decode_heap_oop(oop v)  { return v; }
+ 
++inline oop oopDesc::decode_heap_oop_not_null(TaggedUnion v) { return v.as_ref(); }
++inline oop oopDesc::decode_heap_oop(TaggedUnion v) { return v.as_ref_or_null(); }
++
+ // Load an oop out of the Java heap as is without decoding.
+ // Called by GC to check for null before decoding.
+ inline oop       oopDesc::load_heap_oop(oop* p)          { return *p; }
+ inline narrowOop oopDesc::load_heap_oop(narrowOop* p)    { return *p; }
++inline TaggedUnion oopDesc::load_heap_oop(TaggedUnion* p) { return *p; }
+ 
+ // Load and decode an oop out of the Java heap into a wide oop.
+ inline oop oopDesc::load_decode_heap_oop_not_null(oop* p)       { return *p; }
+ inline oop oopDesc::load_decode_heap_oop_not_null(narrowOop* p) {
+   return decode_heap_oop_not_null(*p);
+ }
++inline oop oopDesc::load_decode_heap_oop_not_null(TaggedUnion* p) {
++  return p->as_ref();
++}
+ 
+ // Load and decode an oop out of the heap accepting null
+ inline oop oopDesc::load_decode_heap_oop(oop* p) { return *p; }
+ inline oop oopDesc::load_decode_heap_oop(narrowOop* p) {
+   return decode_heap_oop(*p);
+ }
++inline oop oopDesc::load_decode_heap_oop(TaggedUnion* p) {
++  return decode_heap_oop(*p);
++}
+ 
+ // Store already encoded heap oop into the heap.
+ inline void oopDesc::store_heap_oop(oop* p, oop v)                 { *p = v; }
+diff --git a/src/share/vm/prims/jni.cpp b/src/share/vm/prims/jni.cpp
+--- a/src/share/vm/prims/jni.cpp
++++ b/src/share/vm/prims/jni.cpp
+@@ -835,6 +835,9 @@
+   virtual void get_double () = 0;
+   virtual void get_object () = 0;
+ 
++  // a T_UNION ('U') is passed as a jlong with an embedded jobject
++  void get_union()  { get_long(); }
++
+   JNI_ArgumentPusher(Symbol* signature) : SignatureIterator(signature) {
+     this->_return_type = T_ILLEGAL;
+     _arguments = NULL;
+@@ -855,6 +858,7 @@
+   inline void do_double()                   { if (!is_return_type()) get_double(); }
+   inline void do_object(int begin, int end) { if (!is_return_type()) get_object(); }
+   inline void do_array(int begin, int end)  { if (!is_return_type()) get_object(); } // do_array uses get_object -- there is no get_array
++  inline void do_union()                    { if (!is_return_type()) get_union(); }
+   inline void do_void()                     { }
+ 
+   JavaCallArguments* arguments()     { return _arguments; }
+diff --git a/src/share/vm/prims/jni.h b/src/share/vm/prims/jni.h
+--- a/src/share/vm/prims/jni.h
++++ b/src/share/vm/prims/jni.h
+@@ -62,6 +62,8 @@
+ 
+ typedef jint            jsize;
+ 
++typedef jlong           junion;  // tagged union, with optional embedded jobject
++
+ #ifdef __cplusplus
+ 
+ class _jobject {};
+@@ -127,6 +129,7 @@
+     jfloat   f;
+     jdouble  d;
+     jobject  l;
++    junion   u;
+ } jvalue;
+ 
+ struct _jfieldID;
+diff --git a/src/share/vm/prims/jvm.h b/src/share/vm/prims/jvm.h
+--- a/src/share/vm/prims/jvm.h
++++ b/src/share/vm/prims/jvm.h
+@@ -1075,6 +1075,7 @@
+ 
+ /* Used in the newarray instruction. */
+ 
++#define JVM_T_UNION   3
+ #define JVM_T_BOOLEAN 4
+ #define JVM_T_CHAR    5
+ #define JVM_T_FLOAT   6
+@@ -1099,6 +1100,7 @@
+ #define JVM_SIGNATURE_INT               'I'
+ #define JVM_SIGNATURE_LONG              'J'
+ #define JVM_SIGNATURE_SHORT             'S'
++#define JVM_SIGNATURE_UNION             'U'
+ #define JVM_SIGNATURE_VOID              'V'
+ #define JVM_SIGNATURE_BOOLEAN           'Z'
+ 
+diff --git a/src/share/vm/prims/jvmtiTagMap.cpp b/src/share/vm/prims/jvmtiTagMap.cpp
+--- a/src/share/vm/prims/jvmtiTagMap.cpp
++++ b/src/share/vm/prims/jvmtiTagMap.cpp
+@@ -1040,6 +1040,7 @@
+ 
+ // helper function to tell if a field is a primitive field or not
+ static inline bool is_primitive_field_type(char type) {
++  assert(type != 'U', "FIXME: handle EnableTaggedUnions");
+   return (type != 'L' && type != '[');
+ }
+ 
+@@ -2853,7 +2854,7 @@
+ static inline bool verify_static_oop(instanceKlass* ik,
+                                      oop mirror, int offset) {
+   address obj_p = (address)mirror + offset;
+-  address start = (address)instanceMirrorKlass::start_of_static_fields(mirror);
++  address start = (address)instanceMirrorKlass::start_of_static_oop_fields(mirror);
+   address end = start + (java_lang_Class::static_oop_field_count(mirror) * heapOopSize);
+   assert(end >= start, "sanity check");
+ 
+diff --git a/src/share/vm/runtime/frame.cpp b/src/share/vm/runtime/frame.cpp
+--- a/src/share/vm/runtime/frame.cpp
++++ b/src/share/vm/runtime/frame.cpp
+@@ -767,12 +767,11 @@
+     _f          = f;
+   }
+ 
+-  void offset_do(int offset) {
++  void offset_do(int offset, BasicType type) {
+     oop* addr;
+     if (offset < _max_locals) {
+       addr = (oop*) _fr->interpreter_frame_local_at(offset);
+       assert((intptr_t*)addr >= _fr->sp(), "must be inside the frame");
+-      _f->do_oop(addr);
+     } else {
+       addr = (oop*) _fr->interpreter_frame_expression_stack_at((offset - _max_locals));
+       // In case of exceptions, the expression stack is invalid and the esp will be reset to express
+@@ -783,8 +782,17 @@
+       } else {
+         in_stack = (intptr_t*)addr >= _fr->interpreter_frame_tos_address();
+       }
+-      if (in_stack) {
+-        _f->do_oop(addr);
++      if (!in_stack) {
++        return;
++      }
++    }
++    if (type == T_OBJECT) {
++      _f->do_oop(addr);
++    } else {
++      assert(EnableTaggedUnions && type == T_UNION, "");
++      TaggedUnion* tagu = TaggedUnion::locate_in_jvm_stack((intptr_t*) addr);
++      if (tagu->is_ref_safe()) {
++        _f->do_oop(tagu->ref_addr());
+       }
+     }
+   }
+diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp
+--- a/src/share/vm/runtime/globals.hpp
++++ b/src/share/vm/runtime/globals.hpp
+@@ -3776,6 +3776,9 @@
+           "support JSR 292 (method handles, invokedynamic, "                \
+           "anonymous classes")                                              \
+                                                                             \
++  diagnostic(bool, EnableTaggedUnions, false,                                \
++             "support tagged unions")                                       \
++                                                                            \
+   product(bool, AnonymousClasses, false,                                    \
+           "support sun.misc.Unsafe.defineAnonymousClass (deprecated)")      \
+                                                                             \
+diff --git a/src/share/vm/runtime/handles.hpp b/src/share/vm/runtime/handles.hpp
+--- a/src/share/vm/runtime/handles.hpp
++++ b/src/share/vm/runtime/handles.hpp
+@@ -27,6 +27,7 @@
+ 
+ #include "oops/klass.hpp"
+ #include "oops/klassOop.hpp"
++#include "utilities/taggedUnion.hpp"
+ #include "utilities/top.hpp"
+ 
+ //------------------------------------------------------------------------------------------------------------------------
+@@ -117,6 +118,35 @@
+ 
+ 
+ //------------------------------------------------------------------------------------------------------------------------
++// Handles for tagged unions.
++class TaggedUnionHandle VALUE_OBJ_CLASS_SPEC {
++ private:
++  Handle _handle;
++  jlong  _bits;
++
++ public:
++  // Constructors
++  TaggedUnionHandle()                : _handle(),       _bits(0) { assert(is_handle(), ""); }
++  TaggedUnionHandle(Handle handle)   : _handle(handle), _bits(0) { assert(is_handle(), ""); }
++  TaggedUnionHandle(jlong prim)      : _handle(), _bits(TaggedUnion::encode_prim(prim)) { assert(is_prim(), ""); }
++  TaggedUnionHandle(Thread* thread, TaggedUnion tagu)
++    : _handle(thread, tagu.as_ref_or_null()), _bits(tagu.raw_bits())
++    {}
++
++  bool    is_prim() const                        { return _bits != 0; }
++  bool    is_handle() const                      { return !is_prim(); }
++  jlong   as_prim() const                        { return TaggedUnion::decode_prim(raw_bits()); }
++  julong  raw_bits() const                       { assert(is_prim(), ""); return _bits; }
++  Handle  as_handle() const                      { assert(is_handle(), ""); return _handle; }
++  Handle  as_handle_or_null() const              { return is_handle() ? _handle : Handle(); }
++  TaggedUnion operator()()                       { return (is_prim()
++                                                           ? TaggedUnion::from_raw_bits(_bits)
++                                                           : TaggedUnion::from_ref(_handle())); }
++};
++
++
++
++//------------------------------------------------------------------------------------------------------------------------
+ // Base class for Handles containing klassOops. Provides overloading of frequently
+ // used operators for ease of use and typed access to the Klass part.
+ class KlassHandle: public Handle {
+diff --git a/src/share/vm/runtime/javaCalls.cpp b/src/share/vm/runtime/javaCalls.cpp
+--- a/src/share/vm/runtime/javaCalls.cpp
++++ b/src/share/vm/runtime/javaCalls.cpp
+@@ -531,6 +531,10 @@
+     check_value(true);
+   }
+ 
++  void check_union(BasicType t) {
++    ShouldNotReachHere();  // FIXME: support EnableTaggedUnions
++  }
++
+   void do_bool()                       { check_int(T_BOOLEAN);       }
+   void do_char()                       { check_int(T_CHAR);          }
+   void do_float()                      { check_int(T_FLOAT);         }
+@@ -540,6 +544,7 @@
+   void do_int()                        { check_int(T_INT);           }
+   void do_long()                       { check_long(T_LONG);         }
+   void do_void()                       { check_return_type(T_VOID);  }
++  void do_union()                      { check_union(T_UNION);  }
+   void do_object(int begin, int end)   { check_obj(T_OBJECT);        }
+   void do_array(int begin, int end)    { check_obj(T_OBJECT);        }
+ };
+diff --git a/src/share/vm/runtime/signature.hpp b/src/share/vm/runtime/signature.hpp
+--- a/src/share/vm/runtime/signature.hpp
++++ b/src/share/vm/runtime/signature.hpp
+@@ -77,7 +77,8 @@
+       float_parm           = 7,
+       double_parm          = 8,
+       obj_parm             = 9,
+-      done_parm            = 10,  // marker for end of parameters
++      union_parm           = 10,
++      done_parm            = 11,  // marker for end of parameters
+ 
+     // max parameters is wordsize minus
+     //    The sign bit, termination field, the result and static bit fields
+@@ -110,6 +111,7 @@
+   virtual void do_int   ()             = 0;
+   virtual void do_long  ()             = 0;
+   virtual void do_void  ()             = 0;
++  virtual void do_union ()             = 0;
+ 
+   // Object types (begin indexes the first character of the entry, end indexes the first character after the entry)
+   virtual void do_object(int begin, int end) = 0;
+@@ -132,6 +134,7 @@
+   void do_int()                        { type_name("jint"    ); }
+   void do_long()                       { type_name("jlong"   ); }
+   void do_void()                       { type_name("void"    ); }
++  void do_union()                      { type_name("union"   ); }
+   void do_object(int begin, int end)   { type_name("jobject" ); }
+   void do_array (int begin, int end)   { type_name("jobject" ); }
+ 
+@@ -160,6 +163,7 @@
+   void do_int   ()                     { set(T_INT_size    , T_INT    ); }
+   void do_long  ()                     { set(T_LONG_size   , T_LONG   ); }
+   void do_void  ()                     { set(T_VOID_size   , T_VOID   ); }
++  void do_union ()                     { set(T_UNION_size  , T_UNION  ); }
+   void do_object(int begin, int end)   { set(T_OBJECT_size , T_OBJECT ); }
+   void do_array (int begin, int end)   { set(T_ARRAY_size  , T_ARRAY  ); }
+ 
+@@ -225,6 +229,7 @@
+   void do_long()    { _fingerprint |= (((uint64_t)long_parm) << _shift_count); _shift_count += parameter_feature_size; }
+   void do_float()   { _fingerprint |= (((uint64_t)float_parm) << _shift_count); _shift_count += parameter_feature_size; }
+   void do_double()  { _fingerprint |= (((uint64_t)double_parm) << _shift_count); _shift_count += parameter_feature_size; }
++  void do_union()   { _fingerprint |= (((uint64_t)union_parm) << _shift_count); _shift_count += parameter_feature_size; }
+ 
+   void do_object(int begin, int end)  { _fingerprint |= (((uint64_t)obj_parm) << _shift_count); _shift_count += parameter_feature_size; }
+   void do_array (int begin, int end)  { _fingerprint |= (((uint64_t)obj_parm) << _shift_count); _shift_count += parameter_feature_size; }
+@@ -286,8 +291,10 @@
+   void do_int   ()                     { pass_int();    _jni_offset++; _offset++;       }
+ #ifdef _LP64
+   void do_long  ()                     { pass_long();   _jni_offset++; _offset += 2;    }
++  void do_union ()                     { pass_long();   _jni_offset++; _offset += 2;    }
+ #else
+   void do_long  ()                     { pass_long();   _jni_offset += 2; _offset += 2; }
++  void do_union ()                     { pass_long();   _jni_offset += 2; _offset += 2; }
+ #endif
+   void do_void  ()                     { ShouldNotReachHere();                               }
+   void do_object(int begin, int end)   { pass_object(); _jni_offset++; _offset++;        }
+diff --git a/src/share/vm/runtime/stackValue.hpp b/src/share/vm/runtime/stackValue.hpp
+--- a/src/share/vm/runtime/stackValue.hpp
++++ b/src/share/vm/runtime/stackValue.hpp
+@@ -48,6 +48,31 @@
+     assert(_i == 0 || _o.is_null(), "not null object should not be marked as scalar replaced");
+   }
+ 
++  StackValue(TaggedUnionHandle tagu, bool upper_word) {
++    if (!upper_word) {
++      if (!tagu.is_prim()) {
++        _type  = T_OBJECT;
++        _i     = 0;
++        _o     = tagu.as_handle();
++      } else {       
++        jlong bits = tagu.raw_bits();
++        _type  = T_INT;
++        _i     = (intptr_t) bits;  // 32 or 64 bits
++      }
++    } else if (wordSize < longSize) {
++      // have to preserve the upper word separately
++      _type  = T_INT;
++      if (tagu.is_prim())
++        _i   = (intptr_t)( high(tagu.raw_bits()) );
++      else
++        _i   = 0;
++    } else {
++      // no separate upper word value
++      _type  = T_CONFLICT;
++      _i     = 0;
++    }
++  }
++
+   StackValue() {
+     _type   = T_CONFLICT;
+     _i      = 0;
+@@ -75,6 +100,12 @@
+     _o = value;
+   }
+ 
++  TaggedUnionHandle get_union() const {
++    assert(type() == T_UNION, "type check");
++    Unimplemented(); //FIXME: not all bits are present
++    return TaggedUnionHandle(99);//@@
++  }
++
+   intptr_t get_int() const {
+     assert(type() == T_INT, "type check");
+     return _i;
+diff --git a/src/share/vm/runtime/vframe.cpp b/src/share/vm/runtime/vframe.cpp
+--- a/src/share/vm/runtime/vframe.cpp
++++ b/src/share/vm/runtime/vframe.cpp
+@@ -315,6 +315,12 @@
+     assert(sv != NULL, "sanity check");
+     if (sv->type() == T_OBJECT) {
+       *(oop *) addr = (sv->get_obj())();
++    } else if (sv->type() == T_UNION) {
++      TaggedUnionHandle tagu = sv->get_union();
++      if (tagu.is_handle())
++        *(oop *) addr = tagu.as_handle()();
++      else
++        *addr = tagu.raw_bits();
+     } else {                   // integer
+       *addr = sv->get_int();
+     }
+diff --git a/src/share/vm/runtime/vframeArray.cpp b/src/share/vm/runtime/vframeArray.cpp
+--- a/src/share/vm/runtime/vframeArray.cpp
++++ b/src/share/vm/runtime/vframeArray.cpp
+@@ -112,6 +112,10 @@
+         // preserve object type
+         _locals->add( new StackValue((intptr_t) (value->get_obj()()), T_OBJECT ));
+         break;
++      case T_UNION:
++        _locals->add( new StackValue(value->get_union(), false) );
++        // Note:  Caller must provide a separate StackValue for the upper half.
++        break;
+       case T_CONFLICT:
+         // A dead local.  Will be initialized to null/zero.
+         _locals->add( new StackValue());
+@@ -137,6 +141,10 @@
+         // preserve object type
+         _expressions->add( new StackValue((intptr_t) (value->get_obj()()), T_OBJECT ));
+         break;
++      case T_UNION:
++        _expressions->add( new StackValue(value->get_union(), false) );
++        // Note:  Caller must provide a separate StackValue for the upper half.
++        break;
+       case T_CONFLICT:
+         // A dead stack element.  Will be initialized to null/zero.
+         // This can occur when the compiler emits a state in which stack
+@@ -323,6 +331,9 @@
+       case T_OBJECT:
+         *addr = value->get_int(T_OBJECT);
+         break;
++      case T_UNION:
++        *addr = (intptr_t) value->get_union().raw_bits();
++        break;
+       case T_CONFLICT:
+         // A dead stack slot.  Initialize to null in case it is an oop.
+         *addr = NULL_WORD;
+@@ -344,6 +355,9 @@
+       case T_OBJECT:
+         *addr = value->get_int(T_OBJECT);
+         break;
++      case T_UNION:
++        *addr = (intptr_t) value->get_union().raw_bits();
++        break;
+       case T_CONFLICT:
+         // A dead location. If it is an oop then we need a NULL to prevent GC from following it
+         *addr = NULL_WORD;
+diff --git a/src/share/vm/runtime/vmStructs.cpp b/src/share/vm/runtime/vmStructs.cpp
+--- a/src/share/vm/runtime/vmStructs.cpp
++++ b/src/share/vm/runtime/vmStructs.cpp
+@@ -305,7 +305,9 @@
+   nonstatic_field(instanceKlass,               _nonstatic_field_size,                         int)                                   \
+   nonstatic_field(instanceKlass,               _static_field_size,                            int)                                   \
+   nonstatic_field(instanceKlass,               _static_oop_field_count,                       int)                                   \
++  nonstatic_field(instanceKlass,               _static_union_field_count,                     int)                                   \
+   nonstatic_field(instanceKlass,               _nonstatic_oop_map_size,                       int)                                   \
++  nonstatic_field(instanceKlass,               _nonstatic_union_map_size,                     int)                                   \
+   nonstatic_field(instanceKlass,               _is_marked_dependent,                          bool)                                  \
+   nonstatic_field(instanceKlass,               _minor_version,                                u2)                                    \
+   nonstatic_field(instanceKlass,               _major_version,                                u2)                                    \
+diff --git a/src/share/vm/services/heapDumper.cpp b/src/share/vm/services/heapDumper.cpp
+--- a/src/share/vm/services/heapDumper.cpp
++++ b/src/share/vm/services/heapDumper.cpp
+@@ -1623,6 +1623,9 @@
+                 writer()->write_u4((u4) (stack_depth + extra_frames));
+               }
+             }
++            if (locals->at(slot)->type() == T_UNION) {
++              Unimplemented(); //FIXME
++            }
+           }
+         } else {
+           // native frame
+diff --git a/src/share/vm/utilities/globalDefinitions.cpp b/src/share/vm/utilities/globalDefinitions.cpp
+--- a/src/share/vm/utilities/globalDefinitions.cpp
++++ b/src/share/vm/utilities/globalDefinitions.cpp
+@@ -93,11 +93,11 @@
+       num_type_chars++;
+     }
+   }
+-  assert(num_type_chars == 11, "must have tested the right number of mappings");
++  assert(num_type_chars == 12, "must have tested the right number of mappings");
+   assert(char2type(0) == T_ILLEGAL, "correct illegality");
+ 
+   {
+-    for (int i = T_BOOLEAN; i <= T_CONFLICT; i++) {
++    for (int i = T_MIN_VALUE; i <= T_CONFLICT; i++) {
+       BasicType vt = (BasicType)i;
+       BasicType ft = type2field[vt];
+       switch (vt) {
+@@ -111,6 +111,7 @@
+       case T_DOUBLE:
+       case T_LONG:
+       case T_OBJECT:
++      case T_UNION:
+       case T_ADDRESS:   // random raw pointer
+       case T_NARROWOOP: // compressed pointer
+       case T_CONFLICT:  // might as well support a bottom type
+@@ -178,11 +179,12 @@
+ 
+ 
+ // Map BasicType to signature character
+-char type2char_tab[T_CONFLICT+1]={ 0, 0, 0, 0, 'Z', 'C', 'F', 'D', 'B', 'S', 'I', 'J', 'L', '[', 'V', 0, 0, 0};
++char type2char_tab[T_CONFLICT+1]={ 0, 0, 0, 'U', 'Z', 'C', 'F', 'D', 'B', 'S', 'I', 'J', 'L', '[', 'V', 0, 0, 0};
+ 
+ // Map BasicType to Java type name
+ const char* type2name_tab[T_CONFLICT+1] = {
+-  NULL, NULL, NULL, NULL,
++  NULL, NULL, NULL,
++  "union",
+   "boolean",
+   "char",
+   "float",
+@@ -201,7 +203,7 @@
+ 
+ 
+ BasicType name2type(const char* name) {
+-  for (int i = T_BOOLEAN; i <= T_VOID; i++) {
++  for (int i = T_MIN_VALUE; i <= T_VOID; i++) {
+     BasicType t = (BasicType)i;
+     if (type2name_tab[t] != NULL && 0 == strcmp(type2name_tab[t], name))
+       return t;
+@@ -210,14 +212,14 @@
+ }
+ 
+ 
+-// Map BasicType to size in words
+-int type2size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, 1, -1};
++// Map BasicType to size in JVM stack slots
++int type2size[T_CONFLICT+1]={ -1, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, 1, -1};
+ 
+ BasicType type2field[T_CONFLICT+1] = {
+   (BasicType)0,            // 0,
+   (BasicType)0,            // 1,
+   (BasicType)0,            // 2,
+-  (BasicType)0,            // 3,
++  T_UNION,                 // T_UNION    =  3,
+   T_BOOLEAN,               // T_BOOLEAN  =  4,
+   T_CHAR,                  // T_CHAR     =  5,
+   T_FLOAT,                 // T_FLOAT    =  6,
+diff --git a/src/share/vm/utilities/globalDefinitions.hpp b/src/share/vm/utilities/globalDefinitions.hpp
+--- a/src/share/vm/utilities/globalDefinitions.hpp
++++ b/src/share/vm/utilities/globalDefinitions.hpp
+@@ -215,7 +215,6 @@
+ 
+ typedef unsigned int uint;   NEEDS_CLEANUP
+ 
+-
+ //----------------------------------------------------------------------------------------------------
+ // Java type definitions
+ 
+@@ -453,6 +452,7 @@
+ 
+ // NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/runtime/BasicType.java
+ enum BasicType {
++  T_UNION    =  3,  // tagged union, signature "U;"
+   T_BOOLEAN  =  4,
+   T_CHAR     =  5,
+   T_FLOAT    =  6,
+@@ -467,6 +467,7 @@
+   T_ADDRESS  = 15,
+   T_NARROWOOP= 16,
+   T_CONFLICT = 17, // for stack value type with conflicting contents
++  T_MIN_VALUE = T_UNION,
+   T_ILLEGAL  = 99
+ };
+ 
+@@ -497,6 +498,7 @@
+   case 'V': return T_VOID;
+   case 'L': return T_OBJECT;
+   case '[': return T_ARRAY;
++  case 'U': return T_UNION;
+   }
+   return T_ILLEGAL;
+ }
+@@ -525,6 +527,7 @@
+   T_LONG_size    = 2,
+   T_OBJECT_size  = 1,
+   T_ARRAY_size   = 1,
++  T_UNION_size   = 2,
+   T_NARROWOOP_size = 1,
+   T_VOID_size    = 0
+ };
+@@ -553,6 +556,7 @@
+   T_OBJECT_aelem_bytes  = 4,
+   T_ARRAY_aelem_bytes   = 4,
+ #endif
++  T_UNION_aelem_bytes   = 8,
+   T_NARROWOOP_aelem_bytes = 4,
+   T_VOID_aelem_bytes    = 0
+ };
+@@ -645,7 +649,8 @@
+   ftos = 5,             // float tos cached
+   dtos = 6,             // double tos cached
+   atos = 7,             // object cached
+-  vtos = 8,             // tos not cached
++  utos = 8,             // union cached (as a long)
++  vtos = 9,             // tos not cached
+   number_of_states,
+   ilgl                  // illegal state: should not occur
+ };
+@@ -661,6 +666,7 @@
+     case T_LONG   : return ltos;
+     case T_FLOAT  : return ftos;
+     case T_DOUBLE : return dtos;
++    case T_UNION  : return utos;
+     case T_VOID   : return vtos;
+     case T_ARRAY  : // fall through
+     case T_OBJECT : return atos;
+@@ -679,6 +685,7 @@
+     case ftos : return T_FLOAT;
+     case dtos : return T_DOUBLE;
+     case atos : return T_OBJECT;
++    case utos : return T_UNION;
+     case vtos : return T_VOID;
+   }
+   return T_ILLEGAL;
+diff --git a/src/share/vm/utilities/taggedUnion.cpp b/src/share/vm/utilities/taggedUnion.cpp
+new file mode 100644
+--- /dev/null
++++ b/src/share/vm/utilities/taggedUnion.cpp
+@@ -0,0 +1,32 @@
++/*
++ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ *
++ */
++
++#include "precompiled.hpp"
++#include "utilities/taggedUnion.hpp"
++
++#ifdef ASSERT
++bool TaggedUnion::interpreter_frame_expression_stack_grows_downward() {
++  return frame::interpreter_frame_expression_stack_direction() < 0;
++}
++#endif //ASSERT
+diff --git a/src/share/vm/utilities/taggedUnion.hpp b/src/share/vm/utilities/taggedUnion.hpp
+new file mode 100644
+--- /dev/null
++++ b/src/share/vm/utilities/taggedUnion.hpp
+@@ -0,0 +1,147 @@
++/*
++ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
++ *
++ * This code is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 only, as
++ * published by the Free Software Foundation.
++ *
++ * This code is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * version 2 for more details (a copy is included in the LICENSE file that
++ * accompanied this code).
++ *
++ * You should have received a copy of the GNU General Public License version
++ * 2 along with this work; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ *
++ */
++
++#ifndef SHARE_VM_UTILITIES_TAGGEDUNION_HPP
++#define SHARE_VM_UTILITIES_TAGGEDUNION_HPP
++
++#include "utilities/top.hpp"
++
++// Standard (portable) limit to T_UNION values which are not references:
++const jlong   max_junion_long = (jlong) (0x7ff0) << 48;  // architectural constant
++
++// The following limits apply only to addresses in the managed heap.
++// If the negative limit (which is inclusive) is 0, this means there are no negative addresses.
++// If the positive limit (which is exclusive) is 0, this means there are no positive addresses.
++#ifndef _LP64
++const julong  positive_address_limit   = (julong)1 << 32;
++const julong  negative_address_start   = (julong)0;
++#else //_LP64
++const julong  positive_address_limit   = (julong)1 << 48;
++#if defined(TARGET_OS_FAMILY_solaris)
++const julong  negative_address_start   = (julong)-1 << 48;
++#else
++// for other 64-bit systems, application addresses are positive
++const julong  negative_address_start   = (julong)0;
++#endif
++#endif //_LP64
++
++class TaggedUnion {
++  // this is the memory layout (cf. class HeapWord)
++  julong _bits;
++
++  TaggedUnion(julong bits) : _bits(bits) { }
++
++  // Internal (JVM-specific) decoding bias for primitive T_UNION values:
++  enum { TAG_SHIFT = 48 };
++  static julong encoding_bias() { return (julong) (0x8001) << TAG_SHIFT; }
++  static julong decoding_bias() { return (julong) (0x7fff) << TAG_SHIFT; }
++
++  bool check_is_ref(bool z) const {
++    assert(z == tag_is_ref(raw_tag()) == z, "same result");
++    return z;
++  }
++
++ public:
++  // Ad hoc transforms:
++  static julong  encode_ref(oop ref)      { return (julong) (uintptr_t) ref; }
++  static oop     decode_ref(julong bits)  { return (oop)(uintptr_t) bits; }
++  static julong  encode_prim(jlong  prim) { return (julong)prim + encoding_bias(); }
++  static jlong   decode_prim(julong bits) { return (jlong)(bits + decoding_bias()); }
++  static bool    bits_is_ref(julong bits) { return ((julong)(bits - negative_address_start) <=
++                                                    (julong)(positive_address_limit - negative_address_start)); }
++#ifdef _LP64
++  static bool    tag_is_ref(intptr_t tag) { return ((julong)(tag - (negative_address_start >> 32)) <=
++                                                    (julong)((positive_address_limit - negative_address_start) >> 32)); }
++#else
++  static bool    tag_is_ref(intptr_t tag) { return (tag == 0); }
++#endif
++
++  // Test if the ref falls between negative_address_start (inclusive) and positive_address_limit (exclusive)
++  static bool can_encode_ref(oop ref) { return bits_is_ref(encode_ref(ref)); }
++
++  TaggedUnion(const TaggedUnion& that) : _bits(that._bits) { }
++  static TaggedUnion from_prim(jlong prim) {
++    assert(prim <= max_junion_long, "oob");
++    return TaggedUnion(prim);
++  }
++  static TaggedUnion from_ref(oop ref) {
++    julong bits = encode_ref(ref);
++    assert(bits_is_ref(bits), "oob");
++    return TaggedUnion(bits);
++  }
++  static TaggedUnion from_raw_bits(julong bits) {
++    assert(decode_prim(bits) <= max_junion_long || bits_is_ref(bits), "oob");
++    return TaggedUnion(bits);
++  }
++  static TaggedUnion* locate_in_jvm_stack(intptr_t* first_local) {
++    // The first local is, e.g., L0 if L0/L1 is the pair.
++    assert(interpreter_frame_expression_stack_grows_downward(), "");
++    // Address of second word pushed is the first (and maybe only) memory word:
++    return (TaggedUnion*) (first_local - 1);
++  }
++
++  bool    is_ref()          const { return check_is_ref(bits_is_ref(_bits)); }
++  bool    is_prim()         const { return !is_ref(); }
++  oop     as_ref()          const { assert(is_ref(), "");    return decode_ref(_bits); }
++  oop     as_ref_or_null()  const { return !is_ref() ? NULL       : decode_ref(_bits); }
++  jlong   as_prim()         const { assert(is_prim(), "");   return decode_prim(_bits); }
++  jlong   as_prim_or_max()  const { return !is_prim() ? max_jlong : decode_prim(_bits); }
++  jlong   raw_bits()        const { return _bits; }
++
++  // in-memory access:
++  bool      is_ref_safe()   const { return tag_is_ref(raw_tag()); }
++  oop*      ref_addr()      const { assert(is_ref_safe(), ""); return (oop*) raw_ref_addr(); }
++  intptr_t  raw_tag()       const { return *raw_tag_addr(); }
++  intptr_t* raw_ref_addr()  const { return (intptr_t*)( (address) this + ref_offset_in_bytes()); }
++  intptr_t* raw_tag_addr()  const { return (intptr_t*)( (address) this + tag_offset_in_bytes()); }
++
++#ifdef _LP64
++  jlong     raw_bits_safe() const { return _bits; }
++#else
++  // this is required for loading from a JVM interpreter stack:
++  jlong     raw_bits_safe() const { return jlong_from((jint) *raw_tag_addr(),
++                                                      (jint) *raw_ref_addr()); }
++#endif
++
++  // for code generators, offsets of intptr_t subcomponents:
++#ifdef _LP64
++  static size_t tag_offset_in_bytes()      { return 0; }
++  static size_t ref_offset_in_bytes()      { return 0; }
++  static jlong  load_raw_bits(intptr_t* p) { return (jlong) *p; }
++#elif defined(VM_LITTLE_ENDIAN)
++  static size_t tag_offset_in_bytes()      { return 1 * wordSize; }
++  static size_t ref_offset_in_bytes()      { return 0 * wordSize; }
++  static jlong  load_raw_bits(intptr_t* p) { return jlong_from(p[1], p[0]); }
++#else // Big-Endian
++  static size_t tag_offset_in_bytes()      { return 0 * wordSize; }
++  static size_t ref_offset_in_bytes()      { return 1 * wordSize; }
++  static jlong  load_raw_bits(intptr_t* p) { return jlong_from(p[0], p[1]); }
++#endif
++
++#ifdef ASSERT
++  static bool interpreter_frame_expression_stack_grows_downward();
++#endif
++};
++
++#endif // SHARE_VM_UTILITIES_TAGGEDUNION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tagu.txt	Tue Sep 27 19:02:18 2011 -0700
@@ -0,0 +1,219 @@
+Notes for tagged unions in the JVM
+John Rose, 9/2011
+
+Goals:
+ - near-complete coverage of jlong range, without gaps
+ - complete coverage of jdouble range (via longBitsToDouble)
+ - support object heap sizes 1000 .. 10^6 times larger than present
+ - fast conversion to/from reference
+ - fast conversion to/from jlong
+ - fast conversion to/from jdouble (via longBitsToDouble, etc.)
+ - allow code space (~ 33 bits) for primitive values other than longs and references
+ - portable specification of jlong range
+ - portable specification of jdouble range (except perhaps for NaN bits)
+ - for non-float values, occupy unused jdouble code points (there are 2^53 - 3)
+ - if data loss occurs (because of silent truncation), magnitudes must be preserved
+
+Key architectural constant:  MAX_UNION_LONG_VALUE = 0x7ff0_000000000000.
+This value is chosen so that there are enough code points to represent all doubles.
+All values in MAX_UNION_LONG_VALUE+1 to Long.MAX_VALUE are reserved for references.
+This is true regardless of the internal representation of the tagged union.
+The internal representation will in general have unused code-points.
+
+The relevant user-visible ranges of primitives are thus:
+  0x8000_000000000000 .. 0x7ff0_000000000000 : partial jlong, full jdouble
+  0x7ff0_000000000001 .. 0x7fff_fffffffffffe : illegal values (never encoded or decoded)
+  0x7fff_ffffffffffff                        : sentinel value (never encoded)
+
+The sentinel value (Long.MAX_VALUE) may be returned from the non-signaling
+decode operations, unionToLongSafe and unionToDoubleSafe.
+
+Key implementation choice:  Which type is favored by being stored natively?
+If longs, we say "long-native".  This also happens to be "double-native",
+since all points above MAX_UNION_LONG_VALUE encode the single NaN value.
+Decoding a long-native representation to a reference costs a test and add.
+
+If addresses are stored natively (with padding), we say "address-native".
+If addresses occupy one 50-bit range, decoding a long costs a test and add.
+Address-native is the only representation that works well with Hotspot's "oops_do" APIs.
+
+If the padding is all zero, we say "positive-address-native".
+In this case, the representation is not "double-native", since native references
+when loaded as doubles represent denormalized numbers or zero.
+
+The bias for decoding must be such that for a pointer p, p+encBias is somewhere
+in the illegal jlong value range.  Of course decBias = uint64_t(-encBias).
+
+Preferred "positive-address-native" representation as padded address (jlong/intptr_t):
+  0x8000_000000000000 .. 0xfff1_000000000000 : biased jlong/jdouble
+  0xfff1_000000000001 .. 0xfffe_ffffffffffff : unused code points (never stored)
+  0xffff_000000000000 .. 0xffff_ffffffffffff : natural negative address (2^48-1 code points)
+  0x0000_000000000000                        : natural null address
+  0x0000_000000000001 .. 0x0000_ffffffffffff : natural positive address (2^48-2 code points)
+  0x0001_000000000000 .. 0x7fff_ffffffffffff : biased jlong/jdouble
+
+The encoding and decoding biases are 0x8001_000000000000 and 0x7fff_000000000000.
+  0x8000_000000000000 .. 0x7ff0_000000000000 : partial jlong (decoded)
+  0x0001_000000000000 :: 0xfff1_000000000000 : encoded (wrapped range)
+  0xfff1_000000000001 .. 0x0000_ffffffffffff : non-jlong code points (range complement)
+
+If pointer signs are negative, the representation is "negative-address-native".
+In this case, the pointer padding is not zeroes but ones, and the bias differs.
+The null address (all zero bits) must be adjoined to the negative range after -1.
+
+(The "negative-address-native" representation can also be "double-native",
+since such values are NaNs.  They include small-magnitude negative jlongs
+like jlong(-1), and so are are key values of the jlong range.
+Unless long and double bit patterns are decoupled, treating such values
+as native doubles does not pay off.  Solaris is notable for allocating
+negative addresses to managed heaps.  Most OSs allocate positive addresses.
+Also, all 32-bit addresses can be treated as positive regardless of bit 31.)
+
+JVM structure:
+  BasicType: T_UNION = 3
+  signature sytax: 'U'
+  occupies two stack slots, like long and double
+  garbage collector can find embedded references, on stack/locals/registers/heap
+
+API in package sun.misc:
+class Union {
+  public static final long MAX_UNION_LONG_VALUE = 0x7ff0_000000000000;
+  assert(longBitsToDouble(MAX_UNION_LONG_VALUE) == POSITIVE_INFINITY);
+
+  public static final long NO_UNION_LONG_VALUE  = 0x7fff_ffffffffffff;
+  // NO_UNION_LONG_VALUE is similar in magnitude to MAX_UNION_LONG_VALUE
+  assert(NO_UNION_LONG_VALUE == Long.MAX_VALUE);
+  // it is also a NaN, when viewed as a double
+  assert(isNan(longBitsToDouble(NO_UNION_LONG_VALUE)));
+
+  public static boolean isReference(U<?> x) { ... }
+
+  public static U<?> longToUnion(long x) throws IllegalArgumentException { encodes the long }
+  public static U<?> doubleToUnion(double x) { encodes the double (no error) }
+  public static <T> U<T> referenceToUnion(T x) { encodes the reference (no error) }
+
+  public static long unionToLong(U<?> x) throws IllegalUnionException { returns long value }
+  public static double unionToDouble(U<?> x) throws IllegalUnionException { returns double value }
+  public static <T> T unionToReference(U<T> x) throws IllegalUnionException { returns reference }
+
+  public static long unionToLongSafe(U<?> x) { returns long value else NO_UNION_LONG_VALUE }
+  public static double unionToDoubleSafe(U<?> x) { returns double value else NaN }
+  public static <T> T unionToReferenceSafe(U<T> x) { returns reference else null }
+}
+class IllegalUnionException extends RuntimeException { ... }
+
+Details of representation (assuming decoding and encoding biases above):
+
+private final long __ENCODING_BIAS = 0x8001_000000000000;
+private final long __DECODING_BIAS = 0x7fff_000000000000;
+assert((long)(__ENCODING_BIAS + __DECODING_BIAS) == 0);
+
+private final long __ADDRESS_LIMIT = 0x0001_000000000000;
+private final bool __NEGATIVE_ADDRESSES = true;  // can set false in ILP32, Windows, etc.
+
+int64_t union_is_object(int64_t ux) {
+  bool r0 = (ux + __DECODING_BIAS) > MAX_UNION_LONG_VALUE;
+  bool r1 = ((uint64_t)ux >> 48 == 0x0000
+             || (__NEGATIVE_ADDRESSES && (uint64_t)ux >> 48 == 0xffff));
+  bool r2 = ((uint64_t)ux < (uint64_t)__ADDRESS_LIMIT
+             || (__NEGATIVE_ADDRESSES && (uint64_t)ux > -__ADDRESS_LIMIT));
+  bool r3 = ((uint64_t)ux + (__NEGATIVE_ADDRESSES ? __ADDRESS_LIMIT : 0)
+             < (uint64_t)__ADDRESS_LIMIT);
+  assert(r0 == r1 && r1 == r2 && r2 == r3);
+  return r1;
+}
+
+jlong union_to_jlong(int64_t ux, bool safe = false) {
+  jlong x = ux + __DECODING_BIAS;
+  if (x <= MAX_UNION_LONG_VALUE) {
+    return x;
+  }
+  if (!safe)  throw new IllegalUnionException("unexpected reference");
+  return NO_UNION_LONG_VALUE;
+}
+
+int64_t jlong_to_union(jlong x, bool safe = false) {
+  if (x <= MAX_UNION_LONG_VALUE)
+    return x + __ENCODING_BIAS;
+  if (!safe)  throw new IllegalUnionException("unexpected reference");
+  // convert to null reference
+  assert(union_to_object(0) == (object)null);
+  return 0;
+}
+
+jdouble union_to_jdouble(int64_t ux, bool safe = false) {
+  return jdouble_cast(union_to_jlong(ux, safe));
+}
+
+int64_t jdouble_to_union(jdouble dx) {
+  bool safe = true;  // always safe to yield a NaN
+  return jlong_to_union(jlong_cast(dx, safe));
+}
+
+object union_to_object(int64_t ux, bool safe = false) {
+  if (union_is_object(ux))
+    return object_cast((uintptr_t)(uint64_t) ux);
+  if (!safe)  throw new IllegalUnionException("unexpected non-reference");
+  return null;
+}
+
+int64_t object_to_union(object rx) {
+  // this is an error-free operation; encode all references, including null
+  return object_uncast(rx);
+}
+
+object object_cast(uintptr_t x) {
+  if (x == 0) {
+    assert((object)x == (object)null);
+  } else {
+    x *= OBJECT_HEAP_SCALE;  // might be a no-op
+    x += OBJECT_HEAP_BASE;   // might be a no-op
+  }
+  return (object)x;
+}
+
+uintptr_t object_uncast(object rx) {
+  uintptr_t x = ((uintptr_t)rx);
+  if (rx == (object)null) {
+    assert(x == 0);
+  } else {
+    x -= OBJECT_HEAP_BASE;   // might be a no-op
+    x /= OBJECT_HEAP_SCALE;  // might be a no-op
+    assert(x > 0 && x < __ENCODING_BIAS);
+  }
+  return x;
+}
+
+typedef void oops_do_function(object* x);
+void union_oops_do(int64_t *field, oops_do_function* fn) {
+  // if primitive or null, return immediately
+  if (!union_is_object(*field) || (*field) == 0)  return;
+  object* objAddr = (object*) field;
+  if (sizeof(object) != sizeof(int64_t) && __BIG_ENDIAN)
+    objAddr = (object*) (field+1) - 1;
+  fn(objAddr);
+}
+
+FTR, relevant ranges which are native to the double format:
+  0x0000_000000000000 .. 0x7fef_ffffffffffff : positive rationals
+  0x7ff0_000000000000                        : positive infinity
+  0x7ff0_000000000001 .. 0x7ff7_ffffffffffff : nonstandard NaNs
+  0x7ff8_000000000000                        : standard NaN (for Java)
+  0x7ff8_000000000001 .. 0x7fff_ffffffffffff : nonstandard NaNs
+  0x8000_000000000000                        : negative zero
+  0x8000_000000000001 .. 0xffef_ffffffffffff : negative rationals
+  0xfff0_000000000000                        : negative infinity
+  0xfff0_000000000001 .. 0xffff_ffffffffffff : nonstandard NaNs
+
+All NaN ranges are available as code points for non-double values.
+Only one NaN code point needs to be reserved to represent all NaNs in common.
+
+Non-preferred "long-native" representation using jlong (int64_t):
+  0x8000_000000000000 .. 0x7ff0_000000000000 : native partial jlong; native full jdouble
+  0x7ff0_000000000001 .. 0x7fff_ffffffffffff : biased reference (2^52 - 1 code points)
+This is non-preferred because the biased references are hard for the Hotspot GC to process.
+
+Non-preferred "unsigned-long-native" representation using non-standard type "julong" (u8/uint64_t)
+  0x0000_000000000000 .. 0xfff0_000000000000 : partial unsigned long; full jdouble
+  0xfff0_000000000001 .. 0xffff_ffffffffffff : biased reference (2^52 - 1 code points)
+This is worse than "long-native" because it opens a hole in the middle of the jlong range.