11/*
2- * Copyright (c) 2013, 2024 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2013, 2025 , 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
4444import jdk .graal .compiler .asm .aarch64 .AArch64MacroAssembler .ScratchRegister ;
4545import jdk .graal .compiler .code .CompilationResult .JumpTable ;
4646import jdk .graal .compiler .code .CompilationResult .JumpTable .EntryFormat ;
47+ import jdk .graal .compiler .core .aarch64 .AArch64LIRGenerator ;
4748import jdk .graal .compiler .core .common .NumUtil ;
4849import jdk .graal .compiler .core .common .calc .Condition ;
4950import jdk .graal .compiler .debug .Assertions ;
@@ -385,7 +386,8 @@ private static void emitCompareHelper(CompilationResultBuilder crb, AArch64Macro
385386
386387 /**
387388 * This operation jumps to the appropriate destination as specified within a JumpTable, or to
388- * the default condition if there is no match within the JumpTable.
389+ * another destination according to {@code remainingStrategy} if there is no match within the
390+ * JumpTable.
389391 *
390392 * <p>
391393 * The JumpTable contains a series of target offsets, relative to the start of the jump table,
@@ -395,8 +397,12 @@ private static void emitCompareHelper(CompilationResultBuilder crb, AArch64Macro
395397 * <ol>
396398 * <li>Determine whether the index is within the JumpTable. This is accomplished by first
397399 * normalizing the index (normalizedIdx == index - lowKey), and then checking whether
398- * <code>(unsigned(normalizedIdx) <= highKey - lowKey</code>). If not, then one must jump to
399- * the defaultTarget.</li>
400+ * <code>(unsigned(normalizedIdx) <= highKey - lowKey</code>).
401+ *
402+ * <li>If normalizedIdx is not in the JumpTable, the destination is decided by {@code
403+ * remainingStrategy}. If {@code remainingStrategy == null}, then the destination is {@code
404+ * defaultTarget}. Otherwise, the destination is {@code defaultTarget} or one of {@code
405+ * remainingTargets} based on the value of {@code key}.</li>
400406 *
401407 * <li>If normalizedIdx is within the JumpTable, then jump to JumpTableStart +
402408 * JumpTable[normalizedIdx].</li>
@@ -407,53 +413,80 @@ public static final class RangeTableSwitchOp extends AArch64BlockEndOp {
407413 private final int lowKey ;
408414 private final LabelRef defaultTarget ;
409415 private final LabelRef [] targets ;
410- @ Use ({REG }) protected AllocatableValue index ;
416+ private final SwitchStrategy remainingStrategy ;
417+ private final LabelRef [] remainingTargets ;
418+ @ Alive ({REG }) protected AllocatableValue key ;
411419
412- public RangeTableSwitchOp (final int lowKey , final LabelRef defaultTarget , final LabelRef [] targets , AllocatableValue index ) {
420+ public RangeTableSwitchOp (int lowKey , LabelRef defaultTarget , LabelRef [] targets , SwitchStrategy remainingStrategy , LabelRef [] remainingTargets , AllocatableValue key ) {
413421 super (TYPE );
414422 this .lowKey = lowKey ;
415423 assert defaultTarget != null ;
416424 this .defaultTarget = defaultTarget ;
417425 this .targets = targets ;
418- this .index = index ;
426+ this .remainingStrategy = remainingStrategy ;
427+ this .remainingTargets = remainingTargets ;
428+ this .key = key ;
419429 }
420430
421431 @ Override
422432 public void emitCode (CompilationResultBuilder crb , AArch64MacroAssembler masm ) {
423433 try (ScratchRegister sc1 = masm .getScratchRegister (); ScratchRegister sc2 = masm .getScratchRegister ()) {
434+ Register keyReg = asRegister (key );
424435 Register scratch1 = sc1 .getRegister ();
425436 Register scratch2 = sc2 .getRegister ();
437+ GraalError .guarantee (!keyReg .equals (scratch1 ) && !keyReg .equals (scratch2 ), "must not alias" );
426438 /* Compare index against jump table bounds */
427439 int highKey = lowKey + targets .length - 1 ;
428- masm .sub (32 , scratch2 , asRegister (index ), lowKey );
429- int keyDiff = highKey - lowKey ; // equivalent to targets.length - 1
430- if (AArch64MacroAssembler .isComparisonImmediate (keyDiff )) {
431- masm .compare (32 , scratch2 , keyDiff );
440+ Register keyOffsetReg = keyReg ;
441+ if (lowKey != 0 ) {
442+ masm .sub (32 , scratch2 , keyReg , lowKey );
443+ keyOffsetReg = scratch2 ;
444+ }
445+
446+ int interval = highKey - lowKey ;
447+ if (AArch64MacroAssembler .isComparisonImmediate (interval )) {
448+ masm .compare (32 , keyOffsetReg , interval );
432449 } else {
433- masm .mov (scratch1 , keyDiff );
434- masm .cmp (32 , scratch2 , scratch1 );
450+ masm .mov (scratch1 , interval );
451+ masm .cmp (32 , keyOffsetReg , scratch1 );
435452 }
436453
437- // Jump to default target if index is not within the jump table
438- masm .branchConditionally (ConditionFlag .HI , defaultTarget .label ());
454+ Label outOfRangeLabel = defaultTarget .label ();
455+ if (remainingStrategy != null ) {
456+ Label remainingLabel = new Label ();
457+ outOfRangeLabel = remainingLabel ;
458+
459+ crb .getLIR ().addSlowPath (this , () -> {
460+ masm .bind (remainingLabel );
461+ new StrategySwitchOp (remainingStrategy , remainingTargets , defaultTarget , key , AArch64LIRGenerator ::toIntConditionFlag ).emitCode (crb , masm );
462+ });
463+ }
464+ // Jump to outOfRangeLabel if index is not within the jump table
465+ masm .branchConditionally (ConditionFlag .HI , outOfRangeLabel );
439466
440- emitJumpTable (crb , masm , scratch1 , scratch2 , lowKey , highKey , Arrays .stream (targets ).map (LabelRef ::label ));
467+ emitJumpTable (crb , masm , keyOffsetReg , scratch1 , scratch2 , lowKey , highKey , Arrays .stream (targets ).map (LabelRef ::label ));
441468 }
442469 }
443470
444- public static void emitJumpTable (CompilationResultBuilder crb , AArch64MacroAssembler masm , Register scratch , Register index , int lowKey , int highKey , Stream <Label > targets ) {
471+ public static void emitJumpTable (CompilationResultBuilder crb , AArch64MacroAssembler masm , Register scratch , Register keyScratch , int lowKey , int highKey , Stream <Label > targets ) {
472+ emitJumpTable (crb , masm , keyScratch , scratch , keyScratch , lowKey , highKey , targets );
473+ }
474+
475+ private static void emitJumpTable (CompilationResultBuilder crb , AArch64MacroAssembler masm , Register key , Register scratch , Register idxScratch , int lowKey , int highKey ,
476+ Stream <Label > targets ) {
477+ GraalError .guarantee (!key .equals (scratch ), "must not alias" );
445478 Label jumpTable = new Label ();
446479 // load start of jump table
447480 masm .adr (scratch , jumpTable );
448481 /*
449482 * Note scratch holds the start of the jump table and index stores the normalized index.
450483 * Because each jumpTable index is 4 bytes large, index should be scaled.
451484 */
452- AArch64Address jumpTableEntryAddr = AArch64Address .createExtendedRegisterOffsetAddress (32 , scratch , index , true , ExtendType .UXTW );
485+ AArch64Address jumpTableEntryAddr = AArch64Address .createExtendedRegisterOffsetAddress (32 , scratch , key , true , ExtendType .UXTW );
453486 // load relative target offset
454- masm .ldrs (64 , 32 , index , jumpTableEntryAddr );
487+ masm .ldrs (64 , 32 , idxScratch , jumpTableEntryAddr );
455488 // compute target address (jumpTableStart + target offset)
456- masm .add (64 , scratch , scratch , index );
489+ masm .add (64 , scratch , scratch , idxScratch );
457490 // jump to target
458491 masm .jmp (scratch );
459492
0 commit comments