rust/tests/codegen/deduced-param-attrs.rs

61 lines
1.7 KiB
Rust
Raw Normal View History

Introduce deduced parameter attributes, and use them for deducing `readonly` on indirect immutable freeze by-value function parameters. Right now, `rustc` only examines function signatures and the platform ABI when determining the LLVM attributes to apply to parameters. This results in missed optimizations, because there are some attributes that can be determined via analysis of the MIR making up the function body. In particular, `readonly` could be applied to most indirectly-passed by-value function arguments (specifically, those that are freeze and are observed not to be mutated), but it currently is not. This patch introduces the machinery that allows `rustc` to determine those attributes. It consists of a query, `deduced_param_attrs`, that, when evaluated, analyzes the MIR of the function to determine supplementary attributes. The results of this query for each function are written into the crate metadata so that the deduced parameter attributes can be applied to cross-crate functions. In this patch, we simply check the parameter for mutations to determine whether the `readonly` attribute should be applied to parameters that are indirect immutable freeze by-value. More attributes could conceivably be deduced in the future: `nocapture` and `noalias` come to mind. Adding `readonly` to indirect function parameters where applicable enables some potential optimizations in LLVM that are discussed in [issue 103103] and [PR 103070] around avoiding stack-to-stack memory copies that appear in functions like `core::fmt::Write::write_fmt` and `core::panicking::assert_failed`. These functions pass a large structure unchanged by value to a subfunction that also doesn't mutate it. Since the structure in this case is passed as an indirect parameter, it's a pointer from LLVM's perspective. As a result, the intermediate copy of the structure that our codegen emits could be optimized away by LLVM's MemCpyOptimizer if it knew that the pointer is `readonly nocapture noalias` in both the caller and callee. We already pass `nocapture noalias`, but we're missing `readonly`, as we can't determine whether a by-value parameter is mutated by examining the signature in Rust. I didn't have much success with having LLVM infer the `readonly` attribute, even with fat LTO; it seems that deducing it at the MIR level is necessary. No large benefits should be expected from this optimization *now*; LLVM needs some changes (discussed in [PR 103070]) to more aggressively use the `noalias nocapture readonly` combination in its alias analysis. I have some LLVM patches for these optimizations and have had them looked over. With all the patches applied locally, I enabled LLVM to remove all the `memcpy`s from the following code: ```rust fn main() { println!("Hello {}", 3); } ``` which is a significant codegen improvement over the status quo. I expect that if this optimization kicks in in multiple places even for such a simple program, then it will apply to Rust code all over the place. [issue 103103]: https://github.com/rust-lang/rust/issues/103103 [PR 103070]: https://github.com/rust-lang/rust/pull/103070
2022-10-17 21:42:15 -05:00
// compile-flags: -O
#![crate_type = "lib"]
#![allow(incomplete_features)]
#![feature(unsized_locals, unsized_fn_params)]
use std::cell::Cell;
use std::hint;
// Check to make sure that we can deduce the `readonly` attribute from function bodies for
// parameters passed indirectly.
pub struct BigStruct {
blah: [i32; 1024],
}
pub struct BigCellContainer {
blah: [Cell<i32>; 1024],
}
// The by-value parameter for this big struct can be marked readonly.
//
// CHECK: @use_big_struct_immutably({{.*}} readonly {{.*}} %big_struct)
#[no_mangle]
pub fn use_big_struct_immutably(big_struct: BigStruct) {
hint::black_box(&big_struct);
}
// The by-value parameter for this big struct can't be marked readonly, because we mutate it.
//
// CHECK-NOT: @use_big_struct_mutably({{.*}} readonly {{.*}} %big_struct)
#[no_mangle]
pub fn use_big_struct_mutably(mut big_struct: BigStruct) {
big_struct.blah[987] = 654;
hint::black_box(&big_struct);
}
// The by-value parameter for this big struct can't be marked readonly, because it contains
// UnsafeCell.
//
// CHECK-NOT: @use_big_cell_container({{.*}} readonly {{.*}} %big_cell_container)
#[no_mangle]
pub fn use_big_cell_container(big_cell_container: BigCellContainer) {
hint::black_box(&big_cell_container);
}
// Make sure that we don't mistakenly mark a big struct as `readonly` when passed through a generic
// type parameter if it contains UnsafeCell.
//
// CHECK-NOT: @use_something({{.*}} readonly {{.*}} %something)
#[no_mangle]
#[inline(never)]
pub fn use_something<T>(something: T) {
hint::black_box(&something);
}
#[no_mangle]
pub fn forward_big_cell_container(big_cell_container: BigCellContainer) {
use_something(big_cell_container)
}