Skip to content

Commit b65361b

Browse files
authored
Hotfix resolve index projection for references/pointers (#637)
The projections stored within a reference or pointer may include `Index(L:Local)` , indexing with an index read from a local. When creating a reference or pointer, the context is lost, so the actual index must be read from the local and stored as a `ConstantIndex` projection. Also adding a test for a previous change (offset correction when passing structs holding references across stack frame boundaries)
1 parent 2a8f615 commit b65361b

File tree

8 files changed

+176
-13
lines changed

8 files changed

+176
-13
lines changed

kmir/src/kmir/kdist/mir-semantics/rt/data.md

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ Other `Value`s are not expected to have pointer `Metadata` as per their types.
785785

786786
```k
787787
rule <k> rvalueRef(_REGION, KIND, place(local(I), PROJS) #as PLACE)
788-
=> #mkRef(PLACE, #mutabilityOf(KIND), #metadata(tyOfLocal({LOCALS[I]}:>TypedLocal), PROJS, TYPEMAP))
788+
=> #mkRef(PLACE, #mutabilityOf(KIND), #metadata(tyOfLocal({LOCALS[I]}:>TypedLocal), PROJS, TYPEMAP), LOCALS)
789789
...
790790
</k>
791791
<locals> LOCALS </locals>
@@ -794,14 +794,14 @@ Other `Value`s are not expected to have pointer `Metadata` as per their types.
794794
andBool isTypedValue(LOCALS[I])
795795
[preserves-definedness] // valid list indexing checked, #metadata should only use static information
796796
797-
syntax Evaluation ::= #mkRef ( Place , Mutability, Metadata)
798-
| #mkDynSizeRef ( Place , Mutability , Evaluation ) [strict(3)]
797+
syntax Evaluation ::= #mkRef ( Place , Mutability, Metadata, List)
798+
| #mkDynSizeRef ( Place , Mutability , List , Evaluation ) [strict(4)]
799799
800-
rule <k> #mkRef(PLACE, MUT, dynamicSize(_)) => #mkDynSizeRef(PLACE, MUT, operandCopy(PLACE)) ... </k>
801-
rule <k> #mkRef(PLACE, MUT, META ) => Reference(0, PLACE, MUT, META) ... </k> [priority(60)]
800+
rule <k> #mkRef( PLACE , MUT, dynamicSize(_), LOCALS) => #mkDynSizeRef(PLACE, MUT, LOCALS, operandCopy(PLACE)) ... </k>
801+
rule <k> #mkRef(place(LOCAL, PROJS), MUT, META , LOCALS) => Reference(0, place(LOCAL, #resolveProjs(PROJS, LOCALS)), MUT, META) ... </k> [priority(60)]
802802
803803
// with dynamic metadata (reading the value)
804-
rule <k> #mkDynSizeRef(PLACE, MUT, VAL:Value) => Reference(0, PLACE, MUT, metadataFor(VAL)) ... </k>
804+
rule <k> #mkDynSizeRef(place(LOCAL, PROJS), MUT, LOCALS, VAL:Value) => Reference(0, place(LOCAL, #resolveProjs(PROJS, LOCALS)), MUT, metadataFor(VAL)) ... </k>
805805
806806
syntax Metadata ::= metadataFor ( Value ) [function, total]
807807
// --------------------------------------------------------
@@ -813,6 +813,18 @@ Other `Value`s are not expected to have pointer `Metadata` as per their types.
813813
rule #mutabilityOf(borrowKindShared) => mutabilityNot
814814
rule #mutabilityOf(borrowKindFake(_)) => mutabilityNot // Shallow fake borrow disallowed in late stages
815815
rule #mutabilityOf(borrowKindMut(_)) => mutabilityMut // all mutable kinds behave equally for us
816+
817+
// turns Index(LOCAL) projections into ConstantIndex(Int)
818+
syntax ProjectionElems ::= #resolveProjs ( ProjectionElems , List ) [function, total]
819+
// ----------------------------------------------------------------------------------
820+
rule #resolveProjs( .ProjectionElems , _LOCALS) => .ProjectionElems
821+
rule #resolveProjs( projectionElemIndex(local(I)) REST, LOCALS ) => projectionElemConstantIndex(#expectUsize(getValue(LOCALS,I)), 0, false) #resolveProjs(REST, LOCALS)
822+
requires 0 <=Int I
823+
andBool I <Int size(LOCALS)
824+
andBool isTypedValue(LOCALS[I])
825+
andBool isInt(#expectUsize(getValue(LOCALS,I)))
826+
[preserves-definedness]
827+
rule #resolveProjs( OTHER:ProjectionElem REST, LOCALS ) => OTHER #resolveProjs(REST, LOCALS) [owise]
816828
```
817829

818830
A `CopyForDeref` `RValue` has the semantics of a simple `Use(operandCopy(...))`,
@@ -831,7 +843,7 @@ The operation typically creates a pointer with empty metadata.
831843
```k
832844
rule <k> rvalueAddressOf(MUT, place(local(I), PROJS) #as PLACE)
833845
=>
834-
#mkPtr(PLACE, MUT, #metadata(tyOfLocal({LOCALS[I]}:>TypedLocal), PROJS, TYPEMAP))
846+
#mkPtr(PLACE, MUT, #metadata(tyOfLocal({LOCALS[I]}:>TypedLocal), PROJS, TYPEMAP), LOCALS)
835847
// we should use #alignOf to emulate the address
836848
...
837849
</k>
@@ -841,14 +853,23 @@ The operation typically creates a pointer with empty metadata.
841853
andBool isTypedValue(LOCALS[I])
842854
[preserves-definedness] // valid list indexing checked, #metadata should only use static information
843855
844-
syntax Evaluation ::= #mkPtr ( Place , Mutability , Metadata )
845-
| #mkDynLengthPtr ( Place , Mutability, Evaluation ) [strict(3)]
856+
syntax Evaluation ::= #mkPtr ( Place , Mutability , Metadata , List )
857+
| #mkDynLengthPtr ( Place , Mutability , List , Evaluation ) [strict(4)]
846858
847-
rule #mkPtr(PLACE, MUT, dynamicSize(_)) => #mkDynLengthPtr(PLACE, MUT, operandCopy(PLACE))
859+
rule <k> #mkPtr( PLACE , MUT, dynamicSize(_), LOCALS)
860+
=> #mkDynLengthPtr(PLACE, MUT, LOCALS, operandCopy(PLACE))
861+
...
862+
</k>
848863
849-
rule #mkPtr(PLACE, MUT, META) => PtrLocal(0, PLACE, MUT, ptrEmulation(META)) [priority(60)]
864+
rule <k> #mkPtr(place(LOCAL, PROJS), MUT, META , LOCALS)
865+
=> PtrLocal(0, place(LOCAL, #resolveProjs(PROJS, LOCALS)), MUT, ptrEmulation(META))
866+
...
867+
</k> [priority(60)]
850868
851-
rule #mkDynLengthPtr(PLACE, MUT, Range(ELEMS)) => PtrLocal(0, PLACE, MUT, ptrEmulation(dynamicSize(size(ELEMS))))
869+
rule <k> #mkDynLengthPtr(place(LOCAL, PROJS), MUT, LOCALS, Range(ELEMS))
870+
=> PtrLocal(0, place(LOCAL, #resolveProjs(PROJS, LOCALS)), MUT, ptrEmulation(dynamicSize(size(ELEMS))))
871+
...
872+
</k>
852873
```
853874

854875
In practice, the `AddressOf` can often be found applied to references that get dereferenced first,

kmir/src/kmir/linker.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ def apply_offset_typeInfo(typeinfo: dict, offset: int) -> dict:
8080
typeinfo['UnionType']['adt_def'] = typeinfo['UnionType']['adt_def'] + offset
8181
elif 'ArrayType' in typeinfo:
8282
typeinfo['ArrayType']['elem_type'] = typeinfo['ArrayType']['elem_type'] + offset
83+
if 'size' in typeinfo:
84+
apply_offset_tyconst(typeinfo['size'], offset)
8385
elif 'PtrType' in typeinfo:
8486
typeinfo['PtrType']['pointee_type'] = typeinfo['PtrType']['pointee_type'] + offset
8587
elif 'RefType' in typeinfo:

kmir/src/tests/integration/data/exec-smir/arrays/array_write.state

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
ListItem ( typedValue ( Integer ( 0 , 64 , false ) , ty ( 25 ) , mutabilityNot ) )
4040
ListItem ( typedValue ( Integer ( 4 , 64 , false ) , ty ( 25 ) , mutabilityMut ) )
4141
ListItem ( typedValue ( Moved , ty ( 30 ) , mutabilityMut ) )
42-
ListItem ( typedValue ( Reference ( 0 , place (... local: local ( 1 ) , projection: projectionElemIndex ( local ( 6 ) ) .ProjectionElems ) , mutabilityMut , noMetadata ) , ty ( 31 ) , mutabilityNot ) )
42+
ListItem ( typedValue ( Reference ( 0 , place (... local: local ( 1 ) , projection: projectionElemConstantIndex (... offset: 1 , minLength: 0 , fromEnd: false ) .ProjectionElems ) , mutabilityMut , noMetadata ) , ty ( 31 ) , mutabilityNot ) )
4343
ListItem ( typedValue ( Integer ( 1 , 64 , false ) , ty ( 25 ) , mutabilityNot ) )
4444
ListItem ( typedValue ( Integer ( 4 , 64 , false ) , ty ( 25 ) , mutabilityMut ) )
4545
ListItem ( typedValue ( Moved , ty ( 30 ) , mutabilityMut ) )
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// reference into array
2+
3+
fn f(x: &u32) {
4+
assert!(*x == 0);
5+
}
6+
7+
fn g(x: *const u32) {
8+
// assert!(unsafe{*x} == 0); // deref alignment check currently failing
9+
}
10+
11+
fn main() {
12+
let a = [0_u32; 4];
13+
let i = 3;
14+
let x = &a[i];
15+
let xx = x as *const u32;
16+
17+
f(x);
18+
g(xx);
19+
}
20+

kmir/src/tests/integration/data/exec-smir/references/array_elem_ref.smir.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

kmir/src/tests/integration/data/exec-smir/references/array_elem_ref.state

Lines changed: 92 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
struct WithRef<'a> {
2+
a_ref: &'a AThing,
3+
}
4+
5+
struct AThing {
6+
a_field: i8,
7+
another: i16,
8+
}
9+
10+
fn main() {
11+
let a_thing = AThing{ a_field: 1, another: 2};
12+
let a_ref = &a_thing;
13+
let with_ref = WithRef{a_ref};
14+
15+
assert!(with_ref.a_ref.a_field == 1 && a_thing.another == 2); // works
16+
f(with_ref);
17+
}
18+
19+
fn f(w: WithRef) {
20+
assert!(2 * w.a_ref.a_field as i16 == w.a_ref.another);
21+
}

kmir/src/tests/integration/test_integration.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,12 @@ def test_crate_examples(main_crate: Path, kmir: KMIR, update_expected_output: bo
276276
EXEC_DATA_DIR / 'pointers' / 'pointer-cast-length-test-fail.state',
277277
1000,
278278
),
279+
(
280+
'Ref-array-elem-ref',
281+
EXEC_DATA_DIR / 'references' / 'array_elem_ref.smir.json',
282+
EXEC_DATA_DIR / 'references' / 'array_elem_ref.state',
283+
None,
284+
),
279285
]
280286

281287

0 commit comments

Comments
 (0)