Skip to content

Commit 058ed6d

Browse files
committed
Fix NoOp move handling and drop glue setup
1 parent 1c30859 commit 058ed6d

2 files changed

Lines changed: 72 additions & 35 deletions

File tree

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

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,10 @@ NB that a stack height of `0` cannot occur here, because the compiler prevents l
219219
If the local `_0` does not have a value (i.e., it remained uninitialised), the function returns unit and writing the value is skipped.
220220

221221
```k
222-
// `place(local(-1), .ProjectionElems)` is a sentinel destination meaning that the caller
223-
// ignores the callee's return value. Skip the normal writeback path in that case, because
224-
// `#setLocalValue` only accepts real local indices and would get stuck on `local(-1)`.
222+
// `place(local(-1), .ProjectionElems)` is the sentinel destination for calls whose
223+
// return should not be written back. Without this rule, the return path would fall
224+
// through to `#setLocalValue`, which only accepts real local indices and would get
225+
// stuck on `local(-1)`.
225226
rule [termReturnIgnored]: <k> #execTerminator(terminator(terminatorKindReturn, _SPAN)) ~> _
226227
=>
227228
#execBlockIdx(TARGET)
@@ -390,21 +391,18 @@ where the returned result should go.
390391
391392
rule <k> #consumeNoOpArg(operandConstant(_)) => .K ... </k>
392393
393-
rule <k> #consumeNoOpArg(operandValue(_)) => .K ... </k>
394-
395394
rule <k> #consumeNoOpArg(operandCopy(place(local(I), .ProjectionElems))) => .K ... </k>
396-
<stack> ListItem(StackFrame(_, _, _, _, CALLERLOCALS)) _:List </stack>
395+
<locals> LOCALS </locals>
397396
requires 0 <=Int I
398-
andBool I <Int size(CALLERLOCALS)
399-
andBool isTypedValue(CALLERLOCALS[I])
397+
andBool I <Int size(LOCALS)
398+
andBool isTypedValue(LOCALS[I])
400399
[preserves-definedness]
401400
402401
rule <k> #consumeNoOpArg(operandMove(place(local(I), _))) => .K ... </k>
403-
<stack> (ListItem(StackFrame(_, _, _, _, CALLERLOCALS) #as CALLERFRAME => #updateStackLocal(CALLERFRAME, I, Moved))) _:List
404-
</stack>
402+
<locals> LOCALS => LOCALS[I <- typedValue(Moved, tyOfLocal(getLocal(LOCALS, I)), mutabilityMut)] </locals>
405403
requires 0 <=Int I
406-
andBool I <Int size(CALLERLOCALS)
407-
andBool isTypedValue(CALLERLOCALS[I])
404+
andBool I <Int size(LOCALS)
405+
andBool isTypedValue(LOCALS[I])
408406
[preserves-definedness]
409407
410408
// Regular function call - full state switching and stack setup
@@ -502,6 +500,7 @@ An operand may be a `Reference` (the only way a function could access another fu
502500

503501
```k
504502
syntax KItem ::= #setUpCalleeData(MonoItemKind, Operands, Span)
503+
| #setUpDropGlueData(MonoItemKind, Value, Span)
505504
506505
// reserve space for local variables and copy/move arguments from old locals into their place
507506
rule [setupCalleeData]: <k> #setUpCalleeData(
@@ -526,6 +525,21 @@ An operand may be a `Reference` (the only way a function could access another fu
526525
</currentFrame>
527526
// TODO: Haven't handled "noBody" case
528527
528+
rule [setupDropGlueData]: <k> #setUpDropGlueData(
529+
monoItemFn(_, _, someBody(body((FIRST:BasicBlock _) #as BLOCKS, NEWLOCALS, _, _, _, _))),
530+
PTR,
531+
_SPAN
532+
)
533+
=>
534+
#setLocalValue(place(local(1), .ProjectionElems), #incrementRef(PTR)) ~> #execBlock(FIRST)
535+
...
536+
</k>
537+
<currentFrame>
538+
<currentBody> _ => toKList(BLOCKS) </currentBody>
539+
<locals> _ => #reserveFor(NEWLOCALS) </locals>
540+
...
541+
</currentFrame>
542+
529543
syntax List ::= #reserveFor( LocalDecls ) [function, total]
530544
531545
rule #reserveFor(.LocalDecls) => .List
@@ -534,11 +548,6 @@ An operand may be a `Reference` (the only way a function could access another fu
534548
=>
535549
ListItem(newLocal(TY, MUT)) #reserveFor(REST)
536550
537-
syntax Operand ::= operandValue ( Value )
538-
539-
// Internal helper operand for already-evaluated runtime values.
540-
rule <k> operandValue(VAL) => VAL ... </k>
541-
542551
syntax KItem ::= #setArgsFromStack ( Int, Operands)
543552
| #setArgFromStack ( Int, Operand)
544553
| #execIntrinsic ( MonoItemKind, Operands, Place, Span )
@@ -558,12 +567,6 @@ An operand may be a `Reference` (the only way a function could access another fu
558567
...
559568
</k>
560569
561-
rule <k> #setArgFromStack(IDX, operandValue(VAL))
562-
=>
563-
#setLocalValue(place(local(IDX), .ProjectionElems), #incrementRef(VAL))
564-
...
565-
</k>
566-
567570
rule <k> #setArgFromStack(IDX, operandCopy(place(local(I), .ProjectionElems)))
568571
=>
569572
#setLocalValue(place(local(IDX), .ProjectionElems), #incrementRef(getValue(CALLERLOCALS, I)))
@@ -772,19 +775,20 @@ Drops are elaborated to Noops but still define the continuing control flow. Unre
772775
...
773776
</k>
774777
775-
rule <k> PTR:Value ~> #callDropGlue(FTY, TARGET, UNWIND, SPAN)
776-
=>
777-
#execTerminatorCall(
778-
FTY,
779-
lookupFunction(FTY),
780-
operandValue(PTR) .Operands,
781-
place(local(-1), .ProjectionElems),
782-
someBasicBlockIdx(TARGET),
783-
UNWIND,
784-
SPAN
785-
)
786-
...
778+
rule [termCallDropGlue]:
779+
<k> PTR:Value ~> #callDropGlue(FTY, TARGET, UNWIND, SPAN) ~> _
780+
=> #setUpDropGlueData(lookupFunction(FTY), PTR, SPAN)
787781
</k>
782+
<currentFunc> CALLER => FTY </currentFunc>
783+
<currentFrame>
784+
<currentBody> _ </currentBody>
785+
<caller> OLDCALLER => CALLER </caller>
786+
<dest> OLDDEST => place(local(-1), .ProjectionElems) </dest>
787+
<target> OLDTARGET => someBasicBlockIdx(TARGET) </target>
788+
<unwind> OLDUNWIND => UNWIND </unwind>
789+
<locals> LOCALS </locals>
790+
</currentFrame>
791+
<stack> STACK => ListItem(StackFrame(OLDCALLER, OLDDEST, OLDTARGET, OLDUNWIND, LOCALS)) STACK </stack>
788792
789793
syntax MIRError ::= "ReachedUnreachable"
790794

kmir/src/tests/integration/test_run_smir_random.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,36 @@ def test_run_smir_random(
132132
handle.write_final(actual_final)
133133
else:
134134
assert handle.expected_final == actual_final
135+
136+
137+
def test_complex_types_noop_move_regression(tmp_path: Path) -> None:
138+
# `complex-types` exercises a `NoOpSym` call with moved arguments.
139+
# Regressions in `termCallNoOp` used the caller frame stored on `<stack>`
140+
# instead of the current `<locals>` cell, which breaks move tracking here.
141+
project_dir = TEST_ROOT_DIR / 'complex-types'
142+
smir_info = SMIRInfo.from_file(project_dir / 'test.smir.json')
143+
kmir = KMIR.from_kompiled_kore(
144+
smir_info=smir_info,
145+
target_dir=tmp_path / 'complex-types-noop-regression',
146+
bug_report=None,
147+
symbolic=False,
148+
)
149+
150+
init_kast, _ = make_call_config(
151+
kmir.definition,
152+
smir_info=smir_info,
153+
start_symbol='test',
154+
mode=RandomMode(0),
155+
)
156+
init_kore = kmir.kast_to_kore(init_kast, sort=GENERATED_TOP_CELL)
157+
final_kore = llvm_interpret(
158+
definition_dir=kmir.definition_dir,
159+
pattern=init_kore,
160+
)
161+
actual_final = kore_print(
162+
definition_dir=kmir.definition_dir,
163+
pattern=final_kore,
164+
output='pretty',
165+
)
166+
167+
assert actual_final == (project_dir / 'final-0.expected').read_text()

0 commit comments

Comments
 (0)