@@ -307,6 +307,60 @@ def _normalize_branch_name(branch):
307307 return branch
308308
309309
310+ def _graalpython_commit_is_ancestor_of_branch (commit , branch ):
311+ """Return True if the GraalPy commit is already reachable from the branch."""
312+ # Check the remote-tracking branch, not the local branch or HEAD. Regular branch-run
313+ # workspaces may put a synthetic merge commit on a local branch, while origin/<branch>
314+ # still represents the target branch state used to decide whether the GraalPy commit has
315+ # really landed.
316+ return _graalpython_merge_base_with_branch (commit , branch ) == commit
317+
318+
319+ def _graalpython_merge_base_with_branch (commit , branch ):
320+ if not commit or not branch or not SUITE .vc :
321+ return None
322+ merge_base = SUITE .vc .git_command (
323+ SUITE .dir ,
324+ ["merge-base" , commit , f"origin/{ branch } " ],
325+ abortOnError = False ,
326+ )
327+ return merge_base .strip () if merge_base else None
328+
329+
330+ def _graalpython_target_branch_profile_candidates (commit , branch , max_age_days ):
331+ """Return target-branch ancestor commits that may have CI-generated PGO profiles."""
332+ merge_base = _graalpython_merge_base_with_branch (commit , branch )
333+ if not merge_base :
334+ return []
335+
336+ commit_ts = SUITE .vc .git_command (
337+ SUITE .dir ,
338+ ["show" , "-s" , "--format=%ct" , merge_base ],
339+ abortOnError = False ,
340+ )
341+ if not commit_ts :
342+ return []
343+ try :
344+ since_ts = int (commit_ts .strip ()) - max_age_days * 24 * 60 * 60
345+ except ValueError :
346+ mx .warn (f"Cannot parse commit timestamp for GraalPy PGO profile search: { commit_ts !r} " )
347+ return []
348+
349+ candidates = SUITE .vc .git_command (
350+ SUITE .dir ,
351+ ["rev-list" , "--first-parent" , f"--since=@{ since_ts } " , merge_base ],
352+ abortOnError = False ,
353+ )
354+ return candidates .splitlines () if candidates else []
355+
356+
357+ def _graalpython_pgo_profile_exists (path ):
358+ try :
359+ return os .path .isfile (path or "" ) and os .path .getsize (path ) > 0
360+ except OSError :
361+ return False
362+
363+
310364def _graalpython_target_import_commit (target_branch ):
311365 """Return the GraalPy commit imported by the target branch's VM suite.
312366
@@ -459,55 +513,70 @@ def libpythonvm_build_args():
459513 profile_source_commit = source_commit
460514 profile_source_branch = source_branch
461515 profile_source_reason = "current GraalPy commit"
516+ profile_source_candidates = [(profile_source_commit , profile_source_branch , profile_source_reason )]
462517 override = os .environ .get ("GRAALPY_PGO_PROFILE_SOURCE_COMMIT" )
463518 if override :
464519 profile_source_commit = override .strip ()
465520 if not re .match (r"^[0-9a-f]{40}$" , profile_source_commit ):
466521 mx .abort (f"GRAALPY_PGO_PROFILE_SOURCE_COMMIT must be a 40-character lowercase git commit, got: { override } " )
467522 profile_source_reason = "GRAALPY_PGO_PROFILE_SOURCE_COMMIT"
523+ profile_source_candidates = [(profile_source_commit , profile_source_branch , profile_source_reason )]
468524 elif (
469525 target_branch
470526 and source_branch
471527 and source_branch != target_branch
472528 and not (source_branch == MAIN_BRANCH or source_branch .startswith (("release/" , "cpu/" )))
529+ and not _graalpython_commit_is_ancestor_of_branch (source_commit , target_branch )
473530 ):
474- # Feature branch merge commits can import a fresh GraalPy revision that
475- # has no released-product profile yet. Use the target branch's imported
476- # GraalPy commit so product-ee consumes the profile that already belongs
477- # to the baseline product launcher.
531+ # Feature branch commits usually have no released-product profile yet,
532+ # but use one if it was explicitly generated. Otherwise prefer the
533+ # nearest available target-branch ancestor profile, then fall back to
534+ # the VM suite's target import for compatibility with older CI workspaces.
535+ profile_search_days = 14
536+ target_candidates = _graalpython_target_branch_profile_candidates (
537+ source_commit , target_branch , profile_search_days
538+ )
478539 target_import_commit = _graalpython_target_import_commit (target_branch )
479- if target_import_commit :
480- profile_source_commit = target_import_commit
481- profile_source_branch = target_branch
482- profile_source_reason = f"target branch import from { target_branch } "
483- elif require_profile :
484- mx .abort ("\n " .join ([
485- "Could not resolve the GraalPy PGO profile source commit from the target branch import." ,
486- f"GraalPy source commit: { source_commit } " ,
487- f"Source branch: { source_branch or '<unknown>' } " ,
488- f"Target branch: { target_branch or '<unknown>' } " ,
489- "Expected to read vm/mx.vm/suite.py from the target branch and find the graalpython import version." ,
490- "Set GRAALPY_PGO_PROFILE_SOURCE_COMMIT=<40-character-commit> to override this lookup for debugging." ,
491- ]))
492- else :
493- mx .warn ("Falling back to the current GraalPy commit for PGO profile lookup because the target branch import could not be resolved" )
540+ profile_source_candidates = [
541+ (source_commit , source_branch , "current GraalPy branch commit" ),
542+ ] + [
543+ (commit , target_branch , f"target branch ancestor from { target_branch } " )
544+ for commit in target_candidates
545+ ]
546+ if (
547+ target_import_commit
548+ and target_import_commit not in {commit for commit , _ , _ in profile_source_candidates }
549+ ):
550+ profile_source_candidates .append (
551+ (target_import_commit , target_branch , f"target branch import from { target_branch } " )
552+ )
494553 artifact_name = f"{ GRAALPY_PGO_PROFILE_ARTIFACT_GROUP } /{ GRAALPY_PGO_PROFILE_ARTIFACT_PREFIX } { profile_source_commit } "
495554 mx .log (f"GraalPy source commit for PGO profile lookup: { source_commit } " )
496- mx .log (f"GraalPy PGO profile source commit: { profile_source_commit } ({ profile_source_reason } )" )
497555
498556 if script := os .environ .get ("ARTIFACT_DOWNLOAD_SCRIPT" ):
499557 # This is always available in the GraalPy CI
500558 profile = f"cached_profile.iprof.gz"
501- run (
502- [
503- sys .executable ,
504- script ,
505- artifact_name ,
506- profile ,
507- ],
508- nonZeroIsFatal = False ,
509- )
559+ for candidate_commit , candidate_branch , candidate_reason in profile_source_candidates :
560+ profile_source_commit = candidate_commit
561+ profile_source_branch = candidate_branch
562+ profile_source_reason = candidate_reason
563+ artifact_name = f"{ GRAALPY_PGO_PROFILE_ARTIFACT_GROUP } /{ GRAALPY_PGO_PROFILE_ARTIFACT_PREFIX } { profile_source_commit } "
564+ mx .log (f"GraalPy PGO profile source commit: { profile_source_commit } ({ profile_source_reason } )" )
565+ if os .path .exists (profile ):
566+ os .remove (profile )
567+ run (
568+ [
569+ sys .executable ,
570+ script ,
571+ artifact_name ,
572+ profile ,
573+ ],
574+ nonZeroIsFatal = False ,
575+ )
576+ if _graalpython_pgo_profile_exists (profile ):
577+ break
510578 elif not require_profile :
579+ mx .log (f"GraalPy PGO profile source commit: { profile_source_commit } ({ profile_source_reason } )" )
511580 # Locally, we try to get a reasonable profile
512581 get_profile = mx .command_function ('python-get-latest-profile' , fatalIfMissing = False )
513582 if get_profile :
@@ -523,7 +592,7 @@ def libpythonvm_build_args():
523592 except BaseException :
524593 pass
525594
526- profile_missing = not profile or not os . path . isfile (profile )
595+ profile_missing = not _graalpython_pgo_profile_exists (profile )
527596 if require_profile and profile_missing :
528597 mx .abort ("\n " .join ([
529598 "GRAALPY_REQUIRE_PGO_PROFILE is set, but no CI generated GraalPy PGO profile was found." ,
@@ -551,7 +620,7 @@ def libpythonvm_build_args():
551620 mx .warn ("PGO profile must exist for benchmarking and release, creating one now..." )
552621 profile = graalpy_native_pgo_build_and_test ()
553622
554- if os . path . isfile (profile or "" ):
623+ if _graalpython_pgo_profile_exists (profile ):
555624 print (invert (f"Automatically chose PGO profile { profile } . To disable this, set GRAALPY_PGO_PROFILE to an empty string'" , blinking = True ), file = sys .stderr )
556625 build_args += [
557626 f"--pgo={ profile } " ,
0 commit comments