-
Notifications
You must be signed in to change notification settings - Fork 4
feat(rt): support round-trip transmute bytes <> u64 casts #804
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
a131661
25f20ec
9ec997d
880521d
08309cb
eb6be66
b101337
63d30ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1406,6 +1406,92 @@ The first cast is reified as a `thunk`, the second one resolves it and eliminate | |
| andBool lookupTy(TY_DEST_INNER) ==K lookupTy(TY_SRC_OUTER) // and is well-formed (invariant) | ||
| ``` | ||
|
|
||
| Casting a byte array/slice to an integer reinterprets the bytes in little-endian order. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect this actually depends on architecture, and we do have access to that information in the configuration via the
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we just add a |
||
|
|
||
| ```k | ||
| rule <k> #cast( | ||
| Range(ELEMS), | ||
| castKindTransmute, | ||
| _TY_SOURCE, | ||
| TY_TARGET | ||
| ) | ||
| => | ||
| #intAsType( | ||
| #littleEndianFromBytes(ELEMS), | ||
| size(ELEMS) *Int 8, | ||
| #numTypeOf(lookupTy(TY_TARGET)) | ||
| ) | ||
| ... | ||
| </k> | ||
| requires #isIntType(lookupTy(TY_TARGET)) | ||
| andBool size(ELEMS) *Int 8 ==Int #bitWidth(#numTypeOf(lookupTy(TY_TARGET))) | ||
| andBool #areLittleEndianBytes(ELEMS) | ||
| [preserves-definedness] // ensures #numTypeOf is defined | ||
|
|
||
| syntax Bool ::= #areLittleEndianBytes ( List ) [function, total] | ||
| // ------------------------------------------------------------- | ||
| rule #areLittleEndianBytes(.List) => true | ||
| rule #areLittleEndianBytes(ListItem(Integer(_, 8, false)) REST) | ||
| => #areLittleEndianBytes(REST) | ||
| rule #areLittleEndianBytes(ListItem(_OTHER) _) => false [owise] | ||
|
|
||
| syntax Int ::= #littleEndianFromBytes ( List ) [function] | ||
| // ----------------------------------------------------- | ||
| rule #littleEndianFromBytes(.List) => 0 | ||
| rule #littleEndianFromBytes(ListItem(Integer(BYTE, 8, false)) REST) | ||
| => BYTE +Int 256 *Int #littleEndianFromBytes(REST) | ||
| ``` | ||
|
|
||
| Casting an integer to a `[u8; N]` array materialises its little-endian bytes. | ||
|
|
||
| ```k | ||
| rule <k> #cast( | ||
| Integer(VAL, WIDTH, _SIGNEDNESS), | ||
| castKindTransmute, | ||
| _TY_SOURCE, | ||
| TY_TARGET | ||
| ) | ||
| => | ||
| Range(#littleEndianBytesFromInt(VAL, WIDTH)) | ||
| ... | ||
| </k> | ||
| requires #isStaticU8Array(lookupTy(TY_TARGET)) | ||
| andBool WIDTH >=Int 0 | ||
| andBool WIDTH %Int 8 ==Int 0 | ||
| andBool WIDTH ==Int #staticArrayLenBits(lookupTy(TY_TARGET)) | ||
| [preserves-definedness] // ensures element type/length are well-formed | ||
|
|
||
| syntax List ::= #littleEndianBytesFromInt ( Int, Int ) [function] | ||
| // ------------------------------------------------------------- | ||
| rule #littleEndianBytesFromInt(VAL, WIDTH) | ||
| => #littleEndianBytes(truncate(VAL, WIDTH, Unsigned), WIDTH /Int 8) | ||
| requires WIDTH %Int 8 ==Int 0 | ||
| andBool WIDTH >=Int 0 | ||
|
Stevengre marked this conversation as resolved.
|
||
|
|
||
| syntax List ::= #littleEndianBytes ( Int, Int ) [function] | ||
| // ------------------------------------------------------------- | ||
| rule #littleEndianBytes(_, COUNT) | ||
| => .List | ||
| requires COUNT <=Int 0 | ||
|
|
||
| rule #littleEndianBytes(VAL, COUNT) | ||
| => ListItem(Integer(VAL %Int 256, 8, false)) #littleEndianBytes(VAL /Int 256, COUNT -Int 1) | ||
| requires COUNT >Int 0 | ||
|
Stevengre marked this conversation as resolved.
|
||
|
|
||
| syntax Bool ::= #isStaticU8Array ( TypeInfo ) [function, total] | ||
| // ------------------------------------------------------------- | ||
| rule #isStaticU8Array(typeInfoArrayType(ELEM_TY, someTyConst(_))) | ||
| => lookupTy(ELEM_TY) ==K typeInfoPrimitiveType(primTypeUint(uintTyU8)) | ||
| rule #isStaticU8Array(_OTHER) => false [owise] | ||
|
|
||
| syntax Int ::= #staticArrayLenBits ( TypeInfo ) [function, total] | ||
| // ------------------------------------------------------------- | ||
| rule #staticArrayLenBits(typeInfoArrayType(_, someTyConst(tyConst(KIND, _)))) | ||
| => readTyConstInt(KIND) *Int 8 | ||
| [preserves-definedness] | ||
| rule #staticArrayLenBits(_OTHER) => 0 [owise] | ||
| ``` | ||
|
|
||
| Another specialisation is getting the discriminant of `enum`s without fields after converting some integer data to it | ||
| (see `#discriminant` and `rvalueDiscriminant`). | ||
| 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: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| fn main() { | ||
| let value = 0x0123_4567_89AB_CDEF_u64; | ||
|
|
||
| let native_bytes = value.to_ne_bytes(); | ||
| assert_eq!(u64::from_ne_bytes(native_bytes), value); | ||
|
|
||
| let little_bytes = value.to_le_bytes(); | ||
| assert_eq!(u64::from_le_bytes(little_bytes), value); | ||
|
|
||
| let big_bytes = value.to_be_bytes(); | ||
| assert_eq!(u64::from_be_bytes(big_bytes), value); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
|
|
||
| ┌─ 1 (root, init) | ||
| │ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC | ||
| │ span: 0 | ||
| │ | ||
| │ (332 steps) | ||
| └─ 3 (stuck, leaf) | ||
| #execIntrinsic ( IntrinsicFunction ( symbol ( "bswap" ) ) , operandMove ( place | ||
|
Stevengre marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| ┌─ 2 (root, leaf, target, terminal) | ||
| │ #EndProgram ~> .K | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #![allow(clippy::transmute_bytes_to_bytes)] | ||
| #![allow(clippy::unnecessary_transmute)] | ||
|
|
||
| use std::mem::transmute; | ||
|
|
||
| fn bytes_to_u64(bytes: [u8; 8]) -> u64 { | ||
| let value = unsafe { transmute::<[u8; 8], u64>(bytes) }; | ||
| let roundtrip = unsafe { transmute::<u64, [u8; 8]>(value) }; | ||
| assert_eq!(roundtrip, bytes); | ||
| value | ||
| } | ||
|
|
||
| fn u64_to_bytes(value: u64) -> [u8; 8] { | ||
| let bytes = unsafe { transmute::<u64, [u8; 8]>(value) }; | ||
| let roundtrip = unsafe { transmute::<[u8; 8], u64>(bytes) }; | ||
| assert_eq!(roundtrip, value); | ||
| bytes | ||
| } | ||
|
|
||
| fn main() { | ||
| let bytes = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]; | ||
| let int = bytes_to_u64(bytes); | ||
| assert_eq!(int, 0xEFCD_AB89_6745_2301); | ||
|
|
||
| let roundtrip = u64_to_bytes(int); | ||
| assert_eq!(roundtrip, bytes); | ||
|
|
||
| let value = 0x1020_3040_5060_7080_u64; | ||
| let value_roundtrip = bytes_to_u64(u64_to_bytes(value)); | ||
| assert_eq!(value_roundtrip, value); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.