| /* |
| * 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); |
| } |
| } |