/*
 * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package jdk.vm.ci.hotspot.aarch64;

import static jdk.vm.ci.aarch64.AArch64.lr;
import static jdk.vm.ci.aarch64.AArch64.r0;
import static jdk.vm.ci.aarch64.AArch64.r1;
import static jdk.vm.ci.aarch64.AArch64.r2;
import static jdk.vm.ci.aarch64.AArch64.r3;
import static jdk.vm.ci.aarch64.AArch64.r4;
import static jdk.vm.ci.aarch64.AArch64.r5;
import static jdk.vm.ci.aarch64.AArch64.r6;
import static jdk.vm.ci.aarch64.AArch64.r7;
import static jdk.vm.ci.aarch64.AArch64.rscratch1;
import static jdk.vm.ci.aarch64.AArch64.rscratch2;
import static jdk.vm.ci.aarch64.AArch64.r12;
import static jdk.vm.ci.aarch64.AArch64.r27;
import static jdk.vm.ci.aarch64.AArch64.r28;
import static jdk.vm.ci.aarch64.AArch64.r29;
import static jdk.vm.ci.aarch64.AArch64.r31;
import static jdk.vm.ci.aarch64.AArch64.sp;
import static jdk.vm.ci.aarch64.AArch64.v0;
import static jdk.vm.ci.aarch64.AArch64.v1;
import static jdk.vm.ci.aarch64.AArch64.v2;
import static jdk.vm.ci.aarch64.AArch64.v3;
import static jdk.vm.ci.aarch64.AArch64.v4;
import static jdk.vm.ci.aarch64.AArch64.v5;
import static jdk.vm.ci.aarch64.AArch64.v6;
import static jdk.vm.ci.aarch64.AArch64.v7;
import static jdk.vm.ci.aarch64.AArch64.zr;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import jdk.vm.ci.aarch64.AArch64;
import jdk.vm.ci.code.Architecture;
import jdk.vm.ci.code.CallingConvention;
import jdk.vm.ci.code.CallingConvention.Type;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.RegisterArray;
import jdk.vm.ci.code.RegisterAttributes;
import jdk.vm.ci.code.RegisterConfig;
import jdk.vm.ci.code.StackSlot;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.code.ValueKindFactory;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.hotspot.HotSpotCallingConventionType;
import jdk.vm.ci.meta.AllocatableValue;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.PlatformKind;
import jdk.vm.ci.meta.Value;
import jdk.vm.ci.meta.ValueKind;

public class AArch64HotSpotRegisterConfig implements RegisterConfig {

    private final TargetDescription target;

    private final RegisterArray allocatable;

    
The caller saved registers always include all parameter registers.
/** * The caller saved registers always include all parameter registers. */
private final RegisterArray callerSaved; private final boolean allAllocatableAreCallerSaved; private final RegisterAttributes[] attributesMap; @Override public RegisterArray getAllocatableRegisters() { return allocatable; } @Override public RegisterArray filterAllocatableRegisters(PlatformKind kind, RegisterArray registers) { ArrayList<Register> list = new ArrayList<>(); for (Register reg : registers) { if (target.arch.canStoreValue(reg.getRegisterCategory(), kind)) { list.add(reg); } } return new RegisterArray(list); } @Override public RegisterAttributes[] getAttributesMap() { return attributesMap.clone(); } private final RegisterArray javaGeneralParameterRegisters = new RegisterArray(r1, r2, r3, r4, r5, r6, r7, r0); private final RegisterArray nativeGeneralParameterRegisters = new RegisterArray(r0, r1, r2, r3, r4, r5, r6, r7); private final RegisterArray simdParameterRegisters = new RegisterArray(v0, v1, v2, v3, v4, v5, v6, v7); public static final Register inlineCacheRegister = rscratch2;
Vtable stubs expect the metaspace Method in r12.
/** * Vtable stubs expect the metaspace Method in r12. */
public static final Register metaspaceMethodRegister = r12; public static final Register heapBaseRegister = r27; public static final Register threadRegister = r28; public static final Register fp = r29;
The heapBaseRegister, i.e. r27, is reserved unconditionally because HotSpot does not intend to support it as an allocatable register even when compressed oops is off. This register is excluded from callee-saved register at cpu/aarch64/sharedRuntime_aarch64.cpp:RegisterSaver::save_live_registers, which may lead to dereferencing unknown value from the stack at share/runtime/stackValue.cpp:StackValue::create_stack_value during deoptimization.
/** * The heapBaseRegister, i.e. r27, is reserved unconditionally because HotSpot does not intend * to support it as an allocatable register even when compressed oops is off. This register is * excluded from callee-saved register at * cpu/aarch64/sharedRuntime_aarch64.cpp:RegisterSaver::save_live_registers, which may lead to * dereferencing unknown value from the stack at * share/runtime/stackValue.cpp:StackValue::create_stack_value during deoptimization. */
private static final RegisterArray reservedRegisters = new RegisterArray(rscratch1, rscratch2, heapBaseRegister, threadRegister, fp, lr, r31, zr, sp); private static RegisterArray initAllocatable(Architecture arch) { RegisterArray allRegisters = arch.getAvailableValueRegisters(); Register[] registers = new Register[allRegisters.size() - reservedRegisters.size()]; List<Register> reservedRegistersList = reservedRegisters.asList(); int idx = 0; for (Register reg : allRegisters) { if (reservedRegistersList.contains(reg)) { // skip reserved registers continue; } assert !(reg.equals(heapBaseRegister) || reg.equals(threadRegister) || reg.equals(fp) || reg.equals(lr) || reg.equals(r31) || reg.equals(zr) || reg.equals(sp)) : reg; registers[idx++] = reg; } assert idx == registers.length; return new RegisterArray(registers); } public AArch64HotSpotRegisterConfig(TargetDescription target) { this(target, initAllocatable(target.arch)); assert callerSaved.size() >= allocatable.size(); } public AArch64HotSpotRegisterConfig(TargetDescription target, RegisterArray allocatable) { this.target = target; this.allocatable = allocatable; Set<Register> callerSaveSet = new HashSet<>(); allocatable.addTo(callerSaveSet); simdParameterRegisters.addTo(callerSaveSet); javaGeneralParameterRegisters.addTo(callerSaveSet); nativeGeneralParameterRegisters.addTo(callerSaveSet); callerSaved = new RegisterArray(callerSaveSet); allAllocatableAreCallerSaved = true; attributesMap = RegisterAttributes.createMap(this, AArch64.allRegisters); } @Override public RegisterArray getCallerSaveRegisters() { return callerSaved; } @Override public RegisterArray getCalleeSaveRegisters() { return null; } @Override public boolean areAllAllocatableRegistersCallerSaved() { return allAllocatableAreCallerSaved; } @Override public CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, ValueKindFactory<?> valueKindFactory) { HotSpotCallingConventionType hotspotType = (HotSpotCallingConventionType) type; if (type == HotSpotCallingConventionType.NativeCall) { return callingConvention(nativeGeneralParameterRegisters, returnType, parameterTypes, hotspotType, valueKindFactory); } // On x64, parameter locations are the same whether viewed // from the caller or callee perspective return callingConvention(javaGeneralParameterRegisters, returnType, parameterTypes, hotspotType, valueKindFactory); } @Override public RegisterArray getCallingConventionRegisters(Type type, JavaKind kind) { HotSpotCallingConventionType hotspotType = (HotSpotCallingConventionType) type; switch (kind) { case Boolean: case Byte: case Short: case Char: case Int: case Long: case Object: return hotspotType == HotSpotCallingConventionType.NativeCall ? nativeGeneralParameterRegisters : javaGeneralParameterRegisters; case Float: case Double: return simdParameterRegisters; default: throw JVMCIError.shouldNotReachHere(); } } private CallingConvention callingConvention(RegisterArray generalParameterRegisters, JavaType returnType, JavaType[] parameterTypes, HotSpotCallingConventionType type, ValueKindFactory<?> valueKindFactory) { AllocatableValue[] locations = new AllocatableValue[parameterTypes.length]; int currentGeneral = 0; int currentSIMD = 0; int currentStackOffset = 0; for (int i = 0; i < parameterTypes.length; i++) { final JavaKind kind = parameterTypes[i].getJavaKind().getStackKind(); switch (kind) { case Byte: case Boolean: case Short: case Char: case Int: case Long: case Object: if (currentGeneral < generalParameterRegisters.size()) { Register register = generalParameterRegisters.get(currentGeneral++); locations[i] = register.asValue(valueKindFactory.getValueKind(kind)); } break; case Float: case Double: if (currentSIMD < simdParameterRegisters.size()) { Register register = simdParameterRegisters.get(currentSIMD++); locations[i] = register.asValue(valueKindFactory.getValueKind(kind)); } break; default: throw JVMCIError.shouldNotReachHere(); } if (locations[i] == null) { ValueKind<?> valueKind = valueKindFactory.getValueKind(kind); locations[i] = StackSlot.get(valueKind, currentStackOffset, !type.out); currentStackOffset += Math.max(valueKind.getPlatformKind().getSizeInBytes(), target.wordSize); } } JavaKind returnKind = returnType == null ? JavaKind.Void : returnType.getJavaKind(); AllocatableValue returnLocation = returnKind == JavaKind.Void ? Value.ILLEGAL : getReturnRegister(returnKind).asValue(valueKindFactory.getValueKind(returnKind.getStackKind())); return new CallingConvention(currentStackOffset, returnLocation, locations); } @Override public Register getReturnRegister(JavaKind kind) { switch (kind) { case Boolean: case Byte: case Char: case Short: case Int: case Long: case Object: return r0; case Float: case Double: return v0; case Void: case Illegal: return null; default: throw new UnsupportedOperationException("no return register for type " + kind); } } @Override public Register getFrameRegister() { return sp; } @Override public String toString() { return String.format("Allocatable: " + getAllocatableRegisters() + "%n" + "CallerSave: " + getCallerSaveRegisters() + "%n"); } }