changeset 9491:789cfd153265

a compiled stub can now specify whether it needs to preserve registers. If a stub does not preserve registers and assertions are enabled, then all non-temporary registers are zapped after a C runtime call from the stub. the ExceptionHandler stub no longer preserves registers
author Doug Simon <doug.simon@oracle.com>
date Thu, 02 May 2013 06:08:02 +0200
parents 3822ce079ec4
children c5bdf71cb5d7 d48b7a4b93e9 659bb6cf930c
files graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotRuntimeCallTarget.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64RegistersPreservationOp.java graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64SaveRegistersOp.java graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ZapRegistersOp.java
diffstat 10 files changed, 243 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Wed May 01 18:08:07 2013 -0700
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Thu May 02 06:08:02 2013 +0200
@@ -825,6 +825,35 @@
     public void beforeRegisterAllocation() {
     }
 
+    /**
+     * Gets an garbage vale for a given kind.
+     */
+    protected Constant zapValueForKind(PlatformKind kind) {
+        long dead = 0xDEADDEADDEADDEADL;
+        switch ((Kind) kind) {
+            case Boolean:
+                return Constant.FALSE;
+            case Byte:
+                return Constant.forByte((byte) dead);
+            case Char:
+                return Constant.forChar((char) dead);
+            case Short:
+                return Constant.forShort((short) dead);
+            case Int:
+                return Constant.forInt((int) dead);
+            case Double:
+                return Constant.forDouble(Double.longBitsToDouble(dead));
+            case Float:
+                return Constant.forFloat(Float.intBitsToFloat((int) dead));
+            case Long:
+                return Constant.forLong(dead);
+            case Object:
+                return Constant.NULL_OBJECT;
+            default:
+                throw new IllegalArgumentException(kind.toString());
+        }
+    }
+
     public abstract void emitBitCount(Variable result, Value operand);
 
     public abstract void emitBitScanForward(Variable result, Value operand);
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Wed May 01 18:08:07 2013 -0700
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Thu May 02 06:08:02 2013 +0200
@@ -173,13 +173,13 @@
         if (stub != null) {
 
             final Set<Register> definedRegisters = gatherDefinedRegisters(lir);
-            stub.initDefinedRegisters(definedRegisters);
+            stub.initDestroyedRegisters(definedRegisters);
 
             // Eliminate unnecessary register preservation and
             // record where preserved registers are saved
-            for (Map.Entry<LIRFrameState, AMD64SaveRegistersOp> e : gen.calleeSaveInfo.entrySet()) {
-                AMD64SaveRegistersOp save = e.getValue();
-                save.updateAndDescribePreservation(definedRegisters, e.getKey().debugInfo(), frameMap);
+            for (Map.Entry<LIRFrameState, AMD64RegistersPreservationOp> e : gen.calleeSaveInfo.entrySet()) {
+                AMD64RegistersPreservationOp save = e.getValue();
+                save.update(definedRegisters, e.getKey().debugInfo(), frameMap);
             }
         }
 
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Wed May 01 18:08:07 2013 -0700
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Thu May 02 06:08:02 2013 +0200
@@ -176,59 +176,79 @@
      * Map from debug infos that need to be updated with callee save information to the operations
      * that provide the information.
      */
-    Map<LIRFrameState, AMD64SaveRegistersOp> calleeSaveInfo = new HashMap<>();
+    Map<LIRFrameState, AMD64RegistersPreservationOp> calleeSaveInfo = new HashMap<>();
 
     private LIRFrameState currentRuntimeCallInfo;
 
     @Override
     protected void emitCall(RuntimeCallTarget callTarget, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
-        boolean needsCalleeSave = ((HotSpotRuntimeCallTarget) callTarget).isCRuntimeCall();
-        if (needsCalleeSave) {
-            currentRuntimeCallInfo = info;
-        }
+        currentRuntimeCallInfo = info;
         super.emitCall(callTarget, result, arguments, temps, info);
     }
 
     @Override
     public Variable emitCall(RuntimeCallTarget callTarget, CallingConvention cc, DeoptimizingNode info, Value... args) {
-        boolean needsCalleeSave = ((HotSpotRuntimeCallTarget) callTarget).isCRuntimeCall();
+        Stub stub = runtime().asStub(method);
+        boolean isCRuntimeCall = ((HotSpotRuntimeCallTarget) callTarget).isCRuntimeCall();
+        assert !isCRuntimeCall || stub != null : "direct call to C runtime can only be made from compiled stubs, not from " + method;
 
         AMD64SaveRegistersOp save = null;
         StackSlot[] savedRegisterLocations = null;
-        if (needsCalleeSave) {
-            Register[] savedRegisters = frameMap.registerConfig.getAllocatableRegisters();
-            savedRegisterLocations = new StackSlot[savedRegisters.length];
-            AMD64LIRInstruction[] savingMoves = new AMD64LIRInstruction[savedRegisters.length];
-            AMD64LIRInstruction[] restoringMoves = new AMD64LIRInstruction[savedRegisters.length];
-            for (int i = 0; i < savedRegisters.length; i++) {
-                PlatformKind kind = target.arch.getLargestStorableKind(savedRegisters[i].getRegisterCategory());
-                assert kind != Kind.Illegal;
-                StackSlot spillSlot = frameMap.allocateSpillSlot(kind);
-                savedRegisterLocations[i] = spillSlot;
+        if (isCRuntimeCall) {
+            if (stub.preservesRegisters()) {
+                Register[] savedRegisters = frameMap.registerConfig.getAllocatableRegisters();
+                savedRegisterLocations = new StackSlot[savedRegisters.length];
+                AMD64LIRInstruction[] savingMoves = new AMD64LIRInstruction[savedRegisters.length];
+                AMD64LIRInstruction[] restoringMoves = new AMD64LIRInstruction[savedRegisters.length];
+                for (int i = 0; i < savedRegisters.length; i++) {
+                    PlatformKind kind = target.arch.getLargestStorableKind(savedRegisters[i].getRegisterCategory());
+                    assert kind != Kind.Illegal;
+                    StackSlot spillSlot = frameMap.allocateSpillSlot(kind);
+                    savedRegisterLocations[i] = spillSlot;
 
-                RegisterValue register = savedRegisters[i].asValue(kind);
-                savingMoves[i] = createMove(spillSlot, register);
-                restoringMoves[i] = createMove(register, spillSlot);
+                    RegisterValue register = savedRegisters[i].asValue(kind);
+                    savingMoves[i] = createMove(spillSlot, register);
+                    restoringMoves[i] = createMove(register, spillSlot);
+                }
+                save = new AMD64SaveRegistersOp(savingMoves, restoringMoves, savedRegisterLocations);
+                append(save);
             }
-            save = new AMD64SaveRegistersOp(savingMoves, restoringMoves, savedRegisterLocations);
-            append(save);
-
             append(new AMD64HotSpotCRuntimeCallPrologueOp());
         }
 
         Variable result = super.emitCall(callTarget, cc, info, args);
 
-        if (needsCalleeSave) {
-            assert !calleeSaveInfo.containsKey(currentRuntimeCallInfo);
-            calleeSaveInfo.put(currentRuntimeCallInfo, save);
+        if (isCRuntimeCall) {
+            append(new AMD64HotSpotCRuntimeCallEpilogueOp());
+            if (stub.preservesRegisters()) {
+                assert !calleeSaveInfo.containsKey(currentRuntimeCallInfo);
+                calleeSaveInfo.put(currentRuntimeCallInfo, save);
 
-            append(new AMD64HotSpotCRuntimeCallEpilogueOp());
-            append(new AMD64RestoreRegistersOp(savedRegisterLocations.clone(), save));
+                append(new AMD64RestoreRegistersOp(savedRegisterLocations.clone(), save));
+            } else {
+                assert zapRegisters();
+            }
         }
 
         return result;
     }
 
+    protected boolean zapRegisters() {
+        Register[] zappedRegisters = frameMap.registerConfig.getAllocatableRegisters();
+        AMD64LIRInstruction[] zappingMoves = new AMD64LIRInstruction[zappedRegisters.length];
+        for (int i = 0; i < zappedRegisters.length; i++) {
+            PlatformKind kind = target.arch.getLargestStorableKind(zappedRegisters[i].getRegisterCategory());
+            assert kind != Kind.Illegal;
+
+            RegisterValue register = zappedRegisters[i].asValue(kind);
+            zappingMoves[i] = createMove(register, zapValueForKind(kind));
+        }
+        AMD64ZapRegistersOp zap = new AMD64ZapRegistersOp(zappingMoves);
+        append(zap);
+        calleeSaveInfo.put(currentRuntimeCallInfo, zap);
+        return true;
+    }
+
     @Override
     protected CallingConvention createCallingConvention() {
         Stub stub = runtime().asStub(method);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotRuntimeCallTarget.java	Wed May 01 18:08:07 2013 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotRuntimeCallTarget.java	Thu May 02 06:08:02 2013 +0200
@@ -99,10 +99,10 @@
                 argumentLocations[i] = cc.getArgument(i);
             }
 
-            Set<Register> definedRegisters = stub.getDefinedRegisters();
-            AllocatableValue[] temporaryLocations = new AllocatableValue[definedRegisters.size()];
+            Set<Register> destroyedRegisters = stub.getDestroyedRegisters();
+            AllocatableValue[] temporaryLocations = new AllocatableValue[destroyedRegisters.size()];
             int i = 0;
-            for (Register reg : definedRegisters) {
+            for (Register reg : destroyedRegisters) {
                 temporaryLocations[i++] = reg.asValue();
             }
             // Update calling convention with temporaries
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Wed May 01 18:08:07 2013 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Thu May 02 06:08:02 2013 +0200
@@ -316,7 +316,7 @@
                         /* arg2:     value */                       Kind.Long,
                         /* arg3:     value */                       Kind.Long));
 
-        addCRuntimeCall(STUB_PRINTF_C, config.vmMessageAddress,
+        addCRuntimeCall(VM_MESSAGE_C, config.vmMessageAddress,
                         /*             ret */ ret(Kind.Void),
                         /* arg0:   vmError */ nativeCallingConvention(Kind.Boolean,
                         /* arg1:    format */                         word,
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java	Wed May 01 18:08:07 2013 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java	Thu May 02 06:08:02 2013 +0200
@@ -52,6 +52,16 @@
         super(runtime, replacements, target, linkage);
     }
 
+    /**
+     * This stub is called when returning to a method to handle an exception thrown by a callee. It
+     * is not used for routing implicit exceptions. Therefore, it does not need to save any
+     * registers as HotSpot uses a caller save convention.
+     */
+    @Override
+    public boolean preservesRegisters() {
+        return false;
+    }
+
     @Snippet
     private static void exceptionHandler(Object exception, Word exceptionPc) {
         checkNoExceptionInThread();
@@ -64,8 +74,6 @@
         // patch throwing pc into return address so that deoptimization finds the right debug info
         patchReturnAddress(exceptionPc);
 
-        // TODO (ds) add support for non-register-preserving C runtime calls and
-        // use it for this call as no registers need saving by this stub
         Word handlerPc = exceptionHandlerForPc(EXCEPTION_HANDLER_FOR_PC, thread());
 
         if (logging()) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java	Wed May 01 18:08:07 2013 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java	Thu May 02 06:08:02 2013 +0200
@@ -88,19 +88,31 @@
     protected InstalledCode code;
 
     /**
-     * The registers/temporaries defined by this stub.
+     * The registers destroyed by this stub.
      */
-    private Set<Register> definedRegisters;
+    private Set<Register> destroyedRegisters;
 
-    public void initDefinedRegisters(Set<Register> registers) {
+    public void initDestroyedRegisters(Set<Register> registers) {
         assert registers != null;
-        assert definedRegisters == null || registers.equals(definedRegisters) : "cannot redefine";
-        definedRegisters = registers;
+        assert destroyedRegisters == null || registers.equals(destroyedRegisters) : "cannot redefine";
+        destroyedRegisters = registers;
     }
 
-    public Set<Register> getDefinedRegisters() {
-        assert definedRegisters != null : "not yet initialized";
-        return definedRegisters;
+    /**
+     * Gets the registers defined by this stub. These are the temporaries of this stub and must thus
+     * be caller saved by a callers of this stub.
+     */
+    public Set<Register> getDestroyedRegisters() {
+        assert destroyedRegisters != null : "not yet initialized";
+        return destroyedRegisters;
+    }
+
+    /**
+     * Determines if this stub preserves all registers apart from those it
+     * {@linkplain #getDestroyedRegisters() destroys}.
+     */
+    public boolean preservesRegisters() {
+        return true;
     }
 
     /**
@@ -198,7 +210,7 @@
 
                     assert checkStubInvariants(compResult);
 
-                    assert definedRegisters != null;
+                    assert destroyedRegisters != null;
                     code = Debug.scope("CodeInstall", new Callable<InstalledCode>() {
 
                         @Override
@@ -259,10 +271,10 @@
         }
     }
 
-    public static final Descriptor STUB_PRINTF_C = descriptorFor(Stub.class, "printfC", false);
+    public static final Descriptor VM_MESSAGE_C = descriptorFor(Stub.class, "vmMessageC", false);
 
     @NodeIntrinsic(CRuntimeCall.class)
-    private static native void printfC(@ConstantNodeParameter Descriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3);
+    private static native void vmMessageC(@ConstantNodeParameter Descriptor stubPrintfC, boolean vmError, Word format, long v1, long v2, long v3);
 
     /**
      * Prints a message to the log stream.
@@ -273,7 +285,7 @@
      * @param message a message string
      */
     public static void printf(String message) {
-        printfC(STUB_PRINTF_C, false, cstring(message), 0L, 0L, 0L);
+        vmMessageC(VM_MESSAGE_C, false, cstring(message), 0L, 0L, 0L);
     }
 
     /**
@@ -286,7 +298,7 @@
      * @param value the value associated with the first conversion specifier in {@code format}
      */
     public static void printf(String format, long value) {
-        printfC(STUB_PRINTF_C, false, cstring(format), value, 0L, 0L);
+        vmMessageC(VM_MESSAGE_C, false, cstring(format), value, 0L, 0L);
     }
 
     /**
@@ -300,7 +312,7 @@
      * @param v2 the value associated with the second conversion specifier in {@code format}
      */
     public static void printf(String format, long v1, long v2) {
-        printfC(STUB_PRINTF_C, false, cstring(format), v1, v2, 0L);
+        vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, 0L);
     }
 
     /**
@@ -315,7 +327,7 @@
      * @param v3 the value associated with the third conversion specifier in {@code format}
      */
     public static void printf(String format, long v1, long v2, long v3) {
-        printfC(STUB_PRINTF_C, false, cstring(format), v1, v2, v3);
+        vmMessageC(VM_MESSAGE_C, false, cstring(format), v1, v2, v3);
     }
 
     /**
@@ -327,7 +339,7 @@
      * @param message an error message
      */
     public static void fatal(String message) {
-        printfC(STUB_PRINTF_C, true, cstring(message), 0L, 0L, 0L);
+        vmMessageC(VM_MESSAGE_C, true, cstring(message), 0L, 0L, 0L);
     }
 
     /**
@@ -340,7 +352,7 @@
      * @param value the value associated with the first conversion specifier in {@code format}
      */
     public static void fatal(String format, long value) {
-        printfC(STUB_PRINTF_C, true, cstring(format), value, 0L, 0L);
+        vmMessageC(VM_MESSAGE_C, true, cstring(format), value, 0L, 0L);
     }
 
     /**
@@ -354,7 +366,7 @@
      * @param v2 the value associated with the second conversion specifier in {@code format}
      */
     public static void fatal(String format, long v1, long v2) {
-        printfC(STUB_PRINTF_C, true, cstring(format), v1, v2, 0L);
+        vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, 0L);
     }
 
     /**
@@ -369,6 +381,6 @@
      * @param v3 the value associated with the third conversion specifier in {@code format}
      */
     public static void fatal(String format, long v1, long v2, long v3) {
-        printfC(STUB_PRINTF_C, true, cstring(format), v1, v2, v3);
+        vmMessageC(VM_MESSAGE_C, true, cstring(format), v1, v2, v3);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64RegistersPreservationOp.java	Thu May 02 06:08:02 2013 +0200
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, 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.amd64;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.lir.*;
+
+/**
+ * Base class for operations that preserve a set of registers.
+ */
+public abstract class AMD64RegistersPreservationOp extends AMD64LIRInstruction {
+
+    /**
+     * Prunes the set of registers preserved by this operation to exclude those in {@code ignored}
+     * and updates {@code debugInfo} with a {@linkplain DebugInfo#getCalleeSaveInfo() description}
+     * of where each preserved register is saved.
+     */
+    public abstract void update(Set<Register> ignored, DebugInfo debugInfo, FrameMap frameMap);
+}
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64SaveRegistersOp.java	Wed May 01 18:08:07 2013 -0700
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64SaveRegistersOp.java	Thu May 02 06:08:02 2013 +0200
@@ -37,7 +37,7 @@
  * Saves registers to stack slots.
  */
 @Opcode("SAVE_REGISTER")
-public final class AMD64SaveRegistersOp extends AMD64LIRInstruction {
+public final class AMD64SaveRegistersOp extends AMD64RegistersPreservationOp {
 
     /**
      * The move instructions for saving the registers.
@@ -70,16 +70,17 @@
     }
 
     /**
-     * Prunes the set of registers saved by this operation to exclude those in {@code notSaved} and
+     * Prunes the set of registers saved by this operation to exclude those in {@code ignored} and
      * updates {@code debugInfo} with a {@linkplain DebugInfo#getCalleeSaveInfo() description} of
      * where each preserved register is saved.
      */
-    public void updateAndDescribePreservation(Set<Register> notSaved, DebugInfo debugInfo, FrameMap frameMap) {
+    @Override
+    public void update(Set<Register> ignored, DebugInfo debugInfo, FrameMap frameMap) {
         int preserved = 0;
         for (int i = 0; i < savingMoves.length; i++) {
             if (savingMoves[i] != null) {
                 Register register = ValueUtil.asRegister(((MoveOp) savingMoves[i]).getInput());
-                if (notSaved.contains(register)) {
+                if (ignored.contains(register)) {
                     savingMoves[i] = null;
                     restoringMoves[i] = null;
                 } else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ZapRegistersOp.java	Thu May 02 06:08:02 2013 +0200
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2013, 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.amd64;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.asm.amd64.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.LIRInstruction.Opcode;
+import com.oracle.graal.lir.StandardOp.MoveOp;
+import com.oracle.graal.lir.asm.*;
+
+/**
+ * Writes well known garbage values to registers.
+ */
+@Opcode("ZAP_REGISTER")
+public final class AMD64ZapRegistersOp extends AMD64RegistersPreservationOp {
+
+    /**
+     * The move instructions for zapping the registers.
+     */
+    protected final AMD64LIRInstruction[] zappingMoves;
+
+    public AMD64ZapRegistersOp(AMD64LIRInstruction[] zappingMoves) {
+        this.zappingMoves = zappingMoves;
+    }
+
+    @Override
+    public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) {
+        for (AMD64LIRInstruction zappingMove : zappingMoves) {
+            if (zappingMove != null) {
+                zappingMove.emitCode(tasm, masm);
+            }
+        }
+    }
+
+    /**
+     * Prunes the set of registers zapped by this operation to exclude those in {@code ignored}.
+     */
+    @Override
+    public void update(Set<Register> ignored, DebugInfo debugInfo, FrameMap frameMap) {
+        for (int i = 0; i < zappingMoves.length; i++) {
+            if (zappingMoves[i] != null) {
+                Register register = ValueUtil.asRegister(((MoveOp) zappingMoves[i]).getResult());
+                if (ignored.contains(register)) {
+                    zappingMoves[i] = null;
+                }
+            }
+        }
+    }
+}