355 Commits

Author SHA1 Message Date
Oscar Smith
afd703e713 Add memoryrefunset intrinsic (#61759)
To allow for  write barriers to be added for concurrent GC.

Coauthored by claude.

---------

Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com>
2026-05-21 12:14:24 -04:00
Keno Fischer
451a00288a subtype: Move constrains_param computation into subtyping (#61645)
This is a significant cleanup of he semantics of subtyping envout by removing any semantic relation between TypeVars and the inputs. See #61634 and #61645 for more details.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 13:11:32 -04:00
Oscar Smith
0413cae71d Merge pull request #61701 from mlechu/debuginfo-cleanup
Debuginfo: misc. cleanup
2026-05-08 20:32:41 -04:00
Shuhei Kadowaki
199855161e inference: unwrap PartialStruct typ in precise_container_type fast path (#61744)
`PartialStruct.typ` may be a `UnionAll` (parametric `Tuple`/`NamedTuple`
produced via `:new`), in which case the `isa(_, DataType)` gate misses
and we fall through to the generic path, discarding the precise
per-field info in `typ.fields`. Unwrap so the fast path engages.

Found as a minor improvement opportunity during the audit done for
JuliaLang/julia#61743. No current code path is known to reach this with
a UnionAll `PartialStruct`, so the change is defensive (thus no tests
are added).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 10:27:04 +09:00
Shuhei Kadowaki
c6f52115a4 optimizer: apply unwrap_unionall after widenconst in sroa_mutables! (#61743)
`sroa_mutables!` derived the allocation `DataType` by calling
`unwrap_unionall` on the raw lattice element of a `:new` and only then
widening it. That order was incorrect: `unwrap_unionall` is meaningful
on a plain `Type`, not on any extended lattice elements (e.g.,
`PartialStruct` whose `.typ` may itself be a `UnionAll`), so when
inference produced a `PartialStruct`-typed `:new` the unwrap was a no-op
and the subsequent `typ::DataType` typeassert could fail on a
`UnionAll`. Reorder so `widenconst` runs first.

This was latent since `Core.PartialStruct` was extended to admit
`UnionAll` for `.typ` (#57304), and surfaced as a typeassert crash via
#61719, which made `:new` actually emit such `PartialStruct`s.

I also directed an AI-driven audit of related sites — every place under
`Compiler/src/` that derives a `DataType` from a lattice element which
could be a `PartialStruct` (passes, inlining, EscapeAnalysis, tfuncs,
typelattice) — and confirmed the rest already use
`unwrap_unionall(widenconst(...))` or guard with explicit `isa(_,
DataType)` / `argument_datatype` checks, so no other site needs the same
reordering.

Both the diagnosis and the patch were developed with the assistance of
generative AI.

Fixes #61740

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 10:05:06 +09:00
Cody Tapscott
6bf6602d94 debugging: Add flag to prevent compiler from discarding Julia IR (#61741)
The Julia compiler typically discards Julia IR after it is converted to
machine code, unless it has a specific reason to keep it around (such as
for inlining).

When debugging, it is common to want to ask "How was this function
inferred?". `code_typed` / Cthulhu provide an approximate answer to this
question, but this flag allows you to observe the results of inference
as they were computed for the machine code you are actually running.

Accessible only via `ccall(:jl_set_type_infer_preserve_ir, Cvoid,
(UInt8,), 1)` since this is a debugging-only feature.
2026-05-07 14:22:59 -04:00
Em Chu
3ffa9f6cc4 ssair: replace deleted test 2026-05-06 17:25:43 -04:00
Em Chu
17a2505aaa plumbing: replace compressed string with debuginfo object in API 2026-05-06 17:24:15 -04:00
Shuhei Kadowaki
c947944da8 inference: propagate PartialStruct for parametric struct construction (#61719)
`abstract_eval_new` previously gated `PartialStruct` formation on
`isconcretedispatch(rt)`. That's correct for fully-instantiated concrete
struct construction, but it discards extended lattice information when
`rt` is a partially-instantiated parametric type, e.g.
`Some{OpaqueClosure{Tuple{Any}, T} where T}` or
`Generator{Vector{Int}, F<:OpaqueClosure{Tuple{Int}, T} where T}`. Such
types still have a well-defined field count and field types, so the
per-field lattice elements (`PartialOpaque`, `Const`, `PartialStruct` of
nested structs, …) carry strictly more precision than the declared field
types — but without `PartialStruct` formation that precision is
discarded at the construction site, and downstream `getfield` reads only
recover the widened type.

Loosen the gate to also accept the parametric case (`fcount !== nothing
&& nargs ≤ fcount`). `nothrow` and the `Const`-folding fast path remain
gated on `isconcretedispatch(rt)` — those genuinely require the concrete
result type — and the new non-concretedispatch arm falls back to
`refine_partial_type(rt)` when no field refinement happens, mirroring
the outer `else` branch.

This complements the eager-body rt refinement
(`abstract_eval_new_opaque_closure`) for an ongoing experiment in JETLS
that runs inference statelessly against top-level chunks, where JETLS
converts closures to opaque closures at re-lowering phase to workaround
the closure name-resolution issues. Common code patterns wrap a closure
in another struct (e.g. `map`'s implementation), and without this change
the wrapping erased the `PartialOpaque` info, so a later call recovered
only the widened `OpaqueClosure{argt, T} where T` and
`abstract_call_unknown`'s OC fallback widened the result to `T<:Any`.
With this change the `PartialStruct` carries the `PartialOpaque` field
through in such cases, so the reading `getfield` hands
`abstract_call_opaque_closure` the precise lattice element it needs.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 18:30:31 -04:00
Jeff Bezanson
4eec0db407 fix effects of special fields that can change between processes (#61595)
These fields are technically mutable, but inference knows they are never
actually mutated. However, they can change between processes since
staticdata.c has special handling to relocate them. fixes #61590
2026-05-01 15:34:37 +09:00
Shuhei Kadowaki
df12394092 inference: concrete-evaluate when const-prop refines effects to foldable (#61677)
`const_prop_call` may refine effects to be foldable when the original
call was not (e.g., when constant arguments make a branch dead,
eliminating side effects). Previously such cases would only receive the
const-prop' result. Now we re-check eligibility on the const-prop'd
effects/edge and prefer concrete evaluation when it becomes applicable,
yielding `Const` returns and `EFFECTS_TOTAL`.

Note that we do not want to simply check concrete-eval eligibility
upfront on const-prop'd effects to avoid the secondary check: concrete
eval was originally introduced as a fast path for const-prop' itself, so
reversing the order (running const-prop' before concrete-eval) would
defeat that purpose by paying the const-prop' cost even for calls that
the cheaper, upfront concrete-eval check would have caught. The
secondary check here is intentionally constrained to `eligibility ===
:none`, where the upfront concrete-eval was not applicable to begin
with.
2026-04-30 07:33:40 +09:00
Kristoffer Carlsson
bada257eab Compiler: egal_tfunc: pass correct arg to issingletontype (#61687)
Claude pointed this out, and I would agree. Passing a `Const` to
`issingletontype` doesn't make sense.
2026-04-29 11:38:54 -04:00
Tim Besard
62af1c92d9 Handle non-standard Method.source in invalidation scanner (#61670)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 11:54:06 +02:00
Sam Schweigel
c24fc18dda Add function to emit multiple CodeInstances to the JIT atomically (remove jl_typeinf_lock) (#61255)
`jl_typeinf_lock` was introduced because it was easy to observe
performance regressions when running code that triggered type inference
on multiple threads. These changes prevent the unexpected invoke
trampolines and remove the type inference lock.

The typical situation is this: let f() and g() be functions, where f()
calls g(). Thread 1 triggers inference for f(), which also infers g().
Then, in `add_codeinst_to_jit!`, thread 1 adds the code for f(), which
becomes visible to other threads because the `invoke` field of the
CodeInstance is set to `jl_fptr_wait_for_compiled` [1]. Before thread 1
adds g() to the JIT, thread 2 comes along, sees the invoke field on f()
and attempts to invoke it. The JIT must then compile a tojlinvoke
trampoline to g(), because it does not yet have IR for it.

This PR renames `jl_add_codeinst_to_jit` to `jl_add_codeinsts_to_jit`
and makes it take a vector of CodeInstances and a vector of CodeInfos.
We then emit all of the CodeInstances to a single `jl_codegen_output_t`
and add it to the JIT with `JuliaOJIT::addOutput`. The JIT, while
holding `JuliaOJIT::LinkerMutex`, sets the `invoke` pointer for every
defined CodeInstance to `jl_fptr_wait_for_compiled`. If another thread
has compiled that CodeInstance in the meantime, we skip it. If another
thread observes the invoke pointer we have just set, it's okay because
it will block waiting to acquire `LinkerMutex` if it attempts to invoke
it.

This pull request also changes the condition in
`JuliaOJIT::linkCallTarget` to match `add_codeinst_to_jit!` to avoid a
few other unnecessary trampolines: namely, we know an equivalent
CodeInstance will have been emitted to the JIT only if the target
CodeInstance is not in the global cache (since that's what inference
checks).

[1] In practice you need another function, because of the order
`add_codeinst_to_jit!` collects invokes in, but it would complicate the
presentation.
2026-04-28 10:33:48 -04:00
Ian Butterworth
59ef3450de math: fix spurious !nothrow effects on asinh, frexp, hypot, and unsafe_trunc (#61616) 2026-04-21 13:11:55 -04:00
Ian Butterworth
d50d2a54d4 codegen: Propagate ipo_purity_bits to LLVM function attributes (#61394)
Translate Julia's inferred effects (consistent, effect_free, nothrow,
terminates, notaskstate) into LLVM function attributes so that
middle-end passes like GVN, LICM, and DSE can exploit them.

The key design insight is that GC interactions don't need to be visible
before GC lowering. Call-site declarations get optimistic memory
attributes (e.g. memory(argmem: read)) that enable pre-GC optimizations,
then LateLowerGCFrame widens them to memory(readwrite) before safepoint
analysis so post-GC passes see correct semantics.

Attributes added:
- nounwind: for nothrow functions (with uwtable(async) on definitions
  to preserve .eh_frame for stack scanning)
- mustprogress: for terminating functions
- willreturn: for nothrow+terminating functions
- memory(argmem: read): for consistent+effect_free functions with no
  user-facing pointer arguments (call-site declarations only)
- readnone on gcstack param: for notaskstate functions, so LICM can
  hoist pure calls past heap stores
- "julia.safepoint" marker: on all call-site declarations, used by
  LateLowerGCFrame to identify and widen optimistic attrs

LateLowerGCFrame strips all optimistic attributes (memory effects,
readnone on gcstack) from both call instructions and function
declarations before safepoint analysis runs.

Previously explored in #47844

Developed with Claude

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Gabriel Baraldi <28694980+gbaraldi@users.noreply.github.com>
2026-04-20 16:38:52 -04:00
Jeff Bezanson
454f0a2040 use named tuple names to refine length of tuple type parameter (#60387)
This improves inference precision in a case like the one in #60252.
Often the field names are known, but the values are collected from
something complicated, and we can use the known length of the names to
refine the type.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 15:16:56 -04:00
Shuhei Kadowaki
3181c36d43 compiler: Filter dominated original methods in overlay matching (#61581)
When merging overlay and base method match results in `findall(sig,
::OverlayMethodTable)`, base methods whose signature is fully covered by
any of matching overlay methods' signatures are now filtered out.

Previously, both the overlay and base methods with the same signature
appeared in the results. This caused type inference to union their
return types even though the overlay should take dispatch priority.

While this is a general inference correctness improvement that makes
inference's emulation of overlay method selection at runtime more
"correct", it might be worth explaining the concrete need that motivates
this change:

Currently, inference for code like `[(x::Vector{Vector{Int}})...;]`
unexpectedly infers `Union{Vector{Int},Vector{Any}}` instead of
`Vector{Int}`, causing type instability isseus as reported in
aviatesk/JET.jl#413.
The root cause of this type instability is the existence of the fallback
method `vcat() = Any[]`.
To fix this, I initially attempted to remove the `vcat()` method
(JuliaLang/julia#47628), but that turned out to be a considerably
breaking change, so I concluded it was not viable to proceed in that
direction as of Julia v1 time.
I also considered alternative approaches: 1.) changing the lowering of
`[x...;]` to produce a primitive for `vcat` that would allow inference
to work correctly in such cases, and 2.) adding a typed required 1st
argument to other `vcat` methods so that dispatch to `vcat()` would not
be considered — but both of those also proved impractical for various
reasons (primarily because the complexity they introduce is too high).
Removing the fallback method definitions for `vcat()` and `hcat()`
themselves would be the simplest and most elegant fix, but that would
have to wait for the release of Julia v2.

Therefore, rather than fixing this type instability on the Julia base
side, I believe the least invasive solution is to define, on the JET
side:
```julia
@overlay JET_METHOD_TABLE Base.vcat() = error()
```
so that, during JET's abstract interpretation, the method match that
could produce the `Vector{Any}` possibility is effectively ignored. And
this commit is required for that to actually work.
2026-04-18 15:48:16 +09:00
Em Chu
778e467aeb Consistently disallow :static_parameter in argument position (#61536)
Undo https://github.com/JuliaLang/julia/pull/61511, and clean up flisp
and IR validator cases where we previously considered this valid.

JL should now recognize `static_parameter` as a lowering input. Both
lowering implementations should now outline `static_parameter`
unconditionally.

Fix https://github.com/JuliaLang/JuliaLowering.jl/issues/175
2026-04-17 13:16:34 -07:00
Ian Butterworth
4e8acb4028 typeinfer: fix is_already_cached assertion with OverlayCodeCache (#61574)
Proposal to fix a Compiler error from a benchmarks run.

The added test seems quite brittle, but locally it does fail on master
and pass on this PR. Not sure if it should stay in this PR (if the PR is
valid).

```
From worker 3:    ERROR: LoadError: AssertionError: isdefined(cache[mi], :inferred)
      From worker 3:    Stacktrace:
      From worker 3:       [1] is_already_cached(interp::BaseBenchmarks.InferenceBenchmarks.InferenceBenchmarker, result::Compiler.InferenceResult)
      From worker 3:         @ Compiler ./../usr/share/julia/Compiler/src/typeinfer.jl:730
      From worker 3:       [2] promotecache!(interp::BaseBenchmarks.InferenceBenchmarks.InferenceBenchmarker, caller::Compiler.InferenceState)
      From worker 3:         @ Compiler ./../usr/share/julia/Compiler/src/typeinfer.jl:205
      From worker 3:       [3] finish_nocycle(interp::BaseBenchmarks.InferenceBenchmarks.InferenceBenchmarker, frame::Compiler.InferenceState, time_before::UInt64)
      From worker 3:         @ Compiler ./../usr/share/julia/Compiler/src/typeinfer.jl:286
      From worker 3:       [4] typeinf(interp::BaseBenchmarks.InferenceBenchmarks.InferenceBenchmarker, frame::Compiler.InferenceState)
      From worker 3:         @ Compiler ./../usr/share/julia/Compiler/src/abstractinterpretation.jl:4821
      From worker 3:       [5] inf_method_instance!(interp::BaseBenchmarks.InferenceBenchmarks.InferenceBenchmarker, mi::Core.MethodInstance; run_optimizer::Bool)
      From worker 3:         @ BaseBenchmarks.InferenceBenchmarks ~ubuntu/.julia/dev/BaseBenchmarks/src/inference/InferenceBenchmarks.jl:138
      From worker 3:       [6] inf_method_instance!
      From worker 3:         @ ~ubuntu/.julia/dev/BaseBenchmarks/src/inference/InferenceBenchmarks.jl:134 [inlined]
      From worker 3:       [7] inf_method_signature!
      From worker 3:         @ ~ubuntu/.julia/dev/BaseBenchmarks/src/inference/InferenceBenchmarks.jl:131 [inlined]
      From worker 3:       [8] inf_gf_by_type!(interp::BaseBenchmarks.InferenceBenchmarks.InferenceBenchmarker, tt::Type{<:Tuple}; kwargs::@Kwargs{run_optimizer::Bool})
      From worker 3:         @ BaseBenchmarks.InferenceBenchmarks ~ubuntu/.julia/dev/BaseBenchmarks/src/inference/InferenceBenchmarks.jl:117
      From worker 3:       [9] inf_gf_by_type!
      From worker 3:         @ ~ubuntu/.julia/dev/BaseBenchmarks/src/inference/InferenceBenchmarks.jl:115 [inlined]
```

Developed with Claude:

----

Since #59413 introduced OverlayCodeCache, `code_cache(interp)[mi]` can
return an `InferenceResult` (from the local inference cache) rather than
a `CodeInstance`. The assertion `isdefined(cache[mi], :inferred)` always
fails for `InferenceResult` since it has no `:inferred` field, even
though the underlying `CodeInstance` is valid.

Extract the `CodeInstance` from the result before checking `isdefined`.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-16 12:54:08 -04:00
Andy Dienes
46fcd1f62a inference: include const-abi CodeInstances in need_inlineable_code check (#61553)
Const-abi CodeInstances have `inferred === nothing`, so `is_inlineable`
returns false and `typeinf_edge` skips source retrieval. This prevents
the inliner from folding these calls to constants.

Fixes #61552

can't wait for Mythos to release and then maybe claude will be able to
take over all my other hobbies too

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 12:31:47 -04:00
Gabriel Baraldi
268f85e80b codegen: emit bounds checks as cold branches instead of ANDs (#61535)
For reference the big win here is that this allows for loop unswitch to
trigger since it doesn't really understand our `and` sequence


## Summary

- Replace AND-combined bounds check conditions with separate branches
annotated with branch weight metadata (2000:1) to mark the error path as
cold in LLVM codegen (`emit_bounds_check`, `emit_memoryref_direct`,
`emit_memoryref`)
- Change `checkbounds_indices` to use `&&` instead of `&` so each
dimension check becomes its own branch rather than being combined into a
single AND chain
- Change `throw_boundserror` to accept varargs for multi-index cases, so
the index tuple is only constructed inside the `@noinline` error path
rather than on the hot path

For a 2D array access `A[i, j]`, the net effect is:
- **Before**: one AND-combined branch + `alloca` for index tuple in the
entry block
- **After**: two separate cold-annotated branches per dimension, zero
tuple allocation on the hot path

Before:
```llvm
  %"new::Tuple" = alloca [2 x i64], align 8
  store i64 %"i::Int64", ptr %"new::Tuple", align 8
  store i64 %"j::Int64", ptr %0, align 8
  ...
  %.not = icmp ult i64 %0, %"x::Array.size.0"
  %1 = icmp ult i64 %2, %"x::Array.size.1"
  %combined = and i1 %.not, %1
  br i1 %combined, label %pass, label %fail
```

After:
```llvm
  %.not = icmp ult i64 %0, %"x::Array.size.sroa.0.0.copyload"
  br i1 %.not, label %L27, label %L29

L27:
  %.not10.not = icmp ult i64 %1, %"x::Array.size.sroa.2.0.copyload"
  br i1 %.not10.not, label %L32, label %L29

L29:
  call void @j_throw_boundserror_1(ptr %"x::Array", i64 %"i::Int64", i64 %"j::Int64")
  unreachable
```

## Test plan

- [x] Full `make -j8` build succeeds including sysimage precompilation
- [x] Verified LLVM IR output shows separate branches per dimension for
`A[i, j]`
- [x] Verified tuple `alloca` is eliminated from the hot path
- [x] Verified single-index `A[i]` still works correctly (no tuple
wrapping)
- [x] Verified `BoundsError` messages are unchanged at runtime
- [ ] CI tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 10:58:03 -04:00
Shuhei Kadowaki
525c655eef inference: Respect @nospecializeinfer semantics in constant prop (#61502)
`@nospecializeinfer` widens the `MethodInstance` signature so that
inference does not specialize on `@nospecialize`'d arguments, but const
prop was not respecting this and `matching_cache_argtypes` would expand
a `Vararg` in the `MI`'s `cache_argtypes` to match the call-site arity,
producing `argtypes` of different lengths for different const-prop call
sites sharing the same `MI`. This caused an assertion failure in
`constprop_cache_lookup` when a second call with a different number of
varargs tried to look up a cached result.

Fix this by widening the `@nospecialize`'d arguments back to the
`cache_argtypes`, using the same `nospecialize` bitmask convention as
`jl_compilation_sig` in `src/gf.c`.
This ensures constprop argtypes always have the same shape as the `MI`'s
baseline argtypes, and avoids unnecessary specialization that
contradicts the intent of `@nospecializeinfer`.

Fixes aviatesk/JETLS.jl#618.
2026-04-09 08:58:40 +09:00
Cody Tapscott
1b3fb0e82d Compiler: Fix usage of @nospecialize on optional parameter (#61519)
As @mlechu pointed out in
https://github.com/JuliaLang/julia/pull/61489#issuecomment-4193626329,
flisp silently ignores this `@nospecialize` if it doesn't wrap the full
arg expression.
2026-04-07 13:57:31 -04:00
Cody Tapscott
8866446c23 Compiler: infer Expr(:static_parameter, ...) in argument position (#61511)
Kind of surprising we haven't encountered this before, but flisp tries
to avoid this form (on accident?)

Previously:
```julia
julia> code_typed(f_static_parameter_argument_position, Tuple{Type{Int}})
1-element Vector{Any}:
 CodeInfo(
1 ─     return Int64
) => Any # should be Type{Any}
```

JuliaLowering _does_ generate it though, so this resolves
https://github.com/JuliaLang/JuliaLowering.jl/issues/152.
2026-04-07 08:46:32 +09:00
Andy Dienes
b5ecba002f avoid false-positive recursion detection in the presence of union splitting (#61461)
fixes https://github.com/JuliaLang/julia/issues/57324

see the MWE closely: the `setindex!` method added is `setindex!(_, _,
i::Int)`, but the index passed is `UInt`. which means we hit
`setindex!(A::AbstractArray, v, I...)` twice to convert the `UInt ->
Int`, once via `T` and again via `Memory`. since `_setindex!(w, i)`
returns `Union{Nothing, Int}`, this gets union-split causing
`napplicable > 1`, triggering the aggressive recursion-limiting
heuristic `hardlimit=true`

note this fix is not super general and there are probably several other
similar scenarios one could construct dispatching through the same
general method several times and ending up hitting this heuristic. but I
think this should address all cases where this happens due to union
splitting?
2026-03-31 17:19:31 -04:00
Ian Butterworth
8ffcedf6cd codegen: Add blackbox and compilerbarrier(:blackbox, ...) for benchmarking (#61438)
Adds `Base.blackbox(x)` and the corresponding `:blackbox` setting for
`compilerbarrier`, motivated by the LICM-hoisting issue seen in #61394.

`Base.blackbox(x)` returns `x` but treats the output as opaque to the
optimizer: it cannot be constant-folded, CSE'd, or treated as
loop-invariant. This is Julia's equivalent of Google Benchmark's
`DoNotOptimize`.

https://google.github.io/benchmark/user_guide.html#preventing-optimization

`blackbox` is implemented as `compilerbarrier(:blackbox, x)`, keeping
all compiler-barrier functionality consolidated in one builtin rather
than introducing a new one.

## Implementation

**Julia-level (inference):** `:blackbox` has `consistent=ALWAYS_FALSE`
and returns `widenconst(val)`, preventing constant propagation and CSE
at the abstract interpretation level.

**Codegen level**, three cases based on the value's LLVM type:

- **Non-pointer scalars** (integers, floats — anything that fits in a
register): emits `asm "" "=r,0"` directly, tying the output register to
the input.
- **Boxed/GC-tracked pointers**: cannot use register-tied asm on
GC-tracked address spaces. Instead, emits a `julia.blackbox` intrinsic
call that is lowered to `asm "" "=r,0"` on the raw untracked pointer
after GC frame expansion.
- **Unboxed aggregates / unboxed pointers** (e.g., structs, `Ptr{T}`):
uses `asm sideeffect "" "~{memory}"` to clobber memory, making the value
appear non-invariant without needing a register constraint.

Ghost types (`Nothing`, etc.) pass through unchanged with no code
emitted.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Valentin Churavy <v.churavy@gmail.com>
Co-authored-by: Keno Fischer <1291671+Keno@users.noreply.github.com>
2026-03-31 06:37:45 -04:00
Andy Dienes
e8208497f7 check issimpleenoughtype for PartialStruct during issimplertype (#61130)
fixes https://github.com/JuliaLang/julia/issues/60715

`tmerge_types_slow` would check `issimpleenoughtype(u)` after the
`PartialStruct`s were stripped from the types, but that path wasn't
getting hit as it would early return via `tmerge_fast_path`

Claude assisted the analysis but the proposed fix is mine
2026-03-30 22:03:34 -04:00
Cody Tapscott
cbb2be44c3 Fix nothrow effects for (s/z)ext_int / trunc_int intrinsics (#61437) 2026-03-30 15:25:00 -04:00
Shuhei Kadowaki
f6ee61ce82 inference: Fix fieldtype_tfunc on malformed UnionAll types (#30807) (#61380)
Types like `NTuple{<:Any, 3}` produce `UnionAll`-wrapped types with
non-`Type` components (e.g., the integer `3`). When `fieldtype_tfunc`
encounters such a type, `rewrap_unionall` fails with a `TypeError`
because it cannot wrap a non-`Type` value in a `UnionAll`.

Fix this by skipping malformed fields in the non-`Const` path and
returning `Bottom` in the `Const` path, since the runtime `fieldtype`
would throw for these types anyway.

- fixes #30807
2026-03-21 14:30:26 +09:00
Shuhei Kadowaki
2968368737 inference: Extend signature constraint propagation to MustAlias (#61345)
Previously, `collect_slot_refinements` only narrowed slot types when a
slot was passed directly as a call argument (the `SlotNumber` case).
This meant that field accesses like `x.value` passed to a call did not
benefit from signature constraint propagation.

For example, in code like:

```julia
code_typed((Some{Any},); optimize=false) do x
    only_accepts_int(x.value)
    return sin(x.value) # x.value was not narrowed to Int
end
```

the call to `only_accepts_int(::Int)` constrains `x.value` to `Int`, but
this information was not propagated back to the slot `x`. After this
change, the slot is refined to `PartialStruct(Some{Any}, [Int])`,
allowing subsequent uses of `x.value` to be inferred as `Int`.

The implementation extracts the `PartialStruct` construction logic from
`form_mustalias_conditional` into a shared `form_mustalias_refinement`
helper and reuses it in `collect_slot_refinements`.
2026-03-18 22:04:09 +09:00
Shuhei Kadowaki
b4aba01002 inference: Cache tombstoned const-prop results to prevent non-termination (#61264)
When a const-prop frame encounters a cycle and gets poisoned,
`finishinfer!` marks the result as tombstoned and sets `cache_mode =
CACHE_MODE_NULL`, which prevents `promotecache!` from pushing the result
to the local inference cache.
Meanwhile, `constprop_cache_lookup` (added in #57545) skips tombstoned
entries entirely. This combination means the same const-prop is
re-attempted on every cycle iteration, causing inference to never
terminate when `aggressive_constant_propagation = true`.

Fix this by:
- In `const_prop_call`, explicitly pushing tombstoned results to the
inference cache after a successful but limited `typeinf`, and returning
`nothing` to fall back to the regular inference result.
- In `constprop_cache_lookup`, no longer skipping tombstoned entries so
they can be found on subsequent lookups, preventing re-attempts.
- In `const_prop_call` cache-hit path, checking `inf_result.tombstone`
and returning `nothing` (same as the existing cycle-hit handling).
- Removing the now-unnecessary tombstone check from
`OverlayCodeCache.get`, where the subsequent `overridden_by_const` and
`isdefined` checks already filter out such entries.

Note that #57545 added `tombstone && continue` to `cache_lookup` to
prevent `LimitedAccuracy` results from propagating to callers via
`const_prop_result`. This change removes that skip but achieves the same
protection by explicitly checking `inf_result.tombstone` in
`const_prop_call` and returning `nothing` instead of using the result.

Fixes #61257

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 13:25:39 +09:00
Shuhei Kadowaki
3b680c3406 Compiler.jl: gitignore CompilerDevTools/Manifest.toml (#61266) 2026-03-11 18:14:01 +09:00
Sam Schweigel
afdac9edc2 Move InterMustAlias to Core (#61244)
Since we publish InterMustAlias to the global cache in rettype_const, it
should live in Core. Loading the Compiler package when it's defined
there results in silent incorrect type inference, since the new compiler
wraps Core.Compiler.InterMustAlias in a Core.Const when it's found in
rettype_const.
2026-03-05 21:38:56 -05:00
Tim Besard
f519f3e8b0 Set sret alignment to julia_alignment (#61192)
The sret parameter's alignment attribute was set to LLVM's preferred type
alignment (getPrefTypeAlign), which can exceed julia_alignment. This caused
misaligned memory accesses on strict-alignment targets like NVPTX, since the
caller's alloca uses julia_alignment. Fix by setting the sret alignment to
julia_alignment and not overriding it in the function definition, so that
caller and callee agree on the same alignment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 08:05:52 +01:00
Valentin Churavy
e386b489e8 Ensure CI from external MI and foreign abs interpreter survive precompilation (#60747)
Forward-port of #60741

---------

Co-authored-by: Gabriel Baraldi <baraldigabriel@gmail.com>
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
2026-03-03 16:23:26 +01:00
Shuhei Kadowaki
d19fdfd5f5 inference: A bunch of minor code quality improvements (#61202)
@nanosoldier `runbenchmarks("inference", vs=":master")`
2026-03-03 11:15:23 +09:00
Gabriel Baraldi
3add5d0680 Compiler: Use contains_is instead of in for builtin membership checks (#61209)
This saves a couple % because the compiler makes a mess of the in call,
but only when building the sysimage (it's bad regardless because == has
some extra cruft that === doesn´t)
It's also more consistent since we use the helper everywhere else

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 11:12:33 +09:00
Shuhei Kadowaki
061b78edce inference: Enable type-based alias analysis (MustAlias) for NativeInterpreter (#47574)
Some checks failed
Whitespace / Check whitespace (push) Has been cancelled
#41199 implements the base logic for new lattice element `MustAlias`,
but doesn't enable it for `NativeInterpreter`.
This PR enables it for our native code execution pipeline, checking its
performance impact.

---

Closes #59975.
2026-03-02 22:54:14 +09:00
Shuhei Kadowaki
6c7ebe0e41 inference: Merge bb_vartables+bb_slot_aliases into BBEntryState (#61204)
Introduce `BBEntryState` that bundles the per-basic-block variable-type
table (`VarTable`) and slot-alias table into a single place:

Previously, `update_bbstate!` set both arrays in the `if` branch but the
type system had no way to express that invariant, forcing a runtime
`typeassert` (`frame.bb_slot_aliases[bb]::Vector{Int}`) in the `else`
branch. With `BBEntryState` the invariant is structural and a
non-`nothing` entry always carries both components, so no assertion is
needed.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 22:32:07 +09:00
Andy Dienes
03f7bcd56b fix false-positive multiloc in AllocOpt escape analysis (#61131)
discovered via automated Claude audit for typos and other minor /
obvious bugs. I manually reviewed each result

if anybody is getting annoyed at the amount of AI code I am pushing
please let me know! it seems to be doing a good job and I am doing my
best to clean it all up + be camera ready, but I just want to double
check.

example benchmark:
```
julia> using BenchmarkTools

julia> mutable struct AnyBox
             val::Any
         end

julia> function preserve_any(x)
             b = AnyBox(x)
             GC.@preserve b begin
                 return b.val
             end
         end

julia> function loop(n)
             s = "v"
             for _ in 1:n
                 s = preserve_any(s)::String
             end
             s
         end

julia> @benchmark loop(10)
### master
BenchmarkTools.Trial: 10000 samples with 996 evaluations per sample.
 Range (min … max):  25.979 ns …  1.413 μs  ┊ GC (min … max): 0.00% … 96.22%
 Time  (median):     28.908 ns              ┊ GC (median):    0.00%
 Time  (mean ± σ):   31.487 ns ± 24.859 ns  ┊ GC (mean ± σ):  6.74% ±  9.68%

  ▃█▅▃                                                        ▁
  █████▇▇▆▆▅▅▅▅▁▄▃▄▃▁▃▁▁▅▁▁▁▃▄▃▁▁▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▄▅▆▇▆▆▆ █
  26 ns        Histogram: log(frequency) by time       134 ns <

 Memory estimate: 144 bytes, allocs estimate: 9.


### PR
BenchmarkTools.Trial: 10000 samples with 1000 evaluations per sample.
 Range (min … max):  2.084 ns … 45.042 ns  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     2.167 ns              ┊ GC (median):    0.00%
 Time  (mean ± σ):   2.204 ns ±  0.560 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

         ▂      █      ▆      ▅       ▂      ▂               ▁
  ▃▁▁▁▁▁▁█▁▁▁▁▁▁█▁▁▁▁▁▁█▁▁▁▁▁▁█▁▁▁▁▁▁▁█▁▁▁▁▁▁█▁▁▁▁▁▁█▁▁▁▁▁▁▆ █
  2.08 ns      Histogram: log(frequency) by time     2.42 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.
```

this is technically two different bugfixes in one. claude found the `?`
precedence one first, then found the other while attempting to write a
test for it.

description:

Fix a false-positive `multiloc` flag in `addMemOp` that prevented
AllocOptPass from stack-promoting any allocation with GC-pointer fields.
The `hasobjref` flag is initialized to `false` in the Field constructor
but only set to `true` after the `multiloc` check, so the first access
to a new objref field always triggered `multiloc = true`. Guard the
check with `!field.second.accesses.empty()` so it only fires when there
is a prior access with a different ref/bits kind.

This caused AllocOptPass to bail with "unusual object reference" on
every non-escaping mutable struct with a reference-typed field (Any,
String, Vector, etc.), blocking both `splitOnStack` and `moveToStack`.

Also fix an operator precedence bug in the atomicrmw/cmpxchg escape
check: `!=`bound tighter than `?:`

74b7adc233/src/llvm-alloc-helpers.cpp (L321)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 09:41:55 -05:00
Shuhei Kadowaki
44c835795b inference: Propagate conditional type refinement to aliased slots (#61168)
Continuation from #61041 (especially this PR implements @vtjnash 's
idea:
https://github.com/JuliaLang/julia/pull/61041#discussion_r2842290067)

When slot `y` is assigned from slot `x` (`y = x`), both hold the same
value. If a `Conditional` subsequently narrows the type of `x` (e.g. via
`x isa Int`), the same refinement should apply to `y`. The same applies
to `typeassert`-based slot refinements.

This adds `bb_slot_aliases` to `InferenceState` to track slot aliasing
across basic blocks. `slot_aliases[i] == j` means slot `i` currently
holds the same value as slot `j`. The table is always kept flat (aliases
point directly to the root slot), so a single lookup suffices.

Aliasing is tracked at two granularities:

- Intra-BB tracking (`slot_aliases`): this local variable in
`typeinf_local` is updated per-statement and reset at each basic-block
boundary.
- Cross-BB tracking (`bb_slot_aliases`): populated lazily during the
main inference loop by `update_bbstate!`, which copies the exit alias
state to each successor BB on first visit and intersects (meet
operation) on subsequent visits. Only aliases present on _all_ incoming
paths are retained.

When a `Conditional` fires, `refine_aliases!` propagates the same type
narrowing to every slot that is currently aliased to the conditional's
slot. The similar refinements from `apply_refinement!` are also applied.

---

For example, this PR improves type stability in cases like the
following:
```julia
@test Base.infer_return_type((Any,)) do x
    y = x
    if x isa Int
        return sin(y)
    end
    error("x is not Int")
end == Float64
```

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 08:58:36 +09:00
Gabriel Baraldi
2325e6a6fd codegen: Add llvm_options parameter to code_llvm for pass instrumentation (#60698)
Some checks failed
Whitespace / Check whitespace (push) Has been cancelled
Add the ability to pass LLVM pass manager options directly to
code_llvm() for debugging and analysis purposes. This allows users to
inspect IR at various stages of the optimization pipeline without
needing to set environment variables.

Supported options include:
- `-print-after-all`: Print IR after each pass
- `-print-before-all`: Print IR before each pass
- `-print-after=<passname>`: Print IR after a specific pass
- `-print-before=<passname>`: Print IR before a specific pass
- `-print-module-scope`: Print entire module instead of just the
function
- `-filter-print-funcs=<name>`: Filter output to specific functions

Example usage:
  code_llvm(+, (Int, Int); llvm_options="-print-after=loop-vectorize")

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Dilum Aluthge <dilum@aluthge.com>
2026-02-28 11:49:47 -05:00
Gabriel Baraldi
eb5feca982 Don't build a full domtree when renaming blocks, dfs is enough (#60976)
This saves around 3/4% of the precompile time of Documenter due to
saving some allocations.

---------

Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com>
Co-authored-by: Dilum Aluthge <dilum@aluthge.com>
2026-02-28 11:49:22 -05:00
Keno Fischer
3e744a7861 bootstrap: Add isa check to inner constructors generated by _defaultctors
Fix a regression introduced in be15e12bf6 (#61036) where the inner
constructor body generated by `_defaultctors` was missing the `isa`
typecheck that the old flisp `convert-for-type-decl` pattern provided.

The old flisp code generated inner constructors with the pattern:

    isa(arg, fieldtype(self, i)) ? arg : convert(fieldtype(self, i), arg)

The new Julia code in `_defaultctors` generated:

    convert(fieldtype(self, i), arg)

This is fragile because the optimizer must inline `convert(Any, x)` to
eliminate the call and produce a `:new` expression. When user code
defines ambiguous convert methods such as `Base.convert(::Any, v::T) =
v` (as test/misc.jl does at line ~581), this creates a method ambiguity
for `convert(::Type{Any}, ::Any)` since `Type{Any} <: Any`. The
compiler cannot prove the call is unambiguous when `x::Any`, so it
refuses to inline it. Without inlining, the constructor remains as a
function call instead of being optimized to `:new`.

The old code was resilient because `isa(x, Any)` is trivially `true`, so
the `convert` branch is dead code that the optimizer eliminates
regardless of method table state.

This caused the Compiler/EscapeAnalysis test to fail intermittently on
CI (Buildkite builds 55100, 55101) when `misc.jl` ran on the same
worker before `EscapeAnalysis`, leaving the ambiguous convert method in
the process's method table. The test at EscapeAnalysis.jl:598 calls
`only(findall(isnew, result.ir.stmts.stmt))` which expects exactly one
`:new` expression in the optimized IR for `SafeRef{Any}(a)`, but finds
none.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 03:52:53 +00:00
Keno Fischer
84329268cf Reapply "bootstrap: Move default constructor generation from C/flisp to Julia" (#61191)
This reverts commit c83d9ae963.
2026-02-28 03:52:40 +00:00
Dilum Aluthge
c83d9ae963 Revert "bootstrap: Move default constructor generation from C/flisp to Julia" (#61191)
Reverts JuliaLang/julia#61036

With the plan of re-landing #61036 once #61176 is ready.
2026-02-27 13:14:51 -05:00
Shuhei Kadowaki
5308f13b01 inference: implement MustAlias forwarding and getproperty support (#61158)
Implement inter-procedural `MustAlias` forwarding through const-prop,
enabling alias-aware union splitting when the base object and its field
are both passed to a callee. Unify `ConditionalSimpleArgtypes` and the
new `MustAlias` handling into a single `ForwardableArgtypes` type.

Also marks `getproperty` with `DISABLE_SEMI_CONCRETE_EVAL` so that
const-prop runs instead of semi-concrete eval, allowing the
`InterMustAlias` return type to be produced via the
`widenreturn`/`from_intermustalias` pipeline.
I hope this does not impact the overall inference performance since
`getproperty` is commonly simply implemented, but it's better to assert
it with the nanosoldier inference performance benchmark.

Finally also widen all slot wrappers (including `MustAlias`, not only
`Conditional`) in `type_annotate!` before optimization, preventing
lattice elements from leaking into the optimizer which only supports
`SimpleInferenceLattice`.
2026-02-26 21:54:18 +09:00
Keno Fischer
be15e12bf6 bootstrap: Move default constructor generation from C/flisp to Julia (#61036)
Currently there is a special backdoor into lowering that is used by the
_defaultctors builtin to generate the code for default inner and outer
constructors. This codegen is specifically delayed because the code we
want to generate is dependent on type information, which is not
available until runtime. The code that performs the type-dependent
analysis is currently in C.

This backdoor is annoying, because it complicates the interface to
lowering, which would otherwise be rather straightforward. It is also
unnecessary. With a little bit of effort, we can define the type
analysis logic relatively early in bootstrap and construct the relevant
Exprs there as well, replacing code that was originally in C/flisp. The
primary
annoyance is that we need to explicitly write out all constructors in
early bootstrap before the implementation is
available. However, it is possible to define this fairly early, so the
impact is not that bad.

This is prepratory work for a future flisp-less bootstrap using
JuliaLowering (which currently has no mechanism for this and depends on
flisp).

There are future questions around how to handle provenance for these
Exprs, but since the current implementation doesn't answer those
questions either, we need not do so at this stage.

Written by Claude.

Co-authored-by: Keno Fischer <Keno@users.noreply.github.com>
2026-02-25 17:35:04 -05:00
Shuhei Kadowaki
6a3f5434f5 inference: Alias-aware union splitting for identical arguments (#61041)
When the same IR variable (`SlotNumber`/`SSAValue`) is passed to
multiple argument positions of a call, the union-split algorithm
previously generated the full cartesian product of `Union` types. For
example, `f(a, a, a)` where `a::Union{A,B}` produced 8 splits when only
2 diagonal combinations are actually reachable, causing wasted inference
on impossible type combinations.

This commit uses `fargs` from `ArgInfo` and `MustAlias` information from
`argtypes` to detect argument aliasing via `compute_alias_groups`, which
builds a leader/follower mapping. Aliasing is detected from two sources:
IR identity (same `SlotNumber`/`SSAValue` in `fargs`) and `MustAlias`
lattice elements with matching `(slot, ssadef, fldidx)`, which covers
cases like `f(a.x, a.x)` where distinct SSAValues refer to the same
field. In cost calculation, each alias group is counted once. In split
generation, only leaders iterate over union types and propagate the
chosen type to their followers.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 01:01:35 +09:00