Skip to content

Commit f9078d7

Browse files
committed
[GR-60395] Allow combination of captureCallState and critical(true).
PullRequest: graal/19579
2 parents 6ac4885 + d041481 commit f9078d7

File tree

2 files changed

+131
-37
lines changed

2 files changed

+131
-37
lines changed

substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java

Lines changed: 128 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@
5050

5151
import com.oracle.svm.core.SubstrateTargetDescription;
5252
import com.oracle.svm.core.config.ConfigurationValues;
53+
import com.oracle.svm.core.foreign.AbiUtils.Adapter.Adaptation;
5354
import com.oracle.svm.core.graal.code.AssignedLocation;
5455
import com.oracle.svm.core.headers.LibC;
5556
import com.oracle.svm.core.headers.WindowsAPIs;
5657
import com.oracle.svm.core.util.BasedOnJDKClass;
58+
import com.oracle.svm.core.util.BasedOnJDKFile;
5759
import com.oracle.svm.core.util.VMError;
5860

5961
import jdk.graal.compiler.api.replacements.Fold;
@@ -230,11 +232,15 @@ public static Adaptation check(Class<?> type) {
230232
}
231233

232234
public static Adaptation extract(Extracted as, Class<?> type) {
233-
return new Extract(Objects.requireNonNull(as), Objects.requireNonNull(type));
235+
return new ExtractSingle(as, type);
236+
}
237+
238+
public static Adaptation extractSegmentPair(Extracted as, Class<?> type) {
239+
return new ExtractSegmentPair(as);
234240
}
235241

236242
public static Adaptation drop() {
237-
return Extract.DROP;
243+
return Drop.SINGLETON;
238244
}
239245

240246
public static Adaptation reinterpret(JavaKind to) {
@@ -342,41 +348,77 @@ public List<AssignedLocation> apply(AssignedLocation parameter) {
342348
@Override
343349
public List<ValueNode> apply(ValueNode parameter, Map<Extracted, ValueNode> extractedArguments, List<ValueNode> originalArguments, int originalArgumentIndex,
344350
Consumer<Node> appendToGraph) {
351+
return List.of(computeAbsolutePointerFromSegmentPair(parameter, originalArguments, originalArgumentIndex, appendToGraph));
352+
}
353+
354+
static ValueNode computeAbsolutePointerFromSegmentPair(ValueNode parameter, List<ValueNode> originalArguments, int originalArgumentIndex, Consumer<Node> appendToGraph) {
345355
var offsetArg = originalArguments.get(originalArgumentIndex + 1);
346356

347-
// I originally wanted to use an OffsetAddressNode here
348-
// (followed by WordCastNode.addressToWord),
349-
// but NativeMemorySegmentImpls return null for `unsafeGetBase`,
350-
// which seems to break the graph somewhere later.
357+
/*
358+
* It would be most suitable to use OffsetAddressNode here (followed by
359+
* WordCastNode.addressToWord) but NativeMemorySegmentImpls return null for
360+
* `unsafeGetBase`,which seems to break the graph somewhere later.
361+
*/
351362
var basePointer = WordCastNode.objectToUntrackedPointer(parameter, ConfigurationValues.getWordKind());
352363
appendToGraph.accept(basePointer);
353364
var absolutePointer = AddNode.add(basePointer, offsetArg);
354365
appendToGraph.accept(absolutePointer);
355366

356-
return List.of(absolutePointer);
367+
return absolutePointer;
357368
}
358369
}
359370

360-
private static final class Extract extends Adaptation {
361-
private static final Extract DROP = new Extract(null, null);
362-
private final Extracted as;
363-
private final Class<?> type;
371+
/**
372+
* Extract adaptations consume one or more stub parameters. In this case, "consuming" means
373+
* that the adapted parameter(s) won't be passed to the stub's target but will be used by
374+
* the stub itself. The result is usually one ValueNode that will be put into the
375+
* 'extractedArguments' table.
376+
*/
377+
private abstract static class Extract extends Adaptation {
378+
final Extracted as;
364379

365-
private Extract(Extracted as, Class<?> type) {
380+
private Extract(Extracted as) {
366381
this.as = as;
367-
this.type = type;
382+
}
383+
384+
@Override
385+
public final List<AssignedLocation> apply(AssignedLocation parameter) {
386+
return List.of();
387+
}
388+
}
389+
390+
private static final class Drop extends Extract {
391+
private static final Drop SINGLETON = new Drop();
392+
393+
private Drop() {
394+
super(null);
368395
}
369396

370397
@Override
371398
public List<Class<?>> apply(Class<?> parameter) {
372-
if (type != null && parameter != type) {
373-
throw new IllegalArgumentException("Expected type " + type + ", got " + parameter);
374-
}
375399
return List.of();
376400
}
377401

378402
@Override
379-
public List<AssignedLocation> apply(AssignedLocation parameter) {
403+
public List<ValueNode> apply(ValueNode parameter, Map<Extracted, ValueNode> extractedArguments, List<ValueNode> originalArguments, int originalArgumentIndex,
404+
Consumer<Node> appendToGraph) {
405+
return List.of();
406+
}
407+
}
408+
409+
private final static class ExtractSingle extends Extract {
410+
private final Class<?> type;
411+
412+
private ExtractSingle(Extracted as, Class<?> type) {
413+
super(Objects.requireNonNull(as));
414+
this.type = Objects.requireNonNull(type);
415+
}
416+
417+
@Override
418+
public List<Class<?>> apply(Class<?> parameter) {
419+
if (type != null && parameter != type) {
420+
throw new IllegalArgumentException("Expected type " + type + ", got " + parameter);
421+
}
380422
return List.of();
381423
}
382424

@@ -392,6 +434,37 @@ public List<ValueNode> apply(ValueNode parameter, Map<Extracted, ValueNode> extr
392434
return List.of();
393435
}
394436
}
437+
438+
/**
439+
* Similar to {@link ComputeAddressFromSegmentPair}, consumes two parameters, i.e., an
440+
* Object + offset pair, and creates and AddNode that computes the absolute address.
441+
*/
442+
private static final class ExtractSegmentPair extends Extract {
443+
444+
private ExtractSegmentPair(Extracted as) {
445+
super(Objects.requireNonNull(as));
446+
}
447+
448+
@Override
449+
public List<Class<?>> apply(Class<?> parameter) {
450+
if (parameter != Object.class) {
451+
throw new IllegalArgumentException("Expected type " + Object.class + ", got " + parameter);
452+
}
453+
return List.of();
454+
}
455+
456+
@Override
457+
public List<ValueNode> apply(ValueNode parameter, Map<Extracted, ValueNode> extractedArguments, List<ValueNode> originalArguments, int originalArgumentIndex,
458+
Consumer<Node> appendToGraph) {
459+
assert as != null;
460+
if (extractedArguments.containsKey(as)) {
461+
throw new IllegalStateException("%s was already extracted (%s).".formatted(as, extractedArguments.get(as)));
462+
}
463+
ValueNode extracted = ComputeAddressFromSegmentPair.computeAbsolutePointerFromSegmentPair(parameter, originalArguments, originalArgumentIndex, appendToGraph);
464+
extractedArguments.put(as, extracted);
465+
return List.of();
466+
}
467+
}
395468
}
396469

397470
@Platforms(Platform.HOSTED_ONLY.class)
@@ -438,6 +511,7 @@ public final Adapter.Result.TypeAdaptation adapt(JavaEntryPointInfo jep) {
438511
/**
439512
* Generate additional argument adaptations which are not done by HotSpot.
440513
*/
514+
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+27/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java#L103-L147")
441515
@Platforms(Platform.HOSTED_ONLY.class)
442516
protected List<Adapter.Adaptation> generateAdaptations(NativeEntryPointInfo nep) {
443517
List<Adapter.Adaptation> adaptations = new ArrayList<>(Collections.nCopies(nep.methodType().parameterCount(), null));
@@ -446,36 +520,55 @@ protected List<Adapter.Adaptation> generateAdaptations(NativeEntryPointInfo nep)
446520
adaptations.set(current++, Adapter.check(long.class));
447521
}
448522
adaptations.set(current++, Adapter.extract(Adapter.Extracted.CallTarget, long.class));
449-
if (nep.capturesCallState()) {
450-
adaptations.set(current++, Adapter.extract(Adapter.Extracted.CaptureBufferAddress, long.class));
451-
}
452523

453524
// Special handling in case Linker.Option.critical(true) is passed.
454-
// See the doc of Adapter.CastObjectToWord.apply.
525+
// See the doc of class Adapter.ComputeAddressFromSegmentPair
455526
var storages = nep.parametersAssignment();
527+
528+
/*
529+
* It is possible to combine linker options 'captureCallState(...)' and 'critical(true)'.
530+
* This means that one can pass a heap memory segment as capture buffer address.
531+
*/
532+
if (nep.capturesCallState()) {
533+
if (nep.allowHeapAccess()) {
534+
VMError.guarantee(storages[current] != null && storages[current + 1] == null);
535+
// consumes two parameters (i.e. object + offset pair)
536+
handleCriticalWithHeapAccess(nep, current + 1, adaptations, Adapter.extractSegmentPair(Adapter.Extracted.CaptureBufferAddress, long.class));
537+
current += 2;
538+
} else {
539+
adaptations.set(current, Adapter.extract(Adapter.Extracted.CaptureBufferAddress, long.class));
540+
current++;
541+
}
542+
}
543+
456544
for (int i = current; i < storages.length; ++i) {
457545
var storage = storages[i];
458546
if (storage == null) {
459-
VMError.guarantee(nep.allowHeapAccess(), "A storage may only be null when the Linker.Option.critical(true) option is passed.");
460-
VMError.guarantee(JavaKind.fromJavaClass(
461-
nep.methodType().parameterArray()[i]) == JavaKind.Long &&
462-
JavaKind.fromJavaClass(nep.methodType().parameterArray()[i - 1]) == JavaKind.Object,
463-
"""
464-
Storage is null, but the other parameters are inconsistent.
465-
Storage may be null only if its kind is Long and previous kind is Object.
466-
See jdk/internal/foreign/abi/x64/sysv/CallArranger.java:286""");
467-
VMError.guarantee(
468-
adaptations.get(i) == null && adaptations.get(i - 1) == null,
469-
"This parameter already has an adaptation when it should not.");
470-
471-
adaptations.set(i, Adapter.drop());
472-
adaptations.set(i - 1, Adapter.computeAddressFromSegmentPair());
547+
handleCriticalWithHeapAccess(nep, i, adaptations, Adapter.computeAddressFromSegmentPair());
473548
}
474549
}
475550

476551
return adaptations;
477552
}
478553

554+
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+27/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java#L280-L290")
555+
private static void handleCriticalWithHeapAccess(NativeEntryPointInfo nep, int i, List<Adaptation> adaptations, Adaptation adaptation) {
556+
VMError.guarantee(nep.allowHeapAccess(), "A storage may only be null when the Linker.Option.critical(true) option is passed.");
557+
VMError.guarantee(
558+
JavaKind.fromJavaClass(nep.methodType().parameterArray()[i]) == JavaKind.Long &&
559+
JavaKind.fromJavaClass(nep.methodType().parameterArray()[i - 1]) == JavaKind.Object,
560+
"""
561+
Storage is null, but the other parameters are inconsistent.
562+
Storage may be null only if its kind is Long and previous kind is Object.
563+
See jdk/internal/foreign/abi/x64/sysv/CallArranger.java:286""");
564+
VMError.guarantee(
565+
adaptations.get(i) == null && adaptations.get(i - 1) == null,
566+
"This parameter already has an adaptation when it should not.");
567+
568+
adaptations.set(i, Adapter.drop());
569+
adaptations.set(i - 1, adaptation);
570+
}
571+
479572
@Platforms(Platform.HOSTED_ONLY.class)
480573
protected List<Adapter.Adaptation> generateAdaptations(JavaEntryPointInfo jep) {
481574
List<Adapter.Adaptation> adaptations = new ArrayList<>(Collections.nCopies(jep.handleType().parameterCount(), null));

substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_abi_NativeEntryPoint.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -58,7 +58,8 @@ public static Target_jdk_internal_foreign_abi_NativeEntryPoint make(ABIDescripto
5858
MethodType methodType,
5959
boolean needsReturnBuffer,
6060
int capturedStateMask,
61-
boolean needsTransition) {
61+
boolean needsTransition,
62+
boolean usingAddressPairs) {
6263
/*
6364
* A VMStorage may be null only when the Linker.Option.critical(allowHeapAccess=true) option
6465
* is passed. (see

0 commit comments

Comments
 (0)