/*
* Copyright (c) 2007, 2012, 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 com.sun.max.platform;
import static java.lang.Integer.*;
import static java.lang.System.*;
import java.io.*;
import java.util.*;
import java.util.regex.*;
import com.oracle.max.asm.target.amd64.*;
import com.sun.cri.ci.*;
import com.sun.max.annotate.*;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.vm.hosted.*;
import com.sun.max.vm.runtime.*;
/**
* Platform configuration information. This class maintains a current {@link #platform() platform} context
* that is initialized via system properties and native methods (where the relevant system property is {@code null}).
* The system properties used to initialize the initial platform context are:
* <ul>
* <li>{@link #PLATFORM_PROPERTY}</li>
* <li>{@link #CPU_PROPERTY}</li>
* <li>{@link #ISA_PROPERTY}</li>
* <li>{@link #ENDIANNESS_PROPERTY}</li>
* <li>{@link #WORD_WIDTH_PROPERTY}</li>
* <li>{@link #OS_PROPERTY}</li>
* <li>{@link #PAGE_SIZE_PROPERTY}</li>
* <li>{@link #NUMBER_OF_SIGNALS_PROPERTY}</li>
* </ul>
*/
public final class Platform {
/**
* The name of the system property whose value (if non-null) specifies the target platform.
*/
public static final String PLATFORM_PROPERTY = "max.platform";
/**
* The name of the system property whose value (if non-null) specifies the target CPU.
* Iff {@code null}, the value returned by {@link CPU#defaultForInstructionSet(ISA)}
* is used.
*/
public static final String CPU_PROPERTY = "max.cpu";
/**
* The name of the system property whose value (if non-null) specifies the target ISA.
* Iff {@code null}, the value returned by {@link #nativeGetISA()} is used.
*/
public static final String ISA_PROPERTY = "max.isa";
/**
* The name of the system property whose value (if non-null) specifies the target endianness.
* Iff {@code null}, the value returned by {@link #nativeIsBigEndian()} is used.
*/
public static final String ENDIANNESS_PROPERTY = "max.endianness";
/**
* The name of the system property whose value (if non-null) specifies the target word size (in bits).
* Iff {@code null}, the value returned by {@link #nativeGetWordWidth()} is used.
*/
public static final String WORD_WIDTH_PROPERTY = "max.bits";
/**
* The name of the system property whose value (if non-null) specifies the target OS.
* Iff {@code null}, the value returned by {@link #nativeGetOS()} is used.
*/
public static final String OS_PROPERTY = "max.os";
/**
* The name of the system property whose value (if non-null) specifies the target page size.
* Iff {@code null}, the value returned by {@link #nativeGetPageSize()} is used.
*/
public static final String PAGE_SIZE_PROPERTY = "max.page";
/**
* The name of the system property whose value (if non-null) specifies the number of signals on the target.
* Iff {@code null}, the value returned by {@link #nativeNumberOfSignals()} is used.
*/
public static final String NUMBER_OF_SIGNALS_PROPERTY = "max.nsig";
public final CPU cpu;
public final ISA isa;
public final DataModel dataModel;
/**
* The operating system.
*/
public final OS os;
/**
* The number of signals.
*/
public final int nsig;
/**
* The number of bytes in a virtual page.
*/
public final int pageSize;
/**
* Stack bias added to the stack pointer to obtain the actual top of the stack frame.
*/
public final int stackBias;
public final CiTarget target;
private CiTarget createTarget() {
CiArchitecture arch = null;
int stackAlignment = -1;
if (isa == ISA.AMD64) {
arch = new AMD64();
if (os == OS.DARWIN) {
// Darwin requires 16-byte stack frame alignment.
stackAlignment = 16;
} else if (os == OS.SOLARIS || os == OS.LINUX) {
// Linux apparently also requires it for functions that pass floating point functions on the stack.
// One such function in the Maxine code base is log_print_float() in log.c which passes a float
// value to fprintf on the stack. However, gcc doesn't fix the alignment itself so we simply
// adopt the global convention on Linux of 16-byte alignment for stacks. If this is a performance issue,
// this can later be refined to only be for JNI stubs that pass a float or double to native code.
// Solaris has the same issues.
stackAlignment = 16;
} else if (os == OS.MAXVE) {
stackAlignment = 8;
} else {
throw FatalError.unexpected("Unimplemented stack alignment: " + os);
}
} else {
return null;
}
assert arch.wordSize == dataModel.wordWidth.numberOfBytes;
boolean isMP = true;
int spillSlotSize = arch.wordSize;
int cacheAlignment = dataModel.cacheAlignment;
boolean inlineObjects = false;
return new CiTarget(arch,
isMP,
spillSlotSize,
stackAlignment,
pageSize,
cacheAlignment,
inlineObjects,
false,
false);
}
private static final Pattern NON_REGEX_TEST_PATTERN = Pattern.compile("\\w+");
/**
* Determines if a given string contains a {@linkplain Pattern regular expression}.
*/
private static boolean isRegex(String input) {
for (int i = 0; i < input.length(); ++i) {
if (!Character.isLetterOrDigit(input.charAt(i)) && input.charAt(i) == '_') {
return true;
}
}
return false;
}
private static boolean isAcceptedBy(String input, String filter) {
if (filter.isEmpty()) {
return true;
}
boolean negate = filter.charAt(0) == '!';
if (negate) {
filter = filter.substring(1);
}
boolean result;
if (!isRegex(filter)) {
result = input.equalsIgnoreCase(filter);
} else {
Pattern pattern = Pattern.compile(filter, Pattern.CASE_INSENSITIVE);
result = pattern.matcher(input).matches();
}
return negate ? !result : result;
}
/**
* Determines if this platform is accepted by a filter specified by a {@link PLATFORM} annotation.
* Each element of {@linkplain PLATFORM} is matched according to the following algorithm.
* <p>
* An element value of "" always matches the corresponding platform component.
* <p>
* An element value that is not a {@linkplain Pattern regular expression}
* is a simple string filter compared for {@linkplain String#equalsIgnoreCase(String) case-insensitive equality} against the
* corresponding platform component. For example {@code @PLATFORM(os = "windows")} will match
* this platform object if {@code this.os().name().equalsIgnoreCase("windows")}. A negative
* filter can be specified by prefixing {@code '!'} to the filter value. That is,
* {@code @PLATFORM(os = "!windows")} can be used to match any platform with a non-Windows
* operating system.
* <p>
* An element value that does contain a regular expression
* is used as a case-insensitive {@linkplain Pattern pattern} that is {@linkplain Pattern#matcher(CharSequence) matched}
* against the corresponding platform component. For example {@code @PLATFORM(cpu = "(amd64|ia32)")} can
* be used to match an x86 based platform. At with simple string matching, a {@code '!'} prefix can
* be employed to specify a negative filter.
*
* @param filter a {@link PLATFORM} instance
* @return {@code true} is this platform is accepted by {@code filter}
*/
public boolean isAcceptedBy(PLATFORM filter) {
if (filter != null) {
if (!isAcceptedBy(os.name(), filter.os())) {
return false;
}
if (!isAcceptedBy(cpu.name(), filter.cpu())) {
return false;
}
}
return true;
}
public Platform(CPU cpu, OS os, int pageSize, int nsig) {
this(cpu, cpu.isa, cpu.defaultDataModel, os, pageSize, nsig);
}
public Platform(CPU cpu, ISA isa, DataModel dataModel, OS os, int pageSize, int nsig) {
this.isa = isa;
this.cpu = cpu;
this.os = os;
this.dataModel = dataModel;
this.pageSize = pageSize;
this.nsig = nsig;
if (cpu == CPU.SPARCV9 && os == OS.SOLARIS) {
this.stackBias = 2047;
} else {
this.stackBias = 0;
}
target = createTarget();
}
/**
* Gets the current platform context. The initial value is created by {@link Platform#createDefaultPlatform()}.
* It can be subsequently modified by {@link #set(Platform)}.
*/
@FOLD
public static Platform platform() {
return current;
}
/**
* Gets the {@linkplain #target target} associated with the {@linkplain #platform() current} platform context.
*/
@FOLD
public static CiTarget target() {
assert current.target != null;
return current.target;
}
/**
* Changes the current platform context.
*
* @param platform the new platform context (must not be {@code null})
* @return the previous platform context
*/
public static Platform set(Platform platform) {
Platform old = current;
current = platform;
return old;
}
public Platform constrainedByInstructionSet(ISA isa) {
CPU cpu = this.cpu;
DataModel dataModel = this.dataModel;
if (this.isa != isa) {
cpu = CPU.defaultForInstructionSet(isa);
dataModel = cpu.defaultDataModel;
}
return new Platform(cpu, isa, dataModel, os, pageSize, nsig);
}
@Override
public String toString() {
return os.toString().toLowerCase() + "-" + cpu.toString().toLowerCase() + ", isa=" + isa + ", " + dataModel + ", page size=" + pageSize;
}
public Endianness endianness() {
return dataModel.endianness;
}
public WordWidth wordWidth() {
return dataModel.wordWidth;
}
public int cacheAlignment() {
return dataModel.cacheAlignment;
}
/**
* Determine the instruction set.
*
* @return a string representing the name of the instruction set on which this VM is running
*/
@HOSTED_ONLY
private static String getInstructionSet() {
Prototype.loadHostedLibrary();
return nativeGetISA();
}
private static native String nativeGetISA();
/**
* Determine whether the underlying memory model is big-endian.
*
* @return {@code true} if this memory model is big-endian; {@code false} otherwise
*/
private static boolean isBigEndian() {
Prototype.loadHostedLibrary();
return nativeIsBigEndian();
}
private static native boolean nativeIsBigEndian();
/**
* Gets the absolute path to the "jni.h" file against which the native code was compiled.
*/
@HOSTED_ONLY
public static String jniHeaderFilePath() {
try {
return nativeJniHeaderFilePath();
} catch (UnsatisfiedLinkError e) {
String javaHome = System.getenv("JAVA_HOME");
FatalError.check(javaHome != null, "Environment variable JAVA_HOME not set");
if (javaHome != null) {
File jni = new File(javaHome, "include" + File.separatorChar + "jni.h");
if (jni.exists()) {
return jni.getAbsolutePath();
}
}
throw FatalError.unexpected("Could not find jni.h");
}
}
public static native String nativeJniHeaderFilePath();
/**
* Gets the absolute path to the "jvmti.h" file against which the native code was compiled.
* This lives in the same directory as jni.h
*/
@HOSTED_ONLY
public static String jvmtiHeaderFilePath() {
final File jniFile = new File(jniHeaderFilePath());
final File jvmtiFile = new File(jniFile.getParentFile(), "jvmti.h");
ProgramError.check(jvmtiFile.exists(), "cannot locate jvmti.h");
return jvmtiFile.getAbsolutePath();
}
/**
* Gets the width of the native word in bits (typically 32 or 64 bits).
*
* @return the width of the native word in bits
*/
@HOSTED_ONLY
private static int getWordWidth() {
Prototype.loadHostedLibrary();
return nativeGetWordWidth();
}
private static native int nativeGetWordWidth();
/**
* Determines the operating system on which this virtual machine is running.
*
* @return a string representing the name of the OS on which this VM is running
*/
@HOSTED_ONLY
private static String getOS() {
Prototype.loadHostedLibrary();
return nativeGetOS();
}
private static native String nativeGetOS();
/**
* Gets the page size in bytes of the platform on which this VM is running.
*
* @return the page size in bytes
*/
@HOSTED_ONLY
private static int getPageSize() {
Prototype.loadHostedLibrary();
return nativeGetPageSize();
}
private static native int nativeGetPageSize();
/**
* Gets the number of signals supported by the target that may be delivered to the VM.
* The range of signal numbers that the VM expects to see is between 0 (inclusive) and
* {@code numberOfSignals()} (exclusive).
* This checks the property {@value #NUMBER_OF_SIGNALS_PROPERTY} first and only calls
* {@link #nativeNumberOfSignals()} if there is no value set for the property.
*/
@HOSTED_ONLY
private static int getNumberOfSignals() {
Prototype.loadHostedLibrary();
return nativeNumberOfSignals();
}
/**
* Get the number of signals supported by the host platform, the assumption being that the generated VM
* will be running on the same platform.
*/
private static native int nativeNumberOfSignals();
private static Platform createDefaultPlatform() {
try {
return createDefaultPlatform0();
} catch (UnsatisfiedLinkError e) {
ProgramWarning.message("Could not create platform from native properties - using default platform instead: " + Default);
return Default;
}
}
/**
* Creates a default {@code Platform} derived from system properties and native code in the
* case where the relevant system properties are {@code null}.
*/
private static Platform createDefaultPlatform0() {
String platformSpec = System.getProperty(PLATFORM_PROPERTY);
if (platformSpec != null) {
Platform platform = parse(platformSpec);
if (platform == null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(baos);
out.println("Invalid platform specification: " + platformSpec);
printPlatformSpecificationHelp(out);
throw ProgramError.unexpected(baos.toString());
}
return platform;
}
String isaProperty = getProperty(ISA_PROPERTY);
ISA isa = ISA.valueOf(isaProperty == null ? getInstructionSet() : isaProperty.toUpperCase());
WordWidth word = WordWidth.fromInt(getInteger(WORD_WIDTH_PROPERTY) == null ? getWordWidth() : getInteger(WORD_WIDTH_PROPERTY));
final Endianness endianness;
final String endiannessProperty = getProperty(ENDIANNESS_PROPERTY);
if (endiannessProperty != null) {
endianness = Endianness.valueOf(endiannessProperty);
} else {
endianness = isBigEndian() ? Endianness.BIG : Endianness.LITTLE;
}
final String cpuName = getProperty(CPU_PROPERTY);
final CPU cpu;
if (cpuName == null) {
cpu = CPU.defaultForInstructionSet(isa);
} else {
cpu = CPU.valueOf(cpuName);
assert cpu.isa == isa;
}
final int cacheAlignment = cpu.defaultDataModel.cacheAlignment;
final DataModel dataModel = new DataModel(word, endianness, cacheAlignment);
String osName = getProperty(OS_PROPERTY) == null ? getOS() : getProperty(OS_PROPERTY);
final OS os = OS.fromName(osName);
final int pageSize = getInteger(PAGE_SIZE_PROPERTY) == null ? getPageSize() : getInteger(PAGE_SIZE_PROPERTY);
final int nsig = getProperty(NUMBER_OF_SIGNALS_PROPERTY) == null ? getNumberOfSignals() : getInteger(NUMBER_OF_SIGNALS_PROPERTY);
return new Platform(cpu, isa, dataModel, os, pageSize, nsig);
}
/**
* The platform that will be used if the platform cannot be created from native properties.
*/
public static final Platform Default;
/**
* A map from platform strings to correlated {@link Platform} objects.
*/
public static final Map<String, Platform> Supported;
static {
Map<String, Platform> map = new TreeMap<String, Platform>();
map.put("linux-amd64", new Platform(CPU.AMD64, OS.LINUX, Ints.K * 8, 32));
map.put("solaris-amd64", new Platform(CPU.AMD64, OS.SOLARIS, Ints.K * 8, 32));
map.put("solaris-sparcv9", new Platform(CPU.SPARCV9, OS.SOLARIS, Ints.K * 8, 32));
map.put("darwin-amd64", new Platform(CPU.AMD64, OS.DARWIN, Ints.K * 8, 32));
map.put("maxve-amd64", new Platform(CPU.AMD64, OS.MAXVE, Ints.K * 8, 32));
Supported = Collections.unmodifiableMap(map);
Default = map.get("linux-amd64");
}
/**
* The global platform {@linkplain #platform() context}.
*/
private static Platform current = Platform.createDefaultPlatform();
/**
* Parses a platform string into a {@link Platform} object. The strings for which a non-null {@code Platform} object
* is returned are any of the keys of {@link #Supported}. The string can include a suffix of a ':' followed by a
* value parsable by {@link Longs#parseScaledValue(String)} specifying a page size.
*
* @param platformSpec a string describing a platform and an optional page size
* @return the Platform object corresponding to {@code platformSpec} or null if none of the preset platforms is
* matched by {@code platformSpec}
*/
public static Platform parse(String platformSpec) {
int colonIndex = platformSpec.indexOf(':');
String pageSizeString = null;
if (colonIndex != -1) {
pageSizeString = platformSpec.substring(colonIndex + 1);
platformSpec = platformSpec.substring(0, colonIndex);
}
Platform platform = Supported.get(platformSpec);
if (platform == null) {
return null;
}
if (pageSizeString != null) {
long pageSize = Longs.parseScaledValue(pageSizeString);
assert pageSize == (int) pageSize;
platform = new Platform(platform.cpu, platform.isa, platform.dataModel, platform.os, (int) pageSize, 32);
}
return platform;
}
public static void main(String[] args) {
printPlatformSpecificationHelp(System.out);
}
/**
* Prints a help message detailing the format of a platform specification accepted by {@link #parse(String)}.
*
* @param out stream to which the help message is printed
*/
public static void printPlatformSpecificationHelp(PrintStream out) {
out.println("A platform specification has one of the following formats:");
out.println();
out.println(" <platform>");
out.println(" <platform>:<page size>");
out.println();
out.println("For example:");
out.println();
out.println(" solaris-amd64");
out.println(" solaris-amd64:8k");
out.println();
out.println("The supported platforms are:");
out.println();
for (Map.Entry<String, Platform> entry : Supported.entrySet()) {
out.println(" " + entry.getKey());
}
}
}