1 /* 2 * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.hotspot.sparc; 26 27 import static jdk.vm.ci.code.ValueUtil.asRegister; 28 import static jdk.vm.ci.code.ValueUtil.isRegister; 29 import static jdk.vm.ci.sparc.SPARC.g0; 30 import static jdk.vm.ci.sparc.SPARC.g5; 31 import static jdk.vm.ci.sparc.SPARC.i0; 32 import static jdk.vm.ci.sparc.SPARC.i7; 33 import static jdk.vm.ci.sparc.SPARC.l0; 34 import static jdk.vm.ci.sparc.SPARC.l7; 35 import static jdk.vm.ci.sparc.SPARC.o0; 36 import static jdk.vm.ci.sparc.SPARC.o7; 37 import static jdk.vm.ci.sparc.SPARC.sp; 38 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BPCC; 39 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.isGlobalRegister; 40 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.NOT_ANNUL; 41 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_NOT_TAKEN; 42 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Xcc; 43 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.NotEqual; 44 import static org.graalvm.compiler.core.common.GraalOptions.ZapStackOnMethodEntry; 45 46 import java.util.ArrayList; 47 import java.util.HashSet; 48 import java.util.Set; 49 import java.util.concurrent.ConcurrentHashMap; 50 51 import jdk.internal.vm.compiler.collections.EconomicSet; 52 import jdk.internal.vm.compiler.collections.Equivalence; 53 import org.graalvm.compiler.asm.Assembler; 54 import org.graalvm.compiler.asm.Label; 55 import org.graalvm.compiler.asm.sparc.SPARCAddress; 56 import org.graalvm.compiler.asm.sparc.SPARCAssembler; 57 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler; 58 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler.ScratchRegister; 59 import org.graalvm.compiler.code.CompilationResult; 60 import org.graalvm.compiler.code.DataSection; 61 import org.graalvm.compiler.code.DataSection.Data; 62 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig; 63 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 64 import org.graalvm.compiler.core.gen.LIRGenerationProvider; 65 import org.graalvm.compiler.core.sparc.SPARCNodeMatchRules; 66 import org.graalvm.compiler.debug.CounterKey; 67 import org.graalvm.compiler.debug.DebugContext; 68 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 69 import org.graalvm.compiler.hotspot.HotSpotDataBuilder; 70 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; 71 import org.graalvm.compiler.hotspot.HotSpotHostBackend; 72 import org.graalvm.compiler.hotspot.HotSpotLIRGenerationResult; 73 import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProvider; 74 import org.graalvm.compiler.hotspot.meta.HotSpotProviders; 75 import org.graalvm.compiler.hotspot.stubs.Stub; 76 import org.graalvm.compiler.lir.InstructionValueConsumer; 77 import org.graalvm.compiler.lir.LIR; 78 import org.graalvm.compiler.lir.LIRInstruction; 79 import org.graalvm.compiler.lir.asm.CompilationResultBuilder; 80 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory; 81 import org.graalvm.compiler.lir.asm.DataBuilder; 82 import org.graalvm.compiler.lir.asm.FrameContext; 83 import org.graalvm.compiler.lir.framemap.FrameMap; 84 import org.graalvm.compiler.lir.framemap.FrameMapBuilder; 85 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 86 import org.graalvm.compiler.lir.gen.LIRGeneratorTool; 87 import org.graalvm.compiler.lir.sparc.SPARCCall; 88 import org.graalvm.compiler.lir.sparc.SPARCDelayedControlTransfer; 89 import org.graalvm.compiler.lir.sparc.SPARCFrameMap; 90 import org.graalvm.compiler.lir.sparc.SPARCFrameMapBuilder; 91 import org.graalvm.compiler.lir.sparc.SPARCLIRInstructionMixin; 92 import org.graalvm.compiler.lir.sparc.SPARCLIRInstructionMixin.SizeEstimate; 93 import org.graalvm.compiler.lir.sparc.SPARCTailDelayedLIRInstruction; 94 import org.graalvm.compiler.nodes.StructuredGraph; 95 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; 96 import org.graalvm.compiler.options.OptionValues; 97 98 import jdk.vm.ci.code.CallingConvention; 99 import jdk.vm.ci.code.Register; 100 import jdk.vm.ci.code.RegisterConfig; 101 import jdk.vm.ci.code.StackSlot; 102 import jdk.vm.ci.hotspot.HotSpotCallingConventionType; 103 import jdk.vm.ci.meta.JavaType; 104 import jdk.vm.ci.meta.ResolvedJavaMethod; 105 106 /** 107 * HotSpot SPARC specific backend. 108 */ 109 public class SPARCHotSpotBackend extends HotSpotHostBackend implements LIRGenerationProvider { 110 111 private static final SizeEstimateStatistics CONSTANT_ESTIMATED_STATS = new SizeEstimateStatistics("ESTIMATE"); 112 private static final SizeEstimateStatistics CONSTANT_ACTUAL_STATS = new SizeEstimateStatistics("ACTUAL"); 113 114 public SPARCHotSpotBackend(GraalHotSpotVMConfig config, HotSpotGraalRuntimeProvider runtime, HotSpotProviders providers) { 115 super(config, runtime, providers); 116 } 117 118 private static class SizeEstimateStatistics { 119 private static final ConcurrentHashMap<String, CounterKey> counters = new ConcurrentHashMap<>(); 120 private final String suffix; 121 122 SizeEstimateStatistics(String suffix) { 123 super(); 124 this.suffix = suffix; 125 } 126 127 public void add(Class<?> c, int count, DebugContext debug) { 128 String name = SizeEstimateStatistics.class.getSimpleName() + "_" + c.getSimpleName() + "." + suffix; 129 CounterKey m = counters.computeIfAbsent(name, (n) -> DebugContext.counter(n)); 130 m.add(debug, count); 131 } 132 } 133 134 @Override 135 protected FrameMapBuilder newFrameMapBuilder(RegisterConfig registerConfig) { 136 RegisterConfig registerConfigNonNull = registerConfig == null ? getCodeCache().getRegisterConfig() : registerConfig; 137 FrameMap frameMap = new SPARCFrameMap(getCodeCache(), registerConfigNonNull, this); 138 return new SPARCFrameMapBuilder(frameMap, getCodeCache(), registerConfigNonNull); 139 } 140 141 @Override 142 public LIRGeneratorTool newLIRGenerator(LIRGenerationResult lirGenRes) { 143 return new SPARCHotSpotLIRGenerator(getProviders(), getRuntime().getVMConfig(), lirGenRes); 144 } 145 146 @Override 147 public NodeLIRBuilderTool newNodeLIRBuilder(StructuredGraph graph, LIRGeneratorTool lirGen) { 148 return new SPARCHotSpotNodeLIRBuilder(graph, lirGen, new SPARCNodeMatchRules(lirGen)); 149 } 150 151 @Override 152 protected void bangStackWithOffset(CompilationResultBuilder crb, int bangOffset) { 153 // Use SPARCAddress to get the final displacement including the stack bias. 154 SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm; 155 SPARCAddress address = new SPARCAddress(sp, -bangOffset); 156 if (SPARCAssembler.isSimm13(address.getDisplacement())) { 157 masm.stx(g0, address); 158 } else { 159 try (ScratchRegister sc = masm.getScratchRegister()) { 160 Register scratch = sc.getRegister(); 161 assert isGlobalRegister(scratch) : "Only global (g1-g7) registers are allowed if the frame was not initialized here. Got register " + scratch; 162 masm.setx(address.getDisplacement(), scratch, false); 163 masm.stx(g0, new SPARCAddress(sp, scratch)); 164 } 165 } 166 } 167 168 public class HotSpotFrameContext implements FrameContext { 169 170 final boolean isStub; 171 172 HotSpotFrameContext(boolean isStub) { 173 this.isStub = isStub; 174 } 175 176 @Override 177 public boolean hasFrame() { 178 return true; 179 } 180 181 @Override 182 public void enter(CompilationResultBuilder crb) { 183 final int frameSize = crb.frameMap.totalFrameSize(); 184 final int stackpointerChange = -frameSize; 185 SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm; 186 if (!isStub) { 187 emitStackOverflowCheck(crb); 188 } 189 190 if (SPARCAssembler.isSimm13(stackpointerChange)) { 191 masm.save(sp, stackpointerChange, sp); 192 } else { 193 try (ScratchRegister sc = masm.getScratchRegister()) { 194 Register scratch = sc.getRegister(); 195 assert isGlobalRegister(scratch) : "Only global registers are allowed before save. Got register " + scratch; 196 masm.setx(stackpointerChange, scratch, false); 197 masm.save(sp, scratch, sp); 198 } 199 } 200 201 if (ZapStackOnMethodEntry.getValue(crb.getOptions())) { 202 final int slotSize = 8; 203 for (int i = 0; i < frameSize / slotSize; ++i) { 204 // 0xC1C1C1C1 205 masm.stx(g0, new SPARCAddress(sp, i * slotSize)); 206 } 207 } 208 } 209 210 @Override 211 public void leave(CompilationResultBuilder crb) { 212 SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm; 213 masm.restoreWindow(); 214 } 215 } 216 217 @Override 218 public CompilationResultBuilder newCompilationResultBuilder(LIRGenerationResult lirGenRes, FrameMap frameMap, CompilationResult compilationResult, CompilationResultBuilderFactory factory) { 219 HotSpotLIRGenerationResult gen = (HotSpotLIRGenerationResult) lirGenRes; 220 LIR lir = gen.getLIR(); 221 assert gen.getDeoptimizationRescueSlot() == null || frameMap.frameNeedsAllocating() : "method that can deoptimize must have a frame"; 222 223 Stub stub = gen.getStub(); 224 Assembler masm = new SPARCMacroAssembler(getTarget()); 225 // On SPARC we always use stack frames. 226 HotSpotFrameContext frameContext = new HotSpotFrameContext(stub != null); 227 DataBuilder dataBuilder = new HotSpotDataBuilder(getCodeCache().getTarget()); 228 OptionValues options = lir.getOptions(); 229 DebugContext debug = lir.getDebug(); 230 CompilationResultBuilder crb = factory.createBuilder(getProviders().getCodeCache(), getProviders().getForeignCalls(), frameMap, masm, dataBuilder, frameContext, options, debug, 231 compilationResult, Register.None); 232 crb.setTotalFrameSize(frameMap.totalFrameSize()); 233 crb.setMaxInterpreterFrameSize(gen.getMaxInterpreterFrameSize()); 234 StackSlot deoptimizationRescueSlot = gen.getDeoptimizationRescueSlot(); 235 if (deoptimizationRescueSlot != null && stub == null) { 236 crb.compilationResult.setCustomStackAreaOffset(deoptimizationRescueSlot); 237 } 238 239 if (stub != null) { 240 updateStub(stub, gen, frameMap); 241 } 242 assert registerSizePredictionValidator(crb, debug); 243 return crb; 244 } 245 246 /** 247 * Registers a verifier which checks if the LIRInstructions estimate of constants size is 248 * greater or equal to the actual one. 249 */ 250 private static boolean registerSizePredictionValidator(final CompilationResultBuilder crb, DebugContext debug) { 251 /** 252 * Used to hold state between beforeOp and afterOp 253 */ 254 class ValidationState { 255 LIRInstruction op; 256 final DebugContext debug; 257 int constantSizeBefore; 258 259 ValidationState(DebugContext debug) { 260 this.debug = debug; 261 } 262 263 public void before(LIRInstruction before) { 264 assert op == null : "LIRInstruction " + op + " no after call received"; 265 op = before; 266 constantSizeBefore = calculateDataSectionSize(crb.compilationResult.getDataSection()); 267 } 268 269 public void after(LIRInstruction after) { 270 assert after.equals(op) : "Instructions before/after don't match " + op + "/" + after; 271 int constantSizeAfter = calculateDataSectionSize(crb.compilationResult.getDataSection()); 272 int actual = constantSizeAfter - constantSizeBefore; 273 if (op instanceof SPARCLIRInstructionMixin) { 274 org.graalvm.compiler.lir.sparc.SPARCLIRInstructionMixin.SizeEstimate size = ((SPARCLIRInstructionMixin) op).estimateSize(); 275 assert size != null : "No size prediction available for op: " + op; 276 Class<?> c = op.getClass(); 277 CONSTANT_ESTIMATED_STATS.add(c, size.constantSize, debug); 278 CONSTANT_ACTUAL_STATS.add(c, actual, debug); 279 assert size.constantSize >= actual : "Op " + op + " exceeded estimated constant size; predicted: " + size.constantSize + " actual: " + actual; 280 } else { 281 assert actual == 0 : "Op " + op + " emitted to DataSection without any estimate."; 282 } 283 op = null; 284 constantSizeBefore = 0; 285 } 286 } 287 final ValidationState state = new ValidationState(debug); 288 crb.setOpCallback(op -> state.before(op), op -> state.after(op)); 289 return true; 290 } 291 292 private static int calculateDataSectionSize(DataSection ds) { 293 int sum = 0; 294 for (Data d : ds) { 295 sum += d.getSize(); 296 } 297 return sum; 298 } 299 300 @Override 301 public void emitCode(CompilationResultBuilder crb, LIR lir, ResolvedJavaMethod installedCodeOwner) { 302 SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm; 303 // TODO: (sa) Fold the two traversals into one 304 stuffDelayedControlTransfers(lir); 305 int constantSize = calculateConstantSize(lir); 306 boolean canUseImmediateConstantLoad = constantSize < (1 << 13); 307 masm.setImmediateConstantLoad(canUseImmediateConstantLoad); 308 FrameMap frameMap = crb.frameMap; 309 RegisterConfig regConfig = frameMap.getRegisterConfig(); 310 Label unverifiedStub = installedCodeOwner == null || installedCodeOwner.isStatic() ? null : new Label(); 311 for (int i = 0; i < 2; i++) { 312 if (i > 0) { 313 crb.resetForEmittingCode(); 314 lir.resetLabels(); 315 resetDelayedControlTransfers(lir); 316 } 317 318 // Emit the prefix 319 if (unverifiedStub != null) { 320 crb.recordMark(config.MARKID_UNVERIFIED_ENTRY); 321 // We need to use JavaCall here because we haven't entered the frame yet. 322 CallingConvention cc = regConfig.getCallingConvention(HotSpotCallingConventionType.JavaCall, null, new JavaType[]{getProviders().getMetaAccess().lookupJavaType(Object.class)}, this); 323 Register inlineCacheKlass = g5; // see MacroAssembler::ic_call 324 325 try (ScratchRegister sc = masm.getScratchRegister()) { 326 Register scratch = sc.getRegister(); 327 Register receiver = asRegister(cc.getArgument(0)); 328 SPARCAddress src = new SPARCAddress(receiver, config.hubOffset); 329 330 masm.ldx(src, scratch); 331 masm.cmp(scratch, inlineCacheKlass); 332 } 333 BPCC.emit(masm, Xcc, NotEqual, NOT_ANNUL, PREDICT_NOT_TAKEN, unverifiedStub); 334 masm.nop(); // delay slot 335 } 336 337 masm.align(config.codeEntryAlignment); 338 crb.recordMark(config.MARKID_OSR_ENTRY); 339 crb.recordMark(config.MARKID_VERIFIED_ENTRY); 340 341 // Emit code for the LIR 342 crb.emit(lir); 343 } 344 profileInstructions(lir, crb); 345 346 HotSpotFrameContext frameContext = (HotSpotFrameContext) crb.frameContext; 347 HotSpotForeignCallsProvider foreignCalls = getProviders().getForeignCalls(); 348 if (!frameContext.isStub) { 349 crb.recordMark(config.MARKID_EXCEPTION_HANDLER_ENTRY); 350 SPARCCall.directCall(crb, masm, foreignCalls.lookupForeignCall(EXCEPTION_HANDLER), null, null); 351 crb.recordMark(config.MARKID_DEOPT_HANDLER_ENTRY); 352 SPARCCall.directCall(crb, masm, foreignCalls.lookupForeignCall(DEOPT_BLOB_UNPACK), null, null); 353 } else { 354 // No need to emit the stubs for entries back into the method since 355 // it has no calls that can cause such "return" entries 356 } 357 358 if (unverifiedStub != null) { 359 masm.bind(unverifiedStub); 360 try (ScratchRegister sc = masm.getScratchRegister()) { 361 Register scratch = sc.getRegister(); 362 SPARCCall.indirectJmp(crb, masm, scratch, foreignCalls.lookupForeignCall(IC_MISS_HANDLER)); 363 } 364 } 365 masm.peephole(); 366 } 367 368 private static int calculateConstantSize(LIR lir) { 369 int size = 0; 370 for (AbstractBlockBase<?> block : lir.codeEmittingOrder()) { 371 if (block == null) { 372 continue; 373 } 374 for (LIRInstruction inst : lir.getLIRforBlock(block)) { 375 if (inst instanceof SPARCLIRInstructionMixin) { 376 SizeEstimate pred = ((SPARCLIRInstructionMixin) inst).estimateSize(); 377 if (pred != null) { 378 size += pred.constantSize; 379 } 380 } 381 } 382 } 383 return size; 384 } 385 386 private static void resetDelayedControlTransfers(LIR lir) { 387 for (AbstractBlockBase<?> block : lir.codeEmittingOrder()) { 388 if (block == null) { 389 continue; 390 } 391 for (LIRInstruction inst : lir.getLIRforBlock(block)) { 392 if (inst instanceof SPARCDelayedControlTransfer) { 393 ((SPARCDelayedControlTransfer) inst).resetState(); 394 } 395 } 396 } 397 } 398 399 /** 400 * Fix-up over whole LIR. 401 * 402 * @see #stuffDelayedControlTransfers(LIR, AbstractBlockBase) 403 * @param l 404 */ 405 private static void stuffDelayedControlTransfers(LIR l) { 406 for (AbstractBlockBase<?> b : l.codeEmittingOrder()) { 407 if (b != null) { 408 stuffDelayedControlTransfers(l, b); 409 } 410 } 411 } 412 413 /** 414 * Tries to put DelayedControlTransfer instructions and DelayableLIRInstructions together. Also 415 * it tries to move the DelayedLIRInstruction to the DelayedControlTransfer instruction, if 416 * possible. 417 */ 418 private static void stuffDelayedControlTransfers(LIR l, AbstractBlockBase<?> block) { 419 ArrayList<LIRInstruction> instructions = l.getLIRforBlock(block); 420 if (instructions.size() >= 2) { 421 LIRDependencyAccumulator acc = new LIRDependencyAccumulator(); 422 SPARCDelayedControlTransfer delayedTransfer = null; 423 int delayTransferPosition = -1; 424 for (int i = instructions.size() - 1; i >= 0; i--) { 425 LIRInstruction inst = instructions.get(i); 426 boolean adjacent = delayTransferPosition - i == 1; 427 if (!adjacent || inst.destroysCallerSavedRegisters() || leavesRegisterWindow(inst)) { 428 delayedTransfer = null; 429 } 430 if (inst instanceof SPARCDelayedControlTransfer) { 431 delayedTransfer = (SPARCDelayedControlTransfer) inst; 432 acc.start(inst); 433 delayTransferPosition = i; 434 } else if (delayedTransfer != null) { 435 boolean overlap = acc.add(inst); 436 if (!overlap && inst instanceof SPARCTailDelayedLIRInstruction) { 437 // We have found a non overlapping LIR instruction which can be delayed 438 ((SPARCTailDelayedLIRInstruction) inst).setDelayedControlTransfer(delayedTransfer); 439 delayedTransfer = null; 440 } 441 } 442 } 443 } 444 } 445 446 private static boolean leavesRegisterWindow(LIRInstruction inst) { 447 return inst instanceof SPARCLIRInstructionMixin && ((SPARCLIRInstructionMixin) inst).leavesRegisterWindow(); 448 } 449 450 /** 451 * Accumulates inputs/outputs/temp/alive in a set along we walk back the LIRInstructions and 452 * detects, if there is any overlap. In this way LIRInstructions can be detected, which can be 453 * moved nearer to the DelayedControlTransfer instruction. 454 */ 455 private static class LIRDependencyAccumulator { 456 private final Set<Object> inputs = new HashSet<>(10); 457 private boolean overlap = false; 458 459 private final InstructionValueConsumer valueConsumer = (instruction, value, mode, flags) -> { 460 Object valueObject = value; 461 if (isRegister(value)) { // Canonicalize registers 462 valueObject = asRegister(value); 463 } 464 if (!inputs.add(valueObject)) { 465 overlap = true; 466 } 467 }; 468 469 public void start(LIRInstruction initial) { 470 inputs.clear(); 471 overlap = false; 472 initial.visitEachInput(valueConsumer); 473 initial.visitEachTemp(valueConsumer); 474 initial.visitEachAlive(valueConsumer); 475 } 476 477 /** 478 * Adds the inputs of lir instruction to the accumulator and returns, true if there was any 479 * overlap of parameters. 480 * 481 * @param inst 482 * @return true if an overlap was found 483 */ 484 public boolean add(LIRInstruction inst) { 485 overlap = false; 486 inst.visitEachOutput(valueConsumer); 487 inst.visitEachTemp(valueConsumer); 488 inst.visitEachInput(valueConsumer); 489 inst.visitEachAlive(valueConsumer); 490 return overlap; 491 } 492 } 493 494 @Override 495 public RegisterAllocationConfig newRegisterAllocationConfig(RegisterConfig registerConfig, String[] allocationRestrictedTo) { 496 RegisterConfig registerConfigNonNull = registerConfig == null ? getCodeCache().getRegisterConfig() : registerConfig; 497 return new SPARCHotSpotRegisterAllocationConfig(registerConfigNonNull, allocationRestrictedTo); 498 } 499 500 @Override 501 public EconomicSet<Register> translateToCallerRegisters(EconomicSet<Register> calleeRegisters) { 502 EconomicSet<Register> callerRegisters = EconomicSet.create(Equivalence.IDENTITY, calleeRegisters.size()); 503 for (Register register : calleeRegisters) { 504 if (l0.number <= register.number && register.number <= l7.number) { 505 // do nothing 506 } else if (o0.number <= register.number && register.number <= o7.number) { 507 // do nothing 508 } else if (i0.number <= register.number && register.number <= i7.number) { 509 // translate input to output registers 510 callerRegisters.add(translateInputToOutputRegister(register)); 511 } else { 512 callerRegisters.add(register); 513 } 514 } 515 return callerRegisters; 516 } 517 518 private Register translateInputToOutputRegister(Register register) { 519 assert i0.number <= register.number && register.number <= i7.number : "Not an input register " + register; 520 return getTarget().arch.getRegisters().get(o0.number + register.number - i0.number); 521 } 522 }