Skip to content

Commit

Permalink
Semantics for operation evaluation (#406)
Browse files Browse the repository at this point in the history
* init

* Unchecked_OP, Add, Sub, Mul done.

* Signed overflow algorithm

* Shift instructions

* equality / comparisons

* Set Version: 0.3.36

* Tidying

* Update kmir/src/kmir/kdist/mir-semantics/utils.k

Co-authored-by: Jost Berthold <[email protected]>

* Set Version: 0.3.37

* removed unused MINT import

* Set Version: 0.3.37

* fixed typo

---------

Co-authored-by: devops <[email protected]>
Co-authored-by: Jost Berthold <[email protected]>
  • Loading branch information
3 people authored Aug 20, 2024
1 parent 256abec commit 99d812f
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 24 deletions.
2 changes: 1 addition & 1 deletion kmir/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "kmir"
version = "0.3.36"
version = "0.3.37"
description = ""
authors = [
"Runtime Verification, Inc. <[email protected]>",
Expand Down
2 changes: 1 addition & 1 deletion kmir/src/kmir/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from typing import Final

VERSION: Final = '0.3.36'
VERSION: Final = '0.3.37'
77 changes: 56 additions & 21 deletions kmir/src/kmir/kdist/mir-semantics/kmir.k
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
requires "kmir-ast.k"
requires "utils.k"

module KMIR-SYNTAX
imports KMIR-AST
Expand Down Expand Up @@ -26,6 +27,7 @@ module KMIR-MEMORY
imports KMIR-SYNTAX
imports LIST
imports MAP
imports UTILS

syntax Value

Expand Down Expand Up @@ -59,7 +61,7 @@ endmodule
// 6. #copy
//
// TODO: fixup incorrect function implementations
// 1. #evalOp
// 1. #evalOp - Pointers
// 2. #evalCast
// 3. #makeTLRef

Expand Down Expand Up @@ -273,29 +275,62 @@ module KMIR-MEMORY-IMPL [private]
// Eval Impl

rule <k> #deinitPlace( _PLACE ) => .K ... </k>

rule #evalOp( binOpAdd, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Struct( 0, ListItem(Scalar( N1 +Int N2, WIDTH, SIGN )) ListItem(Scalar( 0, 1, false )) ))
rule #evalOp( binOpAddUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 +Int N2, WIDTH, SIGN ))
rule #evalOp( binOpSub, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Struct( 0, ListItem(Scalar( N1 -Int N2, WIDTH, SIGN )) ListItem(Scalar( 0, 1, false )) ))
rule #evalOp( binOpSubUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 -Int N2, WIDTH, SIGN ))
rule #evalOp( binOpMul, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Struct( 0, ListItem(Scalar( N1 *Int N2, WIDTH, SIGN )) ListItem(Scalar( 0, 1, false )) ))
rule #evalOp( binOpMulUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 *Int N2, WIDTH, SIGN ))
rule #evalOp( binOpDiv, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 /Int N2, WIDTH, SIGN ))
rule #evalOp( binOpRem, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 %Int N2, WIDTH, SIGN ))

// Unchecked only has defined behaviour when in range
rule #evalOp( binOpAddUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 +Int N2, WIDTH, SIGN )) requires 0 <=Int N1 +Int N2 andBool N1 +Int N2 <=Int #width_max(WIDTH, SIGN)
rule #evalOp( binOpSubUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 -Int N2, WIDTH, SIGN )) requires 0 <=Int N1 -Int N2 andBool N1 -Int N2 <=Int #width_max(WIDTH, SIGN)
rule #evalOp( binOpMulUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 *Int N2, WIDTH, SIGN )) requires 0 <=Int N1 *Int N2 andBool N1 *Int N2 <=Int #width_max(WIDTH, SIGN)
rule #evalOp( binOpShlUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 <<Int N2, WIDTH, SIGN )) requires 0 <=Int N1 <<Int N2 andBool N1 <<Int N2 <=Int #width_max(WIDTH, SIGN)
rule #evalOp( binOpShrUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 >>Int N2, WIDTH, SIGN )) requires 0 <=Int N1 >>Int N2 andBool N1 >>Int N2 <=Int #width_max(WIDTH, SIGN)

// No overflow
rule #evalOp( binOpAdd, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Struct( 0, ListItem(Scalar( N1 +Int N2, WIDTH, SIGN )) ListItem(Scalar( 0, 1, SIGN )) )) requires #width_min(WIDTH, SIGN) <=Int N1 +Int N2 andBool N1 +Int N2 <=Int #width_max(WIDTH, SIGN)
rule #evalOp( binOpSub, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Struct( 0, ListItem(Scalar( N1 -Int N2, WIDTH, SIGN )) ListItem(Scalar( 0, 1, SIGN )) )) requires #width_min(WIDTH, SIGN) <=Int N1 -Int N2 andBool N1 -Int N2 <=Int #width_max(WIDTH, SIGN)
rule #evalOp( binOpMul, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Struct( 0, ListItem(Scalar( N1 *Int N2, WIDTH, SIGN )) ListItem(Scalar( 0, 1, SIGN )) )) requires #width_min(WIDTH, SIGN) <=Int N1 *Int N2 andBool N1 *Int N2 <=Int #width_max(WIDTH, SIGN)
rule #evalOp( binOpShl, ListItem(Scalar(N1, WIDTH1, true)) ListItem(Scalar(N2, WIDTH2, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 <<Int N2, WIDTH1, true), WIDTH2, true )) ListItem(Scalar( 0, 1, false )) )) requires 0 <=Int WIDTH2 orBool WIDTH2 <Int WIDTH1
rule #evalOp( binOpShr, ListItem(Scalar(N1, WIDTH1, true)) ListItem(Scalar(N2, WIDTH2, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 >>Int N2, WIDTH1, true), WIDTH2, true )) ListItem(Scalar( 0, 1, false )) )) requires 0 <=Int WIDTH2 orBool WIDTH2 <Int WIDTH1
rule #evalOp( binOpShl, ListItem(Scalar(N1, WIDTH1, true)) ListItem(Scalar(N2, WIDTH2, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 <<Int N2, WIDTH1, true), WIDTH2, true )) ListItem(Scalar( 0, 1, false )) )) requires WIDTH2 <Int WIDTH1
rule #evalOp( binOpShr, ListItem(Scalar(N1, WIDTH1, true)) ListItem(Scalar(N2, WIDTH2, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 >>Int N2, WIDTH1, true), WIDTH2, true )) ListItem(Scalar( 0, 1, false )) )) requires WIDTH2 <Int WIDTH1

// Unsigned and overflow
rule #evalOp( binOpAdd, ListItem(Scalar(N1, WIDTH, false)) ListItem(Scalar(N2, WIDTH, false)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 +Int N2, WIDTH, false), WIDTH, false )) ListItem(Scalar( 1, 1, false )) )) requires #width_max(WIDTH, false) <Int N1 +Int N2
rule #evalOp( binOpSub, ListItem(Scalar(N1, WIDTH, false)) ListItem(Scalar(N2, WIDTH, false)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 -Int N2, WIDTH, false), WIDTH, false )) ListItem(Scalar( 1, 1, false )) )) requires #width_max(WIDTH, false) <Int N1 -Int N2
rule #evalOp( binOpMul, ListItem(Scalar(N1, WIDTH, false)) ListItem(Scalar(N2, WIDTH, false)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 *Int N2, WIDTH, false), WIDTH, false )) ListItem(Scalar( 1, 1, false )) )) requires #width_max(WIDTH, false) <Int N1 *Int N2
rule #evalOp( binOpShl, ListItem(Scalar(N1, WIDTH1, true)) ListItem(Scalar(N2, WIDTH2, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 <<Int N2, WIDTH1, true), WIDTH2, true )) ListItem(Scalar( 1, 1, false )) )) requires WIDTH1 <=Int WIDTH2
rule #evalOp( binOpShr, ListItem(Scalar(N1, WIDTH1, true)) ListItem(Scalar(N2, WIDTH2, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 >>Int N2, WIDTH1, true), WIDTH2, true )) ListItem(Scalar( 1, 1, false )) )) requires WIDTH1 <=Int WIDTH2

// Signed and overflow / underflow
rule #evalOp( binOpAdd, ListItem(Scalar(N1, WIDTH, true)) ListItem(Scalar(N2, WIDTH, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 +Int N2, WIDTH, true), WIDTH, true )) ListItem(Scalar( 1, 1, true )) )) requires N1 +Int N2 <Int #width_min(WIDTH, true) andBool #width_max(WIDTH, true) <Int N1 +Int N2
rule #evalOp( binOpSub, ListItem(Scalar(N1, WIDTH, true)) ListItem(Scalar(N2, WIDTH, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 -Int N2, WIDTH, true), WIDTH, true )) ListItem(Scalar( 1, 1, true )) )) requires N1 -Int N2 <Int #width_min(WIDTH, true) andBool #width_max(WIDTH, true) <Int N1 -Int N2
rule #evalOp( binOpMul, ListItem(Scalar(N1, WIDTH, true)) ListItem(Scalar(N2, WIDTH, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 *Int N2, WIDTH, true), WIDTH, true )) ListItem(Scalar( 1, 1, true )) )) requires N1 *Int N2 <Int #width_min(WIDTH, true) andBool #width_max(WIDTH, true) <Int N1 *Int N2
rule #evalOp( binOpShl, ListItem(Scalar(N1, WIDTH1, true)) ListItem(Scalar(N2, WIDTH2, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 <<Int N2, WIDTH1, true), WIDTH2, true )) ListItem(Scalar( 1, 1, false )) )) [owise]
rule #evalOp( binOpShr, ListItem(Scalar(N1, WIDTH1, true)) ListItem(Scalar(N2, WIDTH2, true)) ) => #value(Struct( 0, ListItem(Scalar( #chop(N1 >>Int N2, WIDTH1, true), WIDTH2, true )) ListItem(Scalar( 1, 1, false )) )) [owise]

// Bitwise (not shifts)
rule #evalOp( binOpBitXor, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 xorInt N2, WIDTH, SIGN ))
rule #evalOp( binOpBitAnd, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 &Int N2, WIDTH, SIGN ))
rule #evalOp( binOpBitOr, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 |Int N2, WIDTH, SIGN ))
rule #evalOp( binOpShl, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Struct( 0, ListItem(Scalar( N1 <<Int N2, WIDTH, SIGN )) ListItem(Scalar( 0, 1, false )) ))
rule #evalOp( binOpShlUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 <<Int N2, WIDTH, SIGN ))
rule #evalOp( binOpShr, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Struct( 0, ListItem(Scalar( N1 >>Int N2, WIDTH, SIGN )) ListItem(Scalar( 0, 1, false )) ))
rule #evalOp( binOpShrUnchecked, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value(Scalar( N1 >>Int N2, WIDTH, SIGN ))
rule #evalOp( binOpEq, ListItem(_Arg1) ListItem(_Arg2) ) => #value( Any )
rule #evalOp( binOpLt, ListItem(_Arg1) ListItem(_Arg2) ) => #value( Any )
rule #evalOp( binOpLe, ListItem(_Arg1) ListItem(_Arg2) ) => #value( Any )
rule #evalOp( binOpNe, ListItem(_Arg1) ListItem(_Arg2) ) => #value( Any )
rule #evalOp( binOpGe, ListItem(_Arg1) ListItem(_Arg2) ) => #value( Any )
rule #evalOp( binOpGt, ListItem(_Arg1) ListItem(_Arg2) ) => #value( Any )
rule #evalOp( binOpCmp, ListItem(_Arg1) ListItem(_Arg2) ) => #value( Any )

// Comparison operations
rule #evalOp( binOpEq, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(1, 1, false) ) requires N1 ==Int N2
rule #evalOp( binOpLt, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(1, 1, false) ) requires N1 <Int N2
rule #evalOp( binOpLe, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(1, 1, false) ) requires N1 <=Int N2
rule #evalOp( binOpNe, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(1, 1, false) ) requires N1 =/=Int N2
rule #evalOp( binOpGe, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(1, 1, false) ) requires N1 >=Int N2
rule #evalOp( binOpGt, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(1, 1, false) ) requires N1 >Int N2

rule #evalOp( binOpEq, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(0, 1, false) ) requires N1 =/=Int N2
rule #evalOp( binOpLt, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(0, 1, false) ) requires N1 >=Int N2
rule #evalOp( binOpLe, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(0, 1, false) ) requires N1 >Int N2
rule #evalOp( binOpNe, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(0, 1, false) ) requires N1 ==Int N2
rule #evalOp( binOpGe, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(0, 1, false) ) requires N1 <Int N2
rule #evalOp( binOpGt, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(0, 1, false) ) requires N1 <=Int N2

rule #evalOp( binOpCmp, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar( 0, 8, true) ) requires N1 ==Int N2
rule #evalOp( binOpCmp, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar(-1, 8, true) ) requires N1 <Int N2
rule #evalOp( binOpCmp, ListItem(Scalar(N1, WIDTH, SIGN)) ListItem(Scalar(N2, WIDTH, SIGN)) ) => #value( Scalar( 1, 8, true) ) requires N1 >Int N2

// TODO
rule #evalOp( binOpOffset, ListItem(_Arg1) ListItem(_Arg2) ) => #value( Any )

rule #evalOp( unOpNot, ListItem(Scalar(N, WIDTH, SIGN))) => #value( Scalar( #if WIDTH ==Int 1 andBool SIGN ==Bool false #then #boolToInt(notBool (N =/=Int 0)) #else (~Int N) #fi, WIDTH, SIGN) )
Expand Down
56 changes: 56 additions & 0 deletions kmir/src/kmir/kdist/mir-semantics/utils.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
module UTILS-SYNTAX
imports INT-SYNTAX
imports BOOL-SYNTAX

syntax Int ::= "#width_max" "(" Int "," Bool ")" [function] // params: width, signed. Calculates maximum number for the width.
syntax Int ::= "#width_min" "(" Int "," Bool ")" [function] // params: width, signed. Calculates minimum number for the width.
syntax Int ::= "#chop" "(" Int "," Int "," Bool ")" [function] // params: number, width, signed. Wraps a number into it's bound.
endmodule

module UTILS
imports UTILS-SYNTAX
imports INT
imports BOOL

// Unsigned
rule #width_max(8, false) => 255 // 2^8 - 1
rule #width_max(16, false) => 65535 // 2^16 - 1
rule #width_max(32, false) => 4294967295 // 2^32 - 1
rule #width_max(64, false) => 18446744073709551615 // 2^64 - 1
rule #width_max(128, false) => 340282366920938463463374607431768211455 // 2^128 - 1

// Signed with two's complement
rule #width_max(8, true) => 127 // 2^(8-1) - 1
rule #width_max(16, true) => 32767 // 2^(16-1) - 1
rule #width_max(32, true) => 2147483647 // 2^(32-1) - 1
rule #width_max(64, true) => 9223372036854775807 // 2^(64-1) - 1
rule #width_max(128, true) => 170141183460469231731687303715884105727 // 2^(128-1) - 1

// Unsigned (Can be collapsed when width is enum)
rule #width_min(8, false) => 0
rule #width_min(16, false) => 0
rule #width_min(32, false) => 0
rule #width_min(64, false) => 0
rule #width_min(128, false) => 0

// Signed with two's complement
rule #width_min(8, true) => -128 // -2^(8-1)
rule #width_min(16, true) => -32768 // -2^(16-1)
rule #width_min(32, true) => -2147483648 // -2^(32-1)
rule #width_min(64, true) => -9223372036854775808 // -2^(64-1)
rule #width_min(128, true) => -170141183460469231731687303715884105728 // -2^(128-1)

// Unsigned
rule #chop(NUM, WIDTH, false) => NUM %Int (#width_max(WIDTH, false) +Int 1) requires 0 <=Int NUM

// Signed
rule #chop(NUM, WIDTH, true) => // chopped number stays positive if in range
NUM &Int #width_max(WIDTH, false)
requires
NUM &Int #width_max(WIDTH, false) <Int #width_max(WIDTH, true)

rule #chop(NUM, WIDTH, true) => // chopped number wraps around if too large for signed WIDTH
(NUM &Int #width_max(WIDTH, false)) -Int (#width_max(WIDTH, false) +Int 1)
requires
NUM &Int #width_max(WIDTH, false) >=Int #width_max(WIDTH, true)
endmodule
2 changes: 1 addition & 1 deletion package/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.36
0.3.37

0 comments on commit 99d812f

Please sign in to comment.