Skip to content

Commit c0d99e3

Browse files
Stevengrejberthold
andauthored
feat: support terminator call from a function pointer (#894)
- Solve #488 --------- Co-authored-by: Jost Berthold <jost.berthold@gmail.com>
1 parent 16a937b commit c0d99e3

36 files changed

+118
-70
lines changed

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

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -305,21 +305,33 @@ The call stack is not necessarily empty at this point so it is left untouched.
305305
`Call` is calling another function, setting up its stack frame and
306306
where the returned result should go.
307307

308-
309308
```k
310-
// Intrinsic function call - execute directly without state switching
311-
rule [termCallIntrinsic]: <k> #execTerminator(terminator(terminatorKindCall(FUNC, ARGS, DEST, TARGET, _UNWIND), _SPAN)) ~> _
312-
=>
313-
#execIntrinsic(lookupFunction(#tyOfCall(FUNC)), ARGS, DEST) ~> #continueAt(TARGET)
309+
syntax KItem ::= #execTerminatorCall(fty: Ty, func: MonoItemKind, args: Operands, destination: Place, target: MaybeBasicBlockIdx, unwind: UnwindAction)
310+
311+
rule <k> #execTerminator(terminator(terminatorKindCall(operandConstant(constOperand(_, _, mirConst(constantKindZeroSized, Ty, _))), ARGS, DEST, TARGET, UNWIND), _SPAN))
312+
=> #execTerminatorCall(Ty, lookupFunction(Ty), ARGS, DEST, TARGET, UNWIND)
313+
...
314314
</k>
315-
requires isIntrinsicFunction(lookupFunction(#tyOfCall(FUNC)))
315+
316+
rule <k> #execTerminator(terminator(terminatorKindCall(operandMove(place(local(I), .ProjectionElems)), ARGS, DEST, TARGET, UNWIND), _SPAN))
317+
=> #execTerminatorCall(tyOfLocal(getLocal(LOCALS, I)), lookupFunction(tyOfLocal(getLocal(LOCALS, I))), ARGS, DEST, TARGET, UNWIND)
318+
...
319+
</k>
320+
<locals> LOCALS </locals>
321+
322+
// Intrinsic function call - execute directly without state switching
323+
rule [termCallIntrinsic]:
324+
<k> #execTerminatorCall(_, FUNC, ARGS, DEST, TARGET, _UNWIND) ~> _
325+
=> #execIntrinsic(FUNC, ARGS, DEST) ~> #continueAt(TARGET)
326+
</k>
327+
requires isIntrinsicFunction(FUNC)
316328
317329
// Regular function call - full state switching and stack setup
318-
rule [termCallFunction]: <k> #execTerminator(terminator(terminatorKindCall(FUNC, ARGS, DEST, TARGET, UNWIND), _SPAN)) ~> _
319-
=>
320-
#setUpCalleeData(lookupFunction(#tyOfCall(FUNC)), ARGS)
330+
rule [termCallFunction]:
331+
<k> #execTerminatorCall(FTY, FUNC, ARGS, DEST, TARGET, UNWIND) ~> _
332+
=> #setUpCalleeData(FUNC, ARGS)
321333
</k>
322-
<currentFunc> CALLER => #tyOfCall(FUNC) </currentFunc>
334+
<currentFunc> CALLER => FTY </currentFunc>
323335
<currentFrame>
324336
<currentBody> _ </currentBody>
325337
<caller> OLDCALLER => CALLER </caller>
@@ -329,7 +341,7 @@ where the returned result should go.
329341
<locals> LOCALS </locals>
330342
</currentFrame>
331343
<stack> STACK => ListItem(StackFrame(OLDCALLER, OLDDEST, OLDTARGET, OLDUNWIND, LOCALS)) STACK </stack>
332-
requires notBool isIntrinsicFunction(lookupFunction(#tyOfCall(FUNC)))
344+
requires notBool isIntrinsicFunction(FUNC)
333345
334346
syntax Bool ::= isIntrinsicFunction(MonoItemKind) [function]
335347
rule isIntrinsicFunction(IntrinsicFunction(_)) => true
@@ -338,11 +350,6 @@ where the returned result should go.
338350
syntax KItem ::= #continueAt(MaybeBasicBlockIdx)
339351
rule <k> #continueAt(someBasicBlockIdx(TARGET)) => #execBlockIdx(TARGET) ... </k>
340352
rule <k> #continueAt(noBasicBlockIdx) => .K ... </k>
341-
342-
syntax Ty ::= #tyOfCall( Operand ) [function, total]
343-
344-
rule #tyOfCall(operandConstant(constOperand(_, _, mirConst(constantKindZeroSized, Ty, _)))) => Ty
345-
rule #tyOfCall(_) => ty(-1) [owise] // copy, move, non-zero size: not supported
346353
```
347354

348355
The local data has to be set up for the call, which requires information about the local variables of a call. This step is separate from the above call stack setup because it needs to retrieve the locals declaration from the body. Arguments to the call are `Operands` which refer to the old locals (`OLDLOCALS` below), and the data is either _copied_ into the new locals using `#setArgs`, or it needs to be _shared_ via references.
@@ -412,7 +419,8 @@ An operand may be a `Reference` (the only way a function could access another fu
412419
andBool isTypedValue(CALLERLOCALS[I])
413420
[preserves-definedness] // valid list indexing checked
414421
415-
rule <k> #setArgFromStack(IDX, operandMove(place(local(I), .ProjectionElems)))
422+
// TODO: This is not safe, need to add more checks to this.
423+
rule <k> #setArgFromStack(IDX, operandMove(place(local(I), _)))
416424
=>
417425
#setLocalValue(place(local(IDX), .ProjectionElems), #incrementRef(getValue(CALLERLOCALS, I)))
418426
...

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@ Constant operands are simply decoded according to their type.
130130
requires typeInfoVoidType =/=K lookupTy(TY)
131131
```
132132

133+
Function pointers are zero-sized constants whose `Ty` is a key in the function table instaed of the type table.
134+
135+
```k
136+
rule <k> operandConstant(constOperand(_, _, mirConst(constantKindZeroSized, ty(ID) #as TY, _)))
137+
=> FunPtr(TY)
138+
...
139+
</k>
140+
requires typeInfoVoidType ==K lookupTy(TY) // not a valid type info
141+
andBool lookupFunction(TY) =/=K monoItemFn(symbol("** UNKNOWN FUNCTION **"), defId(ID), noBody ) // valid function Ty
142+
```
143+
133144
### Copying and Moving
134145

135146
When an operand is `Copied` by a read, the original remains valid (see `false` passed to `#readProjection`).

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ The special `Moved` value represents values that have been used and should not b
5151
[symbol(Value::PtrLocal)]
5252
// pointer to a local TypedValue (on the stack)
5353
// fields are the same as in Reference
54+
| FunPtr ( Ty )
55+
// function pointer, created by operandConstant only. Ty is a key in the function table
5456
| AllocRef ( AllocId , ProjectionElems , Metadata )
5557
[symbol(Value::AllocRef)]
5658
// reference to static allocation, by AllocId, possibly projected, carrying metadata if applicable

kmir/src/tests/integration/data/crate-tests/single-bin/single_exe::a_module::twice.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
┌─ 1 (root, init)
33
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
44
5-
│ (42 steps)
5+
│ (44 steps)
66
├─ 3 (split)
77
│ #expect ( BoolVal ( notBool ARG_UINT1:Int +Int ARG_UINT1:Int &Int 18446744073709
88

kmir/src/tests/integration/data/crate-tests/single-bin/single_exe::main.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
┌─ 1 (root, init)
33
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
44
5-
│ (225 steps)
5+
│ (228 steps)
66
├─ 3 (terminal)
77
│ #EndProgram ~> .K
88

kmir/src/tests/integration/data/crate-tests/single-dylib/small_test_dylib::add.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
┌─ 1 (root, init)
33
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
44
5-
│ (34 steps)
5+
│ (35 steps)
66
├─ 3 (split)
77
│ #expect ( BoolVal ( notBool ARG_UINT1:Int +Int ARG_UINT2:Int &Int 18446744073709
88

kmir/src/tests/integration/data/crate-tests/single-lib/small_test_lib::testing::test_add_in_range.expected

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
┌─ 1 (root, init)
33
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
44
5-
│ (112 steps)
5+
│ (114 steps)
66
├─ 3 (split)
77
│ #selectBlock ( switchTargets ( ... branches: branch ( 0 , basicBlockIdx ( 1 ) )
88
@@ -14,7 +14,7 @@
1414
┃ ├─ 4
1515
┃ │ #selectBlock ( switchTargets ( ... branches: branch ( 0 , basicBlockIdx ( 1 ) )
1616
┃ │
17-
┃ │ (5 steps)
17+
┃ │ (6 steps)
1818
┃ └─ 6 (stuck, leaf)
1919
┃ #setUpCalleeData ( monoItemFn ( ... name: symbol ( "** UNKNOWN FUNCTION **" ) ,
2020
@@ -25,7 +25,7 @@
2525
├─ 5
2626
│ #selectBlock ( switchTargets ( ... branches: branch ( 0 , basicBlockIdx ( 1 ) )
2727
28-
│ (181 steps)
28+
│ (182 steps)
2929
├─ 7 (terminal)
3030
│ #EndProgram ~> .K
3131

kmir/src/tests/integration/data/crate-tests/two-crate-bin/crate2::main.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
┌─ 1 (root, init)
33
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
44
5-
│ (727 steps)
5+
│ (736 steps)
66
├─ 3 (terminal)
77
│ #EndProgram ~> .K
88

kmir/src/tests/integration/data/crate-tests/two-crate-dylib/crate2::test_crate1_with.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
┌─ 1 (root, init)
33
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
44
5-
│ (212 steps)
5+
│ (216 steps)
66
├─ 3 (terminal)
77
│ #EndProgram ~> .K
88
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::convert::TryFrom;
2+
use std::array;
3+
4+
// inspired by solana-pubkey but very simple code
5+
#[allow(dead_code)]
6+
struct EightBytes([u8;8]);
7+
8+
impl From<[u8;8]> for EightBytes {
9+
fn from(bytes: [u8;8]) -> Self { Self(bytes) }
10+
}
11+
12+
// causing problems with the extraction
13+
// the `try_from` and `from` from stdlib are not available in the SMIR
14+
impl TryFrom<&[u8]> for EightBytes {
15+
type Error = array::TryFromSliceError;
16+
fn try_from(bytes: &[u8]) -> Result<EightBytes, Self::Error> {
17+
<[u8;8]>::try_from(bytes).map(Self::from)
18+
}
19+
}
20+
21+
fn main() {
22+
let bytes: [u8;8] = [1,2,3,4,5,6,7,8];
23+
let _unused = EightBytes::from(bytes);
24+
let slice: &[u8] = &bytes;
25+
let thing: Result<EightBytes, _> = EightBytes::try_from(slice);
26+
assert!(thing.is_ok());
27+
}

0 commit comments

Comments
 (0)