Skip to content

Compile lifted user-defined AndAlso and OrElse operators - #597

Merged
dadhi merged 2 commits into
dadhi:masterfrom
jogibear9988:fix/lifted-user-logical-operators
Nov 9, 2026
Merged

dadhi merged 2 commits into
dadhi:masterfrom
jogibear9988:fix/lifted-user-logical-operators

Conversation

@jogibear9988

@jogibear9988 jogibear9988 commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Compile valid AndAlso/OrElse expressions over nullable structs with user-defined logical operators, which currently fail strict FEC compilation.

Use one TryEmitUserDefinedLogicalOperator helper for both lifted and non-lifted custom operators. Share left operand storage, op_False/op_True testing, right operand evaluation on an empty stack, the selected bitwise method call, short-circuit branches, and result merging. Keep nullable checks, unwrapping, wrapping, and null-result labels conditional on lifting. Existing method lookup and operand contexts are preserved for each path; ordinary Boolean emission remains separate.

Strict Standard/LightExpression tests cover:

  • Lifted values and nulls, short-circuit traces, explicit methods, ignored results, right-side finally cleanup, and direct factories, compared with System.Linq.Expressions.
  • Non-lifted reference identity and operator call counts, struct operators, explicit methods, ignored-result effects, and right operands containing try/catch.

Validation

Current master base: 76f2b28f9363d3364be75dc9830c7e303aea8b3a. Release solution build: zero warnings/errors. Both APIs have strict regression coverage with fallback disabled.

Runtime TestX passed Legacy passed
.NET 10 355 1756
.NET 8 355 1755
.NET Framework 4.7.2 336 1639

Shared-helper measurements

Compare separate emitters (1bac8a3b) with the shared helper (394f8e85), both built on the master base above. Both configurations support the measured lifted expressions; these are direct before/after measurements of helper unification. Master itself does not successfully compile those lifted trees.

Windows ARM64, .NET 8.0.28 and 10.0.9, Release, tiering disabled. Prebuilt trees, Light conversion outside timing, isolated load contexts, one CPU, nine alternating samples per phase, and two fresh process rounds in opposite load order with approximately 100 ms pair calibration. Compilation and invocation are measured separately. Timings combine the 18 samples; allocation counts use thread allocation counters.

Every measured case retains identical emitted IL and unchanged compilation allocations. All measured invocations allocate zero. The measurements cover non-lifted struct/reference operators, lifted value/null paths, AndAlso/OrElse and a Boolean control. CPU timings vary between rounds; slower observations are retained below, and no exact timing-neutrality or speedup claim is made.

All measurements: separate emitters → shared helper
Runtime / API / workload Compile ns Compile B Invoke ns Invoke B Same IL
net10.0 / Standard / boolean_and 4474.4 → 4307.1 1064 → 1064 1.9 → 1.9 0 → 0 yes
net10.0 / Standard / custom_and 4766.8 → 4824.1 1384 → 1384 2.1 → 2.1 0 → 0 yes
net10.0 / Standard / lifted_and_values 9331.8 → 9211.6 2512 → 2512 2.8 → 3.0 0 → 0 yes
net10.0 / Standard / lifted_and_null 14881.0 → 15103.6 2512 → 2512 2.5 → 2.5 0 → 0 yes
net10.0 / Standard / custom_or 5899.1 → 5395.2 1384 → 1384 1.9 → 1.9 0 → 0 yes
net10.0 / Standard / reference_and 4802.6 → 4950.0 1384 → 1384 1.9 → 1.9 0 → 0 yes
net10.0 / Standard / lifted_and_right_null 9316.6 → 9008.9 2512 → 2512 2.2 → 2.2 0 → 0 yes
net10.0 / Standard / lifted_or_values 9457.1 → 10015.2 2512 → 2512 2.3 → 2.3 0 → 0 yes
net10.0 / Light / boolean_and 2597.7 → 2670.8 1064 → 1064 1.9 → 1.9 0 → 0 yes
net10.0 / Light / custom_and 3790.2 → 3859.3 1384 → 1384 2.0 → 2.0 0 → 0 yes
net10.0 / Light / lifted_and_values 8064.9 → 8171.1 2512 → 2512 2.3 → 2.3 0 → 0 yes
net10.0 / Light / lifted_and_null 8219.4 → 8185.0 2512 → 2512 2.2 → 2.2 0 → 0 yes
net10.0 / Light / custom_or 3878.9 → 3947.6 1384 → 1384 1.9 → 1.9 0 → 0 yes
net10.0 / Light / reference_and 3922.5 → 3815.2 1384 → 1384 1.9 → 1.9 0 → 0 yes
net10.0 / Light / lifted_and_right_null 8101.5 → 8063.3 2512 → 2512 2.2 → 2.2 0 → 0 yes
net10.0 / Light / lifted_or_values 9446.6 → 9386.8 2512 → 2512 2.3 → 2.3 0 → 0 yes
net8.0 / Standard / boolean_and 1640.6 → 1652.2 1064 → 1064 2.3 → 2.3 0 → 0 yes
net8.0 / Standard / custom_and 1901.2 → 2047.0 1384 → 1384 2.2 → 2.1 0 → 0 yes
net8.0 / Standard / lifted_and_values 3760.6 → 3908.4 2512 → 2512 2.6 → 2.6 0 → 0 yes
net8.0 / Standard / lifted_and_null 3907.3 → 3754.6 2512 → 2512 2.4 → 2.5 0 → 0 yes
net8.0 / Standard / custom_or 1951.7 → 2028.0 1384 → 1384 2.2 → 2.2 0 → 0 yes
net8.0 / Standard / reference_and 1851.4 → 1804.1 1384 → 1384 1.9 → 1.9 0 → 0 yes
net8.0 / Standard / lifted_and_right_null 3806.3 → 3789.5 2512 → 2512 2.2 → 2.3 0 → 0 yes
net8.0 / Standard / lifted_or_values 3721.4 → 3704.9 2512 → 2512 2.5 → 2.5 0 → 0 yes
net8.0 / Light / boolean_and 1134.8 → 1135.0 1064 → 1064 1.9 → 1.9 0 → 0 yes
net8.0 / Light / custom_and 1645.5 → 1671.8 1384 → 1384 2.1 → 2.2 0 → 0 yes
net8.0 / Light / lifted_and_values 3685.0 → 3493.3 2512 → 2512 2.5 → 2.5 0 → 0 yes
net8.0 / Light / lifted_and_null 3406.5 → 3676.2 2512 → 2512 2.5 → 2.5 0 → 0 yes
net8.0 / Light / custom_or 1718.9 → 1723.4 1384 → 1384 2.1 → 2.1 0 → 0 yes
net8.0 / Light / reference_and 1661.9 → 1665.9 1384 → 1384 1.9 → 1.9 0 → 0 yes
net8.0 / Light / lifted_and_right_null 3409.9 → 3438.3 2512 → 2512 2.3 → 2.2 0 → 0 yes
net8.0 / Light / lifted_or_values 3556.4 → 3630.3 2512 → 2512 2.8 → 2.9 0 → 0 yes

@jogibear9988
jogibear9988 force-pushed the fix/lifted-user-logical-operators branch from e40fdeb to 1f0eb9e Compare November 8, 2026 22:08
@jogibear9988
jogibear9988 requested a review from dadhi November 8, 2026 22:10
Add a nullable custom-operator path using existing locals, nullable accessors and method emission. Preserve null and decisive-left short circuits, selected bitwise methods, ignored effects and right-side finally cleanup. Keep ordinary Boolean and non-lifted operator paths in place.
Move non-lifted custom logical emission out of the main switch and share operand storage, truth testing, ordered right evaluation, method calls and branch merging. Keep nullable checks and labels conditional on lifting, preserving existing lookup and operand contexts. Add standard and Light regression coverage for reference and struct operators, explicit methods, ignored effects and exception regions.
@jogibear9988
jogibear9988 force-pushed the fix/lifted-user-logical-operators branch from 015c10a to 394f8e8 Compare November 9, 2026 05:21
@dadhi

dadhi commented Nov 9, 2026

Copy link
Copy Markdown
Owner

thank you!

@dadhi
dadhi merged commit a095043 into dadhi:master Nov 9, 2026
4 checks passed
@jogibear9988
jogibear9988 deleted the fix/lifted-user-logical-operators branch November 9, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants