50
50
51
51
import com .oracle .svm .core .SubstrateTargetDescription ;
52
52
import com .oracle .svm .core .config .ConfigurationValues ;
53
+ import com .oracle .svm .core .foreign .AbiUtils .Adapter .Adaptation ;
53
54
import com .oracle .svm .core .graal .code .AssignedLocation ;
54
55
import com .oracle .svm .core .headers .LibC ;
55
56
import com .oracle .svm .core .headers .WindowsAPIs ;
56
57
import com .oracle .svm .core .util .BasedOnJDKClass ;
58
+ import com .oracle .svm .core .util .BasedOnJDKFile ;
57
59
import com .oracle .svm .core .util .VMError ;
58
60
59
61
import jdk .graal .compiler .api .replacements .Fold ;
@@ -230,11 +232,15 @@ public static Adaptation check(Class<?> type) {
230
232
}
231
233
232
234
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 );
234
240
}
235
241
236
242
public static Adaptation drop () {
237
- return Extract . DROP ;
243
+ return Drop . SINGLETON ;
238
244
}
239
245
240
246
public static Adaptation reinterpret (JavaKind to ) {
@@ -342,41 +348,77 @@ public List<AssignedLocation> apply(AssignedLocation parameter) {
342
348
@ Override
343
349
public List <ValueNode > apply (ValueNode parameter , Map <Extracted , ValueNode > extractedArguments , List <ValueNode > originalArguments , int originalArgumentIndex ,
344
350
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 ) {
345
355
var offsetArg = originalArguments .get (originalArgumentIndex + 1 );
346
356
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
+ */
351
362
var basePointer = WordCastNode .objectToUntrackedPointer (parameter , ConfigurationValues .getWordKind ());
352
363
appendToGraph .accept (basePointer );
353
364
var absolutePointer = AddNode .add (basePointer , offsetArg );
354
365
appendToGraph .accept (absolutePointer );
355
366
356
- return List . of ( absolutePointer ) ;
367
+ return absolutePointer ;
357
368
}
358
369
}
359
370
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 ;
364
379
365
- private Extract (Extracted as , Class <?> type ) {
380
+ private Extract (Extracted as ) {
366
381
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 );
368
395
}
369
396
370
397
@ Override
371
398
public List <Class <?>> apply (Class <?> parameter ) {
372
- if (type != null && parameter != type ) {
373
- throw new IllegalArgumentException ("Expected type " + type + ", got " + parameter );
374
- }
375
399
return List .of ();
376
400
}
377
401
378
402
@ 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
+ }
380
422
return List .of ();
381
423
}
382
424
@@ -392,6 +434,37 @@ public List<ValueNode> apply(ValueNode parameter, Map<Extracted, ValueNode> extr
392
434
return List .of ();
393
435
}
394
436
}
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
+ }
395
468
}
396
469
397
470
@ Platforms (Platform .HOSTED_ONLY .class )
@@ -438,6 +511,7 @@ public final Adapter.Result.TypeAdaptation adapt(JavaEntryPointInfo jep) {
438
511
/**
439
512
* Generate additional argument adaptations which are not done by HotSpot.
440
513
*/
514
+ @ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-24+27/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java#L103-L147" )
441
515
@ Platforms (Platform .HOSTED_ONLY .class )
442
516
protected List <Adapter .Adaptation > generateAdaptations (NativeEntryPointInfo nep ) {
443
517
List <Adapter .Adaptation > adaptations = new ArrayList <>(Collections .nCopies (nep .methodType ().parameterCount (), null ));
@@ -446,36 +520,55 @@ protected List<Adapter.Adaptation> generateAdaptations(NativeEntryPointInfo nep)
446
520
adaptations .set (current ++, Adapter .check (long .class ));
447
521
}
448
522
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
- }
452
523
453
524
// 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
455
526
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
+
456
544
for (int i = current ; i < storages .length ; ++i ) {
457
545
var storage = storages [i ];
458
546
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 ());
473
548
}
474
549
}
475
550
476
551
return adaptations ;
477
552
}
478
553
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
+
479
572
@ Platforms (Platform .HOSTED_ONLY .class )
480
573
protected List <Adapter .Adaptation > generateAdaptations (JavaEntryPointInfo jep ) {
481
574
List <Adapter .Adaptation > adaptations = new ArrayList <>(Collections .nCopies (jep .handleType ().parameterCount (), null ));
0 commit comments