1 /* 2 * Copyright (c) 2013, 2017, 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.lir.sparc; 26 27 import static jdk.vm.ci.code.ValueUtil.asRegister; 28 import static jdk.vm.ci.sparc.SPARC.CPU; 29 import static jdk.vm.ci.sparc.SPARC.g0; 30 import static jdk.vm.ci.sparc.SPARCKind.WORD; 31 import static jdk.vm.ci.sparc.SPARCKind.XWORD; 32 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BPCC; 33 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CBCOND; 34 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.FBPCC; 35 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.INSTRUCTION_SIZE; 36 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.isSimm10; 37 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.isSimm11; 38 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.isSimm13; 39 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.isSimm5; 40 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.ANNUL; 41 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.NOT_ANNUL; 42 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_NOT_TAKEN; 43 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_TAKEN; 44 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Fcc0; 45 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Icc; 46 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Xcc; 47 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Always; 48 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Equal; 49 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_Equal; 50 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_Greater; 51 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_GreaterOrEqual; 52 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_Less; 53 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_LessOrEqual; 54 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_UnorderedGreaterOrEqual; 55 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_UnorderedOrEqual; 56 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_UnorderedOrGreater; 57 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_UnorderedOrLess; 58 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_UnorderedOrLessOrEqual; 59 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Greater; 60 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.GreaterEqual; 61 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.GreaterEqualUnsigned; 62 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.GreaterUnsigned; 63 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Less; 64 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.LessEqual; 65 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.LessEqualUnsigned; 66 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.LessUnsigned; 67 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.NotEqual; 68 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Op3s.Subcc; 69 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST; 70 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT; 71 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.ILLEGAL; 72 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; 73 import static org.graalvm.compiler.lir.LIRValueUtil.asJavaConstant; 74 import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue; 75 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant; 76 import static org.graalvm.compiler.lir.sparc.SPARCMove.const2reg; 77 import static org.graalvm.compiler.lir.sparc.SPARCOP3Op.emitOp3; 78 79 import java.util.ArrayList; 80 import java.util.EnumSet; 81 import java.util.List; 82 83 import jdk.internal.vm.compiler.collections.EconomicMap; 84 import jdk.internal.vm.compiler.collections.Equivalence; 85 import org.graalvm.compiler.asm.Assembler; 86 import org.graalvm.compiler.asm.Assembler.LabelHint; 87 import org.graalvm.compiler.asm.Label; 88 import org.graalvm.compiler.asm.sparc.SPARCAssembler; 89 import org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict; 90 import org.graalvm.compiler.asm.sparc.SPARCAssembler.CC; 91 import org.graalvm.compiler.asm.sparc.SPARCAssembler.CMOV; 92 import org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag; 93 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler; 94 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler.ScratchRegister; 95 import org.graalvm.compiler.core.common.calc.Condition; 96 import org.graalvm.compiler.debug.GraalError; 97 import org.graalvm.compiler.lir.LIRInstructionClass; 98 import org.graalvm.compiler.lir.LabelRef; 99 import org.graalvm.compiler.lir.Opcode; 100 import org.graalvm.compiler.lir.StandardOp; 101 import org.graalvm.compiler.lir.SwitchStrategy; 102 import org.graalvm.compiler.lir.SwitchStrategy.BaseSwitchClosure; 103 import org.graalvm.compiler.lir.Variable; 104 import org.graalvm.compiler.lir.asm.CompilationResultBuilder; 105 106 import jdk.vm.ci.code.Register; 107 import jdk.vm.ci.meta.AllocatableValue; 108 import jdk.vm.ci.meta.Constant; 109 import jdk.vm.ci.meta.JavaConstant; 110 import jdk.vm.ci.meta.PlatformKind; 111 import jdk.vm.ci.meta.Value; 112 import jdk.vm.ci.sparc.SPARC.CPUFeature; 113 import jdk.vm.ci.sparc.SPARCKind; 114 115 public class SPARCControlFlow { 116 // This describes the maximum offset between the first emitted (load constant in to scratch, 117 // if does not fit into simm5 of cbcond) instruction and the final branch instruction 118 private static final int maximumSelfOffsetInstructions = 2; 119 120 public static final class ReturnOp extends SPARCBlockEndOp { 121 public static final LIRInstructionClass<ReturnOp> TYPE = LIRInstructionClass.create(ReturnOp.class); 122 public static final SizeEstimate SIZE = SizeEstimate.create(2); 123 124 @Use({REG, ILLEGAL}) protected Value x; 125 ReturnOp(Value x)126 public ReturnOp(Value x) { 127 super(TYPE, SIZE); 128 this.x = x; 129 } 130 131 @Override emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)132 public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) { 133 emitCodeHelper(crb, masm); 134 } 135 emitCodeHelper(CompilationResultBuilder crb, SPARCMacroAssembler masm)136 public static void emitCodeHelper(CompilationResultBuilder crb, SPARCMacroAssembler masm) { 137 masm.ret(); 138 // On SPARC we always leave the frame (in the delay slot). 139 crb.frameContext.leave(crb); 140 } 141 } 142 143 public static final class CompareBranchOp extends SPARCBlockEndOp implements SPARCDelayedControlTransfer { 144 public static final LIRInstructionClass<CompareBranchOp> TYPE = LIRInstructionClass.create(CompareBranchOp.class); 145 public static final SizeEstimate SIZE = SizeEstimate.create(3); 146 static final EnumSet<SPARCKind> SUPPORTED_KINDS = EnumSet.of(XWORD, WORD); 147 148 @Use({REG}) protected AllocatableValue x; 149 @Use({REG, CONST}) protected Value y; 150 private ConditionFlag conditionFlag; 151 protected final LabelRef trueDestination; 152 protected LabelHint trueDestinationHint; 153 protected final LabelRef falseDestination; 154 protected LabelHint falseDestinationHint; 155 protected final SPARCKind kind; 156 protected final boolean unorderedIsTrue; 157 private boolean emitted = false; 158 private int delaySlotPosition = -1; 159 private double trueDestinationProbability; 160 CompareBranchOp(AllocatableValue x, Value y, Condition condition, LabelRef trueDestination, LabelRef falseDestination, SPARCKind kind, boolean unorderedIsTrue, double trueDestinationProbability)161 public CompareBranchOp(AllocatableValue x, Value y, Condition condition, LabelRef trueDestination, LabelRef falseDestination, SPARCKind kind, boolean unorderedIsTrue, 162 double trueDestinationProbability) { 163 super(TYPE, SIZE); 164 assert x.getPlatformKind() == y.getPlatformKind() : String.format("PlatformKind of x must match PlatformKind of y. %s!=%s", x.getPlatformKind(), y.getPlatformKind()); 165 this.x = x; 166 this.y = y; 167 this.trueDestination = trueDestination; 168 this.falseDestination = falseDestination; 169 this.kind = kind; 170 this.unorderedIsTrue = unorderedIsTrue; 171 this.trueDestinationProbability = trueDestinationProbability; 172 conditionFlag = fromCondition(kind.isInteger(), condition, unorderedIsTrue); 173 } 174 175 @Override emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)176 public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) { 177 if (emitted) { // Only if delayed control transfer is used we must check this 178 assert masm.position() - delaySlotPosition == 4 : "Only one instruction can be stuffed into the delay slot"; 179 } 180 if (!emitted) { 181 requestHints(masm); 182 int targetPosition = getTargetPosition(masm); 183 if (canUseShortBranch(crb, masm, targetPosition)) { 184 emitted = emitShortCompareBranch(crb, masm); 185 } 186 if (!emitted) { // No short compare/branch was used, so we go into fallback 187 emitted = emitLongCompareBranch(crb, masm, true); 188 emitted = true; 189 } 190 } 191 assert emitted; 192 } 193 emitLongCompareBranch(CompilationResultBuilder crb, SPARCMacroAssembler masm, boolean withDelayedNop)194 private boolean emitLongCompareBranch(CompilationResultBuilder crb, SPARCMacroAssembler masm, boolean withDelayedNop) { 195 emitOp3(masm, Subcc, x, y); 196 return emitBranch(crb, masm, kind, conditionFlag, trueDestination, falseDestination, withDelayedNop, trueDestinationProbability); 197 } 198 getTargetPosition(Assembler asm)199 private static int getTargetPosition(Assembler asm) { 200 return asm.position() + maximumSelfOffsetInstructions * asm.target.wordSize; 201 } 202 203 @Override emitControlTransfer(CompilationResultBuilder crb, SPARCMacroAssembler masm)204 public void emitControlTransfer(CompilationResultBuilder crb, SPARCMacroAssembler masm) { 205 requestHints(masm); 206 // When we use short branches, no delay slot is available 207 int targetPosition = getTargetPosition(masm); 208 if (!canUseShortBranch(crb, masm, targetPosition)) { 209 emitted = emitLongCompareBranch(crb, masm, false); 210 if (emitted) { 211 delaySlotPosition = masm.position(); 212 } 213 } 214 } 215 requestHints(SPARCMacroAssembler masm)216 private void requestHints(SPARCMacroAssembler masm) { 217 if (trueDestinationHint == null) { 218 this.trueDestinationHint = masm.requestLabelHint(trueDestination.label()); 219 } 220 if (falseDestinationHint == null) { 221 this.falseDestinationHint = masm.requestLabelHint(falseDestination.label()); 222 } 223 } 224 225 /** 226 * Tries to use the emit the compare/branch instruction. 227 * <p> 228 * CBcond has follwing limitations 229 * <ul> 230 * <li>Immediate field is only 5 bit and is on the right 231 * <li>Jump offset is maximum of -+512 instruction 232 * 233 * <p> 234 * We get from outside 235 * <ul> 236 * <li>at least one of trueDestination falseDestination is within reach of +-512 237 * instructions 238 * <li>two registers OR one register and a constant which fits simm13 239 * 240 * <p> 241 * We do: 242 * <ul> 243 * <li>find out which target needs to be branched conditionally 244 * <li>find out if fall-through is possible, if not, a unconditional branch is needed after 245 * cbcond (needJump=true) 246 * <li>if no fall through: we need to put the closer jump into the cbcond branch and the 247 * farther into the jmp (unconditional branch) 248 * <li>if constant on the left side, mirror to be on the right 249 * <li>if constant on right does not fit into simm5, put it into a scratch register 250 * 251 * @param crb 252 * @param masm 253 * @return true if the branch could be emitted 254 */ emitShortCompareBranch(CompilationResultBuilder crb, SPARCMacroAssembler masm)255 private boolean emitShortCompareBranch(CompilationResultBuilder crb, SPARCMacroAssembler masm) { 256 boolean isLong = kind == SPARCKind.XWORD; 257 ConditionFlag actualConditionFlag = conditionFlag; 258 Label actualTrueTarget = trueDestination.label(); 259 Label actualFalseTarget = falseDestination.label(); 260 Label tmpTarget; 261 boolean needJump; 262 if (crb.isSuccessorEdge(trueDestination)) { 263 actualConditionFlag = conditionFlag.negate(); 264 tmpTarget = actualTrueTarget; 265 actualTrueTarget = actualFalseTarget; 266 actualFalseTarget = tmpTarget; 267 needJump = false; 268 } else { 269 needJump = !crb.isSuccessorEdge(falseDestination); 270 int targetPosition = getTargetPosition(masm); 271 if (needJump && !isShortBranch(masm, targetPosition, trueDestinationHint, actualTrueTarget)) { 272 // we have to jump in either way, so we must put the shorter 273 // branch into the actualTarget as only one of the two jump targets 274 // is guaranteed to be simm10 275 actualConditionFlag = actualConditionFlag.negate(); 276 tmpTarget = actualTrueTarget; 277 actualTrueTarget = actualFalseTarget; 278 actualFalseTarget = tmpTarget; 279 } 280 } 281 emitCBCond(masm, x, y, actualTrueTarget, actualConditionFlag, isLong); 282 if (needJump) { 283 masm.jmp(actualFalseTarget); 284 masm.nop(); 285 } 286 return true; 287 } 288 emitCBCond(SPARCMacroAssembler masm, Value actualX, Value actualY, Label actualTrueTarget, ConditionFlag cFlag, boolean isLong)289 private static void emitCBCond(SPARCMacroAssembler masm, Value actualX, Value actualY, Label actualTrueTarget, ConditionFlag cFlag, boolean isLong) { 290 PlatformKind xKind = actualX.getPlatformKind(); 291 Register rs1 = asRegister(actualX, xKind); 292 if (isJavaConstant(actualY)) { 293 JavaConstant c = asJavaConstant(actualY); 294 long constantY = c.isNull() ? 0 : c.asLong(); 295 try (ScratchRegister scratch = masm.getScratchRegister()) { 296 if (SPARCMacroAssembler.isSimm5(constantY)) { 297 CBCOND.emit(masm, cFlag, isLong, rs1, (int) constantY, actualTrueTarget); 298 } else { // !simm5 299 Register rs2 = scratch.getRegister(); 300 masm.setx(constantY, rs2, false); 301 CBCOND.emit(masm, cFlag, isLong, rs1, rs2, actualTrueTarget); 302 } 303 } 304 } else { 305 Register rs2 = asRegister(actualY, xKind); 306 CBCOND.emit(masm, cFlag, isLong, rs1, rs2, actualTrueTarget); 307 } 308 } 309 canUseShortBranch(CompilationResultBuilder crb, SPARCAssembler asm, int position)310 private boolean canUseShortBranch(CompilationResultBuilder crb, SPARCAssembler asm, int position) { 311 if (!asm.hasFeature(CPUFeature.CBCOND)) { 312 return false; 313 } 314 if (!((SPARCKind) x.getPlatformKind()).isInteger()) { 315 return false; 316 } 317 // Do not use short branch, if the y value is a constant and does not fit into simm5 but 318 // fits into simm13; this means the code with CBcond would be longer as the code without 319 // CBcond. 320 if (isJavaConstant(y) && !isSimm5(asJavaConstant(y)) && isSimm13(asJavaConstant(y))) { 321 return false; 322 } 323 boolean hasShortJumpTarget = false; 324 if (!crb.isSuccessorEdge(trueDestination)) { 325 hasShortJumpTarget |= isShortBranch(asm, position, trueDestinationHint, trueDestination.label()); 326 } 327 if (!crb.isSuccessorEdge(falseDestination)) { 328 hasShortJumpTarget |= isShortBranch(asm, position, falseDestinationHint, falseDestination.label()); 329 } 330 return hasShortJumpTarget; 331 } 332 333 @Override resetState()334 public void resetState() { 335 emitted = false; 336 delaySlotPosition = -1; 337 } 338 339 @Override verify()340 public void verify() { 341 super.verify(); 342 assert SUPPORTED_KINDS.contains(kind) : kind; 343 assert !isConstantValue(x); 344 assert x.getPlatformKind().equals(kind) && (isConstantValue(y) || y.getPlatformKind().equals(kind)) : x + " " + y; 345 } 346 } 347 isShortBranch(SPARCAssembler asm, int position, LabelHint hint, Label label)348 public static boolean isShortBranch(SPARCAssembler asm, int position, LabelHint hint, Label label) { 349 int disp = 0; 350 boolean dispValid = true; 351 if (label.isBound()) { 352 disp = label.position() - position; 353 } else if (hint != null && hint.isValid()) { 354 disp = hint.getTarget() - hint.getPosition(); 355 } else { 356 dispValid = false; 357 } 358 if (dispValid) { 359 if (disp < 0) { 360 disp -= maximumSelfOffsetInstructions * asm.target.wordSize; 361 } else { 362 disp += maximumSelfOffsetInstructions * asm.target.wordSize; 363 } 364 return isSimm10(disp >> 2); 365 } else if (hint == null) { 366 asm.requestLabelHint(label); 367 } 368 return false; 369 } 370 371 public static final class BranchOp extends SPARCBlockEndOp implements StandardOp.BranchOp { 372 public static final LIRInstructionClass<BranchOp> TYPE = LIRInstructionClass.create(BranchOp.class); 373 public static final SizeEstimate SIZE = SizeEstimate.create(2); 374 protected final ConditionFlag conditionFlag; 375 protected final LabelRef trueDestination; 376 protected final LabelRef falseDestination; 377 protected final SPARCKind kind; 378 protected final double trueDestinationProbability; 379 BranchOp(ConditionFlag conditionFlag, LabelRef trueDestination, LabelRef falseDestination, SPARCKind kind, double trueDestinationProbability)380 public BranchOp(ConditionFlag conditionFlag, LabelRef trueDestination, LabelRef falseDestination, SPARCKind kind, double trueDestinationProbability) { 381 super(TYPE, SIZE); 382 this.trueDestination = trueDestination; 383 this.falseDestination = falseDestination; 384 this.kind = kind; 385 this.conditionFlag = conditionFlag; 386 this.trueDestinationProbability = trueDestinationProbability; 387 } 388 389 @Override emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)390 public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) { 391 emitBranch(crb, masm, kind, conditionFlag, trueDestination, falseDestination, true, trueDestinationProbability); 392 } 393 } 394 emitBranch(CompilationResultBuilder crb, SPARCMacroAssembler masm, SPARCKind kind, ConditionFlag conditionFlag, LabelRef trueDestination, LabelRef falseDestination, boolean withDelayedNop, double trueDestinationProbability)395 private static boolean emitBranch(CompilationResultBuilder crb, SPARCMacroAssembler masm, SPARCKind kind, ConditionFlag conditionFlag, LabelRef trueDestination, LabelRef falseDestination, 396 boolean withDelayedNop, double trueDestinationProbability) { 397 Label actualTarget; 398 ConditionFlag actualConditionFlag; 399 boolean needJump; 400 BranchPredict predictTaken; 401 if (falseDestination != null && crb.isSuccessorEdge(trueDestination)) { 402 actualConditionFlag = conditionFlag != null ? conditionFlag.negate() : null; 403 actualTarget = falseDestination.label(); 404 needJump = false; 405 predictTaken = trueDestinationProbability < .5d ? PREDICT_TAKEN : PREDICT_NOT_TAKEN; 406 } else { 407 actualConditionFlag = conditionFlag; 408 actualTarget = trueDestination.label(); 409 needJump = falseDestination != null && !crb.isSuccessorEdge(falseDestination); 410 predictTaken = trueDestinationProbability > .5d ? PREDICT_TAKEN : PREDICT_NOT_TAKEN; 411 } 412 if (!withDelayedNop && needJump) { 413 // We cannot make use of the delay slot when we jump in true-case and false-case 414 return false; 415 } 416 if (kind.isFloat()) { 417 FBPCC.emit(masm, Fcc0, actualConditionFlag, NOT_ANNUL, predictTaken, actualTarget); 418 } else { 419 assert kind.isInteger(); 420 CC cc = kind.equals(WORD) ? Icc : Xcc; 421 BPCC.emit(masm, cc, actualConditionFlag, NOT_ANNUL, predictTaken, actualTarget); 422 } 423 if (withDelayedNop) { 424 masm.nop(); // delay slot 425 } 426 if (needJump) { 427 masm.jmp(falseDestination.label()); 428 } 429 return true; 430 } 431 432 public static class StrategySwitchOp extends SPARCBlockEndOp { 433 public static final LIRInstructionClass<StrategySwitchOp> TYPE = LIRInstructionClass.create(StrategySwitchOp.class); 434 protected Constant[] keyConstants; 435 private final LabelRef[] keyTargets; 436 private LabelRef defaultTarget; 437 @Alive({REG}) protected Value key; 438 @Alive({REG, ILLEGAL}) protected Value constantTableBase; 439 @Temp({REG}) protected Value scratch; 440 protected final SwitchStrategy strategy; 441 private final EconomicMap<Label, LabelHint> labelHints; 442 private final List<Label> conditionalLabels = new ArrayList<>(); 443 StrategySwitchOp(Value constantTableBase, SwitchStrategy strategy, LabelRef[] keyTargets, LabelRef defaultTarget, AllocatableValue key, Variable scratch)444 public StrategySwitchOp(Value constantTableBase, SwitchStrategy strategy, LabelRef[] keyTargets, LabelRef defaultTarget, AllocatableValue key, Variable scratch) { 445 this(TYPE, constantTableBase, strategy, keyTargets, defaultTarget, key, scratch); 446 } 447 StrategySwitchOp(LIRInstructionClass<? extends StrategySwitchOp> c, Value constantTableBase, SwitchStrategy strategy, LabelRef[] keyTargets, LabelRef defaultTarget, AllocatableValue key, Variable scratch)448 protected StrategySwitchOp(LIRInstructionClass<? extends StrategySwitchOp> c, Value constantTableBase, SwitchStrategy strategy, LabelRef[] keyTargets, LabelRef defaultTarget, 449 AllocatableValue key, 450 Variable scratch) { 451 super(c); 452 this.strategy = strategy; 453 this.keyConstants = strategy.getKeyConstants(); 454 this.keyTargets = keyTargets; 455 this.defaultTarget = defaultTarget; 456 this.constantTableBase = constantTableBase; 457 this.key = key; 458 this.scratch = scratch; 459 this.labelHints = EconomicMap.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE); 460 assert keyConstants.length == keyTargets.length; 461 assert keyConstants.length == strategy.keyProbabilities.length; 462 } 463 464 @Override emitCode(final CompilationResultBuilder crb, final SPARCMacroAssembler masm)465 public void emitCode(final CompilationResultBuilder crb, final SPARCMacroAssembler masm) { 466 final Register keyRegister = asRegister(key); 467 final Register constantBaseRegister = AllocatableValue.ILLEGAL.equals(constantTableBase) ? g0 : asRegister(constantTableBase); 468 strategy.run(new SwitchClosure(keyRegister, constantBaseRegister, crb, masm)); 469 } 470 471 public class SwitchClosure extends BaseSwitchClosure { 472 private int conditionalLabelPointer = 0; 473 474 protected final Register keyRegister; 475 protected final Register constantBaseRegister; 476 protected final CompilationResultBuilder crb; 477 protected final SPARCMacroAssembler masm; 478 SwitchClosure(Register keyRegister, Register constantBaseRegister, CompilationResultBuilder crb, SPARCMacroAssembler masm)479 protected SwitchClosure(Register keyRegister, Register constantBaseRegister, CompilationResultBuilder crb, SPARCMacroAssembler masm) { 480 super(crb, masm, keyTargets, defaultTarget); 481 this.keyRegister = keyRegister; 482 this.constantBaseRegister = constantBaseRegister; 483 this.crb = crb; 484 this.masm = masm; 485 } 486 487 /** 488 * This method caches the generated labels over two assembly passes to get information 489 * about branch lengths. 490 */ 491 @Override conditionalJump(int index, Condition condition)492 public Label conditionalJump(int index, Condition condition) { 493 Label label; 494 if (conditionalLabelPointer <= conditionalLabels.size()) { 495 label = new Label(); 496 conditionalLabels.add(label); 497 conditionalLabelPointer = conditionalLabels.size(); 498 } else { 499 // TODO: (sa) We rely here on the order how the labels are generated during 500 // code generation; if the order is not stable ower two assembly passes, the 501 // result can be wrong 502 label = conditionalLabels.get(conditionalLabelPointer++); 503 } 504 conditionalJump(index, condition, label); 505 return label; 506 } 507 508 @Override conditionalJump(int index, Condition condition, Label target)509 protected void conditionalJump(int index, Condition condition, Label target) { 510 JavaConstant constant = (JavaConstant) keyConstants[index]; 511 CC conditionCode; 512 Long bits = constant.asLong(); 513 switch (constant.getJavaKind()) { 514 case Char: 515 case Byte: 516 case Short: 517 case Int: 518 conditionCode = CC.Icc; 519 break; 520 case Long: 521 conditionCode = CC.Xcc; 522 break; 523 default: 524 throw new GraalError("switch only supported for int, long and object"); 525 } 526 ConditionFlag conditionFlag = fromCondition(keyRegister.getRegisterCategory().equals(CPU), condition, false); 527 LabelHint hint = requestHint(masm, target); 528 boolean isShortConstant = isSimm5(constant); 529 int cbCondPosition = masm.position(); 530 if (!isShortConstant) { // Load constant takes one instruction 531 cbCondPosition += INSTRUCTION_SIZE; 532 } 533 boolean canUseShortBranch = masm.hasFeature(CPUFeature.CBCOND) && isShortBranch(masm, cbCondPosition, hint, target); 534 if (bits != null && canUseShortBranch) { 535 if (isShortConstant) { 536 CBCOND.emit(masm, conditionFlag, conditionCode == Xcc, keyRegister, (int) (long) bits, target); 537 } else { 538 Register scratchRegister = asRegister(scratch); 539 const2reg(crb, masm, scratch, constantBaseRegister, (JavaConstant) keyConstants[index], SPARCDelayedControlTransfer.DUMMY); 540 CBCOND.emit(masm, conditionFlag, conditionCode == Xcc, keyRegister, scratchRegister, target); 541 } 542 } else { 543 if (bits != null && isSimm13(constant)) { 544 masm.cmp(keyRegister, (int) (long) bits); // Cast is safe 545 } else { 546 Register scratchRegister = asRegister(scratch); 547 const2reg(crb, masm, scratch, constantBaseRegister, (JavaConstant) keyConstants[index], SPARCDelayedControlTransfer.DUMMY); 548 masm.cmp(keyRegister, scratchRegister); 549 } 550 BPCC.emit(masm, conditionCode, conditionFlag, ANNUL, PREDICT_TAKEN, target); 551 masm.nop(); // delay slot 552 } 553 } 554 } 555 requestHint(SPARCMacroAssembler masm, Label label)556 protected LabelHint requestHint(SPARCMacroAssembler masm, Label label) { 557 LabelHint hint = labelHints.get(label); 558 if (hint == null) { 559 hint = masm.requestLabelHint(label); 560 labelHints.put(label, hint); 561 } 562 return hint; 563 } 564 estimateEmbeddedSize(Constant c)565 protected int estimateEmbeddedSize(Constant c) { 566 JavaConstant v = (JavaConstant) c; 567 if (!SPARCAssembler.isSimm13(v)) { 568 return v.getJavaKind().getByteCount(); 569 } else { 570 return 0; 571 } 572 } 573 574 @Override estimateSize()575 public SizeEstimate estimateSize() { 576 int constantBytes = 0; 577 for (Constant c : keyConstants) { 578 constantBytes += estimateEmbeddedSize(c); 579 } 580 return new SizeEstimate(4 * keyTargets.length, constantBytes); 581 } 582 } 583 584 public static final class TableSwitchOp extends SPARCBlockEndOp { 585 public static final LIRInstructionClass<TableSwitchOp> TYPE = LIRInstructionClass.create(TableSwitchOp.class); 586 587 private final int lowKey; 588 private final LabelRef defaultTarget; 589 private final LabelRef[] targets; 590 @Alive protected Value index; 591 @Temp protected Value scratch; 592 TableSwitchOp(final int lowKey, final LabelRef defaultTarget, final LabelRef[] targets, Variable index, Variable scratch)593 public TableSwitchOp(final int lowKey, final LabelRef defaultTarget, final LabelRef[] targets, Variable index, Variable scratch) { 594 super(TYPE); 595 this.lowKey = lowKey; 596 this.defaultTarget = defaultTarget; 597 this.targets = targets; 598 this.index = index; 599 this.scratch = scratch; 600 } 601 602 @Override emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)603 public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) { 604 Register value = asRegister(index, SPARCKind.WORD); 605 Register scratchReg = asRegister(scratch, SPARCKind.XWORD); 606 607 // Compare index against jump table bounds 608 int highKey = lowKey + targets.length - 1; 609 610 // subtract the low value from the switch value 611 if (isSimm13(lowKey)) { 612 masm.sub(value, lowKey, scratchReg); 613 } else { 614 try (ScratchRegister sc = masm.getScratchRegister()) { 615 Register scratch2 = sc.getRegister(); 616 masm.setx(lowKey, scratch2, false); 617 masm.sub(value, scratch2, scratchReg); 618 } 619 } 620 int upperLimit = highKey - lowKey; 621 try (ScratchRegister sc = masm.getScratchRegister()) { 622 Register scratch2 = sc.getRegister(); 623 if (isSimm13(upperLimit)) { 624 masm.cmp(scratchReg, upperLimit); 625 } else { 626 masm.setx(upperLimit, scratch2, false); 627 masm.cmp(scratchReg, upperLimit); 628 } 629 630 // Jump to default target if index is not within the jump table 631 if (defaultTarget != null) { 632 BPCC.emit(masm, Icc, GreaterUnsigned, NOT_ANNUL, PREDICT_TAKEN, defaultTarget.label()); 633 masm.nop(); // delay slot 634 } 635 636 // Load jump table entry into scratch and jump to it 637 masm.sll(scratchReg, 3, scratchReg); // Multiply by 8 638 // Zero the left bits sll with shcnt>0 does not mask upper 32 bits 639 masm.srl(scratchReg, 0, scratchReg); 640 masm.rdpc(scratch2); 641 642 // The jump table follows four instructions after rdpc 643 masm.add(scratchReg, 4 * 4, scratchReg); 644 masm.jmpl(scratch2, scratchReg, g0); 645 } 646 masm.nop(); 647 648 // Emit jump table entries 649 for (LabelRef target : targets) { 650 BPCC.emit(masm, Xcc, Always, NOT_ANNUL, PREDICT_TAKEN, target.label()); 651 masm.nop(); // delay slot 652 } 653 } 654 655 @Override estimateSize()656 public SizeEstimate estimateSize() { 657 return SizeEstimate.create(17 + targets.length * 2); 658 } 659 } 660 661 @Opcode("CMOVE") 662 public static final class CondMoveOp extends SPARCLIRInstruction { 663 public static final LIRInstructionClass<CondMoveOp> TYPE = LIRInstructionClass.create(CondMoveOp.class); 664 665 @Def({REG, HINT}) protected Value result; 666 @Use({REG, CONST}) protected Value trueValue; 667 @Use({REG, CONST}) protected Value falseValue; 668 669 private final ConditionFlag condition; 670 private final CC cc; 671 private final CMOV cmove; 672 CondMoveOp(CMOV cmove, CC cc, ConditionFlag condition, Value trueValue, Value falseValue, Value result)673 public CondMoveOp(CMOV cmove, CC cc, ConditionFlag condition, Value trueValue, Value falseValue, Value result) { 674 super(TYPE); 675 this.result = result; 676 this.condition = condition; 677 this.trueValue = trueValue; 678 this.falseValue = falseValue; 679 this.cc = cc; 680 this.cmove = cmove; 681 } 682 683 @Override emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)684 public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) { 685 if (result.equals(trueValue)) { // We have the true value in place, do he opposite 686 cmove(masm, condition.negate(), falseValue); 687 } else if (result.equals(falseValue)) { 688 cmove(masm, condition, trueValue); 689 } else { // We have to move one of the input values to the result 690 ConditionFlag actualCondition = condition; 691 Value actualTrueValue = trueValue; 692 Value actualFalseValue = falseValue; 693 if (isJavaConstant(falseValue) && isSimm11(asJavaConstant(falseValue))) { 694 actualCondition = condition.negate(); 695 actualTrueValue = falseValue; 696 actualFalseValue = trueValue; 697 } 698 SPARCMove.move(crb, masm, result, actualFalseValue, SPARCDelayedControlTransfer.DUMMY); 699 cmove(masm, actualCondition, actualTrueValue); 700 } 701 } 702 cmove(SPARCMacroAssembler masm, ConditionFlag localCondition, Value value)703 private void cmove(SPARCMacroAssembler masm, ConditionFlag localCondition, Value value) { 704 if (isConstantValue(value)) { 705 cmove.emit(masm, localCondition, cc, asImmediate(asJavaConstant(value)), asRegister(result)); 706 } else { 707 cmove.emit(masm, localCondition, cc, asRegister(value), asRegister(result)); 708 } 709 } 710 711 @Override estimateSize()712 public SizeEstimate estimateSize() { 713 int constantSize = 0; 714 if (isJavaConstant(trueValue) && !SPARCAssembler.isSimm13(asJavaConstant(trueValue))) { 715 constantSize += trueValue.getPlatformKind().getSizeInBytes(); 716 } 717 if (isJavaConstant(falseValue) && !SPARCAssembler.isSimm13(asJavaConstant(falseValue))) { 718 constantSize += trueValue.getPlatformKind().getSizeInBytes(); 719 } 720 return SizeEstimate.create(3, constantSize); 721 } 722 } 723 fromCondition(boolean integer, Condition cond, boolean unorderedIsTrue)724 public static ConditionFlag fromCondition(boolean integer, Condition cond, boolean unorderedIsTrue) { 725 if (integer) { 726 switch (cond) { 727 case EQ: 728 return Equal; 729 case NE: 730 return NotEqual; 731 case BT: 732 return LessUnsigned; 733 case LT: 734 return Less; 735 case BE: 736 return LessEqualUnsigned; 737 case LE: 738 return LessEqual; 739 case AE: 740 return GreaterEqualUnsigned; 741 case GE: 742 return GreaterEqual; 743 case AT: 744 return GreaterUnsigned; 745 case GT: 746 return Greater; 747 } 748 throw GraalError.shouldNotReachHere("Unimplemented for: " + cond); 749 } else { 750 switch (cond) { 751 case EQ: 752 return unorderedIsTrue ? F_UnorderedOrEqual : F_Equal; 753 case NE: 754 return ConditionFlag.F_NotEqual; 755 case LT: 756 return unorderedIsTrue ? F_UnorderedOrLess : F_Less; 757 case LE: 758 return unorderedIsTrue ? F_UnorderedOrLessOrEqual : F_LessOrEqual; 759 case GE: 760 return unorderedIsTrue ? F_UnorderedGreaterOrEqual : F_GreaterOrEqual; 761 case GT: 762 return unorderedIsTrue ? F_UnorderedOrGreater : F_Greater; 763 } 764 throw GraalError.shouldNotReachHere("Unkown condition: " + cond); 765 } 766 } 767 } 768