Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion lib/Translation/ForthToMLIR/ForthToMLIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,38 @@ LogicalResult ForthParser::parseBody(Value &stack) {
return emitError("UNLOOP without matching DO");
}

(void)loopStack.pop_back_val();
// No-op: loop control uses CFG blocks and a memref counter, not the
// Forth stack, so there is nothing to discard. We keep the loopStack
// intact so that LOOP can still find its matching DO.

//=== EXIT ===
} else if (word == "EXIT") {
consume();

if (!inWordDefinition) {
return emitError("EXIT outside word definition");
}

Region *parentRegion = builder.getInsertionBlock()->getParent();

// Create a block that performs the return.
auto *returnBlock = createStackBlock(parentRegion, loc);
{
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToStart(returnBlock);
builder.create<func::ReturnOp>(loc, returnBlock->getArgument(0));
}

// Use a dummy cond_br to keep the dead block structurally reachable,
// matching the pattern used by LEAVE.
auto *deadBlock = createStackBlock(parentRegion, loc);
Value cond = builder.create<arith::ConstantOp>(
loc, builder.getI1Type(), builder.getBoolAttr(true));
builder.create<cf::CondBranchOp>(loc, cond, returnBlock,
ValueRange{stack}, deadBlock,
ValueRange{stack});
builder.setInsertionPointToStart(deadBlock);
stack = deadBlock->getArgument(0);

//=== DO ===
} else if (word == "DO") {
Expand Down
6 changes: 6 additions & 0 deletions test/Pipeline/exit.forth
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
\ RUN: %warpforth-translate --forth-to-mlir %s | %warpforth-opt --warpforth-pipeline | %FileCheck %s
\ CHECK: gpu.binary @warpforth_module

PARAM DATA 4
: DO-EXIT 1 IF EXIT THEN 42 ;
DO-EXIT DATA 0 CELLS + !
6 changes: 6 additions & 0 deletions test/Pipeline/unloop-exit.forth
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
\ RUN: %warpforth-translate --forth-to-mlir %s | %warpforth-opt --warpforth-pipeline | %FileCheck %s
\ CHECK: gpu.binary @warpforth_module

PARAM DATA 4
: FIND-FIVE 10 0 DO I 5 = IF UNLOOP EXIT THEN LOOP 0 ;
FIND-FIVE DATA 0 CELLS + !
3 changes: 3 additions & 0 deletions test/Translation/Forth/exit-outside-word-error.forth
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
\ RUN: %not %warpforth-translate --forth-to-mlir %s 2>&1 | %FileCheck %s
\ CHECK: EXIT outside word definition
EXIT
13 changes: 13 additions & 0 deletions test/Translation/Forth/exit.forth
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
\ RUN: %warpforth-translate --forth-to-mlir %s | %FileCheck %s

\ CHECK: func.func private @EARLY_EXIT(%[[A:.*]]: !forth.stack) -> !forth.stack
\ CHECK: cf.cond_br %{{.*}}, ^[[THEN:bb.*]](%{{.*}}), ^[[JOIN:bb.*]](%{{.*}})
\ CHECK: ^[[THEN]](%[[T:.*]]: !forth.stack):
\ CHECK: cf.cond_br %true, ^[[RET:bb.*]](%[[T]]{{.*}}), ^[[DEAD:bb.*]](%[[T]]
\ CHECK: ^[[JOIN]](%{{.*}}: !forth.stack):
\ CHECK: return %{{.*}} : !forth.stack
\ CHECK: ^[[RET]](%[[R:.*]]: !forth.stack):
\ CHECK: return %[[R]] : !forth.stack

: EARLY-EXIT 1 IF EXIT THEN 42 ;
EARLY-EXIT
21 changes: 21 additions & 0 deletions test/Translation/Forth/unloop-exit.forth
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
\ RUN: %warpforth-translate --forth-to-mlir %s | %FileCheck %s

\ Verify the UNLOOP EXIT idiom: early return from a DO LOOP inside a word.

\ CHECK: func.func private @FIND_FIVE(%{{.*}}: !forth.stack) -> !forth.stack
\ CHECK: memref.alloca
\ CHECK: cf.br ^bb[[#CHECK:]]
\ CHECK: ^bb[[#CHECK]](%{{.*}}: !forth.stack):
\ CHECK: cf.cond_br %{{.*}}, ^bb[[#BODY:]](%{{.*}}), ^bb[[#EXIT:]](%{{.*}})
\ CHECK: ^bb[[#BODY]](%{{.*}}: !forth.stack):
\ CHECK: forth.eq
\ CHECK: cf.cond_br %{{.*}}, ^bb[[#THEN:]](%{{.*}}), ^bb[[#ENDIF:]](%{{.*}})
\ CHECK: ^bb[[#EXIT]](%{{.*}}: !forth.stack):
\ CHECK: return
\ CHECK: ^bb[[#THEN]](%[[T:.*]]: !forth.stack):
\ CHECK: cf.cond_br %true, ^bb[[#RET:]](%[[T]]{{.*}})
\ CHECK: ^bb[[#RET]](%[[R:.*]]: !forth.stack):
\ CHECK: return %[[R]] : !forth.stack

: FIND-FIVE 10 0 DO I 5 = IF UNLOOP EXIT THEN LOOP 0 ;
FIND-FIVE