Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/hotspot/cpu/x86/x86_32.ad
Original file line number Diff line number Diff line change
Expand Up @@ -11539,7 +11539,7 @@ instruct rep_stos(eCXRegI cnt, eDIRegP base, regD tmp, eAXRegI zero, Universe du
%}

// Small ClearArray AVX512 non-constant length.
instruct rep_stos_evex(eCXRegI cnt, eDIRegP base, regD tmp, kReg ktmp, eAXRegI zero, Universe dummy, eFlagsReg cr) %{
instruct rep_stos_evex(eCXRegI cnt, eDIRegP base, legRegD tmp, kReg ktmp, eAXRegI zero, Universe dummy, eFlagsReg cr) %{
predicate(!((ClearArrayNode*)n)->is_large() && (UseAVX > 2));
match(Set dummy (ClearArray cnt base));
ins_cost(125);
Expand Down Expand Up @@ -11650,7 +11650,7 @@ instruct rep_stos_large(eCXRegI cnt, eDIRegP base, regD tmp, eAXRegI zero, Unive
%}

// Large ClearArray AVX512.
instruct rep_stos_large_evex(eCXRegI cnt, eDIRegP base, regD tmp, kReg ktmp, eAXRegI zero, Universe dummy, eFlagsReg cr) %{
instruct rep_stos_large_evex(eCXRegI cnt, eDIRegP base, legRegD tmp, kReg ktmp, eAXRegI zero, Universe dummy, eFlagsReg cr) %{
predicate((UseAVX > 2) && ((ClearArrayNode*)n)->is_large());
match(Set dummy (ClearArray cnt base));
effect(USE_KILL cnt, USE_KILL base, TEMP tmp, TEMP ktmp, KILL zero, KILL cr);
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/cpu/x86/x86_64.ad
Original file line number Diff line number Diff line change
Expand Up @@ -11100,7 +11100,7 @@ instruct rep_stos(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegI zero,
%}

// Small ClearArray AVX512 non-constant length.
instruct rep_stos_evex(rcx_RegL cnt, rdi_RegP base, regD tmp, kReg ktmp, rax_RegI zero,
instruct rep_stos_evex(rcx_RegL cnt, rdi_RegP base, legRegD tmp, kReg ktmp, rax_RegI zero,
Universe dummy, rFlagsReg cr)
%{
predicate(!((ClearArrayNode*)n)->is_large() && (UseAVX > 2));
Expand Down Expand Up @@ -11212,7 +11212,7 @@ instruct rep_stos_large(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegI zero,
%}

// Large ClearArray AVX512.
instruct rep_stos_large_evex(rcx_RegL cnt, rdi_RegP base, regD tmp, kReg ktmp, rax_RegI zero,
instruct rep_stos_large_evex(rcx_RegL cnt, rdi_RegP base, legRegD tmp, kReg ktmp, rax_RegI zero,
Universe dummy, rFlagsReg cr)
%{
predicate((UseAVX > 2) && ((ClearArrayNode*)n)->is_large());
Expand Down
10 changes: 8 additions & 2 deletions src/hotspot/share/opto/callnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1411,8 +1411,14 @@ Node *SafePointNode::Ideal(PhaseGVN *phase, bool can_reshape) {
Node* SafePointNode::Identity(PhaseGVN* phase) {

// If you have back to back safepoints, remove one
if( in(TypeFunc::Control)->is_SafePoint() )
return in(TypeFunc::Control);
if (in(TypeFunc::Control)->is_SafePoint()) {
Node* out_c = unique_ctrl_out();
// This can be the safepoint of an outer strip mined loop if the inner loop's backedge was removed. Replacing the
// outer loop's safepoint could confuse removal of the outer loop.
if (out_c != NULL && !out_c->is_OuterStripMinedLoopEnd()) {
return in(TypeFunc::Control);
}
}

// Transforming long counted loops requires a safepoint node. Do not
// eliminate a safepoint until loop opts are over.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2021, 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
Expand Down Expand Up @@ -156,6 +156,10 @@ public synchronized int read() {
* {@code b[off+k-1]} in the manner performed by {@code System.arraycopy}.
* The value {@code k} is added into {@code pos} and {@code k} is returned.
* <p>
* Unlike the {@link InputStream#read(byte[],int,int) overridden method}
* of {@code InputStream}, this method returns {@code -1} instead of zero
* if the end of the stream has been reached and {@code len == 0}.
* <p>
* This {@code read} method cannot block.
*
* @param b the buffer into which the data is read.
Expand Down
137 changes: 129 additions & 8 deletions src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@
package java.lang.runtime;

import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantBootstraps;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Stream;

import jdk.internal.javac.PreviewFeature;
Expand All @@ -53,12 +52,15 @@ private SwitchBootstraps() {}

private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();

private static final MethodHandle DO_SWITCH;
private static final MethodHandle DO_TYPE_SWITCH;
private static final MethodHandle DO_ENUM_SWITCH;

static {
try {
DO_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doSwitch",
DO_TYPE_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doTypeSwitch",
MethodType.methodType(int.class, Object.class, int.class, Object[].class));
DO_ENUM_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doEnumSwitch",
MethodType.methodType(int.class, Enum.class, int.class, Object[].class));
}
catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
Expand Down Expand Up @@ -108,14 +110,13 @@ private SwitchBootstraps() {}
* second parameter of type {@code int} and with {@code int} as its return type,
* or if {@code labels} contains an element that is not of type {@code String},
* {@code Integer} or {@code Class}.
* @throws Throwable if there is any error linking the call site
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
*/
public static CallSite typeSwitch(MethodHandles.Lookup lookup,
String invocationName,
MethodType invocationType,
Object... labels) throws Throwable {
Object... labels) {
if (invocationType.parameterCount() != 2
|| (!invocationType.returnType().equals(int.class))
|| invocationType.parameterType(0).isPrimitive()
Expand All @@ -126,7 +127,7 @@ public static CallSite typeSwitch(MethodHandles.Lookup lookup,
labels = labels.clone();
Stream.of(labels).forEach(SwitchBootstraps::verifyLabel);

MethodHandle target = MethodHandles.insertArguments(DO_SWITCH, 2, (Object) labels);
MethodHandle target = MethodHandles.insertArguments(DO_TYPE_SWITCH, 2, (Object) labels);
return new ConstantCallSite(target);
}

Expand All @@ -142,7 +143,7 @@ private static void verifyLabel(Object label) {
}
}

private static int doSwitch(Object target, int startIndex, Object[] labels) {
private static int doTypeSwitch(Object target, int startIndex, Object[] labels) {
if (target == null)
return -1;

Expand All @@ -167,4 +168,124 @@ private static int doSwitch(Object target, int startIndex, Object[] labels) {
return labels.length;
}

/**
* Bootstrap method for linking an {@code invokedynamic} call site that
* implements a {@code switch} on a target of an enum type. The static
* arguments are used to encode the case labels associated to the switch
* construct, where each label can be encoded in two ways:
* <ul>
* <li>as a {@code String} value, which represents the name of
* the enum constant associated with the label</li>
* <li>as a {@code Class} value, which represents the enum type
* associated with a type test pattern</li>
* </ul>
* <p>
* The returned {@code CallSite}'s method handle will have
* a return type of {@code int} and accepts two parameters: the first argument
* will be an {@code Enum} instance ({@code target}) and the second
* will be {@code int} ({@code restart}).
* <p>
* If the {@code target} is {@code null}, then the method of the call site
* returns {@literal -1}.
* <p>
* If the {@code target} is not {@code null}, then the method of the call site
* returns the index of the first element in the {@code labels} array starting from
* the {@code restart} index matching one of the following conditions:
* <ul>
* <li>the element is of type {@code Class} that is assignable
* from the target's class; or</li>
* <li>the element is of type {@code String} and equals to the target
* enum constant's {@link Enum#name()}.</li>
* </ul>
* <p>
* If no element in the {@code labels} array matches the target, then
* the method of the call site return the length of the {@code labels} array.
*
* @param lookup Represents a lookup context with the accessibility
* privileges of the caller. When used with {@code invokedynamic},
* this is stacked automatically by the VM.
* @param invocationName unused
* @param invocationType The invocation type of the {@code CallSite} with two parameters,
* an enum type, an {@code int}, and {@code int} as a return type.
* @param labels case labels - {@code String} constants and {@code Class} instances,
* in any combination
* @return a {@code CallSite} returning the first matching element as described above
*
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if any element in the labels array is null, if the
* invocation type is not a method type whose first parameter type is an enum type,
* second parameter of type {@code int} and whose return type is {@code int},
* or if {@code labels} contains an element that is not of type {@code String} or
* {@code Class} of the target enum type.
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
*/
public static CallSite enumSwitch(MethodHandles.Lookup lookup,
String invocationName,
MethodType invocationType,
Object... labels) {
if (invocationType.parameterCount() != 2
|| (!invocationType.returnType().equals(int.class))
|| invocationType.parameterType(0).isPrimitive()
|| !invocationType.parameterType(0).isEnum()
|| !invocationType.parameterType(1).equals(int.class))
throw new IllegalArgumentException("Illegal invocation type " + invocationType);
requireNonNull(labels);

labels = labels.clone();

Class<?> enumClass = invocationType.parameterType(0);
labels = Stream.of(labels).map(l -> convertEnumConstants(lookup, enumClass, l)).toArray();

MethodHandle target =
MethodHandles.insertArguments(DO_ENUM_SWITCH, 2, (Object) labels);
target = target.asType(invocationType);

return new ConstantCallSite(target);
}

private static <E extends Enum<E>> Object convertEnumConstants(MethodHandles.Lookup lookup, Class<?> enumClassTemplate, Object label) {
if (label == null) {
throw new IllegalArgumentException("null label found");
}
Class<?> labelClass = label.getClass();
if (labelClass == Class.class) {
if (label != enumClassTemplate) {
throw new IllegalArgumentException("the Class label: " + label +
", expected the provided enum class: " + enumClassTemplate);
}
return label;
} else if (labelClass == String.class) {
@SuppressWarnings("unchecked")
Class<E> enumClass = (Class<E>) enumClassTemplate;
try {
return ConstantBootstraps.enumConstant(lookup, (String) label, enumClass);
} catch (IllegalArgumentException ex) {
return null;
}
} else {
throw new IllegalArgumentException("label with illegal type found: " + labelClass +
", expected label of type either String or Class");
}
}

private static int doEnumSwitch(Enum<?> target, int startIndex, Object[] labels) {
if (target == null)
return -1;

// Dumbest possible strategy
Class<?> targetClass = target.getClass();
for (int i = startIndex; i < labels.length; i++) {
Object label = labels[i];
if (label instanceof Class<?> c) {
if (c.isAssignableFrom(targetClass))
return i;
} else if (label == target) {
return i;
}
}

return labels.length;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,11 @@ public void visitSwitch(JCSwitch tree) {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
scan(tree.selector);
Set<Symbol> constants = tree.patternSwitch ? new HashSet<>() : null;
boolean exhaustiveSwitch = tree.patternSwitch ||
tree.cases.stream()
.flatMap(c -> c.labels.stream())
.anyMatch(l -> TreeInfo.isNull(l));
Set<Symbol> constants = exhaustiveSwitch ? new HashSet<>() : null;
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
alive = Liveness.ALIVE;
JCCase c = l.head;
Expand All @@ -686,7 +690,7 @@ public void visitSwitch(JCSwitch tree) {
l.tail.head.pos(),
Warnings.PossibleFallThroughIntoCase);
}
if (!tree.hasTotalPattern && tree.patternSwitch &&
if (!tree.hasTotalPattern && exhaustiveSwitch &&
!TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) &&
(constants == null || !isExhaustive(tree.selector.type, constants))) {
log.error(tree, Errors.NotExhaustiveStatement);
Expand Down
Loading