Skip to content

Commit ecf9336

Browse files
committed
feat: support roundtrip pointer <> int cast
1 parent 470f857 commit ecf9336

File tree

3 files changed

+376
-34
lines changed

3 files changed

+376
-34
lines changed

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

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ requires "./types.md"
1010
requires "./value.md"
1111
requires "./numbers.md"
1212
requires "./decoding.md"
13+
requires "./pointer-int.md"
1314
1415
module RT-DATA
1516
imports INT
@@ -27,6 +28,7 @@ module RT-DATA
2728
imports RT-NUMBERS
2829
imports RT-DECODING
2930
imports RT-TYPES
31+
imports RT-POINTER-INT
3032
imports KMIR-CONFIGURATION
3133
3234
```
@@ -316,7 +318,6 @@ These helpers mark down, as we traverse the projection, what `Place` we are curr
316318
| CtxSubslice( List , Int , Int ) // start and end always counted from beginning
317319
| CtxPointerOffset( List, Int, Int ) // pointer offset for accessing elements with an offset (Offset, Origin Length)
318320
319-
syntax ProjectionElem ::= PointerOffset( Int, Int ) // Same as subslice but coming from BinopOffset injected by us
320321
321322
syntax Contexts ::= List{Context, ""}
322323
@@ -1456,28 +1457,6 @@ What can be supported without additional layout consideration is trivial casts b
14561457
requires lookupTy(TY_SOURCE) ==K lookupTy(TY_TARGET)
14571458
```
14581459

1459-
Transmuting a pointer to an integer discards provenance and reinterprets the pointer’s offset as a value of the target integer type.
1460-
1461-
```k
1462-
// `prove-rs/interior-mut3.rs` needs this
1463-
// TODO: check its correctness, I assume the pointer offset is the address here and we can use it to recover the PtrLocal
1464-
rule <k> #cast(
1465-
PtrLocal(_, _, _, metadata(_, PTR_OFFSET, _)),
1466-
castKindTransmute,
1467-
_TY_SOURCE,
1468-
TY_TARGET
1469-
)
1470-
=>
1471-
#intAsType(
1472-
PTR_OFFSET,
1473-
#bitWidth(#numTypeOf(lookupTy(TY_TARGET))),
1474-
#numTypeOf(lookupTy(TY_TARGET))
1475-
)
1476-
...
1477-
</k>
1478-
requires #isIntType(lookupTy(TY_TARGET))
1479-
```
1480-
14811460
Other `Transmute` casts that can be resolved are round-trip casts from type A to type B and then directly back from B to A.
14821461
The first cast is reified as a `thunk`, the second one resolves it and eliminates the `thunk`:
14831462

@@ -1582,17 +1561,57 @@ Casting an integer to a `[u8; N]` array materialises its little-endian bytes.
15821561
rule #staticArrayLenBits(_OTHER) => 0 [owise]
15831562
```
15841563

1585-
A transmutation from an integer to an enum is wellformed if:
1586-
- The bit width of the incoming integer is the same as the discriminant type of the enum
1587-
(e.g. `u8 -> i8` fine, `u8 -> u16` not fine) - this is guaranteed by the compiler;
1588-
- The incoming integer has a bit pattern that matches a discriminant of the enum
1589-
(e.g. `255_u8` and `-1_i8` fine iff `0b1111_1111` is a discriminant of the enum);
1590-
1591-
Note that discriminants are stored as `u128` in the type data even if they are signed
1592-
or unsigned at the source level. This means that our approach to soundly transmute an
1593-
integer into a enum is to treat the incoming integer as unsigned (converting if signed),
1594-
and check if the value is in the discriminants. If yes, find the corresponding variant
1595-
index; if not, return `#UBErrorInvalidDiscriminantsInEnumCast`.
1564+
Transmuting a pointer to an integer uses the helpers defined in `RT-POINTER-INT` to encode the entire pointer (stack depth, place, mutability, and metadata) into a single natural number; decoding applies the inverse helpers. The encoding must fit into the target integer bit-width, otherwise the transmute is stuck.
1565+
1566+
```k
1567+
syntax Value ::= #ptrWithOffset ( Value , Int , TypeInfo ) [function, total]
1568+
rule #ptrWithOffset(PtrLocal(STACK, PLACE, MUT, metadata(SIZE, _, ORIGIN)), OFFSET, TYINFO)
1569+
=> PtrLocal(STACK, PLACE, MUT, #convertMetadata(metadata(SIZE, OFFSET, ORIGIN), TYINFO))
1570+
rule #ptrWithOffset(VAL:Value, _, _) => VAL [owise]
1571+
1572+
rule <k> #cast(
1573+
PtrLocal(_, _, _, _) #as PTR,
1574+
castKindTransmute,
1575+
TY_SOURCE,
1576+
TY_TARGET
1577+
)
1578+
=>
1579+
#intAsType(
1580+
#encodePtrInt(PTR, pointeeTy(lookupTy(TY_SOURCE))),
1581+
#bitWidth(#numTypeOf(lookupTy(TY_TARGET))),
1582+
#numTypeOf(lookupTy(TY_TARGET))
1583+
)
1584+
...
1585+
</k>
1586+
requires #isIntType(lookupTy(TY_TARGET))
1587+
andBool #fitsInWidth(
1588+
#encodePtrInt(PTR, pointeeTy(lookupTy(TY_SOURCE))),
1589+
#bitWidth(#numTypeOf(lookupTy(TY_TARGET)))
1590+
)
1591+
1592+
rule <k> #cast(
1593+
Integer(VAL, WIDTH, SIGNED) #as _INT,
1594+
castKindTransmute,
1595+
TY_SOURCE,
1596+
TY_TARGET
1597+
)
1598+
=>
1599+
#ptrWithOffset(
1600+
#decodePtrBase(#cantorUnpairLeft(#unsignedFromIntValue(VAL, WIDTH, SIGNED))),
1601+
#bytesToPtrOffset(
1602+
#cantorUnpairRight(#unsignedFromIntValue(VAL, WIDTH, SIGNED)),
1603+
pointeeTy(lookupTy(TY_TARGET))
1604+
),
1605+
lookupTy(TY_TARGET)
1606+
)
1607+
...
1608+
</k>
1609+
requires #isIntType(lookupTy(TY_SOURCE))
1610+
```
1611+
1612+
Another specialisation is getting the discriminant of `enum`s without fields after converting some integer data to it
1613+
(see `#discriminant` and `rvalueDiscriminant`).
1614+
If none of the `enum` variants has any fields, the `Transmute` of a number to the `enum` data is necessarily just the discriminant itself., and can be returned as the integer value afgter adjusting to the byte length of the discriminant:
15961615

15971616
```k
15981617
syntax Bool ::= #isEnumWithoutFields ( TypeInfo ) [function, total]

0 commit comments

Comments
 (0)