2024-04-20 06:19:34 -05:00
|
|
|
//@ test-mir-pass: DataflowConstProp
|
2024-06-14 18:01:26 -05:00
|
|
|
//@ compile-flags: -Zdump-mir-exclude-alloc-bytes
|
2023-09-09 11:47:17 -05:00
|
|
|
// EMIT_MIR_FOR_EACH_BIT_WIDTH
|
2022-11-09 11:21:42 -06:00
|
|
|
|
2023-01-21 16:28:54 -06:00
|
|
|
#![feature(custom_mir, core_intrinsics, rustc_attrs)]
|
|
|
|
|
|
|
|
use std::intrinsics::mir::*;
|
|
|
|
|
2023-05-13 09:19:31 -05:00
|
|
|
#[derive(Copy, Clone)]
|
2022-11-09 11:21:42 -06:00
|
|
|
enum E {
|
|
|
|
V1(i32),
|
2024-06-02 19:18:33 -05:00
|
|
|
V2(i32),
|
2022-11-09 11:21:42 -06:00
|
|
|
}
|
|
|
|
|
2023-01-21 16:28:54 -06:00
|
|
|
// EMIT_MIR enum.simple.DataflowConstProp.diff
|
2024-01-08 00:03:42 -06:00
|
|
|
|
|
|
|
// CHECK-LABEL: fn simple(
|
2023-01-21 16:28:54 -06:00
|
|
|
fn simple() {
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK: debug e => [[e:_.*]];
|
|
|
|
// CHECK: debug x => [[x:_.*]];
|
|
|
|
// CHECK: [[e]] = const E::V1(0_i32);
|
2022-11-09 11:21:42 -06:00
|
|
|
let e = E::V1(0);
|
2024-01-08 00:03:42 -06:00
|
|
|
|
2024-02-11 21:56:03 -06:00
|
|
|
// CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb2, otherwise: bb1];
|
2024-01-12 01:22:33 -06:00
|
|
|
// CHECK: [[target_bb]]: {
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK: [[x]] = const 0_i32;
|
2024-06-02 19:18:33 -05:00
|
|
|
let x = match e {
|
|
|
|
E::V1(x1) => x1,
|
|
|
|
E::V2(x2) => x2,
|
|
|
|
};
|
2022-11-09 11:21:42 -06:00
|
|
|
}
|
2023-01-21 16:28:54 -06:00
|
|
|
|
2023-05-13 07:30:40 -05:00
|
|
|
// EMIT_MIR enum.constant.DataflowConstProp.diff
|
2024-01-08 00:03:42 -06:00
|
|
|
|
|
|
|
// CHECK-LABEL: fn constant(
|
2023-05-13 07:30:40 -05:00
|
|
|
fn constant() {
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK: debug e => [[e:_.*]];
|
|
|
|
// CHECK: debug x => [[x:_.*]];
|
2023-05-13 07:30:40 -05:00
|
|
|
const C: E = E::V1(0);
|
2024-01-08 00:03:42 -06:00
|
|
|
|
2024-03-10 08:05:11 -05:00
|
|
|
// CHECK: [[e]] = const constant::C;
|
2023-05-13 07:30:40 -05:00
|
|
|
let e = C;
|
2024-02-11 21:56:03 -06:00
|
|
|
// CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb2, otherwise: bb1];
|
2024-01-12 01:22:33 -06:00
|
|
|
// CHECK: [[target_bb]]: {
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK: [[x]] = const 0_i32;
|
2024-06-02 19:18:33 -05:00
|
|
|
let x = match e {
|
|
|
|
E::V1(x1) => x1,
|
|
|
|
E::V2(x2) => x2,
|
|
|
|
};
|
2023-05-13 07:30:40 -05:00
|
|
|
}
|
|
|
|
|
2023-05-13 09:19:31 -05:00
|
|
|
// EMIT_MIR enum.statics.DataflowConstProp.diff
|
2024-01-08 00:03:42 -06:00
|
|
|
|
|
|
|
// CHECK-LABEL: fn statics(
|
2023-05-13 09:19:31 -05:00
|
|
|
fn statics() {
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK: debug e1 => [[e1:_.*]];
|
|
|
|
// CHECK: debug x1 => [[x1:_.*]];
|
|
|
|
// CHECK: debug e2 => [[e2:_.*]];
|
|
|
|
// CHECK: debug x2 => [[x2:_.*]];
|
|
|
|
|
2023-05-13 09:19:31 -05:00
|
|
|
static C: E = E::V1(0);
|
2024-01-08 00:03:42 -06:00
|
|
|
|
|
|
|
// CHECK: [[e1]] = const E::V1(0_i32);
|
|
|
|
let e1 = C;
|
2024-02-11 21:56:03 -06:00
|
|
|
// CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb2, otherwise: bb1];
|
2024-01-12 01:22:33 -06:00
|
|
|
// CHECK: [[target_bb]]: {
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK: [[x1]] = const 0_i32;
|
2024-06-02 19:18:33 -05:00
|
|
|
let x1 = match e1 {
|
|
|
|
E::V1(x11) => x11,
|
|
|
|
E::V2(x12) => x12,
|
|
|
|
};
|
2023-05-13 09:19:31 -05:00
|
|
|
|
|
|
|
static RC: &E = &E::V2(4);
|
2024-01-08 00:03:42 -06:00
|
|
|
|
2024-02-14 06:28:07 -06:00
|
|
|
// CHECK: [[t:_.*]] = const {alloc5: &&E};
|
2024-08-18 17:51:53 -05:00
|
|
|
// CHECK: [[e2]] = copy (*[[t]]);
|
2024-01-08 00:03:42 -06:00
|
|
|
let e2 = RC;
|
2024-01-12 01:22:33 -06:00
|
|
|
|
|
|
|
// CHECK: switchInt({{move _.*}}) -> {{.*}}
|
2024-01-08 00:03:42 -06:00
|
|
|
// FIXME: add checks for x2. Currently, their MIRs are not symmetric in the two
|
|
|
|
// switch branches.
|
|
|
|
// One is `_9 = &(*_12) and another is `_9 = _11`. It is different from what we can
|
|
|
|
// get by printing MIR directly. It is better to check if there are any bugs in the
|
|
|
|
// MIR passes around this stage.
|
2024-06-02 19:18:33 -05:00
|
|
|
let x2 = match e2 {
|
|
|
|
E::V1(x21) => x21,
|
|
|
|
E::V2(x22) => x22,
|
|
|
|
};
|
2023-05-13 09:19:31 -05:00
|
|
|
}
|
|
|
|
|
2023-01-21 16:28:54 -06:00
|
|
|
#[rustc_layout_scalar_valid_range_start(1)]
|
|
|
|
#[rustc_nonnull_optimization_guaranteed]
|
|
|
|
struct NonZeroUsize(usize);
|
|
|
|
|
|
|
|
// EMIT_MIR enum.mutate_discriminant.DataflowConstProp.diff
|
2024-01-08 00:03:42 -06:00
|
|
|
|
|
|
|
// CHECK-LABEL: fn mutate_discriminant(
|
2023-01-21 16:28:54 -06:00
|
|
|
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
|
|
|
fn mutate_discriminant() -> u8 {
|
Reformat `mir!` macro invocations to use braces.
The `mir!` macro has multiple parts:
- An optional return type annotation.
- A sequence of zero or more local declarations.
- A mandatory starting anonymous basic block, which is brace-delimited.
- A sequence of zero of more additional named basic blocks.
Some `mir!` invocations use braces with a "block" style, like so:
```
mir! {
let _unit: ();
{
let non_copy = S(42);
let ptr = std::ptr::addr_of_mut!(non_copy);
// Inside `callee`, the first argument and `*ptr` are basically
// aliasing places!
Call(_unit = callee(Move(*ptr), ptr), ReturnTo(after_call), UnwindContinue())
}
after_call = {
Return()
}
}
```
Some invocations use parens with a "block" style, like so:
```
mir!(
let x: [i32; 2];
let one: i32;
{
x = [42, 43];
one = 1;
x = [one, 2];
RET = Move(x);
Return()
}
)
```
And some invocations uses parens with a "tighter" style, like so:
```
mir!({
SetDiscriminant(*b, 0);
Return()
})
```
This last style is generally used for cases where just the mandatory
starting basic block is present. Its braces are placed next to the
parens.
This commit changes all `mir!` invocations to use braces with a "block"
style. Why?
- Consistency is good.
- The contents of the invocation is a block of code, so it's odd to use
parens. They are more normally used for function-like macros.
- Most importantly, the next commit will enable rustfmt for
`tests/mir-opt/`. rustfmt is more aggressive about formatting macros
that use parens than macros that use braces. Without this commit's
changes, rustfmt would break a couple of `mir!` macro invocations that
use braces within `tests/mir-opt` by inserting an extraneous comma.
E.g.:
```
mir!(type RET = (i32, bool);, { // extraneous comma after ';'
RET.0 = 1;
RET.1 = true;
Return()
})
```
Switching those `mir!` invocations to use braces avoids that problem,
resulting in this, which is nicer to read as well as being valid
syntax:
```
mir! {
type RET = (i32, bool);
{
RET.0 = 1;
RET.1 = true;
Return()
}
}
```
2024-06-02 19:19:57 -05:00
|
|
|
mir! {
|
2023-01-21 16:28:54 -06:00
|
|
|
let x: Option<NonZeroUsize>;
|
|
|
|
{
|
|
|
|
SetDiscriminant(x, 1);
|
|
|
|
// This assignment overwrites the niche in which the discriminant is stored.
|
|
|
|
place!(Field(Field(Variant(x, 1), 0), 0)) = 0_usize;
|
|
|
|
// So we cannot know the value of this discriminant.
|
2024-01-08 00:03:42 -06:00
|
|
|
|
2024-01-12 01:22:33 -06:00
|
|
|
// CHECK: [[a:_.*]] = discriminant({{_.*}});
|
2023-01-21 16:28:54 -06:00
|
|
|
let a = Discriminant(x);
|
2024-01-08 00:03:42 -06:00
|
|
|
|
2024-08-18 17:51:53 -05:00
|
|
|
// CHECK: switchInt(copy [[a]]) -> [0: {{bb.*}}, otherwise: {{bb.*}}];
|
2023-01-21 16:28:54 -06:00
|
|
|
match a {
|
|
|
|
0 => bb1,
|
|
|
|
_ => bad,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bb1 = {
|
|
|
|
RET = 1;
|
|
|
|
Return()
|
|
|
|
}
|
|
|
|
bad = {
|
|
|
|
RET = 2;
|
|
|
|
Unreachable()
|
|
|
|
}
|
Reformat `mir!` macro invocations to use braces.
The `mir!` macro has multiple parts:
- An optional return type annotation.
- A sequence of zero or more local declarations.
- A mandatory starting anonymous basic block, which is brace-delimited.
- A sequence of zero of more additional named basic blocks.
Some `mir!` invocations use braces with a "block" style, like so:
```
mir! {
let _unit: ();
{
let non_copy = S(42);
let ptr = std::ptr::addr_of_mut!(non_copy);
// Inside `callee`, the first argument and `*ptr` are basically
// aliasing places!
Call(_unit = callee(Move(*ptr), ptr), ReturnTo(after_call), UnwindContinue())
}
after_call = {
Return()
}
}
```
Some invocations use parens with a "block" style, like so:
```
mir!(
let x: [i32; 2];
let one: i32;
{
x = [42, 43];
one = 1;
x = [one, 2];
RET = Move(x);
Return()
}
)
```
And some invocations uses parens with a "tighter" style, like so:
```
mir!({
SetDiscriminant(*b, 0);
Return()
})
```
This last style is generally used for cases where just the mandatory
starting basic block is present. Its braces are placed next to the
parens.
This commit changes all `mir!` invocations to use braces with a "block"
style. Why?
- Consistency is good.
- The contents of the invocation is a block of code, so it's odd to use
parens. They are more normally used for function-like macros.
- Most importantly, the next commit will enable rustfmt for
`tests/mir-opt/`. rustfmt is more aggressive about formatting macros
that use parens than macros that use braces. Without this commit's
changes, rustfmt would break a couple of `mir!` macro invocations that
use braces within `tests/mir-opt` by inserting an extraneous comma.
E.g.:
```
mir!(type RET = (i32, bool);, { // extraneous comma after ';'
RET.0 = 1;
RET.1 = true;
Return()
})
```
Switching those `mir!` invocations to use braces avoids that problem,
resulting in this, which is nicer to read as well as being valid
syntax:
```
mir! {
type RET = (i32, bool);
{
RET.0 = 1;
RET.1 = true;
Return()
}
}
```
2024-06-02 19:19:57 -05:00
|
|
|
}
|
2023-01-21 16:28:54 -06:00
|
|
|
}
|
|
|
|
|
2023-02-18 03:35:52 -06:00
|
|
|
// EMIT_MIR enum.multiple.DataflowConstProp.diff
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK-LABEL: fn multiple(
|
2023-02-18 03:35:52 -06:00
|
|
|
fn multiple(x: bool, i: u8) {
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK: debug x => [[x:_.*]];
|
|
|
|
// CHECK: debug e => [[e:_.*]];
|
|
|
|
// CHECK: debug x2 => [[x2:_.*]];
|
2024-01-12 22:50:28 -06:00
|
|
|
// CHECK: debug y => [[y:_.*]];
|
2023-02-18 03:35:52 -06:00
|
|
|
let e = if x {
|
2024-01-12 01:22:33 -06:00
|
|
|
// CHECK: [[e]] = Option::<u8>::Some(move {{_.*}});
|
2023-02-18 03:35:52 -06:00
|
|
|
Some(i)
|
|
|
|
} else {
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK: [[e]] = Option::<u8>::None;
|
2023-02-18 03:35:52 -06:00
|
|
|
None
|
|
|
|
};
|
|
|
|
// The dataflow state must have:
|
|
|
|
// discriminant(e) => Top
|
|
|
|
// (e as Some).0 => Top
|
2024-01-08 00:03:42 -06:00
|
|
|
// CHECK: [[x2]] = const 0_u8;
|
2024-08-18 17:51:53 -05:00
|
|
|
// CHECK: [[some:_.*]] = copy (({{_.*}} as Some).0: u8)
|
|
|
|
// CHECK: [[x2]] = copy [[some]];
|
2024-06-02 19:18:33 -05:00
|
|
|
let x2 = match e {
|
|
|
|
Some(i) => i,
|
|
|
|
None => 0,
|
|
|
|
};
|
2024-01-12 22:50:28 -06:00
|
|
|
|
2024-01-08 00:03:42 -06:00
|
|
|
// Therefore, `x2` should be `Top` here, and no replacement shall happen.
|
2024-01-12 22:50:28 -06:00
|
|
|
|
|
|
|
// CHECK-NOT: [[y]] = const
|
2024-08-18 17:51:53 -05:00
|
|
|
// CHECK: [[y]] = copy [[x2]];
|
2024-01-12 22:50:28 -06:00
|
|
|
// CHECK-NOT: [[y]] = const
|
2024-01-08 00:03:42 -06:00
|
|
|
let y = x2;
|
2023-02-18 03:35:52 -06:00
|
|
|
}
|
|
|
|
|
2023-01-21 16:28:54 -06:00
|
|
|
fn main() {
|
|
|
|
simple();
|
2023-05-13 07:30:40 -05:00
|
|
|
constant();
|
2023-05-13 09:19:31 -05:00
|
|
|
statics();
|
2023-01-21 16:28:54 -06:00
|
|
|
mutate_discriminant();
|
2023-02-18 03:35:52 -06:00
|
|
|
multiple(false, 5);
|
2023-01-21 16:28:54 -06:00
|
|
|
}
|