[all-commits] [llvm/llvm-project] 891b8f: [BPF] crash fix for memcpy with unsupported alignm...
Claire Fan via All-commits
all-commits at lists.llvm.org
Thu May 28 23:11:11 PDT 2026
Branch: refs/heads/main
Home: https://github.com/llvm/llvm-project
Commit: 891b8f6fa8e796464b34fa9460933fccf69f3cc3
https://github.com/llvm/llvm-project/commit/891b8f6fa8e796464b34fa9460933fccf69f3cc3
Author: Claire Fan <fanyungching at gmail.com>
Date: 2026-05-28 (Thu, 28 May 2026)
Changed paths:
M llvm/lib/Target/BPF/BPFSelectionDAGInfo.cpp
M llvm/lib/Target/BPF/BPFSelectionDAGInfo.h
A llvm/test/CodeGen/BPF/libcall-memcpy-threshold.ll
Log Message:
-----------
[BPF] crash fix for memcpy with unsupported alignment (#199541)
### Summary
BPF only has scalar load/store opcodes up to 8 bytes and `memcpy` with
16 byte alignment crashes with `llvm_unreachable("unsupported memcpy
alignment");` in `BPFInstrInfo::expandMEMCPY`
### Issue
The crash can be reproduced from Rust code like:
```rust
#[no_mangle]
pub extern "C" fn entry(src: *const u128) -> u64 {
let mut dst = [0u128; 2];
unsafe { ptr::copy_nonoverlapping(src, dst.as_mut_ptr(), 2) };
let _ = black_box(dst);
0
}
```
This produces a `memcpy` with 16-byte alignment and
`BPFSelectionDAGInfo::EmitTargetCodeForMemcpy` emit the `BPFISD::MEMCPY`
pseudo for 16-byte alignment. That pseudo later reaches `expandMEMCPY`
during the Post-RA pseudo expansion pass and hits:
```
llvm_unreachable("unsupported memcpy alignment");
```
The crash report looks like:
```
= note: PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/
and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0. Running pass 'Function Pass Manager' on module 'linked_module'.
1. Running pass 'Post-RA pseudo instruction expansion pass' on function '@entry'
```
### Fix
Return `SDValue()` in `BPFSelectionDAGInfo::EmitTargetCodeForMemcpy`
when the alignment is larger than 8 bytes. LLVM then naturally falls
back to the existing libcall path and emits `call memcpy` instead of
creating a pseudo instruction that cannot be expanded later.
Since `memcpy`, `memset`, and `memmove` are now whitelisted, I think
this fallback behavior is the right fix: unsupported target expansion
should decline and use the existing libcall path instead of crashing.
### Opt-in controls around libcalls
I also want to use this chance to introduce two opt-in controls around
libcalls for VM/runtime use cases built on top of the BPF backend (both
controls are opt-in and disabled by default, so existing kernel projects
and current BPF behavior remain unchanged),
### --bpf-max-stores-per-memfunc
First, an opt-in flag to control over
`BPFSelectionDAGInfo::getCommonMaxStoresPerMemFunc()`.
Today, the target memory expansion path uses:
```c++
if (StoresNumEstimate > getCommonMaxStoresPerMemFunc())
return SDValue();
```
This threshold controls whether `EmitTargetCodeForMemcpy` continues
emitting the BPF memory pseudo(which is later expanded with load/store
ops) or falls back to the libcall path.
Currently this value is effectively fixed at 128, which means most
constant-size memory operations are expanded into inline load/store
sequences. Exposing the threshold gives VM/runtime users a way to allow
more memory operations to fall through to libcalls, while keeping the
current default behavior unchanged.
### --bpf-allows-libcalls (PR #199542 )
Second, an opt-in flag to allow additional libcalls beyond the currently
whitelisted ones.
Today BPF rejects unsupported libcalls here:
```c++
if (Sym != BPF_TRAP && Sym != "__multi3" && Sym != "__divti3" &&
Sym != "__modti3" && Sym != "__udivti3" &&
Sym != "__umodti3" && Sym != "memcpy" &&
Sym != "memset" && Sym != "memmove")
fail(
CLI.DL, DAG,
Twine("A call to built-in function '" + Sym +
"' is not supported."));
```
With the new flag enabled, _this error path is skipped_ so VM/runtime
environments can provide their own libcall implementations.
For VM runtimes using the BPF backend, libcalls are useful because they
create an interface between normal compiler-generated code and
runtime-specific optimization. We have already explored this model for
u128 arithmetic, where libcalls allow normal Rust code to stay unchanged
while the runtime can provide optimized behavior:
https://blueshift.gg/research/accelerating-u128-math-with-libcalls-and-jit-intrinsics
The same idea applies to memory operations. Users can continue writing
normal Rust like `ptr::copy_nonoverlapping`, while the runtime can
choose an implementation tailored to its execution environment.
Both controls are opt-in and disabled by default, so existing kernel
projects and current BPF behavior remain unchanged.
To unsubscribe from these emails, change your notification settings at https://github.com/llvm/llvm-project/settings/notifications
More information about the All-commits
mailing list