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