changeset 10768:058abc2b59a5

Merge
author Lukas Stadler <lukas.stadler@jku.at>
date Mon, 15 Jul 2013 17:54:00 +0200
parents 88d0dc388450 (current diff) dd7a8807378b (diff)
children 395d34c10e26 f0fdbb2b7135
files graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java
diffstat 17 files changed, 228 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Mon Jul 15 17:54:00 2013 +0200
@@ -70,6 +70,11 @@
     private ValueNode lastInstructionPrinted; // Debugging only
 
     /**
+     * Records whether the code being generated makes at least one foreign call.
+     */
+    private boolean hasForeignCall;
+
+    /**
      * Checks whether the supplied constant can be used without loading it into a register for store
      * operations, i.e., on the right hand side of a memory access.
      * 
@@ -116,6 +121,13 @@
     }
 
     /**
+     * Determines whether the code being generated makes at least one foreign call.
+     */
+    public boolean hasForeignCall() {
+        return hasForeignCall;
+    }
+
+    /**
      * Returns the operand that has been previously initialized by
      * {@link #setResult(ValueNode, Value)} with the result of an instruction.
      * 
@@ -601,6 +613,7 @@
             emitMove(loc, arg);
             argLocations[i] = loc;
         }
+        this.hasForeignCall = true;
         emitForeignCall(linkage, linkageCc.getReturn(), argLocations, linkage.getTemporaries(), state);
 
         if (isLegal(linkageCc.getReturn())) {
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Mon Jul 15 17:54:00 2013 +0200
@@ -153,11 +153,13 @@
         // - has no spill slots or other slots allocated during register allocation
         // - has no callee-saved registers
         // - has no incoming arguments passed on the stack
-        // - has no instructions with debug info
+        // - has no deoptimization points
+        // - makes no foreign calls (which require an aligned stack)
         AMD64HotSpotLIRGenerator gen = (AMD64HotSpotLIRGenerator) lirGen;
         FrameMap frameMap = gen.frameMap;
         LIR lir = gen.lir;
-        boolean omitFrame = CanOmitFrame.getValue() && !frameMap.frameNeedsAllocating() && !lir.hasArgInCallerFrame();
+        assert gen.deoptimizationRescueSlot == null || frameMap.frameNeedsAllocating() : "method that can deoptimize must have a frame";
+        boolean omitFrame = CanOmitFrame.getValue() && !frameMap.frameNeedsAllocating() && !lir.hasArgInCallerFrame() && !gen.hasForeignCall();
 
         Stub stub = gen.getStub();
         AbstractAssembler masm = createAssembler(frameMap);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierAdditionPhase.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierAdditionPhase.java	Mon Jul 15 17:54:00 2013 +0200
@@ -69,10 +69,12 @@
         WriteBarrierType barrierType = node.getWriteBarrierType();
         if (barrierType == WriteBarrierType.PRECISE) {
             if (useG1GC()) {
-                G1PreWriteBarrier preBarrier = graph.add(new G1PreWriteBarrier(node.object(), null, node.location(), true, node.getNullCheck()));
-                preBarrier.setDeoptimizationState(node.getDeoptimizationState());
-                node.setNullCheck(false);
-                graph.addBeforeFixed(node, preBarrier);
+                if (node.isInitialized()) {
+                    G1PreWriteBarrier preBarrier = graph.add(new G1PreWriteBarrier(node.object(), null, node.location(), true, node.getNullCheck()));
+                    preBarrier.setDeoptimizationState(node.getDeoptimizationState());
+                    node.setNullCheck(false);
+                    graph.addBeforeFixed(node, preBarrier);
+                }
                 graph.addAfterFixed(node, graph.add(new G1PostWriteBarrier(node.object(), node.value(), node.location(), true)));
             } else {
                 graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(node.object(), node.location(), true)));
@@ -116,9 +118,11 @@
 
     private static void addArrayRangeBarriers(ArrayRangeWriteNode node, StructuredGraph graph) {
         if (useG1GC()) {
-            G1ArrayRangePreWriteBarrier g1ArrayRangePreWriteBarrier = graph.add(new G1ArrayRangePreWriteBarrier(node.getArray(), node.getIndex(), node.getLength()));
+            if (node.isInitialized()) {
+                G1ArrayRangePreWriteBarrier g1ArrayRangePreWriteBarrier = graph.add(new G1ArrayRangePreWriteBarrier(node.getArray(), node.getIndex(), node.getLength()));
+                graph.addBeforeFixed(node, g1ArrayRangePreWriteBarrier);
+            }
             G1ArrayRangePostWriteBarrier g1ArrayRangePostWriteBarrier = graph.add(new G1ArrayRangePostWriteBarrier(node.getArray(), node.getIndex(), node.getLength()));
-            graph.addBeforeFixed(node, g1ArrayRangePreWriteBarrier);
             graph.addAfterFixed(node, g1ArrayRangePostWriteBarrier);
         } else {
             SerialArrayRangeWriteBarrier serialArrayRangeWriteBarrier = graph.add(new SerialArrayRangeWriteBarrier(node.getArray(), node.getIndex(), node.getLength()));
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Mon Jul 15 17:54:00 2013 +0200
@@ -259,7 +259,7 @@
             args.addConst("size", size);
             args.add("hub", hub);
             args.add("prototypeMarkWord", type.prototypeMarkWord());
-            args.addConst("fillContents", useG1GC() || newInstanceNode.fillContents());
+            args.addConst("fillContents", newInstanceNode.fillContents());
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering allocateInstance in %s: node=%s, template=%s, arguments=%s", graph, newInstanceNode, template, args);
@@ -284,7 +284,7 @@
             args.add("prototypeMarkWord", arrayType.prototypeMarkWord());
             args.addConst("headerSize", headerSize);
             args.addConst("log2ElementSize", log2ElementSize);
-            args.addConst("fillContents", useG1GC() || newArrayNode.fillContents());
+            args.addConst("fillContents", newArrayNode.fillContents());
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering allocateArray in %s: node=%s, template=%s, arguments=%s", graph, newArrayNode, template, args);
@@ -295,7 +295,7 @@
             Arguments args = new Arguments(allocateArrayDynamic);
             args.add("elementType", newArrayNode.getElementType());
             args.add("length", newArrayNode.length());
-            args.addConst("fillContents", useG1GC() || newArrayNode.fillContents());
+            args.addConst("fillContents", newArrayNode.fillContents());
 
             SnippetTemplate template = template(args);
             template.instantiate(runtime, newArrayNode, DEFAULT_REPLACER, args);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneSnippets.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneSnippets.java	Mon Jul 15 17:54:00 2013 +0200
@@ -54,7 +54,7 @@
     private static Object instanceClone(Object src, Word hub, int layoutHelper) {
         int instanceSize = layoutHelper;
         Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
-        Object result = NewObjectSnippets.allocateInstance(instanceSize, hub, prototypeMarkWord, useG1GC());
+        Object result = NewObjectSnippets.allocateInstance(instanceSize, hub, prototypeMarkWord, false);
 
         Pointer memory = Word.fromObject(result);
         for (int offset = instanceHeaderSize(); offset < instanceSize; offset += wordSize()) {
@@ -71,7 +71,7 @@
         int sizeInBytes = NewObjectSnippets.computeArrayAllocationSize(arrayLength, wordSize(), headerSize, log2ElementSize);
 
         Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
-        Object result = NewObjectSnippets.allocateArray(hub, arrayLength, prototypeMarkWord, headerSize, log2ElementSize, useG1GC());
+        Object result = NewObjectSnippets.allocateArray(hub, arrayLength, prototypeMarkWord, headerSize, log2ElementSize, false);
 
         Pointer memory = Word.fromObject(result);
         for (int offset = headerSize; offset < sizeInBytes; offset += wordSize()) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/UnsafeArrayCopyNode.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/UnsafeArrayCopyNode.java	Mon Jul 15 17:54:00 2013 +0200
@@ -82,6 +82,11 @@
         return elementKind == Kind.Object;
     }
 
+    @Override
+    public boolean isInitialized() {
+        return true;
+    }
+
     public Kind getElementKind() {
         return elementKind;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ArrayRangeWriteNode.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ArrayRangeWriteNode.java	Mon Jul 15 17:54:00 2013 +0200
@@ -54,4 +54,10 @@
      * Return true if the written array is an object array, false if it is a primitive array.
      */
     public abstract boolean isObjectArray();
+
+    /**
+     * If {@link #isInitialized()} is true, the memory location contains a valid value. If
+     * {@link #isInitialized()} is false, the memory location is uninitialized or zero.
+     */
+    public abstract boolean isInitialized();
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/WriteNode.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/WriteNode.java	Mon Jul 15 17:54:00 2013 +0200
@@ -37,6 +37,7 @@
 
     @Input private ValueNode value;
     @Input(notDataflow = true) private FrameState stateAfter;
+    private final boolean initialized;
 
     public FrameState stateAfter() {
         return stateAfter;
@@ -56,9 +57,22 @@
         return value;
     }
 
+    /**
+     * If {@link #isInitialized()} is true, the memory location contains a valid value. If
+     * {@link #isInitialized()} is false, the memory location is uninitialized or zero.
+     */
+    public boolean isInitialized() {
+        return initialized;
+    }
+
     public WriteNode(ValueNode object, ValueNode value, ValueNode location, WriteBarrierType barrierType, boolean compress) {
+        this(object, value, location, barrierType, compress, true);
+    }
+
+    public WriteNode(ValueNode object, ValueNode value, ValueNode location, WriteBarrierType barrierType, boolean compress, boolean initialized) {
         super(object, location, StampFactory.forVoid(), barrierType, compress);
         this.value = value;
+        this.initialized = initialized;
     }
 
     @Override
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Mon Jul 15 17:54:00 2013 +0200
@@ -65,8 +65,7 @@
         if (truffleCompiler == null) {
             truffleCompiler = new TruffleCompilerImpl();
         }
-        return new OptimizedCallTarget(rootNode, frameDescriptor, truffleCompiler, TruffleCompilationThreshold.getValue(), TruffleInliningReprofileCount.getValue(),
-                        TruffleInvalidationReprofileCount.getValue());
+        return new OptimizedCallTarget(rootNode, frameDescriptor, truffleCompiler, TruffleCompilationThreshold.getValue());
     }
 
     @Override
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Mon Jul 15 17:54:00 2013 +0200
@@ -40,19 +40,19 @@
 public final class OptimizedCallTarget extends DefaultCallTarget implements LoopCountReceiver, FrameFactory {
 
     private static final PrintStream OUT = TTY.out().out();
+    private static final int MIN_INVOKES_AFTER_INLINING = 2;
 
-    private final int inliningReprofileCount;
-    private final int invalidationReprofileCount;
-
-    protected OptimizedCallTarget(RootNode rootNode, FrameDescriptor descriptor, TruffleCompiler compiler, int compilationThreshold, int inliningReprofileCount, int invalidationReprofileCount) {
+    protected OptimizedCallTarget(RootNode rootNode, FrameDescriptor descriptor, TruffleCompiler compiler, int compilationThreshold) {
         super(rootNode, descriptor);
         this.compiler = compiler;
         this.invokeCounter = compilationThreshold >> 7;
         this.loopAndInvokeCounter = compilationThreshold;
         this.originalInvokeCounter = compilationThreshold;
         this.rootNode.setCallTarget(this);
-        this.inliningReprofileCount = inliningReprofileCount;
-        this.invalidationReprofileCount = invalidationReprofileCount;
+
+        if (TruffleProfiling.getValue()) {
+            registerCallTarget(this);
+        }
     }
 
     private InstalledCode compiledMethod;
@@ -62,6 +62,11 @@
     private int loopAndInvokeCounter;
     private boolean disableCompilation;
 
+    // TruffleProfiling
+    private int callCount;
+    private int inlinedCallSiteCount;
+
+    // TraceTruffleCompilation
     long timeCompilationStarted;
     long timePartialEvaluationFinished;
     long timeCompilationFinished;
@@ -71,6 +76,9 @@
 
     @Override
     public Object call(PackedFrame caller, Arguments args) {
+        if (TruffleProfiling.getValue()) {
+            callCount++;
+        }
         if (CompilerDirectives.injectBranchProbability(CompilerDirectives.FASTPATH_PROBABILITY, compiledMethod != null)) {
             try {
                 return compiledMethod.execute(this, caller, args);
@@ -82,8 +90,9 @@
         }
     }
 
-    protected Object compiledCodeInvalidated(PackedFrame caller, Arguments args) {
+    private Object compiledCodeInvalidated(PackedFrame caller, Arguments args) {
         compiledMethod = null;
+        int invalidationReprofileCount = TruffleInvalidationReprofileCount.getValue();
         invokeCounter = invalidationReprofileCount;
         if (TruffleFunctionInlining.getValue()) {
             originalInvokeCounter += invalidationReprofileCount;
@@ -101,7 +110,8 @@
             return executeHelper(caller, args);
         } else {
             if (TruffleFunctionInlining.getValue() && inline()) {
-                invokeCounter = 2;
+                invokeCounter = MIN_INVOKES_AFTER_INLINING;
+                int inliningReprofileCount = TruffleInliningReprofileCount.getValue();
                 loopAndInvokeCounter = inliningReprofileCount;
                 originalInvokeCounter = inliningReprofileCount;
             } else {
@@ -112,10 +122,12 @@
     }
 
     public boolean inline() {
+        CompilerAsserts.neverPartOfCompilation();
         return new InliningHelper(this).inline();
     }
 
     public void compile() {
+        CompilerAsserts.neverPartOfCompilation();
         try {
             compiledMethod = compiler.compile(this);
             if (compiledMethod == null) {
@@ -127,6 +139,9 @@
                                     (timeCompilationFinished - timeCompilationStarted) / 1e6, (timePartialEvaluationFinished - timeCompilationStarted) / 1e6,
                                     (timeCompilationFinished - timePartialEvaluationFinished) / 1e6, nodeCountPartialEval, nodeCountLowered, codeSize);
                 }
+                if (TruffleProfiling.getValue()) {
+                    resetProfiling();
+                }
             }
         } catch (Throwable e) {
             disableCompilation = true;
@@ -202,10 +217,14 @@
 
             boolean inlined = false;
             for (InlinableCallSiteInfo inlinableCallSite : inlinableCallSites) {
-                if (policy.isWorthInlining(inlinableCallSite) && inlinableCallSite.getCallSite().inline(target)) {
+                if (!policy.isWorthInlining(inlinableCallSite)) {
+                    break;
+                }
+                if (inlinableCallSite.getCallSite().inline(target)) {
                     if (TraceTruffleInlining.getValue()) {
                         printCallSiteInfo(policy, inlinableCallSite, "inlined");
                     }
+                    target.inlinedCallSiteCount++;
                     inlined = true;
                     break;
                 }
@@ -267,7 +286,11 @@
 
                     @Override
                     public int compare(InlinableCallSiteInfo cs1, InlinableCallSiteInfo cs2) {
-                        return Double.compare(metric(cs2), metric(cs1));
+                        int result = (isWorthInlining(cs2) ? 1 : 0) - (isWorthInlining(cs1) ? 1 : 0);
+                        if (result == 0) {
+                            return Double.compare(metric(cs2), metric(cs1));
+                        }
+                        return result;
                     }
                 });
             }
@@ -313,4 +336,65 @@
             return inlinableCallSites;
         }
     }
+
+    private static void resetProfiling() {
+        for (OptimizedCallTarget callTarget : OptimizedCallTarget.callTargets.keySet()) {
+            callTarget.callCount = 0;
+        }
+    }
+
+    private static void printProfiling() {
+        List<OptimizedCallTarget> sortedCallTargets = new ArrayList<>(OptimizedCallTarget.callTargets.keySet());
+        Collections.sort(sortedCallTargets, new Comparator<OptimizedCallTarget>() {
+
+            @Override
+            public int compare(OptimizedCallTarget o1, OptimizedCallTarget o2) {
+                return o2.callCount - o1.callCount;
+            }
+        });
+
+        int totalCallCount = 0;
+        int totalInlinedCallSiteCount = 0;
+        int totalNotInlinedCallSiteCount = 0;
+        int totalNodeCount = 0;
+
+        PrintStream out = TTY.out().out();
+        out.println();
+        out.printf("%-50s | %-10s | %s / %s | %s\n", "Call Target", "Call Count", "Calls Sites Inlined", "Not Inlined", "Node Count");
+        for (OptimizedCallTarget callTarget : sortedCallTargets) {
+            if (callTarget.callCount == 0) {
+                continue;
+            }
+
+            int notInlinedCallSiteCount = InliningHelper.getInlinableCallSites(callTarget).size();
+            int nodeCount = NodeUtil.countNodes(callTarget.rootNode);
+            String comment = callTarget.compiledMethod == null ? " int" : "";
+            out.printf("%-50s | %10s | %15s | %15s | %10s%s\n", callTarget.getRootNode(), callTarget.callCount, callTarget.inlinedCallSiteCount, notInlinedCallSiteCount, nodeCount, comment);
+
+            totalCallCount += callTarget.callCount;
+            totalInlinedCallSiteCount += callTarget.inlinedCallSiteCount;
+            totalNotInlinedCallSiteCount += notInlinedCallSiteCount;
+            totalNodeCount += nodeCount;
+        }
+        out.printf("%-50s | %10s | %15s | %15s | %10s\n", "Total", totalCallCount, totalInlinedCallSiteCount, totalNotInlinedCallSiteCount, totalNodeCount);
+    }
+
+    private static void registerCallTarget(OptimizedCallTarget callTarget) {
+        callTargets.put(callTarget, 0);
+    }
+
+    private static Map<OptimizedCallTarget, Integer> callTargets;
+    static {
+        if (TruffleProfiling.getValue()) {
+            callTargets = new WeakHashMap<>();
+
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+
+                @Override
+                public void run() {
+                    printProfiling();
+                }
+            });
+        }
+    }
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Mon Jul 15 17:54:00 2013 +0200
@@ -64,7 +64,7 @@
     private final HotSpotGraalRuntime graalRuntime;
     private final TruffleCache truffleCache;
 
-    private static final Class[] SKIPPED_EXCEPTION_CLASSES = new Class[]{SlowPathException.class, UnexpectedResultException.class, ArithmeticException.class};
+    private static final Class[] SKIPPED_EXCEPTION_CLASSES = new Class[]{SlowPathException.class, UnexpectedResultException.class, ArithmeticException.class, InvalidInstalledCodeException.class};
 
     public static final OptimisticOptimizations Optimizations = OptimisticOptimizations.ALL.remove(OptimisticOptimizations.Optimization.UseExceptionProbability,
                     OptimisticOptimizations.Optimization.RemoveNeverExecutedCode, OptimisticOptimizations.Optimization.UseTypeCheckedInlining, OptimisticOptimizations.Optimization.UseTypeCheckHints);
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Mon Jul 15 17:54:00 2013 +0200
@@ -81,5 +81,7 @@
     public static final OptionValue<Boolean> TraceTruffleInlining = new OptionValue<>(true);
     @Option(help = "")
     public static final OptionValue<Boolean> TraceTruffleInliningDetails = new OptionValue<>(false);
+    @Option(help = "")
+    public static final OptionValue<Boolean> TruffleProfiling = new StableOptionValue<>(false);
     // @formatter:on
 }
--- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/Pointer.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/Pointer.java	Mon Jul 15 17:54:00 2013 +0200
@@ -365,6 +365,20 @@
     void writeWord(WordBase offset, WordBase val, LocationIdentity locationIdentity);
 
     /**
+     * Initializes the memory at address {@code (this + offset)}. Both the base address and offset
+     * are in bytes. The memory must be uninitialized or zero prior to this operation.
+     * <p>
+     * The offset is always treated as a {@link Signed} value. However, the static type is
+     * {@link WordBase} to avoid the frequent casts to of {@link Unsigned} values (where the caller
+     * knows that the highest-order bit of the unsigned value is never used).
+     * 
+     * @param offset the signed offset for the memory access
+     * @param locationIdentity the identity of the write (see {@link LocationNode})
+     * @param val the value to be written to memory
+     */
+    void initializeWord(WordBase offset, WordBase val, LocationIdentity locationIdentity);
+
+    /**
      * Writes the memory at address {@code (this + offset)}. Both the base address and offset are in
      * bytes.
      * <p>
@@ -459,6 +473,16 @@
     void writeWord(int offset, WordBase val, LocationIdentity locationIdentity);
 
     /**
+     * Initializes the memory at address {@code (this + offset)}. Both the base address and offset
+     * are in bytes. The memory must be uninitialized or zero prior to this operation.
+     * 
+     * @param offset the signed offset for the memory access
+     * @param locationIdentity the identity of the write (see {@link LocationNode})
+     * @param val the value to be written to memory
+     */
+    void initializeWord(int offset, WordBase val, LocationIdentity locationIdentity);
+
+    /**
      * Writes the memory at address {@code (this + offset)}. Both the base address and offset are in
      * bytes.
      * 
--- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/Word.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/Word.java	Mon Jul 15 17:54:00 2013 +0200
@@ -61,6 +61,7 @@
          READ,
          READ_COMPRESSED,
          WRITE,
+         INITIALIZE,
          ZERO,
          FROM_UNSIGNED,
          FROM_SIGNED,
@@ -758,6 +759,12 @@
     }
 
     @Override
+    @Operation(opcode = Opcode.INITIALIZE)
+    public void initializeWord(WordBase offset, WordBase val, LocationIdentity locationIdentity) {
+        unsafe.putAddress(add((Word) offset).unbox(), ((Word) val).unbox());
+    }
+
+    @Override
     @Operation(opcode = Opcode.WRITE)
     public native void writeObject(WordBase offset, Object val, LocationIdentity locationIdentity);
 
@@ -810,6 +817,12 @@
     }
 
     @Override
+    @Operation(opcode = Opcode.INITIALIZE)
+    public void initializeWord(int offset, WordBase val, LocationIdentity locationIdentity) {
+        initializeWord(signed(offset), val, locationIdentity);
+    }
+
+    @Override
     @Operation(opcode = Opcode.WRITE)
     public void writeObject(int offset, Object val, LocationIdentity locationIdentity) {
         writeObject(signed(offset), val, locationIdentity);
--- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java	Mon Jul 15 17:54:00 2013 +0200
@@ -38,6 +38,7 @@
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.word.*;
+import com.oracle.graal.word.Word.Opcode;
 import com.oracle.graal.word.Word.Operation;
 
 /**
@@ -189,7 +190,8 @@
                         replace(invoke, readOp(graph, arguments.get(0), invoke, location, true));
                         break;
                     }
-                    case WRITE: {
+                    case WRITE:
+                    case INITIALIZE: {
                         assert arguments.size() == 3 || arguments.size() == 4;
                         Kind writeKind = asKind(targetMethod.getSignature().getParameterType(1, targetMethod.getDeclaringClass()));
                         LocationNode location;
@@ -198,7 +200,7 @@
                         } else {
                             location = makeLocation(graph, arguments.get(1), writeKind, arguments.get(3));
                         }
-                        replace(invoke, writeOp(graph, arguments.get(0), arguments.get(2), invoke, location));
+                        replace(invoke, writeOp(graph, arguments.get(0), arguments.get(2), invoke, location, operation.opcode()));
                         break;
                     }
                     case ZERO:
@@ -335,8 +337,9 @@
         return read;
     }
 
-    private static ValueNode writeOp(StructuredGraph graph, ValueNode base, ValueNode value, Invoke invoke, LocationNode location) {
-        WriteNode write = graph.add(new WriteNode(base, value, location, WriteBarrierType.NONE, false));
+    private static ValueNode writeOp(StructuredGraph graph, ValueNode base, ValueNode value, Invoke invoke, LocationNode location, Opcode op) {
+        assert op == Opcode.WRITE || op == Opcode.INITIALIZE;
+        WriteNode write = graph.add(new WriteNode(base, value, location, WriteBarrierType.NONE, false, op == Opcode.WRITE));
         write.setStateAfter(invoke.stateAfter());
         graph.addBeforeFixed(invoke.asNode(), write);
         return write;
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java	Mon Jul 15 17:54:00 2013 +0200
@@ -81,7 +81,7 @@
 
     class TestRootNode extends RootNode {
 
-        @Children private ValueNode[] children;
+        @Children private final ValueNode[] children;
 
         public TestRootNode(ValueNode[] children) {
             this.children = adoptChildren(children);
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Mon Jul 15 17:52:35 2013 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Mon Jul 15 17:54:00 2013 +0200
@@ -163,9 +163,11 @@
                 } else if (Node.class.isAssignableFrom(field.getType()) && field.getAnnotation(Child.class) != null) {
                     kind = NodeFieldKind.CHILD;
                     childOffsetsList.add(fieldOffsetProvider.objectFieldOffset(field));
+                    assert !Modifier.isFinal(field.getModifiers()) : "child field must not be final (\"" + field.getName() + "\", " + clazz + ")";
                 } else if (field.getType().isArray() && Node.class.isAssignableFrom(field.getType().getComponentType()) && field.getAnnotation(Children.class) != null) {
                     kind = NodeFieldKind.CHILDREN;
                     childrenOffsetsList.add(fieldOffsetProvider.objectFieldOffset(field));
+                    assert Modifier.isFinal(field.getModifiers()) : "children array field must be final (\"" + field.getName() + "\", " + clazz + ")";
                 } else {
                     kind = NodeFieldKind.DATA;
                 }
@@ -352,51 +354,52 @@
     public static void replaceChild(Node parent, Node oldChild, Node newChild) {
         NodeClass nodeClass = NodeClass.get(parent.getClass());
 
-        long[] fieldOffsets = nodeClass.childOffsets;
-        for (int i = 0; i < fieldOffsets.length; i++) {
-            long fieldOffset = fieldOffsets[i];
+        for (long fieldOffset : nodeClass.getChildOffsets()) {
             if (unsafe.getObject(parent, fieldOffset) == oldChild) {
-                assert assertAssignable(nodeClass, parent, oldChild, newChild);
+                assert assertAssignable(nodeClass, fieldOffset, newChild);
                 unsafe.putObject(parent, fieldOffset, newChild);
             }
         }
 
-        long[] childrenOffsets = nodeClass.childrenOffsets;
-        for (int i = 0; i < childrenOffsets.length; i++) {
-            long fieldOffset = childrenOffsets[i];
+        for (long fieldOffset : nodeClass.getChildrenOffsets()) {
             Object arrayObject = unsafe.getObject(parent, fieldOffset);
             if (arrayObject != null) {
-                assert arrayObject instanceof Node[] : "Children must be instanceof Node[] ";
+                assert arrayObject instanceof Node[] : "Children array must be instanceof Node[] ";
                 Node[] array = (Node[]) arrayObject;
-                for (int j = 0; j < array.length; j++) {
-                    if (array[j] == oldChild) {
-                        assert newChild != null && array.getClass().getComponentType().isAssignableFrom(newChild.getClass()) : "Array type does not match";
-                        array[j] = newChild;
-                        return;
+                for (int i = 0; i < array.length; i++) {
+                    if (array[i] == oldChild) {
+                        assert assertAssignable(nodeClass, fieldOffset, newChild);
+                        array[i] = newChild;
                     }
                 }
             }
         }
     }
 
-    private static boolean assertAssignable(NodeClass clazz, Node parent, Object oldValue, Object newValue) {
+    private static boolean assertAssignable(NodeClass clazz, long fieldOffset, Object newValue) {
         if (newValue == null) {
             return true;
         }
-        for (NodeField field : clazz.fields) {
-            if (field.kind != NodeFieldKind.CHILD) {
-                continue;
-            }
-            if (unsafe.getObject(parent, field.offset) == oldValue) {
-                if (!field.type.isAssignableFrom(newValue.getClass())) {
-                    assert false : "Child class " + newValue.getClass() + " is not assignable to field " + field.type.getName() + " at " + field.name + " in ";
-                    return false;
-                } else {
-                    break;
+        for (NodeField field : clazz.getFields()) {
+            if (field.getOffset() == fieldOffset) {
+                if (field.getKind() == NodeFieldKind.CHILD) {
+                    if (field.getType().isAssignableFrom(newValue.getClass())) {
+                        return true;
+                    } else {
+                        assert false : "Child class " + newValue.getClass().getName() + " is not assignable to field \"" + field.getName() + "\" of type " + field.getType().getName();
+                        return false;
+                    }
+                } else if (field.getKind() == NodeFieldKind.CHILDREN) {
+                    if (field.getType().getComponentType().isAssignableFrom(newValue.getClass())) {
+                        return true;
+                    } else {
+                        assert false : "Child class " + newValue.getClass().getName() + " is not assignable to field \"" + field.getName() + "\" of type " + field.getType().getName();
+                        return false;
+                    }
                 }
             }
         }
-        return true;
+        throw new IllegalArgumentException();
     }
 
     /** Returns all declared fields in the class hierarchy. */