hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotBackend.java
changeset 43972 1ade39b8381b
child 46344 694c102fd8ed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotBackend.java	Thu Feb 16 15:46:09 2017 -0800
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2013, 2016, 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 org.graalvm.compiler.hotspot.sparc;
+
+import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BPCC;
+import static org.graalvm.compiler.asm.sparc.SPARCAssembler.isGlobalRegister;
+import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.NOT_ANNUL;
+import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_NOT_TAKEN;
+import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Xcc;
+import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.NotEqual;
+import static org.graalvm.compiler.core.common.GraalOptions.ZapStackOnMethodEntry;
+import static jdk.vm.ci.code.ValueUtil.asRegister;
+import static jdk.vm.ci.code.ValueUtil.isRegister;
+import static jdk.vm.ci.sparc.SPARC.g0;
+import static jdk.vm.ci.sparc.SPARC.g5;
+import static jdk.vm.ci.sparc.SPARC.i0;
+import static jdk.vm.ci.sparc.SPARC.i7;
+import static jdk.vm.ci.sparc.SPARC.l0;
+import static jdk.vm.ci.sparc.SPARC.l7;
+import static jdk.vm.ci.sparc.SPARC.o0;
+import static jdk.vm.ci.sparc.SPARC.o7;
+import static jdk.vm.ci.sparc.SPARC.sp;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.graalvm.compiler.asm.Assembler;
+import org.graalvm.compiler.asm.Label;
+import org.graalvm.compiler.asm.sparc.SPARCAddress;
+import org.graalvm.compiler.asm.sparc.SPARCAssembler;
+import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler;
+import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler.ScratchRegister;
+import org.graalvm.compiler.code.CompilationResult;
+import org.graalvm.compiler.code.DataSection;
+import org.graalvm.compiler.code.DataSection.Data;
+import org.graalvm.compiler.core.common.CompilationIdentifier;
+import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
+import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
+import org.graalvm.compiler.core.sparc.SPARCNodeMatchRules;
+import org.graalvm.compiler.debug.Debug;
+import org.graalvm.compiler.debug.DebugCounter;
+import org.graalvm.compiler.hotspot.HotSpotDataBuilder;
+import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
+import org.graalvm.compiler.hotspot.HotSpotHostBackend;
+import org.graalvm.compiler.hotspot.HotSpotLIRGenerationResult;
+import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
+import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProvider;
+import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
+import org.graalvm.compiler.hotspot.stubs.Stub;
+import org.graalvm.compiler.lir.InstructionValueConsumer;
+import org.graalvm.compiler.lir.LIR;
+import org.graalvm.compiler.lir.LIRFrameState;
+import org.graalvm.compiler.lir.LIRInstruction;
+import org.graalvm.compiler.lir.StandardOp.SaveRegistersOp;
+import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
+import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory;
+import org.graalvm.compiler.lir.asm.DataBuilder;
+import org.graalvm.compiler.lir.asm.FrameContext;
+import org.graalvm.compiler.lir.framemap.FrameMap;
+import org.graalvm.compiler.lir.framemap.FrameMapBuilder;
+import org.graalvm.compiler.lir.gen.LIRGenerationResult;
+import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
+import org.graalvm.compiler.lir.sparc.SPARCCall;
+import org.graalvm.compiler.lir.sparc.SPARCDelayedControlTransfer;
+import org.graalvm.compiler.lir.sparc.SPARCFrameMap;
+import org.graalvm.compiler.lir.sparc.SPARCFrameMapBuilder;
+import org.graalvm.compiler.lir.sparc.SPARCLIRInstructionMixin;
+import org.graalvm.compiler.lir.sparc.SPARCLIRInstructionMixin.SizeEstimate;
+import org.graalvm.compiler.lir.sparc.SPARCTailDelayedLIRInstruction;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
+
+import jdk.vm.ci.code.CallingConvention;
+import jdk.vm.ci.code.Register;
+import jdk.vm.ci.code.RegisterConfig;
+import jdk.vm.ci.code.StackSlot;
+import jdk.vm.ci.hotspot.HotSpotCallingConventionType;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+/**
+ * HotSpot SPARC specific backend.
+ */
+public class SPARCHotSpotBackend extends HotSpotHostBackend {
+
+    private static final SizeEstimateStatistics CONSTANT_ESTIMATED_STATS = new SizeEstimateStatistics("ESTIMATE");
+    private static final SizeEstimateStatistics CONSTANT_ACTUAL_STATS = new SizeEstimateStatistics("ACTUAL");
+
+    public SPARCHotSpotBackend(GraalHotSpotVMConfig config, HotSpotGraalRuntimeProvider runtime, HotSpotProviders providers) {
+        super(config, runtime, providers);
+    }
+
+    private static class SizeEstimateStatistics {
+        private static final ConcurrentHashMap<String, DebugCounter> counters = new ConcurrentHashMap<>();
+        private final String suffix;
+
+        SizeEstimateStatistics(String suffix) {
+            super();
+            this.suffix = suffix;
+        }
+
+        public void add(Class<?> c, int count) {
+            String name = SizeEstimateStatistics.class.getSimpleName() + "_" + c.getSimpleName() + "." + suffix;
+            DebugCounter m = counters.computeIfAbsent(name, (n) -> Debug.counter(n));
+            m.add(count);
+        }
+    }
+
+    @Override
+    public FrameMapBuilder newFrameMapBuilder(RegisterConfig registerConfig) {
+        RegisterConfig registerConfigNonNull = registerConfig == null ? getCodeCache().getRegisterConfig() : registerConfig;
+        return new SPARCFrameMapBuilder(newFrameMap(registerConfigNonNull), getCodeCache(), registerConfigNonNull);
+    }
+
+    @Override
+    public FrameMap newFrameMap(RegisterConfig registerConfig) {
+        return new SPARCFrameMap(getCodeCache(), registerConfig, this);
+    }
+
+    @Override
+    public LIRGeneratorTool newLIRGenerator(LIRGenerationResult lirGenRes) {
+        return new SPARCHotSpotLIRGenerator(getProviders(), getRuntime().getVMConfig(), lirGenRes);
+    }
+
+    @Override
+    public LIRGenerationResult newLIRGenerationResult(CompilationIdentifier compilationId, LIR lir, FrameMapBuilder frameMapBuilder, StructuredGraph graph, Object stub) {
+        return new HotSpotLIRGenerationResult(compilationId, lir, frameMapBuilder, makeCallingConvention(graph, (Stub) stub), stub);
+    }
+
+    @Override
+    public NodeLIRBuilderTool newNodeLIRBuilder(StructuredGraph graph, LIRGeneratorTool lirGen) {
+        return new SPARCHotSpotNodeLIRBuilder(graph, lirGen, new SPARCNodeMatchRules(lirGen));
+    }
+
+    @Override
+    protected void bangStackWithOffset(CompilationResultBuilder crb, int bangOffset) {
+        // Use SPARCAddress to get the final displacement including the stack bias.
+        SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm;
+        SPARCAddress address = new SPARCAddress(sp, -bangOffset);
+        if (SPARCAssembler.isSimm13(address.getDisplacement())) {
+            masm.stx(g0, address);
+        } else {
+            try (ScratchRegister sc = masm.getScratchRegister()) {
+                Register scratch = sc.getRegister();
+                assert isGlobalRegister(scratch) : "Only global (g1-g7) registers are allowed if the frame was not initialized here. Got register " + scratch;
+                masm.setx(address.getDisplacement(), scratch, false);
+                masm.stx(g0, new SPARCAddress(sp, scratch));
+            }
+        }
+    }
+
+    public class HotSpotFrameContext implements FrameContext {
+
+        final boolean isStub;
+
+        HotSpotFrameContext(boolean isStub) {
+            this.isStub = isStub;
+        }
+
+        @Override
+        public boolean hasFrame() {
+            return true;
+        }
+
+        @Override
+        public void enter(CompilationResultBuilder crb) {
+            final int frameSize = crb.frameMap.totalFrameSize();
+            final int stackpoinerChange = -frameSize;
+            SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm;
+            emitStackOverflowCheck(crb);
+
+            if (SPARCAssembler.isSimm13(stackpoinerChange)) {
+                masm.save(sp, stackpoinerChange, sp);
+            } else {
+                try (ScratchRegister sc = masm.getScratchRegister()) {
+                    Register scratch = sc.getRegister();
+                    assert isGlobalRegister(scratch) : "Only global registers are allowed before save. Got register " + scratch;
+                    masm.setx(stackpoinerChange, scratch, false);
+                    masm.save(sp, scratch, sp);
+                }
+            }
+
+            if (ZapStackOnMethodEntry.getValue()) {
+                final int slotSize = 8;
+                for (int i = 0; i < frameSize / slotSize; ++i) {
+                    // 0xC1C1C1C1
+                    masm.stx(g0, new SPARCAddress(sp, i * slotSize));
+                }
+            }
+        }
+
+        @Override
+        public void leave(CompilationResultBuilder crb) {
+            SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm;
+            masm.restoreWindow();
+        }
+    }
+
+    @Override
+    protected Assembler createAssembler(FrameMap frameMap) {
+        return new SPARCMacroAssembler(getTarget());
+    }
+
+    @Override
+    public CompilationResultBuilder newCompilationResultBuilder(LIRGenerationResult lirGenRes, FrameMap frameMap, CompilationResult compilationResult, CompilationResultBuilderFactory factory) {
+        HotSpotLIRGenerationResult gen = (HotSpotLIRGenerationResult) lirGenRes;
+        LIR lir = gen.getLIR();
+        assert gen.getDeoptimizationRescueSlot() == null || frameMap.frameNeedsAllocating() : "method that can deoptimize must have a frame";
+
+        Stub stub = gen.getStub();
+        Assembler masm = createAssembler(frameMap);
+        // On SPARC we always use stack frames.
+        HotSpotFrameContext frameContext = new HotSpotFrameContext(stub != null);
+        DataBuilder dataBuilder = new HotSpotDataBuilder(getCodeCache().getTarget());
+        CompilationResultBuilder crb = factory.createBuilder(getProviders().getCodeCache(), getProviders().getForeignCalls(), frameMap, masm, dataBuilder, frameContext, compilationResult);
+        crb.setTotalFrameSize(frameMap.totalFrameSize());
+        crb.setMaxInterpreterFrameSize(gen.getMaxInterpreterFrameSize());
+        StackSlot deoptimizationRescueSlot = gen.getDeoptimizationRescueSlot();
+        if (deoptimizationRescueSlot != null && stub == null) {
+            crb.compilationResult.setCustomStackAreaOffset(deoptimizationRescueSlot);
+        }
+
+        if (stub != null) {
+            // Even on sparc we need to save floating point registers
+            Set<Register> destroyedCallerRegisters = gatherDestroyedCallerRegisters(lir);
+            Map<LIRFrameState, SaveRegistersOp> calleeSaveInfo = gen.getCalleeSaveInfo();
+            updateStub(stub, destroyedCallerRegisters, calleeSaveInfo, frameMap);
+        }
+        assert registerSizePredictionValidator(crb);
+        return crb;
+    }
+
+    /**
+     * Registers a verifier which checks if the LIRInstructions estimate of constants size is
+     * greater or equal to the actual one.
+     */
+    private static boolean registerSizePredictionValidator(final CompilationResultBuilder crb) {
+        /**
+         * Used to hold state between beforeOp and afterOp
+         */
+        class ValidationState {
+            LIRInstruction op;
+            int constantSizeBefore;
+
+            public void before(LIRInstruction before) {
+                assert op == null : "LIRInstruction " + op + " no after call received";
+                op = before;
+                constantSizeBefore = calculateDataSectionSize(crb.compilationResult.getDataSection());
+            }
+
+            public void after(LIRInstruction after) {
+                assert after.equals(op) : "Instructions before/after don't match " + op + "/" + after;
+                int constantSizeAfter = calculateDataSectionSize(crb.compilationResult.getDataSection());
+                int actual = constantSizeAfter - constantSizeBefore;
+                if (op instanceof SPARCLIRInstructionMixin) {
+                    org.graalvm.compiler.lir.sparc.SPARCLIRInstructionMixin.SizeEstimate size = ((SPARCLIRInstructionMixin) op).estimateSize();
+                    assert size != null : "No size prediction available for op: " + op;
+                    Class<?> c = op.getClass();
+                    CONSTANT_ESTIMATED_STATS.add(c, size.constantSize);
+                    CONSTANT_ACTUAL_STATS.add(c, actual);
+                    assert size.constantSize >= actual : "Op " + op + " exceeded estimated constant size; predicted: " + size.constantSize + " actual: " + actual;
+                } else {
+                    assert actual == 0 : "Op " + op + " emitted to DataSection without any estimate.";
+                }
+                op = null;
+                constantSizeBefore = 0;
+            }
+        }
+        final ValidationState state = new ValidationState();
+        crb.setOpCallback(op -> state.before(op), op -> state.after(op));
+        return true;
+    }
+
+    private static int calculateDataSectionSize(DataSection ds) {
+        int sum = 0;
+        for (Data d : ds) {
+            sum += d.getSize();
+        }
+        return sum;
+    }
+
+    @Override
+    public void emitCode(CompilationResultBuilder crb, LIR lir, ResolvedJavaMethod installedCodeOwner) {
+        SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm;
+        // TODO: (sa) Fold the two traversals into one
+        stuffDelayedControlTransfers(lir);
+        int constantSize = calculateConstantSize(lir);
+        boolean canUseImmediateConstantLoad = constantSize < (1 << 13);
+        masm.setImmediateConstantLoad(canUseImmediateConstantLoad);
+        FrameMap frameMap = crb.frameMap;
+        RegisterConfig regConfig = frameMap.getRegisterConfig();
+        Label unverifiedStub = installedCodeOwner == null || installedCodeOwner.isStatic() ? null : new Label();
+        for (int i = 0; i < 2; i++) {
+            if (i > 0) {
+                crb.resetForEmittingCode();
+                lir.resetLabels();
+                resetDelayedControlTransfers(lir);
+            }
+
+            // Emit the prefix
+            if (unverifiedStub != null) {
+                crb.recordMark(config.MARKID_UNVERIFIED_ENTRY);
+                // We need to use JavaCall here because we haven't entered the frame yet.
+                CallingConvention cc = regConfig.getCallingConvention(HotSpotCallingConventionType.JavaCall, null, new JavaType[]{getProviders().getMetaAccess().lookupJavaType(Object.class)}, this);
+                Register inlineCacheKlass = g5; // see MacroAssembler::ic_call
+
+                try (ScratchRegister sc = masm.getScratchRegister()) {
+                    Register scratch = sc.getRegister();
+                    Register receiver = asRegister(cc.getArgument(0));
+                    SPARCAddress src = new SPARCAddress(receiver, config.hubOffset);
+
+                    masm.ldx(src, scratch);
+                    masm.cmp(scratch, inlineCacheKlass);
+                }
+                BPCC.emit(masm, Xcc, NotEqual, NOT_ANNUL, PREDICT_NOT_TAKEN, unverifiedStub);
+                masm.nop();  // delay slot
+            }
+
+            masm.align(config.codeEntryAlignment);
+            crb.recordMark(config.MARKID_OSR_ENTRY);
+            crb.recordMark(config.MARKID_VERIFIED_ENTRY);
+
+            // Emit code for the LIR
+            crb.emit(lir);
+        }
+        profileInstructions(lir, crb);
+
+        HotSpotFrameContext frameContext = (HotSpotFrameContext) crb.frameContext;
+        HotSpotForeignCallsProvider foreignCalls = getProviders().getForeignCalls();
+        if (!frameContext.isStub) {
+            crb.recordMark(config.MARKID_EXCEPTION_HANDLER_ENTRY);
+            SPARCCall.directCall(crb, masm, foreignCalls.lookupForeignCall(EXCEPTION_HANDLER), null, null);
+            crb.recordMark(config.MARKID_DEOPT_HANDLER_ENTRY);
+            SPARCCall.directCall(crb, masm, foreignCalls.lookupForeignCall(DEOPTIMIZATION_HANDLER), null, null);
+        } else {
+            // No need to emit the stubs for entries back into the method since
+            // it has no calls that can cause such "return" entries
+        }
+
+        if (unverifiedStub != null) {
+            masm.bind(unverifiedStub);
+            try (ScratchRegister sc = masm.getScratchRegister()) {
+                Register scratch = sc.getRegister();
+                SPARCCall.indirectJmp(crb, masm, scratch, foreignCalls.lookupForeignCall(IC_MISS_HANDLER));
+            }
+        }
+        masm.peephole();
+    }
+
+    private static int calculateConstantSize(LIR lir) {
+        int size = 0;
+        for (AbstractBlockBase<?> block : lir.codeEmittingOrder()) {
+            if (block == null) {
+                continue;
+            }
+            for (LIRInstruction inst : lir.getLIRforBlock(block)) {
+                if (inst instanceof SPARCLIRInstructionMixin) {
+                    SizeEstimate pred = ((SPARCLIRInstructionMixin) inst).estimateSize();
+                    if (pred != null) {
+                        size += pred.constantSize;
+                    }
+                }
+            }
+        }
+        return size;
+    }
+
+    private static void resetDelayedControlTransfers(LIR lir) {
+        for (AbstractBlockBase<?> block : lir.codeEmittingOrder()) {
+            if (block == null) {
+                continue;
+            }
+            for (LIRInstruction inst : lir.getLIRforBlock(block)) {
+                if (inst instanceof SPARCDelayedControlTransfer) {
+                    ((SPARCDelayedControlTransfer) inst).resetState();
+                }
+            }
+        }
+    }
+
+    /**
+     * Fix-up over whole LIR.
+     *
+     * @see #stuffDelayedControlTransfers(LIR, AbstractBlockBase)
+     * @param l
+     */
+    private static void stuffDelayedControlTransfers(LIR l) {
+        for (AbstractBlockBase<?> b : l.codeEmittingOrder()) {
+            if (b != null) {
+                stuffDelayedControlTransfers(l, b);
+            }
+        }
+    }
+
+    /**
+     * Tries to put DelayedControlTransfer instructions and DelayableLIRInstructions together. Also
+     * it tries to move the DelayedLIRInstruction to the DelayedControlTransfer instruction, if
+     * possible.
+     */
+    private static void stuffDelayedControlTransfers(LIR l, AbstractBlockBase<?> block) {
+        List<LIRInstruction> instructions = l.getLIRforBlock(block);
+        if (instructions.size() >= 2) {
+            LIRDependencyAccumulator acc = new LIRDependencyAccumulator();
+            SPARCDelayedControlTransfer delayedTransfer = null;
+            int delayTransferPosition = -1;
+            for (int i = instructions.size() - 1; i >= 0; i--) {
+                LIRInstruction inst = instructions.get(i);
+                boolean adjacent = delayTransferPosition - i == 1;
+                if (!adjacent || inst.destroysCallerSavedRegisters() || leavesRegisterWindow(inst)) {
+                    delayedTransfer = null;
+                }
+                if (inst instanceof SPARCDelayedControlTransfer) {
+                    delayedTransfer = (SPARCDelayedControlTransfer) inst;
+                    acc.start(inst);
+                    delayTransferPosition = i;
+                } else if (delayedTransfer != null) {
+                    boolean overlap = acc.add(inst);
+                    if (!overlap && inst instanceof SPARCTailDelayedLIRInstruction) {
+                        // We have found a non overlapping LIR instruction which can be delayed
+                        ((SPARCTailDelayedLIRInstruction) inst).setDelayedControlTransfer(delayedTransfer);
+                        delayedTransfer = null;
+                    }
+                }
+            }
+        }
+    }
+
+    private static boolean leavesRegisterWindow(LIRInstruction inst) {
+        return inst instanceof SPARCLIRInstructionMixin && ((SPARCLIRInstructionMixin) inst).leavesRegisterWindow();
+    }
+
+    /**
+     * Accumulates inputs/outputs/temp/alive in a set along we walk back the LIRInstructions and
+     * detects, if there is any overlap. In this way LIRInstructions can be detected, which can be
+     * moved nearer to the DelayedControlTransfer instruction.
+     */
+    private static class LIRDependencyAccumulator {
+        private final Set<Object> inputs = new HashSet<>(10);
+        private boolean overlap = false;
+
+        private final InstructionValueConsumer valueConsumer = (instruction, value, mode, flags) -> {
+            Object valueObject = value;
+            if (isRegister(value)) { // Canonicalize registers
+                valueObject = asRegister(value);
+            }
+            if (!inputs.add(valueObject)) {
+                overlap = true;
+            }
+        };
+
+        public void start(LIRInstruction initial) {
+            inputs.clear();
+            overlap = false;
+            initial.visitEachInput(valueConsumer);
+            initial.visitEachTemp(valueConsumer);
+            initial.visitEachAlive(valueConsumer);
+        }
+
+        /**
+         * Adds the inputs of lir instruction to the accumulator and returns, true if there was any
+         * overlap of parameters.
+         *
+         * @param inst
+         * @return true if an overlap was found
+         */
+        public boolean add(LIRInstruction inst) {
+            overlap = false;
+            inst.visitEachOutput(valueConsumer);
+            inst.visitEachTemp(valueConsumer);
+            inst.visitEachInput(valueConsumer);
+            inst.visitEachAlive(valueConsumer);
+            return overlap;
+        }
+    }
+
+    @Override
+    public RegisterAllocationConfig newRegisterAllocationConfig(RegisterConfig registerConfig) {
+        RegisterConfig registerConfigNonNull = registerConfig == null ? getCodeCache().getRegisterConfig() : registerConfig;
+        return new SPARCHotSpotRegisterAllocationConfig(registerConfigNonNull);
+    }
+
+    @Override
+    public Set<Register> translateToCallerRegisters(Set<Register> calleeRegisters) {
+        HashSet<Register> callerRegisters = new HashSet<>(calleeRegisters.size());
+        for (Register register : calleeRegisters) {
+            if (l0.number <= register.number && register.number <= l7.number) {
+                // do nothing
+            } else if (o0.number <= register.number && register.number <= o7.number) {
+                // do nothing
+            } else if (i0.number <= register.number && register.number <= i7.number) {
+                // translate input to output registers
+                callerRegisters.add(translateInputToOutputRegister(register));
+            } else {
+                callerRegisters.add(register);
+            }
+        }
+        return callerRegisters;
+    }
+
+    private Register translateInputToOutputRegister(Register register) {
+        assert i0.number <= register.number && register.number <= i7.number : "Not an input register " + register;
+        return getTarget().arch.getRegisters().get(o0.number + register.number - i0.number);
+    }
+}