Skip to content

Make CoreCLR + crossgen2 ioslike via --dynamiccodecompiled false#124168

Merged
BrzVlad merged 5 commits intodotnet:mainfrom
BrzVlad:feature-clr-ioslike2
Feb 12, 2026
Merged

Make CoreCLR + crossgen2 ioslike via --dynamiccodecompiled false#124168
BrzVlad merged 5 commits intodotnet:mainfrom
BrzVlad:feature-clr-ioslike2

Conversation

@BrzVlad
Copy link
Member

@BrzVlad BrzVlad commented Feb 9, 2026

On desktop, for example, this enables building the runtime with ./build.sh -subset clr -c Release -dynamiccodecompiled false.

This configures the runtime to behave very similar to what would run on iOS, where we are not allowed to generate dynamic executable code. This configuration mainly disables jit, enables interpreter, enables cached interface dispatch, sets IsDynamicCodeCompiled to false, enables cached interface dispatch support in crossgen2 compiled images.

This option is meant to be used for convenience, when doing perf testing or bug investigation, without having to build/deploy for actual devices.

Copilot AI review requested due to automatic review settings February 9, 2026 08:32
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 9, 2026
@BrzVlad BrzVlad added area-CodeGen-Interpreter-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 9, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @BrzVlad, @janvorli, @kg
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new build-time switch (--clrnodynamiccodegen) to configure CoreCLR + crossgen2 into an “iOS-like” mode where dynamic executable code generation is disabled (JIT off, interpreter on), primarily for desktop perf/debug investigations without deploying to devices.

Changes:

  • Introduces FeatureNoDynamicCodeGen / FEATURE_NO_DYNAMIC_CODEGEN plumbing across MSBuild, CMake, CoreCLR, and crossgen2.
  • Updates runtime feature flags/config defaults and executable memory allocation behaviors to match no-dynamic-codegen constraints.
  • Adds CLI surface area in eng/build.sh and eng/build.ps1 to enable the configuration.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.NonNativeAot.cs Makes RuntimeFeature.IsDynamicCodeCompiled return false under FEATURE_NO_DYNAMIC_CODEGEN.
src/coreclr/vm/peimagelayout.cpp Adjusts use of MEM_RESERVE_EXECUTABLE for image loading allocations under the new feature.
src/coreclr/utilcode/executableallocator.cpp Alters executable allocator commit/reserve behavior to avoid executable allocations under the new feature.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj Defines FEATURE_NO_DYNAMIC_CODEGEN constant for crossgen2 build when enabled.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs Forces TargetAllowsRuntimeCodeGeneration to false under the new feature.
src/coreclr/runtime.proj Plumbs FeatureNoDynamicCodeGen to native build via -DFEATURE_NO_DYNAMIC_CODEGEN=1.
src/coreclr/inc/clrconfigvalues.h Aligns config defaults (e.g., cached interface dispatch, write barrier copy) with no-dynamic-codegen behavior.
src/coreclr/clrfeatures.cmake Disables JIT-related features and enables embedded-style behaviors when FEATURE_NO_DYNAMIC_CODEGEN is set.
src/coreclr/clrdefinitions.cmake Adds compile definition emission for FEATURE_NO_DYNAMIC_CODEGEN.
src/coreclr/clr.featuredefines.props Adds managed FEATURE_NO_DYNAMIC_CODEGEN define constant when FeatureNoDynamicCodeGen=true.
eng/build.sh Adds --clrnodynamiccodegen switch to set FeatureInterpreter=true and FeatureNoDynamicCodeGen=true.
eng/build.ps1 Adds -clrnodynamiccodegen switch equivalent for Windows builds.
Comments suppressed due to low confidence (1)

src/coreclr/vm/peimagelayout.cpp:841

  • On Unix (including macOS), LoadImageByCopyingParts always allocates RW memory and later flips executable sections to PAGE_EXECUTE_*. The comment states MEM_RESERVE_EXECUTABLE is required on macOS for this pattern; however, the new !defined(FEATURE_NO_DYNAMIC_CODEGEN) guard disables MEM_RESERVE_EXECUTABLE when the new flag is used. This can cause ClrVirtualProtect to fail when marking R2R sections executable on macOS, making the new --clrnodynamiccodegen mode unusable there. Consider leaving MEM_RESERVE_EXECUTABLE enabled on macOS (or reworking the copy/protect flow to stay compatible with hardened runtime/W^X constraints).
#if defined(HOST_UNIX) && !defined(TARGET_IOS) && !defined(TARGET_TVOS) && !defined(TARGET_MACCATALYST) && !defined(FEATURE_NO_DYNAMIC_CODEGEN)
    // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory.
    // This is required on MacOS and otherwise will allow us to place native R2R code close to the
    // coreclr library and thus improve performance by avoiding jump stubs in managed code.
    allocationType |= MEM_RESERVE_EXECUTABLE;

@stephentoub
Copy link
Member

🤖 Copilot Code Review — PR #124168

Holistic Assessment

Motivation: This PR enables building CoreCLR with iOS-like configuration on desktop platforms for testing and development purposes. This is a valid and useful capability for testing interpreter/no-JIT scenarios without needing actual iOS hardware or simulator.

Approach: Using a compile-time FEATURE_NO_DYNAMIC_CODEGEN define that integrates into the existing feature flag system is consistent with how iOS is handled today.

Net positive/negative: Net positive - adds testing capability without affecting default builds.


Detailed Findings

⚠️ Consistency Issue — Program.cs optimistic instruction sets not updated

In crossgen2/Program.cs lines 88-96, there's a direct TargetOS check that disables optimistic instruction sets for iOS platforms:

bool allowOptimistic = _command.OptimizationMode != OptimizationMode.PreferSize;

if (targetOS is TargetOS.iOS or TargetOS.tvOS or TargetOS.iOSSimulator or TargetOS.tvOSSimulator or TargetOS.MacCatalyst)
{
    // These platforms do not support jitted code, so we want to ensure that we don't
    // need to fall back to the interpreter for any hardware-intrinsic optimizations.
    // Disable optimistic instruction sets by default.
    allowOptimistic = false;
}

This check does not account for FEATURE_NO_DYNAMIC_CODEGEN. When building with --clrnodynamiccodegen, optimistic instruction sets would still be allowed for non-iOS target platforms. This means the simulated "iOS-like" configuration would behave differently from actual iOS in instruction set selection. For complete iOS simulation, consider adding:

#if FEATURE_NO_DYNAMIC_CODEGEN
bool allowOptimistic = false;
#else
bool allowOptimistic = _command.OptimizationMode != OptimizationMode.PreferSize;
if (targetOS is TargetOS.iOS or TargetOS.tvOS ...)
    allowOptimistic = false;
#endif

However, this may be intentional if the goal is to test "no dynamic codegen" with normal instruction sets for desktop.


💡 Suggestion — Consider runtime flag alternative for crossgen2

The #ifdef FEATURE_NO_DYNAMIC_CODEGEN approach in ReadyToRunCompilerContext.cs means crossgen2 must be rebuilt to test this scenario. An alternative design would be a runtime flag (e.g., --no-dynamic-codegen) that sets TargetAllowsRuntimeCodeGeneration = false regardless of target OS. This would allow testing without rebuilding crossgen2. However, the compile-time approach is consistent with how iOS is handled today, so this may be intentional for full iOS simulation. (Flagged by multiple models)


✅ Good — clrfeatures.cmake integration

The cmake changes correctly mirror all iOS-like conditions:

  • Disabling FEATURE_TIERED_COMPILATION, FEATURE_REJIT, FEATURE_JIT
  • Enabling FEATURE_STATICALLY_LINKED, FEATURE_STUBPRECODE_DYNAMIC_HELPERS, FEATURE_CORECLR_CACHED_INTERFACE_DISPATCH
  • Default value initialization for FEATURE_NO_DYNAMIC_CODEGEN added

✅ Good — clrconfigvalues.h defaults

The existing #if defined(TARGET_IOS) || defined(TARGET_TVOS) || defined(TARGET_MACCATALYST) guards for UseGCWriteBarrierCopy and UseCachedInterfaceDispatch are correctly extended to include FEATURE_NO_DYNAMIC_CODEGEN.


✅ Good — executableallocator.cpp and peimagelayout.cpp

Memory allocation behavior changes (using PAGE_READWRITE instead of PAGE_EXECUTE_READWRITE, skipping MEM_RESERVE_EXECUTABLE) are correctly updated to include FEATURE_NO_DYNAMIC_CODEGEN. This ensures no executable memory is allocated when building with this flag.


✅ Good — RuntimeFeature.NonNativeAot.cs

The IsDynamicCodeCompiled property correctly returns false when FEATURE_NO_DYNAMIC_CODEGEN is defined, matching iOS behavior.


✅ Good — AllowR2RForImage not modified (intentional)

The AllowR2RForImage function in peimagelayout.cpp returns false for iOS platforms to disable R2R loading on those platforms at runtime. This was correctly not modified in this PR, since the goal of --clrnodynamiccodegen is to test iOS-like code generation behavior on desktop, but still allow loading of crossgen2-generated R2R images. The actual iOS check prevents R2R because iOS binaries truly cannot contain/execute native code, but on desktop with this flag we want the pre-compiled R2R code to work.


Summary

LGTM with minor consideration. The PR is well-designed and correctly implements iOS-like configuration for desktop testing. The one potential consistency gap in crossgen2/Program.cs around optimistic instruction sets may be intentional depending on the testing goals. The compile-time approach in crossgen2 is noted by multiple reviewers but is consistent with how iOS is currently handled.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 5 comments.

@jkotas jkotas changed the title Make CoreCLR + crossgen2 ioslike via --clrnodynamiccodegen Make CoreCLR + crossgen2 ioslike via --dynamiccodecompiled false Feb 10, 2026
…ED being defined

FEATURE_JIT uses are removed and FEATURE_DYNAMIC_CODE_COMPILED is used instead
FEATURE_DYNAMIC_CODE_COMPILED will also set knobs that we expect to use on all platforms where jit-ing is not allowed.
If `--dynamiccodecompiled` value is not passed to the build, then it will be set to the default value based on the target. The build will always end up receiving the final property value in `FeatureDynamicCodeCompiled`.
Copilot AI review requested due to automatic review settings February 11, 2026 08:25
@BrzVlad BrzVlad force-pushed the feature-clr-ioslike2 branch from 150a3e9 to 557ba32 Compare February 11, 2026 08:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 2 comments.

Copilot AI review requested due to automatic review settings February 11, 2026 13:13
@BrzVlad BrzVlad force-pushed the feature-clr-ioslike2 branch from ad0b951 to b7b5d6a Compare February 11, 2026 13:13
iOS build will use crossgen2 built on desktop, which will always have `FEATURE_DYNAMIC_CODE_COMPILED` defined. We still need to resolve this property by checking the actual target platform. If we build the runtime without `FEATURE_DYNAMIC_CODE_COMPILED` then we can hardcode the behavior in crossgen2, as always false.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs:113

  • There's an undefined variable arch and an incorrect enum value. The code should use Target.Architecture instead of arch, and TargetArchitecture.Wasm32 instead of TargetArchitecture.Wasm (since only Wasm32 exists in the TargetArchitecture enum).
                if (Target.Architecture is TargetArchitecture.Wasm32)

@BrzVlad BrzVlad merged commit f7ccc3c into dotnet:main Feb 12, 2026
146 of 158 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants