Skip to content

Commit a696420

Browse files
committed
add more InitMask test coverage
1 parent 3f80529 commit a696420

File tree

1 file changed

+175
-0
lines changed
  • compiler/rustc_middle/src/mir/interpret/allocation

1 file changed

+175
-0
lines changed

Diff for: compiler/rustc_middle/src/mir/interpret/allocation/tests.rs

+175
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,178 @@ fn uninit_mask() {
1717
assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set");
1818
}
1919
}
20+
21+
/// Returns the number of materialized blocks for this mask.
22+
fn materialized_block_count(mask: &InitMask) -> usize {
23+
match mask.blocks {
24+
InitMaskBlocks::Lazy { .. } => 0,
25+
InitMaskBlocks::Materialized(ref blocks) => blocks.blocks.len(),
26+
}
27+
}
28+
29+
#[test]
30+
fn materialize_mask_within_range() {
31+
// To have spare bits, we use a mask size smaller than its block size of 64.
32+
let mut mask = InitMask::new(Size::from_bytes(16), false);
33+
assert_eq!(materialized_block_count(&mask), 0);
34+
35+
// Forces materialization, but doesn't require growth. This is case #1 documented in the
36+
// `set_range` method.
37+
mask.set_range((8..16).into(), true);
38+
assert_eq!(materialized_block_count(&mask), 1);
39+
40+
for i in 0..8 {
41+
assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set");
42+
}
43+
for i in 8..16 {
44+
assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
45+
}
46+
}
47+
48+
#[test]
49+
fn grow_within_unused_bits_with_full_overwrite() {
50+
// To have spare bits, we use a mask size smaller than its block size of 64.
51+
let mut mask = InitMask::new(Size::from_bytes(16), true);
52+
for i in 0..16 {
53+
assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
54+
}
55+
56+
// Grow without requiring an additional block. Full overwrite.
57+
// This can be fully handled without materialization.
58+
let range = (0..32).into();
59+
mask.set_range(range, true);
60+
61+
for i in 0..32 {
62+
assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
63+
}
64+
65+
assert_eq!(materialized_block_count(&mask), 0);
66+
}
67+
68+
// This test checks that an initmask's spare capacity is correctly used when growing within block
69+
// capacity. This can be fully handled without materialization.
70+
#[test]
71+
fn grow_same_state_within_unused_bits() {
72+
// To have spare bits, we use a mask size smaller than its block size of 64.
73+
let mut mask = InitMask::new(Size::from_bytes(16), true);
74+
for i in 0..16 {
75+
assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
76+
}
77+
78+
// Grow without requiring an additional block. The gap between the current length and the
79+
// range's beginning should be set to the same value as the range.
80+
let range = (24..32).into();
81+
mask.set_range(range, true);
82+
83+
// We want to make sure the unused bits in the first block are correct
84+
for i in 16..24 {
85+
assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
86+
}
87+
88+
for i in 24..32 {
89+
assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
90+
}
91+
92+
assert_eq!(1, mask.range_as_init_chunks((0..32).into()).count());
93+
assert_eq!(materialized_block_count(&mask), 0);
94+
}
95+
96+
// This is the same test as `grow_same_state_within_unused_bits` but with both init and uninit
97+
// states: this forces materialization; otherwise the mask could stay lazy even when needing to
98+
// grow.
99+
#[test]
100+
fn grow_mixed_state_within_unused_bits() {
101+
// To have spare bits, we use a mask size smaller than its block size of 64.
102+
let mut mask = InitMask::new(Size::from_bytes(16), true);
103+
for i in 0..16 {
104+
assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
105+
}
106+
107+
// Grow without requiring an additional block. The gap between the current length and the
108+
// range's beginning should be set to the same value as the range. Note: since this is fully
109+
// out-of-bounds of the current mask, this is case #3 described in the `set_range` method.
110+
let range = (24..32).into();
111+
mask.set_range(range, false);
112+
113+
// We want to make sure the unused bits in the first block are correct
114+
for i in 16..24 {
115+
assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set");
116+
}
117+
118+
for i in 24..32 {
119+
assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set");
120+
}
121+
122+
assert_eq!(1, mask.range_as_init_chunks((0..16).into()).count());
123+
assert_eq!(2, mask.range_as_init_chunks((0..32).into()).count());
124+
assert_eq!(materialized_block_count(&mask), 1);
125+
}
126+
127+
// This is similar to `grow_mixed_state_within_unused_bits` to force materialization, but the range
128+
// to set partially overlaps the mask, so this requires a different growth + write pattern in the
129+
// mask.
130+
#[test]
131+
fn grow_within_unused_bits_with_overlap() {
132+
// To have spare bits, we use a mask size smaller than its block size of 64.
133+
let mut mask = InitMask::new(Size::from_bytes(16), true);
134+
for i in 0..16 {
135+
assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
136+
}
137+
138+
// Grow without requiring an additional block, but leave no gap after the current len. Note:
139+
// since this is partially out-of-bounds of the current mask, this is case #2 described in the
140+
// `set_range` method.
141+
let range = (8..24).into();
142+
mask.set_range(range, false);
143+
144+
// We want to make sure the unused bits in the first block are correct
145+
for i in 8..24 {
146+
assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set");
147+
}
148+
149+
assert_eq!(1, mask.range_as_init_chunks((0..8).into()).count());
150+
assert_eq!(2, mask.range_as_init_chunks((0..24).into()).count());
151+
assert_eq!(materialized_block_count(&mask), 1);
152+
}
153+
154+
// Force materialization before a full overwrite: the mask can now become lazy.
155+
#[test]
156+
fn grow_mixed_state_within_unused_bits_and_full_overwrite() {
157+
// To have spare bits, we use a mask size smaller than its block size of 64.
158+
let mut mask = InitMask::new(Size::from_bytes(16), true);
159+
let range = (0..16).into();
160+
assert!(mask.is_range_initialized(range).is_ok());
161+
162+
// Force materialization.
163+
let range = (8..24).into();
164+
mask.set_range(range, false);
165+
assert!(mask.is_range_initialized(range).is_err());
166+
assert_eq!(materialized_block_count(&mask), 1);
167+
168+
// Full overwrite, lazy blocks would be enough from now on.
169+
let range = (0..32).into();
170+
mask.set_range(range, true);
171+
assert!(mask.is_range_initialized(range).is_ok());
172+
173+
assert_eq!(1, mask.range_as_init_chunks((0..32).into()).count());
174+
assert_eq!(materialized_block_count(&mask), 0);
175+
}
176+
177+
// Check that growth outside the current capacity can still be lazy: if the init state doesn't
178+
// change, we don't need materialized blocks.
179+
#[test]
180+
fn grow_same_state_outside_capacity() {
181+
// To have spare bits, we use a mask size smaller than its block size of 64.
182+
let mut mask = InitMask::new(Size::from_bytes(16), true);
183+
for i in 0..16 {
184+
assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
185+
}
186+
assert_eq!(materialized_block_count(&mask), 0);
187+
188+
// Grow to 10 blocks with the same init state.
189+
let range = (24..640).into();
190+
mask.set_range(range, true);
191+
192+
assert_eq!(1, mask.range_as_init_chunks((0..640).into()).count());
193+
assert_eq!(materialized_block_count(&mask), 0);
194+
}

0 commit comments

Comments
 (0)