1 /*
   2  * Copyright (c) 2011, 2015, 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 jdk.vm.ci.hotspot.amd64;
  24 
  25 import static jdk.vm.ci.amd64.AMD64.*;
  26 
  27 import java.util.*;
  28 
  29 import jdk.vm.ci.amd64.*;
  30 import jdk.vm.ci.code.*;
  31 import jdk.vm.ci.code.CallingConvention.*;
  32 import jdk.vm.ci.common.*;
  33 import jdk.vm.ci.hotspot.*;
  34 import jdk.vm.ci.meta.*;
  35 
  36 public class AMD64HotSpotRegisterConfig implements RegisterConfig {
  37 
  38     private final Architecture architecture;
  39 
  40     private final Register[] allocatable;
  41 
  42     private final int maxFrameSize;
  43 
  44     /**
  45      * The caller saved registers always include all parameter registers.
  46      */
  47     private final Register[] callerSaved;
  48 
  49     private final boolean allAllocatableAreCallerSaved;
  50 
  51     private final RegisterAttributes[] attributesMap;
  52 
  53     public int getMaximumFrameSize() {
  54         return maxFrameSize;
  55     }
  56 
  57     @Override
  58     public Register[] getAllocatableRegisters() {
  59         return allocatable.clone();
  60     }
  61 
  62     public Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers) {
  63         ArrayList<Register> list = new ArrayList<>();
  64         for (Register reg : registers) {
  65             if (architecture.canStoreValue(reg.getRegisterCategory(), kind)) {
  66                 list.add(reg);
  67             }
  68         }
  69 
  70         Register[] ret = list.toArray(new Register[list.size()]);
  71         return ret;
  72     }
  73 
  74     @Override
  75     public RegisterAttributes[] getAttributesMap() {
  76         return attributesMap.clone();
  77     }
  78 
  79     private final Register[] javaGeneralParameterRegisters;
  80     private final Register[] nativeGeneralParameterRegisters;
  81     private final Register[] xmmParameterRegisters = {xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7};
  82 
  83     /*
  84      * Some ABIs (e.g. Windows) require a so-called "home space", that is a save area on the stack
  85      * to store the argument registers
  86      */
  87     private final boolean needsNativeStackHomeSpace;
  88 
  89     private static Register[] initAllocatable(boolean reserveForHeapBase) {
  90         Register[] registers = null;
  91         // @formatter:off
  92         if (reserveForHeapBase) {
  93             registers = new Register[] {
  94                         rax, rbx, rcx, rdx, /*rsp,*/ rbp, rsi, rdi, r8, r9,  r10, r11, /*r12,*/ r13, r14, /*r15, */
  95                         xmm0, xmm1, xmm2,  xmm3,  xmm4,  xmm5,  xmm6,  xmm7,
  96                         xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15
  97                       };
  98         } else {
  99             registers = new Register[] {
 100                         rax, rbx, rcx, rdx, /*rsp,*/ rbp, rsi, rdi, r8, r9,  r10, r11, r12, r13, r14, /*r15, */
 101                         xmm0, xmm1, xmm2,  xmm3,  xmm4,  xmm5,  xmm6,  xmm7,
 102                         xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15
 103                       };
 104         }
 105        // @formatter:on
 106         return registers;
 107     }
 108 
 109     public AMD64HotSpotRegisterConfig(Architecture architecture, HotSpotVMConfig config) {
 110         this(architecture, config, initAllocatable(config.useCompressedOops));
 111         assert callerSaved.length >= allocatable.length;
 112     }
 113 
 114     public AMD64HotSpotRegisterConfig(Architecture architecture, HotSpotVMConfig config, Register[] allocatable) {
 115         this.architecture = architecture;
 116         this.maxFrameSize = config.maxFrameSize;
 117 
 118         if (config.windowsOs) {
 119             javaGeneralParameterRegisters = new Register[]{rdx, r8, r9, rdi, rsi, rcx};
 120             nativeGeneralParameterRegisters = new Register[]{rcx, rdx, r8, r9};
 121             this.needsNativeStackHomeSpace = true;
 122         } else {
 123             javaGeneralParameterRegisters = new Register[]{rsi, rdx, rcx, r8, r9, rdi};
 124             nativeGeneralParameterRegisters = new Register[]{rdi, rsi, rdx, rcx, r8, r9};
 125             this.needsNativeStackHomeSpace = false;
 126         }
 127 
 128         this.allocatable = allocatable.clone();
 129         Set<Register> callerSaveSet = new HashSet<>();
 130         Collections.addAll(callerSaveSet, allocatable);
 131         Collections.addAll(callerSaveSet, xmmParameterRegisters);
 132         Collections.addAll(callerSaveSet, javaGeneralParameterRegisters);
 133         Collections.addAll(callerSaveSet, nativeGeneralParameterRegisters);
 134         callerSaved = callerSaveSet.toArray(new Register[callerSaveSet.size()]);
 135 
 136         allAllocatableAreCallerSaved = true;
 137         attributesMap = RegisterAttributes.createMap(this, AMD64.allRegisters);
 138     }
 139 
 140     @Override
 141     public Register[] getCallerSaveRegisters() {
 142         return callerSaved;
 143     }
 144 
 145     public Register[] getCalleeSaveRegisters() {
 146         return null;
 147     }
 148 
 149     @Override
 150     public boolean areAllAllocatableRegistersCallerSaved() {
 151         return allAllocatableAreCallerSaved;
 152     }
 153 
 154     @Override
 155     public Register getRegisterForRole(int index) {
 156         throw new UnsupportedOperationException();
 157     }
 158 
 159     @Override
 160     public CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target, boolean stackOnly) {
 161         if (type == Type.NativeCall) {
 162             return callingConvention(nativeGeneralParameterRegisters, returnType, parameterTypes, type, target, stackOnly);
 163         }
 164         // On x64, parameter locations are the same whether viewed
 165         // from the caller or callee perspective
 166         return callingConvention(javaGeneralParameterRegisters, returnType, parameterTypes, type, target, stackOnly);
 167     }
 168 
 169     public Register[] getCallingConventionRegisters(Type type, JavaKind kind) {
 170         switch (kind) {
 171             case Boolean:
 172             case Byte:
 173             case Short:
 174             case Char:
 175             case Int:
 176             case Long:
 177             case Object:
 178                 return type == Type.NativeCall ? nativeGeneralParameterRegisters : javaGeneralParameterRegisters;
 179             case Float:
 180             case Double:
 181                 return xmmParameterRegisters;
 182             default:
 183                 throw JVMCIError.shouldNotReachHere();
 184         }
 185     }
 186 
 187     private CallingConvention callingConvention(Register[] generalParameterRegisters, JavaType returnType, JavaType[] parameterTypes, Type type, TargetDescription target, boolean stackOnly) {
 188         AllocatableValue[] locations = new AllocatableValue[parameterTypes.length];
 189 
 190         int currentGeneral = 0;
 191         int currentXMM = 0;
 192         int currentStackOffset = type == Type.NativeCall && needsNativeStackHomeSpace ? generalParameterRegisters.length * target.wordSize : 0;
 193 
 194         for (int i = 0; i < parameterTypes.length; i++) {
 195             final JavaKind kind = parameterTypes[i].getJavaKind().getStackKind();
 196 
 197             switch (kind) {
 198                 case Byte:
 199                 case Boolean:
 200                 case Short:
 201                 case Char:
 202                 case Int:
 203                 case Long:
 204                 case Object:
 205                     if (!stackOnly && currentGeneral < generalParameterRegisters.length) {
 206                         Register register = generalParameterRegisters[currentGeneral++];
 207                         locations[i] = register.asValue(target.getLIRKind(kind));
 208                     }
 209                     break;
 210                 case Float:
 211                 case Double:
 212                     if (!stackOnly && currentXMM < xmmParameterRegisters.length) {
 213                         Register register = xmmParameterRegisters[currentXMM++];
 214                         locations[i] = register.asValue(target.getLIRKind(kind));
 215                     }
 216                     break;
 217                 default:
 218                     throw JVMCIError.shouldNotReachHere();
 219             }
 220 
 221             if (locations[i] == null) {
 222                 LIRKind lirKind = target.getLIRKind(kind);
 223                 locations[i] = StackSlot.get(lirKind, currentStackOffset, !type.out);
 224                 currentStackOffset += Math.max(target.getSizeInBytes(lirKind.getPlatformKind()), target.wordSize);
 225             }
 226         }
 227 
 228         JavaKind returnKind = returnType == null ? JavaKind.Void : returnType.getJavaKind();
 229         AllocatableValue returnLocation = returnKind == JavaKind.Void ? Value.ILLEGAL : getReturnRegister(returnKind).asValue(target.getLIRKind(returnKind.getStackKind()));
 230         return new CallingConvention(currentStackOffset, returnLocation, locations);
 231     }
 232 
 233     @Override
 234     public Register getReturnRegister(JavaKind kind) {
 235         switch (kind) {
 236             case Boolean:
 237             case Byte:
 238             case Char:
 239             case Short:
 240             case Int:
 241             case Long:
 242             case Object:
 243                 return rax;
 244             case Float:
 245             case Double:
 246                 return xmm0;
 247             case Void:
 248             case Illegal:
 249                 return null;
 250             default:
 251                 throw new UnsupportedOperationException("no return register for type " + kind);
 252         }
 253     }
 254 
 255     @Override
 256     public Register getFrameRegister() {
 257         return rsp;
 258     }
 259 
 260     @Override
 261     public String toString() {
 262         return String.format("Allocatable: " + Arrays.toString(getAllocatableRegisters()) + "%n" + "CallerSave:  " + Arrays.toString(getCallerSaveRegisters()) + "%n");
 263     }
 264 }