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