001/*
002 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package jdk.internal.jvmci.hotspot.amd64;
024
025import static jdk.internal.jvmci.amd64.AMD64.*;
026
027import java.util.*;
028
029import jdk.internal.jvmci.amd64.*;
030import jdk.internal.jvmci.code.*;
031import jdk.internal.jvmci.code.CallingConvention.*;
032import jdk.internal.jvmci.common.*;
033import jdk.internal.jvmci.hotspot.*;
034import jdk.internal.jvmci.meta.*;
035
036public class AMD64HotSpotRegisterConfig implements RegisterConfig {
037
038    private final Architecture architecture;
039
040    private final Register[] allocatable;
041
042    private final int maxFrameSize;
043
044    /**
045     * The caller saved registers always include all parameter registers.
046     */
047    private final Register[] callerSaved;
048
049    private final boolean allAllocatableAreCallerSaved;
050
051    private final RegisterAttributes[] attributesMap;
052
053    public int getMaximumFrameSize() {
054        return maxFrameSize;
055    }
056
057    @Override
058    public Register[] getAllocatableRegisters() {
059        return allocatable.clone();
060    }
061
062    public Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers) {
063        ArrayList<Register> list = new ArrayList<>();
064        for (Register reg : registers) {
065            if (architecture.canStoreValue(reg.getRegisterCategory(), kind)) {
066                list.add(reg);
067            }
068        }
069
070        Register[] ret = list.toArray(new Register[list.size()]);
071        return ret;
072    }
073
074    @Override
075    public RegisterAttributes[] getAttributesMap() {
076        return attributesMap.clone();
077    }
078
079    private final Register[] javaGeneralParameterRegisters;
080    private final Register[] nativeGeneralParameterRegisters;
081    private final Register[] xmmParameterRegisters = {xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7};
082
083    /*
084     * Some ABIs (e.g. Windows) require a so-called "home space", that is a save area on the stack
085     * to store the argument registers
086     */
087    private final boolean needsNativeStackHomeSpace;
088
089    private final CalleeSaveLayout csl;
090
091    private static Register[] initAllocatable(boolean reserveForHeapBase) {
092        Register[] registers = null;
093        // @formatter:off
094        if (reserveForHeapBase) {
095            registers = new Register[] {
096                        rax, rbx, rcx, rdx, /*rsp,*/ rbp, rsi, rdi, r8, r9,  r10, r11, /*r12,*/ r13, r14, /*r15, */
097                        xmm0, xmm1, xmm2,  xmm3,  xmm4,  xmm5,  xmm6,  xmm7,
098                        xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15
099                      };
100        } else {
101            registers = new Register[] {
102                        rax, rbx, rcx, rdx, /*rsp,*/ rbp, rsi, rdi, r8, r9,  r10, r11, r12, r13, r14, /*r15, */
103                        xmm0, xmm1, xmm2,  xmm3,  xmm4,  xmm5,  xmm6,  xmm7,
104                        xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15
105                      };
106        }
107       // @formatter:on
108        return registers;
109    }
110
111    public AMD64HotSpotRegisterConfig(Architecture architecture, HotSpotVMConfig config) {
112        this(architecture, config, initAllocatable(config.useCompressedOops));
113        assert callerSaved.length >= allocatable.length;
114    }
115
116    public AMD64HotSpotRegisterConfig(Architecture architecture, HotSpotVMConfig config, Register[] allocatable) {
117        this.architecture = architecture;
118        this.maxFrameSize = config.maxFrameSize;
119
120        if (config.windowsOs) {
121            javaGeneralParameterRegisters = new Register[]{rdx, r8, r9, rdi, rsi, rcx};
122            nativeGeneralParameterRegisters = new Register[]{rcx, rdx, r8, r9};
123            this.needsNativeStackHomeSpace = true;
124        } else {
125            javaGeneralParameterRegisters = new Register[]{rsi, rdx, rcx, r8, r9, rdi};
126            nativeGeneralParameterRegisters = new Register[]{rdi, rsi, rdx, rcx, r8, r9};
127            this.needsNativeStackHomeSpace = false;
128        }
129
130        csl = null;
131        this.allocatable = allocatable.clone();
132        Set<Register> callerSaveSet = new HashSet<>();
133        Collections.addAll(callerSaveSet, allocatable);
134        Collections.addAll(callerSaveSet, xmmParameterRegisters);
135        Collections.addAll(callerSaveSet, javaGeneralParameterRegisters);
136        Collections.addAll(callerSaveSet, nativeGeneralParameterRegisters);
137        callerSaved = callerSaveSet.toArray(new Register[callerSaveSet.size()]);
138
139        allAllocatableAreCallerSaved = true;
140        attributesMap = RegisterAttributes.createMap(this, AMD64.allRegisters);
141    }
142
143    @Override
144    public Register[] getCallerSaveRegisters() {
145        return callerSaved;
146    }
147
148    @Override
149    public boolean areAllAllocatableRegistersCallerSaved() {
150        return allAllocatableAreCallerSaved;
151    }
152
153    @Override
154    public Register getRegisterForRole(int index) {
155        throw new UnsupportedOperationException();
156    }
157
158    @Override
159    public CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target, boolean stackOnly) {
160        if (type == Type.NativeCall) {
161            return callingConvention(nativeGeneralParameterRegisters, returnType, parameterTypes, type, target, stackOnly);
162        }
163        // On x64, parameter locations are the same whether viewed
164        // from the caller or callee perspective
165        return callingConvention(javaGeneralParameterRegisters, returnType, parameterTypes, type, target, stackOnly);
166    }
167
168    public Register[] getCallingConventionRegisters(Type type, Kind kind) {
169        if (architecture.canStoreValue(XMM, kind)) {
170            return xmmParameterRegisters;
171        }
172        assert architecture.canStoreValue(CPU, kind);
173        return type == Type.NativeCall ? nativeGeneralParameterRegisters : javaGeneralParameterRegisters;
174    }
175
176    private CallingConvention callingConvention(Register[] generalParameterRegisters, JavaType returnType, JavaType[] parameterTypes, Type type, TargetDescription target, boolean stackOnly) {
177        AllocatableValue[] locations = new AllocatableValue[parameterTypes.length];
178
179        int currentGeneral = 0;
180        int currentXMM = 0;
181        int currentStackOffset = type == Type.NativeCall && needsNativeStackHomeSpace ? generalParameterRegisters.length * target.wordSize : 0;
182
183        for (int i = 0; i < parameterTypes.length; i++) {
184            final Kind kind = parameterTypes[i].getKind();
185
186            switch (kind) {
187                case Byte:
188                case Boolean:
189                case Short:
190                case Char:
191                case Int:
192                case Long:
193                case Object:
194                    if (!stackOnly && currentGeneral < generalParameterRegisters.length) {
195                        Register register = generalParameterRegisters[currentGeneral++];
196                        locations[i] = register.asValue(target.getLIRKind(kind));
197                    }
198                    break;
199                case Float:
200                case Double:
201                    if (!stackOnly && currentXMM < xmmParameterRegisters.length) {
202                        Register register = xmmParameterRegisters[currentXMM++];
203                        locations[i] = register.asValue(target.getLIRKind(kind));
204                    }
205                    break;
206                default:
207                    throw JVMCIError.shouldNotReachHere();
208            }
209
210            if (locations[i] == null) {
211                locations[i] = StackSlot.get(target.getLIRKind(kind.getStackKind()), currentStackOffset, !type.out);
212                currentStackOffset += Math.max(target.getSizeInBytes(kind), target.wordSize);
213            }
214        }
215
216        Kind returnKind = returnType == null ? Kind.Void : returnType.getKind();
217        AllocatableValue returnLocation = returnKind == Kind.Void ? Value.ILLEGAL : getReturnRegister(returnKind).asValue(target.getLIRKind(returnKind.getStackKind()));
218        return new CallingConvention(currentStackOffset, returnLocation, locations);
219    }
220
221    @Override
222    public Register getReturnRegister(Kind kind) {
223        switch (kind) {
224            case Boolean:
225            case Byte:
226            case Char:
227            case Short:
228            case Int:
229            case Long:
230            case Object:
231                return rax;
232            case Float:
233            case Double:
234                return xmm0;
235            case Void:
236            case Illegal:
237                return null;
238            default:
239                throw new UnsupportedOperationException("no return register for type " + kind);
240        }
241    }
242
243    @Override
244    public Register getFrameRegister() {
245        return rsp;
246    }
247
248    public CalleeSaveLayout getCalleeSaveLayout() {
249        return csl;
250    }
251
252    @Override
253    public String toString() {
254        return String.format("Allocatable: " + Arrays.toString(getAllocatableRegisters()) + "%n" + "CallerSave:  " + Arrays.toString(getCallerSaveRegisters()) + "%n");
255    }
256}