changeset 22240:67b20ea8496c

Track base pointers of derived references in backend.
author Roland Schatz <roland.schatz@oracle.com>
date Fri, 17 Jul 2015 11:41:55 +0200
parents e45ce323bdf5
children 7ca4c8c37ca9 2fbb9ce20d7d
files graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64AddressNode.java graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCImmediateAddressNode.java graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCIndexedAddressNode.java graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReferenceMapBuilder.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/LocationMarker.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/MarkBasePointersPhase.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/ValueSet.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/AllocationStage.java graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DerivedOopTest.java jvmci/jdk.internal.jvmci.meta/src/jdk/internal/jvmci/meta/LIRKind.java
diffstat 14 files changed, 619 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64AddressNode.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64AddressNode.java	Fri Jul 17 11:41:55 2015 +0200
@@ -66,7 +66,19 @@
         AllocatableValue baseValue = base == null ? Value.ILLEGAL : tool.asAllocatable(gen.operand(base));
         AllocatableValue indexValue = index == null ? Value.ILLEGAL : tool.asAllocatable(gen.operand(index));
 
-        LIRKind kind = tool.getLIRKind(stamp());
+        AllocatableValue baseReference = LIRKind.derivedBaseFromValue(baseValue);
+        AllocatableValue indexReference;
+        if (scale.equals(Scale.Times1)) {
+            indexReference = LIRKind.derivedBaseFromValue(indexValue);
+        } else {
+            if (indexValue.getLIRKind().isValue()) {
+                indexReference = null;
+            } else {
+                indexReference = Value.ILLEGAL;
+            }
+        }
+
+        LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp()), baseReference, indexReference);
         gen.setResult(this, new AMD64AddressValue(kind, baseValue, indexValue, scale, displacement));
     }
 
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Fri Jul 17 11:41:55 2015 +0200
@@ -662,29 +662,29 @@
         return result;
     }
 
-    private Variable emitBinary(AMD64BinaryArithmetic op, OperandSize size, boolean commutative, Value a, Value b, boolean setFlags) {
+    private Variable emitBinary(LIRKind resultKind, AMD64BinaryArithmetic op, OperandSize size, boolean commutative, Value a, Value b, boolean setFlags) {
         if (isConstant(b)) {
-            return emitBinaryConst(op, size, commutative, asAllocatable(a), asConstant(b), setFlags);
+            return emitBinaryConst(resultKind, op, size, commutative, asAllocatable(a), asConstant(b), setFlags);
         } else if (commutative && isConstant(a)) {
-            return emitBinaryConst(op, size, commutative, asAllocatable(b), asConstant(a), setFlags);
+            return emitBinaryConst(resultKind, op, size, commutative, asAllocatable(b), asConstant(a), setFlags);
         } else {
-            return emitBinaryVar(op.getRMOpcode(size), size, commutative, asAllocatable(a), asAllocatable(b));
+            return emitBinaryVar(resultKind, op.getRMOpcode(size), size, commutative, asAllocatable(a), asAllocatable(b));
         }
     }
 
-    private Variable emitBinary(AMD64RMOp op, OperandSize size, boolean commutative, Value a, Value b) {
+    private Variable emitBinary(LIRKind resultKind, AMD64RMOp op, OperandSize size, boolean commutative, Value a, Value b) {
         if (isConstant(b)) {
-            return emitBinaryConst(op, size, asAllocatable(a), asConstant(b));
+            return emitBinaryConst(resultKind, op, size, asAllocatable(a), asConstant(b));
         } else if (commutative && isConstant(a)) {
-            return emitBinaryConst(op, size, asAllocatable(b), asConstant(a));
+            return emitBinaryConst(resultKind, op, size, asAllocatable(b), asConstant(a));
         } else {
-            return emitBinaryVar(op, size, commutative, asAllocatable(a), asAllocatable(b));
+            return emitBinaryVar(resultKind, op, size, commutative, asAllocatable(a), asAllocatable(b));
         }
     }
 
-    private Variable emitBinaryConst(AMD64BinaryArithmetic op, OperandSize size, boolean commutative, AllocatableValue a, JavaConstant b, boolean setFlags) {
+    private Variable emitBinaryConst(LIRKind resultKind, AMD64BinaryArithmetic op, OperandSize size, boolean commutative, AllocatableValue a, JavaConstant b, boolean setFlags) {
         if (NumUtil.isInt(b.asLong())) {
-            Variable result = newVariable(LIRKind.combine(a, b));
+            Variable result = newVariable(resultKind);
             int constant = (int) b.asLong();
 
             if (!setFlags) {
@@ -698,7 +698,7 @@
             append(new AMD64Binary.ConstOp(op, size, result, a, constant));
             return result;
         } else {
-            return emitBinaryVar(op.getRMOpcode(size), size, commutative, a, asAllocatable(b));
+            return emitBinaryVar(resultKind, op.getRMOpcode(size), size, commutative, a, asAllocatable(b));
         }
     }
 
@@ -721,14 +721,14 @@
         return null;
     }
 
-    private Variable emitBinaryConst(AMD64RMOp op, OperandSize size, AllocatableValue a, JavaConstant b) {
-        Variable result = newVariable(LIRKind.combine(a, b));
+    private Variable emitBinaryConst(LIRKind resultKind, AMD64RMOp op, OperandSize size, AllocatableValue a, JavaConstant b) {
+        Variable result = newVariable(resultKind);
         append(new AMD64Binary.DataOp(op, size, result, a, b));
         return result;
     }
 
-    private Variable emitBinaryVar(AMD64RMOp op, OperandSize size, boolean commutative, AllocatableValue a, AllocatableValue b) {
-        Variable result = newVariable(LIRKind.combine(a, b));
+    private Variable emitBinaryVar(LIRKind resultKind, AMD64RMOp op, OperandSize size, boolean commutative, AllocatableValue a, AllocatableValue b) {
+        Variable result = newVariable(resultKind);
         if (commutative) {
             append(new AMD64Binary.CommutativeOp(op, size, result, a, b));
         } else {
@@ -738,32 +738,32 @@
     }
 
     @Override
-    public Variable emitAdd(Value a, Value b, boolean setFlags) {
+    public Variable emitAdd(LIRKind resultKind, Value a, Value b, boolean setFlags) {
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(ADD, DWORD, true, a, b, setFlags);
+                return emitBinary(resultKind, ADD, DWORD, true, a, b, setFlags);
             case Long:
-                return emitBinary(ADD, QWORD, true, a, b, setFlags);
+                return emitBinary(resultKind, ADD, QWORD, true, a, b, setFlags);
             case Float:
-                return emitBinary(SSEOp.ADD, SS, true, a, b);
+                return emitBinary(resultKind, SSEOp.ADD, SS, true, a, b);
             case Double:
-                return emitBinary(SSEOp.ADD, SD, true, a, b);
+                return emitBinary(resultKind, SSEOp.ADD, SD, true, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
     }
 
     @Override
-    public Variable emitSub(Value a, Value b, boolean setFlags) {
+    public Variable emitSub(LIRKind resultKind, Value a, Value b, boolean setFlags) {
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(SUB, DWORD, false, a, b, setFlags);
+                return emitBinary(resultKind, SUB, DWORD, false, a, b, setFlags);
             case Long:
-                return emitBinary(SUB, QWORD, false, a, b, setFlags);
+                return emitBinary(resultKind, SUB, QWORD, false, a, b, setFlags);
             case Float:
-                return emitBinary(SSEOp.SUB, SS, false, a, b);
+                return emitBinary(resultKind, SSEOp.SUB, SS, false, a, b);
             case Double:
-                return emitBinary(SSEOp.SUB, SD, false, a, b);
+                return emitBinary(resultKind, SSEOp.SUB, SD, false, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
@@ -783,7 +783,7 @@
             append(new AMD64Binary.RMIOp(op, size, ret, a, imm));
             return ret;
         } else {
-            return emitBinaryVar(AMD64RMOp.IMUL, size, true, a, asAllocatable(b));
+            return emitBinaryVar(LIRKind.combine(a, b), AMD64RMOp.IMUL, size, true, a, asAllocatable(b));
         }
     }
 
@@ -793,7 +793,7 @@
         } else if (isConstant(a)) {
             return emitIMULConst(size, asAllocatable(b), asConstant(a));
         } else {
-            return emitBinaryVar(AMD64RMOp.IMUL, size, true, asAllocatable(a), asAllocatable(b));
+            return emitBinaryVar(LIRKind.combine(a, b), AMD64RMOp.IMUL, size, true, asAllocatable(a), asAllocatable(b));
         }
     }
 
@@ -805,9 +805,9 @@
             case Long:
                 return emitIMUL(QWORD, a, b);
             case Float:
-                return emitBinary(SSEOp.MUL, SS, true, a, b);
+                return emitBinary(LIRKind.combine(a, b), SSEOp.MUL, SS, true, a, b);
             case Double:
-                return emitBinary(SSEOp.MUL, SD, true, a, b);
+                return emitBinary(LIRKind.combine(a, b), SSEOp.MUL, SD, true, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
@@ -926,9 +926,9 @@
                 AMD64MulDivOp lop = emitIDIV(QWORD, a, b, state);
                 return emitMove(lop.getQuotient());
             case Float:
-                return emitBinary(SSEOp.DIV, SS, false, a, b);
+                return emitBinary(LIRKind.combine(a, b), SSEOp.DIV, SS, false, a, b);
             case Double:
-                return emitBinary(SSEOp.DIV, SD, false, a, b);
+                return emitBinary(LIRKind.combine(a, b), SSEOp.DIV, SD, false, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
@@ -992,15 +992,16 @@
 
     @Override
     public Variable emitAnd(Value a, Value b) {
+        LIRKind resultKind = LIRKind.combine(a, b);
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(AND, DWORD, true, a, b, false);
+                return emitBinary(resultKind, AND, DWORD, true, a, b, false);
             case Long:
-                return emitBinary(AND, QWORD, true, a, b, false);
+                return emitBinary(resultKind, AND, QWORD, true, a, b, false);
             case Float:
-                return emitBinary(SSEOp.AND, PS, true, a, b);
+                return emitBinary(resultKind, SSEOp.AND, PS, true, a, b);
             case Double:
-                return emitBinary(SSEOp.AND, PD, true, a, b);
+                return emitBinary(resultKind, SSEOp.AND, PD, true, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
@@ -1008,15 +1009,16 @@
 
     @Override
     public Variable emitOr(Value a, Value b) {
+        LIRKind resultKind = LIRKind.combine(a, b);
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(OR, DWORD, true, a, b, false);
+                return emitBinary(resultKind, OR, DWORD, true, a, b, false);
             case Long:
-                return emitBinary(OR, QWORD, true, a, b, false);
+                return emitBinary(resultKind, OR, QWORD, true, a, b, false);
             case Float:
-                return emitBinary(SSEOp.OR, PS, true, a, b);
+                return emitBinary(resultKind, SSEOp.OR, PS, true, a, b);
             case Double:
-                return emitBinary(SSEOp.OR, PD, true, a, b);
+                return emitBinary(resultKind, SSEOp.OR, PD, true, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
@@ -1024,15 +1026,16 @@
 
     @Override
     public Variable emitXor(Value a, Value b) {
+        LIRKind resultKind = LIRKind.combine(a, b);
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(XOR, DWORD, true, a, b, false);
+                return emitBinary(resultKind, XOR, DWORD, true, a, b, false);
             case Long:
-                return emitBinary(XOR, QWORD, true, a, b, false);
+                return emitBinary(resultKind, XOR, QWORD, true, a, b, false);
             case Float:
-                return emitBinary(SSEOp.XOR, PS, true, a, b);
+                return emitBinary(resultKind, SSEOp.XOR, PS, true, a, b);
             case Double:
-                return emitBinary(SSEOp.XOR, PD, true, a, b);
+                return emitBinary(resultKind, SSEOp.XOR, PD, true, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCImmediateAddressNode.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCImmediateAddressNode.java	Fri Jul 17 11:41:55 2015 +0200
@@ -57,6 +57,11 @@
         AllocatableValue baseValue = tool.asAllocatable(gen.operand(base));
 
         LIRKind kind = tool.getLIRKind(stamp());
+        AllocatableValue baseReference = LIRKind.derivedBaseFromValue(baseValue);
+        if (baseReference != null) {
+            kind = kind.makeDerivedReference(baseReference);
+        }
+
         gen.setResult(this, new SPARCImmediateAddressValue(kind, baseValue, displacement));
     }
 
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCIndexedAddressNode.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCIndexedAddressNode.java	Fri Jul 17 11:41:55 2015 +0200
@@ -55,7 +55,10 @@
         AllocatableValue baseValue = tool.asAllocatable(gen.operand(base));
         AllocatableValue indexValue = tool.asAllocatable(gen.operand(index));
 
-        LIRKind kind = tool.getLIRKind(stamp());
+        AllocatableValue baseReference = LIRKind.derivedBaseFromValue(baseValue);
+        AllocatableValue indexReference = LIRKind.derivedBaseFromValue(indexValue);
+
+        LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp()), baseReference, indexReference);
         gen.setResult(this, new SPARCIndexedAddressValue(kind, baseValue, indexValue));
     }
 
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Fri Jul 17 11:41:55 2015 +0200
@@ -508,21 +508,21 @@
         return result;
     }
 
-    private Variable emitBinary(SPARCArithmetic op, boolean commutative, Value a, Value b) {
-        return emitBinary(op, commutative, a, b, null);
+    private Variable emitBinary(LIRKind resultKind, SPARCArithmetic op, boolean commutative, Value a, Value b) {
+        return emitBinary(resultKind, op, commutative, a, b, null);
     }
 
-    private Variable emitBinary(SPARCArithmetic op, boolean commutative, Value a, Value b, LIRFrameState state) {
+    private Variable emitBinary(LIRKind resultKind, SPARCArithmetic op, boolean commutative, Value a, Value b, LIRFrameState state) {
         if (isConstant(b) && canInlineConstant(asConstant(b))) {
-            return emitBinaryConst(op, load(a), asConstant(b), state);
+            return emitBinaryConst(resultKind, op, load(a), asConstant(b), state);
         } else if (commutative && isConstant(a) && canInlineConstant(asConstant(a))) {
-            return emitBinaryConst(op, load(b), asConstant(a), state);
+            return emitBinaryConst(resultKind, op, load(b), asConstant(a), state);
         } else {
-            return emitBinaryVar(op, load(a), load(b), state);
+            return emitBinaryVar(resultKind, op, load(a), load(b), state);
         }
     }
 
-    private Variable emitBinaryConst(SPARCArithmetic op, AllocatableValue a, JavaConstant b, LIRFrameState state) {
+    private Variable emitBinaryConst(LIRKind resultKind, SPARCArithmetic op, AllocatableValue a, JavaConstant b, LIRFrameState state) {
         switch (op) {
             case IADD:
             case LADD:
@@ -537,48 +537,48 @@
             case IMUL:
             case LMUL:
                 if (canInlineConstant(b)) {
-                    Variable result = newVariable(LIRKind.combine(a, b));
+                    Variable result = newVariable(resultKind);
                     append(new BinaryRegConst(op, result, a, b, state));
                     return result;
                 }
                 break;
         }
-        return emitBinaryVar(op, a, asAllocatable(b), state);
+        return emitBinaryVar(resultKind, op, a, asAllocatable(b), state);
     }
 
-    private Variable emitBinaryVar(SPARCArithmetic op, AllocatableValue a, AllocatableValue b, LIRFrameState state) {
-        Variable result = newVariable(LIRKind.combine(a, b));
+    private Variable emitBinaryVar(LIRKind resultKind, SPARCArithmetic op, AllocatableValue a, AllocatableValue b, LIRFrameState state) {
+        Variable result = newVariable(resultKind);
         append(new BinaryRegReg(op, result, a, b, state));
         return result;
     }
 
     @Override
-    public Variable emitAdd(Value a, Value b, boolean setFlags) {
+    public Variable emitAdd(LIRKind resultKind, Value a, Value b, boolean setFlags) {
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(setFlags ? IADDCC : IADD, true, a, b);
+                return emitBinary(resultKind, setFlags ? IADDCC : IADD, true, a, b);
             case Long:
-                return emitBinary(setFlags ? LADDCC : LADD, true, a, b);
+                return emitBinary(resultKind, setFlags ? LADDCC : LADD, true, a, b);
             case Float:
-                return emitBinary(FADD, true, a, b);
+                return emitBinary(resultKind, FADD, true, a, b);
             case Double:
-                return emitBinary(DADD, true, a, b);
+                return emitBinary(resultKind, DADD, true, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
     }
 
     @Override
-    public Variable emitSub(Value a, Value b, boolean setFlags) {
+    public Variable emitSub(LIRKind resultKind, Value a, Value b, boolean setFlags) {
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(setFlags ? ISUBCC : ISUB, false, a, b);
+                return emitBinary(resultKind, setFlags ? ISUBCC : ISUB, false, a, b);
             case Long:
-                return emitBinary(setFlags ? LSUBCC : LSUB, false, a, b);
+                return emitBinary(resultKind, setFlags ? LSUBCC : LSUB, false, a, b);
             case Float:
-                return emitBinary(FSUB, false, a, b);
+                return emitBinary(resultKind, FSUB, false, a, b);
             case Double:
-                return emitBinary(DSUB, false, a, b);
+                return emitBinary(resultKind, DSUB, false, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere("missing: " + a.getKind());
         }
@@ -586,21 +586,22 @@
 
     @Override
     public Variable emitMul(Value a, Value b, boolean setFlags) {
+        LIRKind resultKind = LIRKind.combine(a, b);
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(setFlags ? IMULCC : IMUL, true, a, b);
+                return emitBinary(resultKind, setFlags ? IMULCC : IMUL, true, a, b);
             case Long:
                 if (setFlags) {
                     Variable result = newVariable(LIRKind.combine(a, b));
                     append(new SPARCLMulccOp(result, load(a), load(b), this));
                     return result;
                 } else {
-                    return emitBinary(LMUL, true, a, b);
+                    return emitBinary(resultKind, LMUL, true, a, b);
                 }
             case Float:
-                return emitBinary(FMUL, true, a, b);
+                return emitBinary(resultKind, FMUL, true, a, b);
             case Double:
-                return emitBinary(DMUL, true, a, b);
+                return emitBinary(resultKind, DMUL, true, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere("missing: " + a.getKind());
         }
@@ -639,15 +640,16 @@
 
     @Override
     public Value emitDiv(Value a, Value b, LIRFrameState state) {
+        LIRKind resultKind = LIRKind.combine(a, b);
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(IDIV, false, a, b, state);
+                return emitBinary(resultKind, IDIV, false, a, b, state);
             case Long:
-                return emitBinary(LDIV, false, a, b, state);
+                return emitBinary(resultKind, LDIV, false, a, b, state);
             case Float:
-                return emitBinary(FDIV, false, a, b, state);
+                return emitBinary(resultKind, FDIV, false, a, b, state);
             case Double:
-                return emitBinary(DDIV, false, a, b, state);
+                return emitBinary(resultKind, DDIV, false, a, b, state);
             default:
                 throw JVMCIError.shouldNotReachHere("missing: " + a.getKind());
         }
@@ -729,16 +731,17 @@
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
-        return emitBinary(op, false, actualA, actualB, state);
+        return emitBinary(LIRKind.combine(actualA, actualB), op, false, actualA, actualB, state);
     }
 
     @Override
     public Variable emitAnd(Value a, Value b) {
+        LIRKind resultKind = LIRKind.combine(a, b);
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(IAND, true, a, b);
+                return emitBinary(resultKind, IAND, true, a, b);
             case Long:
-                return emitBinary(LAND, true, a, b);
+                return emitBinary(resultKind, LAND, true, a, b);
 
             default:
                 throw JVMCIError.shouldNotReachHere("missing: " + a.getKind());
@@ -747,11 +750,12 @@
 
     @Override
     public Variable emitOr(Value a, Value b) {
+        LIRKind resultKind = LIRKind.combine(a, b);
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(IOR, true, a, b);
+                return emitBinary(resultKind, IOR, true, a, b);
             case Long:
-                return emitBinary(LOR, true, a, b);
+                return emitBinary(resultKind, LOR, true, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere("missing: " + a.getKind());
         }
@@ -759,11 +763,12 @@
 
     @Override
     public Variable emitXor(Value a, Value b) {
+        LIRKind resultKind = LIRKind.combine(a, b);
         switch (a.getKind().getStackKind()) {
             case Int:
-                return emitBinary(IXOR, true, a, b);
+                return emitBinary(resultKind, IXOR, true, a, b);
             case Long:
-                return emitBinary(LXOR, true, a, b);
+                return emitBinary(resultKind, LXOR, true, a, b);
             default:
                 throw JVMCIError.shouldNotReachHere();
         }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReferenceMapBuilder.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReferenceMapBuilder.java	Fri Jul 17 11:41:55 2015 +0200
@@ -87,10 +87,18 @@
             if (kind.isUnknownReference()) {
                 throw JVMCIError.unimplemented("derived references not yet implemented");
             } else {
+                Location base = null;
+                if (kind.isDerivedReference()) {
+                    Variable baseVariable = (Variable) kind.getDerivedReferenceBase();
+                    Value baseValue = state.getLiveBasePointers().get(baseVariable.index);
+                    assert baseValue.getPlatformKind().getVectorLength() == 1 && baseValue.getLIRKind().isReference(0) && !baseValue.getLIRKind().isDerivedReference();
+                    base = toLocation(baseValue, 0);
+                }
+
                 for (int i = 0; i < kind.getPlatformKind().getVectorLength(); i++) {
                     if (kind.isReference(i)) {
                         objects[idx] = toLocation(obj, i * bytes);
-                        derivedBase[idx] = null;
+                        derivedBase[idx] = base;
                         sizeInBytes[idx] = bytes;
                         idx++;
                     }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java	Fri Jul 17 11:41:55 2015 +0200
@@ -31,6 +31,7 @@
 
 import com.oracle.graal.lir.LIRInstruction.OperandFlag;
 import com.oracle.graal.lir.LIRInstruction.OperandMode;
+import com.oracle.graal.lir.dfa.*;
 import com.oracle.graal.lir.framemap.*;
 
 /**
@@ -44,6 +45,8 @@
     public final LabelRef exceptionEdge;
     protected DebugInfo debugInfo;
 
+    private ValueSet liveBasePointers;
+
     public LIRFrameState(BytecodeFrame topFrame, VirtualObject[] virtualObjects, LabelRef exceptionEdge) {
         this.topFrame = topFrame;
         this.virtualObjects = virtualObjects;
@@ -73,6 +76,9 @@
                 processValues(inst, obj.getValues(), proc);
             }
         }
+        if (liveBasePointers != null) {
+            liveBasePointers.forEach(inst, OperandMode.ALIVE, STATE_FLAGS, proc);
+        }
     }
 
     /**
@@ -89,6 +95,9 @@
                 processValues(inst, obj.getValues(), proc);
             }
         }
+        if (liveBasePointers != null) {
+            liveBasePointers.forEach(inst, OperandMode.ALIVE, STATE_FLAGS, proc);
+        }
     }
 
     /**
@@ -172,6 +181,14 @@
         debugInfo = new DebugInfo(topFrame, virtualObjects);
     }
 
+    public ValueSet getLiveBasePointers() {
+        return liveBasePointers;
+    }
+
+    public void setLiveBasePointers(ValueSet liveBasePointers) {
+        this.liveBasePointers = liveBasePointers;
+    }
+
     @Override
     public String toString() {
         return debugInfo != null ? debugInfo.toString() : topFrame != null ? topFrame.toString() : "<empty>";
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/LocationMarker.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/LocationMarker.java	Fri Jul 17 11:41:55 2015 +0200
@@ -120,7 +120,7 @@
 
             op.visitEachTemp(defConsumer);
             op.visitEachOutput(defConsumer);
-            if (op.destroysCallerSavedRegisters()) {
+            if (frameMap != null && op.destroysCallerSavedRegisters()) {
                 for (Register reg : frameMap.getRegisterConfig().getCallerSaveRegisters()) {
                     defConsumer.visitValue(reg.asValue(REFERENCE_KIND), OperandMode.TEMP, REGISTER_FLAG_SET);
                 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/MarkBasePointersPhase.java	Fri Jul 17 11:41:55 2015 +0200
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2015, 2015, 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.
+ */
+package com.oracle.graal.lir.dfa;
+
+import java.util.*;
+
+import jdk.internal.jvmci.code.*;
+import jdk.internal.jvmci.meta.*;
+
+import com.oracle.graal.compiler.common.alloc.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.framemap.*;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
+import com.oracle.graal.lir.phases.*;
+
+/**
+ * Record all derived reference base pointers in a frame state.
+ */
+public final class MarkBasePointersPhase extends AllocationPhase {
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory,
+                    RegisterAllocationConfig registerAllocationConfig) {
+        new Marker<B>(lirGenRes.getLIR(), null).build();
+    }
+
+    private static final class Marker<T extends AbstractBlockBase<T>> extends LocationMarker<T, Marker<T>.BasePointersSet> {
+
+        private final class BasePointersSet extends LiveValueSet<Marker<T>.BasePointersSet> {
+
+            private final ValueSet variables;
+
+            public BasePointersSet() {
+                variables = new ValueSet();
+            }
+
+            private BasePointersSet(BasePointersSet s) {
+                variables = new ValueSet(s.variables);
+            }
+
+            @Override
+            public Marker<T>.BasePointersSet copy() {
+                return new BasePointersSet(this);
+            }
+
+            @Override
+            public void put(Value v) {
+                Variable base = (Variable) v.getLIRKind().getDerivedReferenceBase();
+                variables.put(base.index, base);
+            }
+
+            @Override
+            public void putAll(BasePointersSet v) {
+                variables.putAll(v.variables);
+            }
+
+            @Override
+            public void remove(Value v) {
+                Variable base = (Variable) v.getLIRKind().getDerivedReferenceBase();
+                variables.put(base.index, null);
+            }
+
+            @SuppressWarnings("unchecked")
+            @Override
+            public boolean equals(Object obj) {
+                if (obj instanceof Marker.BasePointersSet) {
+                    BasePointersSet other = (BasePointersSet) obj;
+                    return variables.equals(other.variables);
+                } else {
+                    return false;
+                }
+            }
+
+            @Override
+            public int hashCode() {
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        private Marker(LIR lir, FrameMap frameMap) {
+            super(lir, frameMap);
+        }
+
+        @Override
+        protected Marker<T>.BasePointersSet newLiveValueSet() {
+            return new BasePointersSet();
+        }
+
+        @Override
+        protected boolean shouldProcessValue(Value operand) {
+            return operand.getLIRKind().isDerivedReference();
+        }
+
+        @Override
+        protected void processState(LIRInstruction op, LIRFrameState info, BasePointersSet values) {
+            info.setLiveBasePointers(new ValueSet(values.variables));
+        }
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/ValueSet.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/ValueSet.java	Fri Jul 17 11:41:55 2015 +0200
@@ -26,9 +26,12 @@
 
 import jdk.internal.jvmci.meta.*;
 
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.LIRInstruction.OperandFlag;
+import com.oracle.graal.lir.LIRInstruction.OperandMode;
 import com.oracle.graal.lir.framemap.*;
 
-final class ValueSet {
+public final class ValueSet {
     private Value[] values;
 
     ValueSet() {
@@ -48,6 +51,10 @@
         System.arraycopy(other.values, 0, values, 0, values.length);
     }
 
+    public Value get(int index) {
+        return values[index];
+    }
+
     void put(int index, Value value) {
         if (value != null && value.getLIRKind().isValue()) {
             return;
@@ -123,6 +130,22 @@
         }
     }
 
+    public void forEach(LIRInstruction inst, OperandMode mode, EnumSet<OperandFlag> flags, InstructionValueProcedure proc) {
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] != null) {
+                values[i] = proc.doValue(inst, values[i], mode, flags);
+            }
+        }
+    }
+
+    public void forEach(LIRInstruction inst, OperandMode mode, EnumSet<OperandFlag> flags, InstructionValueConsumer consumer) {
+        for (Value v : values) {
+            if (v != null) {
+                consumer.visitValue(inst, v, mode, flags);
+            }
+        }
+    }
+
     @Override
     public int hashCode() {
         throw new UnsupportedOperationException();
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java	Fri Jul 17 11:41:55 2015 +0200
@@ -420,4 +420,90 @@
     public LIRInstruction createMultiBenchmarkCounter(String[] names, String[] groups, Value[] increments) {
         throw JVMCIError.unimplemented();
     }
+
+    // automatic derived reference handling
+
+    protected abstract Variable emitAdd(LIRKind resultKind, Value a, Value b, boolean setFlags);
+
+    public final Variable emitAdd(Value aVal, Value bVal, boolean setFlags) {
+        LIRKind resultKind;
+        Value a = aVal;
+        Value b = bVal;
+
+        if (a.getKind().isNumericInteger()) {
+            LIRKind aKind = a.getLIRKind();
+            LIRKind bKind = b.getLIRKind();
+            assert a.getPlatformKind() == b.getPlatformKind();
+
+            if (aKind.isUnknownReference()) {
+                resultKind = aKind;
+            } else if (bKind.isUnknownReference()) {
+                resultKind = bKind;
+            } else if (aKind.isValue() && bKind.isValue()) {
+                resultKind = aKind;
+            } else if (aKind.isValue()) {
+                if (bKind.isDerivedReference()) {
+                    resultKind = bKind;
+                } else {
+                    AllocatableValue allocatable = asAllocatable(b);
+                    resultKind = bKind.makeDerivedReference(allocatable);
+                    b = allocatable;
+                }
+            } else if (bKind.isValue()) {
+                if (aKind.isDerivedReference()) {
+                    resultKind = aKind;
+                } else {
+                    AllocatableValue allocatable = asAllocatable(a);
+                    resultKind = aKind.makeDerivedReference(allocatable);
+                    a = allocatable;
+                }
+            } else {
+                resultKind = aKind.makeUnknownReference();
+            }
+        } else {
+            resultKind = LIRKind.combine(a, b);
+        }
+
+        return emitAdd(resultKind, a, b, setFlags);
+    }
+
+    protected abstract Variable emitSub(LIRKind resultKind, Value a, Value b, boolean setFlags);
+
+    public final Variable emitSub(Value aVal, Value bVal, boolean setFlags) {
+        LIRKind resultKind;
+        Value a = aVal;
+        Value b = bVal;
+
+        if (a.getKind().isNumericInteger()) {
+            LIRKind aKind = a.getLIRKind();
+            LIRKind bKind = b.getLIRKind();
+            assert a.getPlatformKind() == b.getPlatformKind();
+
+            if (aKind.isUnknownReference()) {
+                resultKind = aKind;
+            } else if (bKind.isUnknownReference()) {
+                resultKind = bKind;
+            }
+
+            if (aKind.isValue() && bKind.isValue()) {
+                resultKind = aKind;
+            } else if (bKind.isValue()) {
+                if (aKind.isDerivedReference()) {
+                    resultKind = aKind;
+                } else {
+                    AllocatableValue allocatable = asAllocatable(a);
+                    resultKind = aKind.makeDerivedReference(allocatable);
+                    a = allocatable;
+                }
+            } else if (aKind.isDerivedReference() && bKind.isDerivedReference() && aKind.getDerivedReferenceBase().equals(bKind.getDerivedReferenceBase())) {
+                resultKind = LIRKind.value(a.getPlatformKind());
+            } else {
+                resultKind = aKind.makeUnknownReference();
+            }
+        } else {
+            resultKind = LIRKind.combine(a, b);
+        }
+
+        return emitSub(resultKind, a, b, setFlags);
+    }
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/AllocationStage.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/AllocationStage.java	Fri Jul 17 11:41:55 2015 +0200
@@ -29,6 +29,7 @@
 
 public class AllocationStage extends LIRPhaseSuite<AllocationContext> {
     public AllocationStage() {
+        appendPhase(new MarkBasePointersPhase());
         appendPhase(new LinearScanPhase());
 
         // build frame map
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DerivedOopTest.java	Fri Jul 17 11:41:55 2015 +0200
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2015, 2015, 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.
+ */
+package com.oracle.graal.replacements.test;
+
+import java.util.*;
+
+import jdk.internal.jvmci.meta.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.directives.*;
+import com.oracle.graal.compiler.test.*;
+import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
+import com.oracle.graal.graphbuilderconf.*;
+import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.replacements.*;
+import com.oracle.graal.word.*;
+import com.oracle.graal.word.nodes.*;
+
+/**
+ * Tests for derived oops in reference maps.
+ */
+public class DerivedOopTest extends GraalCompilerTest implements Snippets {
+
+    private static class Pointers {
+        public long basePointer;
+        public long internalPointer;
+
+        public long delta() {
+            return internalPointer - basePointer;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Pointers)) {
+                return false;
+            }
+
+            Pointers other = (Pointers) obj;
+            return this.delta() == other.delta();
+        }
+
+        @Override
+        public int hashCode() {
+            return (int) delta();
+        }
+    }
+
+    private static class Result {
+        public Pointers beforeGC;
+        public Pointers afterGC;
+
+        public Result() {
+            beforeGC = new Pointers();
+            afterGC = new Pointers();
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((afterGC == null) ? 0 : afterGC.hashCode());
+            result = prime * result + ((beforeGC == null) ? 0 : beforeGC.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Result)) {
+                return false;
+            }
+            Result other = (Result) obj;
+            return Objects.equals(this.beforeGC, other.beforeGC) && Objects.equals(this.afterGC, other.afterGC);
+        }
+    }
+
+    @Test
+    public void testFieldOffset() {
+        Result r = new Result();
+        test("fieldOffsetSnippet", r, 16L);
+
+        Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
+    }
+
+    static long getRawPointer(Object obj) {
+        // fake implementation for interpreter
+        return obj.hashCode();
+    }
+
+    static long getRawPointerIntrinsic(Object obj) {
+        return Word.fromObject(obj).rawValue();
+    }
+
+    public static Result fieldOffsetSnippet(Result obj, long offset) {
+        long internalPointer = getRawPointer(obj) + offset;
+
+        // make sure the internal pointer is computed before the safepoint
+        GraalDirectives.blackhole(internalPointer);
+
+        obj.beforeGC.basePointer = getRawPointer(obj);
+        obj.beforeGC.internalPointer = internalPointer;
+
+        System.gc();
+
+        obj.afterGC.basePointer = getRawPointer(obj);
+        obj.afterGC.internalPointer = internalPointer;
+
+        return obj;
+    }
+
+    @Override
+    protected Plugins getDefaultGraphBuilderPlugins() {
+        Plugins plugins = super.getDefaultGraphBuilderPlugins();
+        Registration r = new Registration(plugins.getInvocationPlugins(), DerivedOopTest.class);
+
+        ResolvedJavaMethod intrinsic = getResolvedJavaMethod("getRawPointerIntrinsic");
+        r.register1("getRawPointer", Object.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
+                b.intrinsify(targetMethod, intrinsic, new ValueNode[]{arg});
+                return true;
+            }
+        });
+
+        return plugins;
+    }
+
+    @Override
+    protected boolean checkHighTierGraph(StructuredGraph graph) {
+        assert graph.getNodes().filter(WordCastNode.class).count() > 0 : "DerivedOopTest.toLong should be intrinsified";
+        return super.checkHighTierGraph(graph);
+    }
+}
--- a/jvmci/jdk.internal.jvmci.meta/src/jdk/internal/jvmci/meta/LIRKind.java	Thu Jul 16 17:44:54 2015 +0200
+++ b/jvmci/jdk.internal.jvmci.meta/src/jdk/internal/jvmci/meta/LIRKind.java	Fri Jul 17 11:41:55 2015 +0200
@@ -27,7 +27,7 @@
 /**
  * Represents the type of values in the LIR. It is composed of a {@link PlatformKind} that gives the
  * low level representation of the value, and a {@link #referenceMask} that describes the location
- * of object references in the value.
+ * of object references in the value, and optionally a {@link #derivedReferenceBase}.
  *
  * <h2>Constructing {@link LIRKind} instances</h2>
  *
@@ -65,11 +65,16 @@
     private final PlatformKind platformKind;
     private final int referenceMask;
 
+    private AllocatableValue derivedReferenceBase;
+
     private static final int UNKNOWN_REFERENCE = -1;
 
-    private LIRKind(PlatformKind platformKind, int referenceMask) {
+    private LIRKind(PlatformKind platformKind, int referenceMask, AllocatableValue derivedReferenceBase) {
         this.platformKind = platformKind;
         this.referenceMask = referenceMask;
+        this.derivedReferenceBase = derivedReferenceBase;
+
+        assert derivedReferenceBase == null || !derivedReferenceBase.getLIRKind().isDerivedReference() : "derived reference can't have another derived reference as base";
     }
 
     /**
@@ -79,7 +84,7 @@
      */
     public static LIRKind value(PlatformKind platformKind) {
         assert platformKind != Kind.Object : "Object should always be used as reference type";
-        return new LIRKind(platformKind, 0);
+        return new LIRKind(platformKind, 0, null);
     }
 
     /**
@@ -87,9 +92,16 @@
      * reference.
      */
     public static LIRKind reference(PlatformKind platformKind) {
+        return derivedReference(platformKind, null);
+    }
+
+    /**
+     * Create a {@link LIRKind} of type {@code platformKind} that contains a derived reference.
+     */
+    public static LIRKind derivedReference(PlatformKind platformKind, AllocatableValue base) {
         int length = platformKind.getVectorLength();
         assert 0 < length && length < 32 : "vector of " + length + " references not supported";
-        return new LIRKind(platformKind, (1 << length) - 1);
+        return new LIRKind(platformKind, (1 << length) - 1, base);
     }
 
     /**
@@ -99,7 +111,25 @@
      * used instead to automatically propagate this information.
      */
     public static LIRKind unknownReference(PlatformKind platformKind) {
-        return new LIRKind(platformKind, UNKNOWN_REFERENCE);
+        return new LIRKind(platformKind, UNKNOWN_REFERENCE, null);
+    }
+
+    /**
+     * Create a derived reference.
+     *
+     * @param base An {@link AllocatableValue} containing the base pointer of the derived reference.
+     */
+    public LIRKind makeDerivedReference(AllocatableValue base) {
+        assert !isUnknownReference() && derivedReferenceBase == null;
+        if (Value.ILLEGAL.equals(base)) {
+            return makeUnknownReference();
+        } else {
+            if (isValue()) {
+                return derivedReference(platformKind, base);
+            } else {
+                return new LIRKind(platformKind, referenceMask, base);
+            }
+        }
     }
 
     /**
@@ -143,6 +173,42 @@
     }
 
     /**
+     * Helper method to construct derived reference kinds. Returns the base value of a reference or
+     * derived reference. For values it returns {@code null}, and for unknown references it returns
+     * {@link Value#ILLEGAL}.
+     */
+    public static AllocatableValue derivedBaseFromValue(AllocatableValue value) {
+        LIRKind kind = value.getLIRKind();
+        if (kind.isValue()) {
+            return null;
+        } else if (kind.isDerivedReference()) {
+            return kind.getDerivedReferenceBase();
+        } else if (kind.isUnknownReference()) {
+            return Value.ILLEGAL;
+        } else {
+            // kind is a reference
+            return value;
+        }
+    }
+
+    /**
+     * Helper method to construct derived reference kinds. If one of {@code base1} or {@code base2}
+     * are set, it creates a derived reference using it as the base. If both are set, the result is
+     * an unknown reference.
+     */
+    public static LIRKind combineDerived(LIRKind kind, AllocatableValue base1, AllocatableValue base2) {
+        if (base1 == null && base2 == null) {
+            return kind;
+        } else if (base1 == null) {
+            return kind.makeDerivedReference(base2);
+        } else if (base2 == null) {
+            return kind.makeDerivedReference(base1);
+        } else {
+            return kind.makeUnknownReference();
+        }
+    }
+
+    /**
      * @see #merge(Value...)
      */
     public static LIRKind merge(Iterable<LIRKind> kinds) {
@@ -211,13 +277,13 @@
             return unknownReference(newPlatformKind);
         } else if (referenceMask == 0) {
             // value type
-            return new LIRKind(newPlatformKind, 0);
+            return LIRKind.value(newPlatformKind);
         } else {
             // reference type
             int newLength = Math.min(32, newPlatformKind.getVectorLength());
             int newReferenceMask = referenceMask & (0xFFFFFFFF >>> (32 - newLength));
             assert newReferenceMask != UNKNOWN_REFERENCE;
-            return new LIRKind(newPlatformKind, newReferenceMask);
+            return new LIRKind(newPlatformKind, newReferenceMask, derivedReferenceBase);
         }
     }
 
@@ -230,7 +296,7 @@
             return unknownReference(newPlatformKind);
         } else if (referenceMask == 0) {
             // value type
-            return new LIRKind(newPlatformKind, 0);
+            return LIRKind.value(newPlatformKind);
         } else {
             // reference type
             int oldLength = platformKind.getVectorLength();
@@ -244,7 +310,7 @@
             }
 
             assert newReferenceMask != UNKNOWN_REFERENCE;
-            return new LIRKind(newPlatformKind, newReferenceMask);
+            return new LIRKind(newPlatformKind, newReferenceMask, derivedReferenceBase);
         }
     }
 
@@ -253,7 +319,7 @@
      * {@link LIRKind#unknownReference}.
      */
     public LIRKind makeUnknownReference() {
-        return new LIRKind(platformKind, UNKNOWN_REFERENCE);
+        return new LIRKind(platformKind, UNKNOWN_REFERENCE, null);
     }
 
     /**
@@ -264,6 +330,28 @@
     }
 
     /**
+     * Check whether this value is a derived reference.
+     */
+    public boolean isDerivedReference() {
+        return getDerivedReferenceBase() != null;
+    }
+
+    /**
+     * Get the base value of a derived reference.
+     */
+    public AllocatableValue getDerivedReferenceBase() {
+        return derivedReferenceBase;
+    }
+
+    /**
+     * Change the base value of a derived reference. This must be called on derived references only.
+     */
+    public void setDerivedReferenceBase(AllocatableValue derivedReferenceBase) {
+        assert isDerivedReference();
+        this.derivedReferenceBase = derivedReferenceBase;
+    }
+
+    /**
      * Check whether this value is derived from a reference in a non-linear way. If this returns
      * {@code true}, this value must not be live at safepoints.
      */