1 /*
   2  * Copyright (c) 2013, 2019, 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.amd64.vector;
  26 
  27 import static jdk.vm.ci.code.ValueUtil.asRegister;
  28 import static jdk.vm.ci.code.ValueUtil.isRegister;
  29 import static jdk.vm.ci.code.ValueUtil.isStackSlot;
  30 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.VexMoveOp.VMOVD;
  31 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.VexMoveOp.VMOVDQU32;
  32 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.VexMoveOp.VMOVQ;
  33 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.VexMoveOp.VMOVSD;
  34 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.VexMoveOp.VMOVSS;
  35 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.VexMoveOp.VMOVUPD;
  36 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.VexMoveOp.VMOVUPS;
  37 import static org.graalvm.compiler.asm.amd64.AMD64Assembler.VexRVMOp.VXORPD;
  38 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.COMPOSITE;
  39 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT;
  40 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
  41 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK;
  42 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.UNINITIALIZED;
  43 
  44 import org.graalvm.compiler.asm.amd64.AMD64Address;
  45 import org.graalvm.compiler.asm.amd64.AMD64Assembler.VexMoveOp;
  46 import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler;
  47 import org.graalvm.compiler.asm.amd64.AVXKind;
  48 import org.graalvm.compiler.asm.amd64.AVXKind.AVXSize;
  49 import org.graalvm.compiler.debug.GraalError;
  50 import org.graalvm.compiler.lir.LIRFrameState;
  51 import org.graalvm.compiler.lir.LIRInstructionClass;
  52 import org.graalvm.compiler.lir.Opcode;
  53 import org.graalvm.compiler.lir.StandardOp.LoadConstantOp;
  54 import org.graalvm.compiler.lir.StandardOp.ValueMoveOp;
  55 import org.graalvm.compiler.lir.amd64.AMD64AddressValue;
  56 import org.graalvm.compiler.lir.amd64.AMD64LIRInstruction;
  57 import org.graalvm.compiler.lir.amd64.AMD64Move;
  58 import org.graalvm.compiler.lir.amd64.AMD64RestoreRegistersOp;
  59 import org.graalvm.compiler.lir.amd64.AMD64SaveRegistersOp;
  60 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  61 
  62 import jdk.vm.ci.amd64.AMD64Kind;
  63 import jdk.vm.ci.code.Register;
  64 import jdk.vm.ci.code.RegisterValue;
  65 import jdk.vm.ci.code.StackSlot;
  66 import jdk.vm.ci.meta.AllocatableValue;
  67 import jdk.vm.ci.meta.Constant;
  68 import jdk.vm.ci.meta.JavaConstant;
  69 import jdk.vm.ci.meta.Value;
  70 
  71 public class AMD64VectorMove {
  72 
  73     @Opcode("VMOVE")
  74     public static final class MoveToRegOp extends AMD64LIRInstruction implements ValueMoveOp {
  75         public static final LIRInstructionClass<MoveToRegOp> TYPE = LIRInstructionClass.create(MoveToRegOp.class);
  76 
  77         @Def({REG, HINT}) protected AllocatableValue result;
  78         @Use({REG, STACK}) protected AllocatableValue input;
  79 
  80         public MoveToRegOp(AllocatableValue result, AllocatableValue input) {
  81             super(TYPE);
  82             this.result = result;
  83             this.input = input;
  84         }
  85 
  86         @Override
  87         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
  88             move(crb, masm, result, input);
  89         }
  90 
  91         @Override
  92         public AllocatableValue getInput() {
  93             return input;
  94         }
  95 
  96         @Override
  97         public AllocatableValue getResult() {
  98             return result;
  99         }
 100     }
 101 
 102     @Opcode("VMOVE")
 103     public static final class MoveFromRegOp extends AMD64LIRInstruction implements ValueMoveOp {
 104         public static final LIRInstructionClass<MoveFromRegOp> TYPE = LIRInstructionClass.create(MoveFromRegOp.class);
 105 
 106         @Def({REG, STACK}) protected AllocatableValue result;
 107         @Use({REG, HINT}) protected AllocatableValue input;
 108 
 109         public MoveFromRegOp(AllocatableValue result, AllocatableValue input) {
 110             super(TYPE);
 111             this.result = result;
 112             this.input = input;
 113         }
 114 
 115         @Override
 116         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 117             move(crb, masm, result, input);
 118         }
 119 
 120         @Override
 121         public AllocatableValue getInput() {
 122             return input;
 123         }
 124 
 125         @Override
 126         public AllocatableValue getResult() {
 127             return result;
 128         }
 129     }
 130 
 131     @Opcode("VMOVE")
 132     public static class MoveFromConstOp extends AMD64LIRInstruction implements LoadConstantOp {
 133         public static final LIRInstructionClass<MoveFromConstOp> TYPE = LIRInstructionClass.create(MoveFromConstOp.class);
 134 
 135         @Def({REG, STACK}) protected AllocatableValue result;
 136         private final JavaConstant input;
 137 
 138         public MoveFromConstOp(AllocatableValue result, JavaConstant input) {
 139             super(TYPE);
 140             this.result = result;
 141             this.input = input;
 142         }
 143 
 144         @Override
 145         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 146             if (isRegister(result)) {
 147                 const2reg(crb, masm, (RegisterValue) result, input);
 148             } else {
 149                 assert isStackSlot(result);
 150                 AMD64Move.const2stack(crb, masm, result, input);
 151             }
 152         }
 153 
 154         @Override
 155         public Constant getConstant() {
 156             return input;
 157         }
 158 
 159         @Override
 160         public AllocatableValue getResult() {
 161             return result;
 162         }
 163     }
 164 
 165     @Opcode("VSTACKMOVE")
 166     public static final class StackMoveOp extends AMD64LIRInstruction implements ValueMoveOp {
 167         public static final LIRInstructionClass<StackMoveOp> TYPE = LIRInstructionClass.create(StackMoveOp.class);
 168 
 169         @Def({STACK}) protected AllocatableValue result;
 170         @Use({STACK, HINT}) protected AllocatableValue input;
 171         @Alive({STACK, UNINITIALIZED}) private AllocatableValue backupSlot;
 172 
 173         private Register scratch;
 174 
 175         public StackMoveOp(AllocatableValue result, AllocatableValue input, Register scratch, AllocatableValue backupSlot) {
 176             super(TYPE);
 177             this.result = result;
 178             this.input = input;
 179             this.backupSlot = backupSlot;
 180             this.scratch = scratch;
 181         }
 182 
 183         @Override
 184         public AllocatableValue getInput() {
 185             return input;
 186         }
 187 
 188         @Override
 189         public AllocatableValue getResult() {
 190             return result;
 191         }
 192 
 193         @Override
 194         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 195             // backup scratch register
 196             move(crb, masm, backupSlot, scratch.asValue(backupSlot.getValueKind()));
 197             // move stack slot
 198             move(crb, masm, scratch.asValue(getInput().getValueKind()), getInput());
 199             move(crb, masm, getResult(), scratch.asValue(getResult().getValueKind()));
 200             // restore scratch register
 201             move(crb, masm, scratch.asValue(backupSlot.getValueKind()), backupSlot);
 202 
 203         }
 204     }
 205 
 206     public abstract static class VectorMemOp extends AMD64LIRInstruction {
 207 
 208         protected final AVXSize size;
 209         protected final VexMoveOp op;
 210 
 211         @Use({COMPOSITE}) protected AMD64AddressValue address;
 212         @State protected LIRFrameState state;
 213 
 214         protected VectorMemOp(LIRInstructionClass<? extends VectorMemOp> c, AVXSize size, VexMoveOp op, AMD64AddressValue address, LIRFrameState state) {
 215             super(c);
 216             this.size = size;
 217             this.op = op;
 218             this.address = address;
 219             this.state = state;
 220         }
 221 
 222         protected abstract void emitMemAccess(AMD64MacroAssembler masm);
 223 
 224         @Override
 225         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 226             if (state != null) {
 227                 crb.recordImplicitException(masm.position(), state);
 228             }
 229             emitMemAccess(masm);
 230         }
 231     }
 232 
 233     public static final class VectorLoadOp extends VectorMemOp {
 234         public static final LIRInstructionClass<VectorLoadOp> TYPE = LIRInstructionClass.create(VectorLoadOp.class);
 235 
 236         @Def({REG}) protected AllocatableValue result;
 237 
 238         public VectorLoadOp(AVXSize size, VexMoveOp op, AllocatableValue result, AMD64AddressValue address, LIRFrameState state) {
 239             super(TYPE, size, op, address, state);
 240             this.result = result;
 241         }
 242 
 243         @Override
 244         public void emitMemAccess(AMD64MacroAssembler masm) {
 245             op.emit(masm, size, asRegister(result), address.toAddress());
 246         }
 247     }
 248 
 249     public static class VectorStoreOp extends VectorMemOp {
 250         public static final LIRInstructionClass<VectorStoreOp> TYPE = LIRInstructionClass.create(VectorStoreOp.class);
 251 
 252         @Use({REG}) protected AllocatableValue input;
 253 
 254         public VectorStoreOp(AVXSize size, VexMoveOp op, AMD64AddressValue address, AllocatableValue input, LIRFrameState state) {
 255             super(TYPE, size, op, address, state);
 256             this.input = input;
 257         }
 258 
 259         @Override
 260         public void emitMemAccess(AMD64MacroAssembler masm) {
 261             op.emit(masm, size, address.toAddress(), asRegister(input));
 262         }
 263     }
 264 
 265     @Opcode("SAVE_REGISTER")
 266     public static class SaveRegistersOp extends AMD64SaveRegistersOp {
 267         public static final LIRInstructionClass<SaveRegistersOp> TYPE = LIRInstructionClass.create(SaveRegistersOp.class);
 268 
 269         public SaveRegistersOp(Register[] savedRegisters, AllocatableValue[] slots) {
 270             super(TYPE, savedRegisters, slots);
 271         }
 272 
 273         @Override
 274         protected void saveRegister(CompilationResultBuilder crb, AMD64MacroAssembler masm, StackSlot result, Register register) {
 275             AMD64Kind kind = (AMD64Kind) result.getPlatformKind();
 276             if (kind.isXMM()) {
 277                 VexMoveOp op;
 278                 if (kind.getVectorLength() > 1) {
 279                     op = getVectorMoveOp(kind.getScalar());
 280                 } else {
 281                     op = getScalarMoveOp(kind);
 282                 }
 283 
 284                 AMD64Address addr = (AMD64Address) crb.asAddress(result);
 285                 op.emit(masm, AVXKind.getRegisterSize(kind), addr, register);
 286             } else {
 287                 super.saveRegister(crb, masm, result, register);
 288             }
 289         }
 290     }
 291 
 292     @Opcode("RESTORE_REGISTER")
 293     public static final class RestoreRegistersOp extends AMD64RestoreRegistersOp {
 294         public static final LIRInstructionClass<RestoreRegistersOp> TYPE = LIRInstructionClass.create(RestoreRegistersOp.class);
 295 
 296         public RestoreRegistersOp(AllocatableValue[] source, AMD64SaveRegistersOp save) {
 297             super(TYPE, source, save);
 298         }
 299 
 300         @Override
 301         protected void restoreRegister(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register register, StackSlot input) {
 302             AMD64Kind kind = (AMD64Kind) input.getPlatformKind();
 303             if (kind.isXMM()) {
 304                 VexMoveOp op;
 305                 if (kind.getVectorLength() > 1) {
 306                     op = getVectorMoveOp(kind.getScalar());
 307                 } else {
 308                     op = getScalarMoveOp(kind);
 309                 }
 310 
 311                 AMD64Address addr = (AMD64Address) crb.asAddress(input);
 312                 op.emit(masm, AVXKind.getRegisterSize(kind), register, addr);
 313             } else {
 314                 super.restoreRegister(crb, masm, register, input);
 315             }
 316         }
 317     }
 318 
 319     private static VexMoveOp getScalarMoveOp(AMD64Kind kind) {
 320         switch (kind) {
 321             case SINGLE:
 322                 return VMOVSS;
 323             case DOUBLE:
 324                 return VMOVSD;
 325             default:
 326                 throw GraalError.shouldNotReachHere();
 327         }
 328     }
 329 
 330     private static VexMoveOp getVectorMoveOp(AMD64Kind kind) {
 331         switch (kind) {
 332             case SINGLE:
 333                 return VMOVUPS;
 334             case DOUBLE:
 335                 return VMOVUPD;
 336             default:
 337                 return VMOVDQU32;
 338         }
 339     }
 340 
 341     private static VexMoveOp getVectorMemMoveOp(AMD64Kind kind) {
 342         switch (AVXKind.getDataSize(kind)) {
 343             case DWORD:
 344                 return VMOVD;
 345             case QWORD:
 346                 return VMOVQ;
 347             default:
 348                 return getVectorMoveOp(kind.getScalar());
 349         }
 350     }
 351 
 352     private static void move(CompilationResultBuilder crb, AMD64MacroAssembler masm, AllocatableValue result, Value input) {
 353         VexMoveOp op;
 354         AVXSize size;
 355         AMD64Kind kind = (AMD64Kind) result.getPlatformKind();
 356         if (kind.getVectorLength() > 1) {
 357             size = AVXKind.getRegisterSize(kind);
 358             if (isRegister(input) && isRegister(result)) {
 359                 op = getVectorMoveOp(kind.getScalar());
 360             } else {
 361                 op = getVectorMemMoveOp(kind);
 362             }
 363         } else {
 364             size = AVXSize.XMM;
 365             if (isRegister(input) && isRegister(result)) {
 366                 op = getVectorMoveOp(kind);
 367             } else {
 368                 op = getScalarMoveOp(kind);
 369             }
 370         }
 371 
 372         if (isRegister(input)) {
 373             if (isRegister(result)) {
 374                 if (!asRegister(input).equals(asRegister(result))) {
 375                     op.emit(masm, size, asRegister(result), asRegister(input));
 376                 }
 377             } else {
 378                 assert isStackSlot(result);
 379                 op.emit(masm, size, (AMD64Address) crb.asAddress(result), asRegister(input));
 380             }
 381         } else {
 382             assert isStackSlot(input) && isRegister(result);
 383             op.emit(masm, size, asRegister(result), (AMD64Address) crb.asAddress(input));
 384         }
 385     }
 386 
 387     private static void const2reg(CompilationResultBuilder crb, AMD64MacroAssembler masm, RegisterValue result, JavaConstant input) {
 388         if (input.isDefaultForKind()) {
 389             AMD64Kind kind = (AMD64Kind) result.getPlatformKind();
 390             Register register = result.getRegister();
 391             VXORPD.emit(masm, AVXKind.getRegisterSize(kind), register, register, register);
 392             return;
 393         }
 394 
 395         AMD64Address address;
 396         switch (input.getJavaKind()) {
 397             case Float:
 398                 address = (AMD64Address) crb.asFloatConstRef(input);
 399                 break;
 400 
 401             case Double:
 402                 address = (AMD64Address) crb.asDoubleConstRef(input);
 403                 break;
 404 
 405             default:
 406                 throw GraalError.shouldNotReachHere();
 407         }
 408         VexMoveOp op = getScalarMoveOp((AMD64Kind) result.getPlatformKind());
 409         op.emit(masm, AVXSize.XMM, asRegister(result), address);
 410     }
 411 
 412     public static final class AVXMoveToIntOp extends AMD64LIRInstruction {
 413         public static final LIRInstructionClass<AVXMoveToIntOp> TYPE = LIRInstructionClass.create(AVXMoveToIntOp.class);
 414 
 415         @Opcode private final VexMoveOp opcode;
 416 
 417         @Def({REG, STACK}) protected AllocatableValue result;
 418         @Use({REG}) protected AllocatableValue input;
 419 
 420         public AVXMoveToIntOp(VexMoveOp opcode, AllocatableValue result, AllocatableValue input) {
 421             super(TYPE);
 422             this.opcode = opcode;
 423             this.result = result;
 424             this.input = input;
 425         }
 426 
 427         @Override
 428         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 429             if (isRegister(result)) {
 430                 opcode.emitReverse(masm, AVXSize.XMM, asRegister(result), asRegister(input));
 431             } else {
 432                 opcode.emit(masm, AVXSize.XMM, (AMD64Address) crb.asAddress(result), asRegister(input));
 433             }
 434         }
 435     }
 436 }