1 /* 2 * Copyright (c) 2013, 2016, 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 package org.graalvm.compiler.lir.aarch64; 24 25 import static jdk.vm.ci.code.ValueUtil.asAllocatableValue; 26 import static jdk.vm.ci.code.ValueUtil.asRegister; 27 28 import java.util.function.Function; 29 30 import org.graalvm.compiler.asm.Label; 31 import org.graalvm.compiler.asm.NumUtil; 32 import org.graalvm.compiler.asm.aarch64.AArch64Address; 33 import org.graalvm.compiler.asm.aarch64.AArch64Assembler; 34 import org.graalvm.compiler.asm.aarch64.AArch64Assembler.ConditionFlag; 35 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler; 36 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler.PatchLabelKind; 37 import org.graalvm.compiler.code.CompilationResult.JumpTable; 38 import org.graalvm.compiler.core.common.LIRKind; 39 import org.graalvm.compiler.core.common.calc.Condition; 40 import org.graalvm.compiler.debug.GraalError; 41 import org.graalvm.compiler.lir.ConstantValue; 42 import org.graalvm.compiler.lir.LIRInstructionClass; 43 import org.graalvm.compiler.lir.LabelRef; 44 import org.graalvm.compiler.lir.Opcode; 45 import org.graalvm.compiler.lir.StandardOp; 46 import org.graalvm.compiler.lir.SwitchStrategy; 47 import org.graalvm.compiler.lir.SwitchStrategy.BaseSwitchClosure; 48 import org.graalvm.compiler.lir.Variable; 49 import org.graalvm.compiler.lir.asm.CompilationResultBuilder; 50 51 import jdk.vm.ci.aarch64.AArch64Kind; 52 import jdk.vm.ci.code.Register; 53 import jdk.vm.ci.meta.Constant; 54 import jdk.vm.ci.meta.JavaConstant; 55 import jdk.vm.ci.meta.Value; 56 57 public class AArch64ControlFlow { 58 59 /** 60 * Compares integer register to 0 and branches if condition is true. Condition may only be equal 61 * or non-equal. 62 */ 63 // TODO (das) where do we need this? 64 // public static class CompareAndBranchOp extends AArch64LIRInstruction implements 65 // StandardOp.BranchOp { 66 // private final ConditionFlag condition; 67 // private final LabelRef destination; 68 // @Use({REG}) private Value x; 69 // 70 // public CompareAndBranchOp(Condition condition, LabelRef destination, Value x) { 71 // assert condition == Condition.EQ || condition == Condition.NE; 72 // assert ARMv8.isGpKind(x.getKind()); 73 // this.condition = condition == Condition.EQ ? ConditionFlag.EQ : ConditionFlag.NE; 74 // this.destination = destination; 75 // this.x = x; 76 // } 77 // 78 // @Override 79 // public void emitCode(CompilationResultBuilder crb, ARMv8MacroAssembler masm) { 80 // int size = ARMv8.bitsize(x.getKind()); 81 // if (condition == ConditionFlag.EQ) { 82 // masm.cbz(size, asRegister(x), destination.label()); 83 // } else { 84 // masm.cbnz(size, asRegister(x), destination.label()); 85 // } 86 // } 87 // } 88 89 public static class BranchOp extends AArch64BlockEndOp implements StandardOp.BranchOp { 90 public static final LIRInstructionClass<BranchOp> TYPE = LIRInstructionClass.create(BranchOp.class); 91 92 private final AArch64Assembler.ConditionFlag condition; 93 private final LabelRef trueDestination; 94 private final LabelRef falseDestination; 95 96 private final double trueDestinationProbability; 97 98 public BranchOp(AArch64Assembler.ConditionFlag condition, LabelRef trueDestination, LabelRef falseDestination, double trueDestinationProbability) { 99 super(TYPE); 100 this.condition = condition; 101 this.trueDestination = trueDestination; 102 this.falseDestination = falseDestination; 103 this.trueDestinationProbability = trueDestinationProbability; 104 } 105 106 @Override 107 public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { 108 /* 109 * Explanation: Depending on what the successor edge is, we can use the fall-through to 110 * optimize the generated code. If neither is a successor edge, use the branch 111 * probability to try to take the conditional jump as often as possible to avoid 112 * executing two instructions instead of one. 113 */ 114 if (crb.isSuccessorEdge(trueDestination)) { 115 masm.branchConditionally(condition.negate(), falseDestination.label()); 116 } else if (crb.isSuccessorEdge(falseDestination)) { 117 masm.branchConditionally(condition, trueDestination.label()); 118 } else if (trueDestinationProbability < 0.5) { 119 masm.branchConditionally(condition.negate(), falseDestination.label()); 120 masm.jmp(trueDestination.label()); 121 } else { 122 masm.branchConditionally(condition, trueDestination.label()); 123 masm.jmp(falseDestination.label()); 124 } 125 } 126 127 } 128 129 @Opcode("CMOVE") 130 public static class CondMoveOp extends AArch64LIRInstruction { 131 public static final LIRInstructionClass<CondMoveOp> TYPE = LIRInstructionClass.create(CondMoveOp.class); 132 133 @Def protected Value result; 134 @Use protected Value trueValue; 135 @Use protected Value falseValue; 136 private final AArch64Assembler.ConditionFlag condition; 137 138 public CondMoveOp(Variable result, AArch64Assembler.ConditionFlag condition, Value trueValue, Value falseValue) { 139 super(TYPE); 140 assert trueValue.getPlatformKind() == falseValue.getPlatformKind() && trueValue.getPlatformKind() == result.getPlatformKind(); 141 this.result = result; 142 this.condition = condition; 143 this.trueValue = trueValue; 144 this.falseValue = falseValue; 145 } 146 147 @Override 148 public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { 149 AArch64Kind kind = (AArch64Kind) trueValue.getPlatformKind(); 150 int size = kind.getSizeInBytes() * Byte.SIZE; 151 if (kind.isInteger()) { 152 masm.cmov(size, asRegister(result), asRegister(trueValue), asRegister(falseValue), condition); 153 } else { 154 masm.fcmov(size, asRegister(result), asRegister(trueValue), asRegister(falseValue), condition); 155 } 156 } 157 } 158 159 public static class StrategySwitchOp extends AArch64BlockEndOp implements StandardOp.BlockEndOp { 160 public static final LIRInstructionClass<StrategySwitchOp> TYPE = LIRInstructionClass.create(StrategySwitchOp.class); 161 162 private final Constant[] keyConstants; 163 protected final SwitchStrategy strategy; 164 private final Function<Condition, ConditionFlag> converter; 165 private final LabelRef[] keyTargets; 166 private final LabelRef defaultTarget; 167 @Alive protected Value key; 168 // TODO (das) This could be optimized: We only need the scratch register in case of a 169 // datapatch, or too large immediates. 170 @Temp protected Value scratch; 171 172 public StrategySwitchOp(SwitchStrategy strategy, LabelRef[] keyTargets, LabelRef defaultTarget, Value key, Value scratch, 173 Function<Condition, ConditionFlag> converter) { 174 this(TYPE, strategy, keyTargets, defaultTarget, key, scratch, converter); 175 } 176 177 protected StrategySwitchOp(LIRInstructionClass<? extends StrategySwitchOp> c, SwitchStrategy strategy, LabelRef[] keyTargets, LabelRef defaultTarget, Value key, Value scratch, 178 Function<Condition, ConditionFlag> converter) { 179 super(c); 180 this.strategy = strategy; 181 this.converter = converter; 182 this.keyConstants = strategy.getKeyConstants(); 183 this.keyTargets = keyTargets; 184 this.defaultTarget = defaultTarget; 185 this.key = key; 186 this.scratch = scratch; 187 assert keyConstants.length == keyTargets.length; 188 assert keyConstants.length == strategy.keyProbabilities.length; 189 } 190 191 @Override 192 public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { 193 strategy.run(new SwitchClosure(asRegister(key), crb, masm)); 194 } 195 196 public class SwitchClosure extends BaseSwitchClosure { 197 198 protected final Register keyRegister; 199 protected final CompilationResultBuilder crb; 200 protected final AArch64MacroAssembler masm; 201 202 protected SwitchClosure(Register keyRegister, CompilationResultBuilder crb, AArch64MacroAssembler masm) { 203 super(crb, masm, keyTargets, defaultTarget); 204 this.keyRegister = keyRegister; 205 this.crb = crb; 206 this.masm = masm; 207 } 208 209 protected void emitComparison(Constant c) { 210 JavaConstant jc = (JavaConstant) c; 211 ConstantValue constVal = new ConstantValue(LIRKind.value(key.getPlatformKind()), c); 212 switch (jc.getJavaKind()) { 213 case Int: 214 long lc = jc.asLong(); 215 assert NumUtil.isInt(lc); 216 emitCompare(crb, masm, key, scratch, constVal); 217 break; 218 case Long: 219 emitCompare(crb, masm, key, scratch, constVal); 220 break; 221 case Object: 222 emitCompare(crb, masm, key, scratch, constVal); 223 break; 224 default: 225 throw new GraalError("switch only supported for int, long and object"); 226 } 227 } 228 229 @Override 230 protected void conditionalJump(int index, Condition condition, Label target) { 231 emitComparison(keyConstants[index]); 232 masm.branchConditionally(converter.apply(condition), target); 233 } 234 } 235 } 236 237 public static class TableSwitchOp extends AArch64BlockEndOp implements StandardOp.BlockEndOp { 238 public static final LIRInstructionClass<TableSwitchOp> TYPE = LIRInstructionClass.create(TableSwitchOp.class); 239 240 private final int lowKey; 241 private final LabelRef defaultTarget; 242 private final LabelRef[] targets; 243 @Alive protected Variable keyValue; 244 @Temp protected Variable scratchValue; 245 246 public TableSwitchOp(int lowKey, LabelRef defaultTarget, LabelRef[] targets, Variable key, Variable scratch) { 247 super(TYPE); 248 this.lowKey = lowKey; 249 this.defaultTarget = defaultTarget; 250 this.targets = targets; 251 this.keyValue = key; 252 this.scratchValue = scratch; 253 } 254 255 @Override 256 public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { 257 Register key = asRegister(keyValue); 258 Register scratch = asRegister(scratchValue); 259 if (lowKey != 0) { 260 if (AArch64MacroAssembler.isArithmeticImmediate(lowKey)) { 261 masm.sub(32, key, key, lowKey); 262 } else { 263 ConstantValue constVal = new ConstantValue(LIRKind.value(AArch64Kind.WORD), JavaConstant.forInt(lowKey)); 264 AArch64Move.move(crb, masm, scratchValue, constVal); 265 masm.sub(32, key, key, scratch); 266 } 267 } 268 if (defaultTarget != null) { 269 // if key is not in table range, jump to default target if it exists. 270 ConstantValue constVal = new ConstantValue(LIRKind.value(AArch64Kind.WORD), JavaConstant.forInt(targets.length)); 271 emitCompare(crb, masm, keyValue, scratchValue, constVal); 272 masm.branchConditionally(AArch64Assembler.ConditionFlag.HS, defaultTarget.label()); 273 } 274 275 // Load the start address of the jump table - which starts 3 instructions after the adr 276 // - into scratch. 277 masm.adr(scratch, 4 * 3); 278 masm.ldr(32, scratch, AArch64Address.createRegisterOffsetAddress(scratch, key, /* scaled */true)); 279 masm.jmp(scratch); 280 int jumpTablePos = masm.position(); 281 // emit jump table entries 282 for (LabelRef target : targets) { 283 Label label = target.label(); 284 if (label.isBound()) { 285 masm.emitInt(target.label().position()); 286 } else { 287 label.addPatchAt(masm.position()); 288 masm.emitInt(PatchLabelKind.JUMP_ADDRESS.encoding); 289 } 290 } 291 JumpTable jt = new JumpTable(jumpTablePos, lowKey, lowKey + targets.length - 1, 4); 292 crb.compilationResult.addAnnotation(jt); 293 } 294 } 295 296 private static void emitCompare(CompilationResultBuilder crb, AArch64MacroAssembler masm, Value key, Value scratchValue, ConstantValue c) { 297 long imm = c.getJavaConstant().asLong(); 298 final int size = key.getPlatformKind().getSizeInBytes() * Byte.SIZE; 299 if (AArch64MacroAssembler.isComparisonImmediate(imm)) { 300 masm.cmp(size, asRegister(key), (int) imm); 301 } else { 302 AArch64Move.move(crb, masm, asAllocatableValue(scratchValue), c); 303 masm.cmp(size, asRegister(key), asRegister(scratchValue)); 304 } 305 } 306 307 }