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.sparc;
  24 
  25 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BPCC;
  26 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CCR_V_SHIFT;
  27 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CCR_XCC_SHIFT;
  28 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.FBPCC;
  29 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.isSimm13;
  30 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.ANNUL;
  31 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.NOT_ANNUL;
  32 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_TAKEN;
  33 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Fcc0;
  34 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Xcc;
  35 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Equal;
  36 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_Ordered;
  37 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fcmpd;
  38 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fcmps;
  39 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST;
  40 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT;
  41 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
  42 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant;
  43 import static jdk.vm.ci.code.ValueUtil.asRegister;
  44 import static jdk.vm.ci.code.ValueUtil.isRegister;
  45 import static jdk.vm.ci.sparc.SPARC.g0;
  46 import static jdk.vm.ci.sparc.SPARCKind.DOUBLE;
  47 import static jdk.vm.ci.sparc.SPARCKind.SINGLE;
  48 import static jdk.vm.ci.sparc.SPARCKind.WORD;
  49 import static jdk.vm.ci.sparc.SPARCKind.XWORD;
  50 
  51 import org.graalvm.compiler.asm.Label;
  52 import org.graalvm.compiler.asm.sparc.SPARCAssembler;
  53 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler;
  54 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler.ScratchRegister;
  55 import org.graalvm.compiler.core.common.LIRKind;
  56 import org.graalvm.compiler.debug.GraalError;
  57 import org.graalvm.compiler.lir.LIRFrameState;
  58 import org.graalvm.compiler.lir.LIRInstructionClass;
  59 import org.graalvm.compiler.lir.Opcode;
  60 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  61 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  62 
  63 import jdk.vm.ci.code.Register;
  64 import jdk.vm.ci.meta.AllocatableValue;
  65 import jdk.vm.ci.meta.Value;
  66 import jdk.vm.ci.sparc.SPARC;
  67 
  68 public class SPARCArithmetic {
  69     public static final class FloatConvertOp extends SPARCLIRInstruction {
  70         public static final LIRInstructionClass<FloatConvertOp> TYPE = LIRInstructionClass.create(FloatConvertOp.class);
  71         public static final SizeEstimate SIZE = SizeEstimate.create(5);
  72 
  73         @Opcode private final FloatConvert opcode;
  74         @Def({REG, HINT}) protected Value result;
  75         @Use({REG}) protected Value x;
  76 
  77         public enum FloatConvert {
  78             F2I,
  79             D2I,
  80             F2L,
  81             D2L
  82         }
  83 
  84         public FloatConvertOp(FloatConvert opcode, Value x, Value result) {
  85             super(TYPE, SIZE);
  86             this.opcode = opcode;
  87             this.x = x;
  88             this.result = result;
  89         }
  90 
  91         @Override
  92         protected void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
  93             Label notOrdered = new Label();
  94             switch (opcode) {
  95                 case F2L:
  96                     masm.fcmp(Fcc0, Fcmps, asRegister(x, SINGLE), asRegister(x, SINGLE));
  97                     FBPCC.emit(masm, Fcc0, F_Ordered, ANNUL, PREDICT_TAKEN, notOrdered);
  98                     masm.fstox(asRegister(x, SINGLE), asRegister(result, DOUBLE));
  99                     masm.fxtod(asRegister(result), asRegister(result));
 100                     masm.fsubd(asRegister(result, DOUBLE), asRegister(result, DOUBLE), asRegister(result, DOUBLE));
 101                     masm.bind(notOrdered);
 102                     break;
 103                 case F2I:
 104                     masm.fcmp(Fcc0, Fcmps, asRegister(x, SINGLE), asRegister(x, SINGLE));
 105                     FBPCC.emit(masm, Fcc0, F_Ordered, ANNUL, PREDICT_TAKEN, notOrdered);
 106                     masm.fstoi(asRegister(x, SINGLE), asRegister(result, SINGLE));
 107                     masm.fitos(asRegister(result, SINGLE), asRegister(result, SINGLE));
 108                     masm.fsubs(asRegister(result, SINGLE), asRegister(result, SINGLE), asRegister(result, SINGLE));
 109                     masm.bind(notOrdered);
 110                     break;
 111                 case D2L:
 112                     masm.fcmp(Fcc0, Fcmpd, asRegister(x, DOUBLE), asRegister(x, DOUBLE));
 113                     FBPCC.emit(masm, Fcc0, F_Ordered, ANNUL, PREDICT_TAKEN, notOrdered);
 114                     masm.fdtox(asRegister(x, DOUBLE), asRegister(result, DOUBLE));
 115                     masm.fxtod(asRegister(result, DOUBLE), asRegister(result, DOUBLE));
 116                     masm.fsubd(asRegister(result, DOUBLE), asRegister(result, DOUBLE), asRegister(result, DOUBLE));
 117                     masm.bind(notOrdered);
 118                     break;
 119                 case D2I:
 120                     masm.fcmp(Fcc0, Fcmpd, asRegister(x, DOUBLE), asRegister(x, DOUBLE));
 121                     FBPCC.emit(masm, Fcc0, F_Ordered, ANNUL, PREDICT_TAKEN, notOrdered);
 122                     masm.fdtoi(asRegister(x, DOUBLE), asRegister(result, SINGLE));
 123                     masm.fitos(asRegister(result, SINGLE), asRegister(result, SINGLE));
 124                     masm.fsubs(asRegister(result, SINGLE), asRegister(result, SINGLE), asRegister(result, SINGLE));
 125                     masm.bind(notOrdered);
 126                     break;
 127                 default:
 128                     throw GraalError.shouldNotReachHere("missing: " + opcode);
 129             }
 130         }
 131     }
 132 
 133     /**
 134      * Special LIR instruction as it requires a bunch of scratch registers.
 135      */
 136     public static final class RemOp extends SPARCLIRInstruction implements SPARCTailDelayedLIRInstruction {
 137         public static final LIRInstructionClass<RemOp> TYPE = LIRInstructionClass.create(RemOp.class);
 138         public static final SizeEstimate SIZE = SizeEstimate.create(4);
 139 
 140         @Opcode private final Rem opcode;
 141         @Def({REG}) protected Value result;
 142         @Alive({REG, CONST}) protected Value x;
 143         @Alive({REG, CONST}) protected Value y;
 144         @Temp({REG}) protected Value scratch1;
 145         @Temp({REG}) protected Value scratch2;
 146         @State protected LIRFrameState state;
 147 
 148         public enum Rem {
 149             IUREM,
 150             LUREM
 151         }
 152 
 153         public RemOp(Rem opcode, Value result, Value x, Value y, Value scratch1, Value scratch2, LIRFrameState state) {
 154             super(TYPE, SIZE);
 155             this.opcode = opcode;
 156             this.result = result;
 157             this.x = x;
 158             this.y = y;
 159             this.scratch1 = scratch1;
 160             this.scratch2 = scratch2;
 161             this.state = state;
 162         }
 163 
 164         @Override
 165         public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
 166             if (!isJavaConstant(x) && isJavaConstant(y)) {
 167                 assert isSimm13(crb.asIntConst(y));
 168                 assert !x.equals(scratch1);
 169                 assert !x.equals(scratch2);
 170                 assert !y.equals(scratch1);
 171                 switch (opcode) {
 172                     case LUREM:
 173                         crb.recordImplicitException(masm.position(), state);
 174                         masm.udivx(asRegister(x, XWORD), crb.asIntConst(y), asRegister(scratch1, XWORD));
 175                         masm.mulx(asRegister(scratch1, XWORD), crb.asIntConst(y), asRegister(scratch2, XWORD));
 176                         getDelayedControlTransfer().emitControlTransfer(crb, masm);
 177                         masm.sub(asRegister(x, XWORD), asRegister(scratch2, XWORD), asRegister(result, XWORD));
 178                         break;
 179                     case IUREM:
 180                         GraalError.unimplemented();
 181                         break;
 182                     default:
 183                         throw GraalError.shouldNotReachHere();
 184                 }
 185             } else if (isRegister(x) && isRegister(y)) {
 186                 Value xLeft = x;
 187                 switch (opcode) {
 188                     case LUREM:
 189                         if (isJavaConstant(x)) {
 190                             masm.setx(crb.asLongConst(x), asRegister(scratch2, XWORD), false);
 191                             xLeft = scratch2;
 192                         }
 193                         assert !asRegister(xLeft, XWORD).equals(asRegister(scratch1, XWORD));
 194                         assert !asRegister(y, XWORD).equals(asRegister(scratch1, XWORD));
 195                         crb.recordImplicitException(masm.position(), state);
 196                         masm.udivx(asRegister(xLeft, XWORD), asRegister(y, XWORD), asRegister(scratch1, XWORD));
 197                         masm.mulx(asRegister(scratch1, XWORD), asRegister(y, XWORD), asRegister(scratch1, XWORD));
 198                         getDelayedControlTransfer().emitControlTransfer(crb, masm);
 199                         masm.sub(asRegister(xLeft, XWORD), asRegister(scratch1, XWORD), asRegister(result, XWORD));
 200                         break;
 201                     case IUREM:
 202                         assert !asRegister(result, WORD).equals(asRegister(scratch1, WORD));
 203                         assert !asRegister(result, WORD).equals(asRegister(scratch2, WORD));
 204                         masm.srl(asRegister(x, WORD), 0, asRegister(scratch1, WORD));
 205                         masm.srl(asRegister(y, WORD), 0, asRegister(result, WORD));
 206                         crb.recordImplicitException(masm.position(), state);
 207                         masm.udivx(asRegister(scratch1, WORD), asRegister(result, WORD), asRegister(scratch2, WORD));
 208                         masm.mulx(asRegister(scratch2, WORD), asRegister(result, WORD), asRegister(result, WORD));
 209                         getDelayedControlTransfer().emitControlTransfer(crb, masm);
 210                         masm.sub(asRegister(scratch1, WORD), asRegister(result, WORD), asRegister(result, WORD));
 211                         break;
 212                     default:
 213                         throw GraalError.shouldNotReachHere();
 214                 }
 215             } else {
 216                 throw GraalError.shouldNotReachHere();
 217             }
 218         }
 219     }
 220 
 221     public static final class SPARCIMulccOp extends SPARCLIRInstruction {
 222         public static final LIRInstructionClass<SPARCIMulccOp> TYPE = LIRInstructionClass.create(SPARCIMulccOp.class);
 223         public static final SizeEstimate SIZE = SizeEstimate.create(10);
 224         @Def({REG}) protected Value result;
 225         @Alive({REG}) protected Value x;
 226         @Alive({REG}) protected Value y;
 227 
 228         public SPARCIMulccOp(Value result, Value x, Value y) {
 229             super(TYPE, SIZE);
 230             this.result = result;
 231             this.x = x;
 232             this.y = y;
 233         }
 234 
 235         @Override
 236         protected void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
 237             try (ScratchRegister tmpScratch = masm.getScratchRegister()) {
 238                 Register tmp = tmpScratch.getRegister();
 239                 Register resultRegister = asRegister(result, WORD);
 240                 Register xRegister = asRegister(x, WORD);
 241                 Register yRegister = asRegister(y, WORD);
 242                 masm.sra(xRegister, 0, xRegister);
 243                 masm.sra(yRegister, 0, yRegister);
 244                 masm.mulx(xRegister, yRegister, resultRegister);
 245                 Label noOverflow = new Label();
 246                 masm.sra(resultRegister, 0, tmp);
 247                 masm.compareBranch(tmp, resultRegister, Equal, Xcc, noOverflow, PREDICT_TAKEN, null);
 248                 masm.wrccr(SPARC.g0, 1 << (SPARCAssembler.CCR_ICC_SHIFT + SPARCAssembler.CCR_V_SHIFT));
 249                 masm.bind(noOverflow);
 250             }
 251         }
 252     }
 253 
 254     /**
 255      * Calculates the product and condition code for long multiplication of long values.
 256      */
 257     public static final class SPARCLMulccOp extends SPARCLIRInstruction {
 258         public static final LIRInstructionClass<SPARCLMulccOp> TYPE = LIRInstructionClass.create(SPARCLMulccOp.class);
 259         public static final SizeEstimate SIZE = SizeEstimate.create(13);
 260 
 261         @Def({REG}) protected Value result;
 262         @Alive({REG}) protected Value x;
 263         @Alive({REG}) protected Value y;
 264         @Temp({REG}) protected Value scratch1;
 265         @Temp({REG}) protected Value scratch2;
 266 
 267         public SPARCLMulccOp(Value result, Value x, Value y, LIRGeneratorTool gen) {
 268             super(TYPE, SIZE);
 269             this.result = result;
 270             this.x = x;
 271             this.y = y;
 272             this.scratch1 = gen.newVariable(LIRKind.combine(x, y));
 273             this.scratch2 = gen.newVariable(LIRKind.combine(x, y));
 274         }
 275 
 276         @Override
 277         public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
 278             Label noOverflow = new Label();
 279             masm.mulx(asRegister(x, XWORD), asRegister(y, XWORD), asRegister(result, XWORD));
 280 
 281             // Calculate the upper 64 bit signed := (umulxhi product - (x{63}&y + y{63}&x))
 282             masm.umulxhi(asRegister(x, XWORD), asRegister(y, XWORD), asRegister(scratch1, XWORD));
 283             masm.srax(asRegister(x, XWORD), 63, asRegister(scratch2, XWORD));
 284             masm.and(asRegister(scratch2, XWORD), asRegister(y, XWORD), asRegister(scratch2, XWORD));
 285             masm.sub(asRegister(scratch1, XWORD), asRegister(scratch2, XWORD), asRegister(scratch1, XWORD));
 286 
 287             masm.srax(asRegister(y, XWORD), 63, asRegister(scratch2, XWORD));
 288             masm.and(asRegister(scratch2, XWORD), asRegister(x, XWORD), asRegister(scratch2, XWORD));
 289             masm.sub(asRegister(scratch1, XWORD), asRegister(scratch2, XWORD), asRegister(scratch1, XWORD));
 290 
 291             // Now construct the lower half and compare
 292             masm.srax(asRegister(result, XWORD), 63, asRegister(scratch2, XWORD));
 293             masm.cmp(asRegister(scratch1, XWORD), asRegister(scratch2, XWORD));
 294             BPCC.emit(masm, Xcc, Equal, NOT_ANNUL, PREDICT_TAKEN, noOverflow);
 295             masm.nop();
 296             masm.wrccr(g0, 1 << (CCR_XCC_SHIFT + CCR_V_SHIFT));
 297             masm.bind(noOverflow);
 298         }
 299     }
 300 
 301     public static final class MulHighOp extends SPARCLIRInstruction {
 302         public static final LIRInstructionClass<MulHighOp> TYPE = LIRInstructionClass.create(MulHighOp.class);
 303         public static final SizeEstimate SIZE = SizeEstimate.create(4);
 304 
 305         @Opcode private final MulHigh opcode;
 306         @Def({REG}) public AllocatableValue result;
 307         @Alive({REG}) public AllocatableValue x;
 308         @Alive({REG}) public AllocatableValue y;
 309         @Temp({REG}) public AllocatableValue scratch;
 310 
 311         public enum MulHigh {
 312             IMUL,
 313             LMUL
 314         }
 315 
 316         public MulHighOp(MulHigh opcode, AllocatableValue x, AllocatableValue y, AllocatableValue result, AllocatableValue scratch) {
 317             super(TYPE, SIZE);
 318             this.opcode = opcode;
 319             this.x = x;
 320             this.y = y;
 321             this.scratch = scratch;
 322             this.result = result;
 323         }
 324 
 325         @Override
 326         public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
 327             assert isRegister(x) && isRegister(y) && isRegister(result) && isRegister(scratch);
 328             switch (opcode) {
 329                 case IMUL:
 330                     masm.sra(asRegister(x), 0, asRegister(x));
 331                     masm.sra(asRegister(y), 0, asRegister(y));
 332                     masm.mulx(asRegister(x, WORD), asRegister(y, WORD), asRegister(result, WORD));
 333                     masm.srax(asRegister(result, WORD), 32, asRegister(result, WORD));
 334                     break;
 335                 case LMUL:
 336                     assert !asRegister(scratch, XWORD).equals(asRegister(result, XWORD));
 337                     masm.umulxhi(asRegister(x, XWORD), asRegister(y, XWORD), asRegister(result, XWORD));
 338 
 339                     masm.srlx(asRegister(x, XWORD), 63, asRegister(scratch, XWORD));
 340                     masm.mulx(asRegister(scratch, XWORD), asRegister(y, XWORD), asRegister(scratch, XWORD));
 341                     masm.sub(asRegister(result, XWORD), asRegister(scratch, XWORD), asRegister(result, XWORD));
 342 
 343                     masm.srlx(asRegister(y, XWORD), 63, asRegister(scratch, XWORD));
 344                     masm.mulx(asRegister(scratch, XWORD), asRegister(x, XWORD), asRegister(scratch, XWORD));
 345                     masm.sub(asRegister(result, XWORD), asRegister(scratch, XWORD), asRegister(result, XWORD));
 346                     break;
 347                 default:
 348                     throw GraalError.shouldNotReachHere();
 349             }
 350         }
 351     }
 352 }