diff --git a/lib/Translation/ForthToMLIR/ForthToMLIR.cpp b/lib/Translation/ForthToMLIR/ForthToMLIR.cpp index d000ea4..0bf746f 100644 --- a/lib/Translation/ForthToMLIR/ForthToMLIR.cpp +++ b/lib/Translation/ForthToMLIR/ForthToMLIR.cpp @@ -557,6 +557,39 @@ LogicalResult ForthParser::parseBody(Value &stack) { builder.setInsertionPointToStart(exitBlock); stack = exitBlock->getArgument(0); + //=== LEAVE === + } else if (word == "LEAVE") { + consume(); + + if (loopStack.empty()) { + return emitError("LEAVE without matching DO"); + } + + Region *parentRegion = builder.getInsertionBlock()->getParent(); + auto &ctx = loopStack.back(); + + // Continue parsing in a dead block to avoid inserting after a + // terminator. Use a dummy cond_br to create a reachable dead block that + // carries a stack argument, keeping cf->memref type conversion + // consistent. + auto *deadBlock = createStackBlock(parentRegion, loc); + Value cond = builder.create( + loc, builder.getI1Type(), builder.getBoolAttr(true)); + builder.create(loc, cond, ctx.exit, ValueRange{stack}, + deadBlock, ValueRange{stack}); + builder.setInsertionPointToStart(deadBlock); + stack = deadBlock->getArgument(0); + + //=== UNLOOP === + } else if (word == "UNLOOP") { + consume(); + + if (loopStack.empty()) { + return emitError("UNLOOP without matching DO"); + } + + (void)loopStack.pop_back_val(); + //=== DO === } else if (word == "DO") { consume(); diff --git a/test/Conversion/ForthToMemRef/leave.mlir b/test/Conversion/ForthToMemRef/leave.mlir new file mode 100644 index 0000000..60f4979 --- /dev/null +++ b/test/Conversion/ForthToMemRef/leave.mlir @@ -0,0 +1,37 @@ +// RUN: %warpforth-opt --convert-forth-to-memref %s | %FileCheck %s + +// Test: DO...LEAVE...LOOP lowers to CF branch to loop exit. +// Forth: 10 0 DO LEAVE LOOP + +// CHECK-LABEL: func.func private @main +// CHECK: %[[STACK:.*]] = memref.alloca() : memref<256xi64> +// CHECK: cf.br ^bb1(%[[STACK]], %{{.*}} : memref<256xi64>, index) +// CHECK: ^bb1(%{{.*}}: memref<256xi64>, %{{.*}}: index): +// CHECK: cf.cond_br %{{.*}}, ^bb2(%{{.*}}: memref<256xi64>, index), ^bb3(%{{.*}}: memref<256xi64>, index) +// CHECK: ^bb2(%{{.*}}: memref<256xi64>, %{{.*}}: index): +// CHECK-NEXT: cf.br ^bb3(%{{.*}}: memref<256xi64>, index) +// CHECK: ^bb3(%{{.*}}: memref<256xi64>, %{{.*}}: index): +// CHECK: return + +module { + func.func private @main() { + %0 = forth.stack !forth.stack + %1 = forth.literal %0 10 : !forth.stack -> !forth.stack + %2 = forth.literal %1 0 : !forth.stack -> !forth.stack + %output_stack, %value = forth.pop %2 : !forth.stack -> !forth.stack, i64 + %output_stack_0, %value_1 = forth.pop %output_stack : !forth.stack -> !forth.stack, i64 + %alloca = memref.alloca() : memref<1xi64> + %c0 = arith.constant 0 : index + memref.store %value, %alloca[%c0] : memref<1xi64> + cf.br ^bb1(%output_stack_0 : !forth.stack) + ^bb1(%3: !forth.stack): + %c0_2 = arith.constant 0 : index + %4 = memref.load %alloca[%c0_2] : memref<1xi64> + %5 = arith.cmpi slt, %4, %value_1 : i64 + cf.cond_br %5, ^bb2(%3 : !forth.stack), ^bb3(%3 : !forth.stack) + ^bb2(%6: !forth.stack): + cf.br ^bb3(%6 : !forth.stack) + ^bb3(%7: !forth.stack): + return + } +} diff --git a/test/Pipeline/leave.forth b/test/Pipeline/leave.forth new file mode 100644 index 0000000..f6bcb6f --- /dev/null +++ b/test/Pipeline/leave.forth @@ -0,0 +1,15 @@ +\ RUN: %warpforth-translate --forth-to-mlir %s | %warpforth-opt --warpforth-pipeline | %FileCheck %s +\ RUN: %warpforth-translate --forth-to-mlir %s | %warpforth-opt --convert-forth-to-memref --convert-scf-to-cf --convert-forth-to-gpu | %FileCheck %s --check-prefix=MID + +\ Verify that LEAVE through the full pipeline produces a gpu.binary +\ CHECK: gpu.binary @warpforth_module + +\ Verify intermediate MLIR: gpu.func with CF control flow +\ MID: gpu.module @warpforth_module +\ MID: gpu.func @main(%arg0: memref<4xi64> {forth.param_name = "DATA"}) kernel +\ MID: cf.br +\ MID: cf.cond_br +\ MID: gpu.return + +PARAM DATA 4 +10 0 DO LEAVE LOOP DATA 0 CELLS + ! diff --git a/test/Translation/Forth/leave-conditional.forth b/test/Translation/Forth/leave-conditional.forth new file mode 100644 index 0000000..1f3f327 --- /dev/null +++ b/test/Translation/Forth/leave-conditional.forth @@ -0,0 +1,22 @@ +\ RUN: %warpforth-translate --forth-to-mlir %s | %FileCheck %s + +\ Verify conditional LEAVE preserves the loop backedge for non-LEAVE paths. + +\ CHECK: cf.br ^bb1(%{{.*}} : !forth.stack) +\ CHECK: ^bb1(%[[CHK:.*]]: !forth.stack): +\ CHECK: cf.cond_br %{{.*}}, ^bb2(%[[CHK]] : !forth.stack), ^bb[[EXIT:[0-9]+]](%[[CHK]] : !forth.stack) +\ CHECK: ^bb2(%[[B:.*]]: !forth.stack): +\ CHECK: cf.cond_br %{{.*}}, ^bb[[LEAVE:[0-9]+]](%{{.*}} : !forth.stack), ^bb[[JOIN:[0-9]+]](%{{.*}} : !forth.stack) +\ CHECK: ^bb[[EXIT]](%{{.*}}: !forth.stack): +\ CHECK: return +\ CHECK: ^bb[[LEAVE]](%{{.*}}: !forth.stack): +\ CHECK: cf.cond_br %{{.*}}, ^bb[[EXIT]](%{{.*}} : !forth.stack), ^bb[[DEAD:[0-9]+]](%{{.*}} : !forth.stack) +\ CHECK: ^bb[[JOIN]](%{{.*}}: !forth.stack): +\ CHECK: cf.br ^bb1(%{{.*}} : !forth.stack) +\ CHECK: ^bb[[DEAD]](%{{.*}}: !forth.stack): +\ CHECK: cf.br ^bb[[JOIN]](%{{.*}} : !forth.stack) + +10 0 DO + I 5 = IF LEAVE THEN + 1 DROP +LOOP diff --git a/test/Translation/Forth/leave.forth b/test/Translation/Forth/leave.forth new file mode 100644 index 0000000..0a185c0 --- /dev/null +++ b/test/Translation/Forth/leave.forth @@ -0,0 +1,17 @@ +\ RUN: %warpforth-translate --forth-to-mlir %s | %FileCheck %s + +\ Verify LEAVE branches to the loop exit block. + +\ CHECK: %[[S0:.*]] = forth.stack !forth.stack +\ CHECK-NEXT: %[[S1:.*]] = forth.literal %[[S0]] 10 : !forth.stack -> !forth.stack +\ CHECK-NEXT: %[[S2:.*]] = forth.literal %[[S1]] 0 : !forth.stack -> !forth.stack +\ CHECK: cf.br ^bb1(%{{.*}} : !forth.stack) +\ CHECK: ^bb1(%[[B1:.*]]: !forth.stack): +\ CHECK: cf.cond_br %{{.*}}, ^bb2(%[[B1]] : !forth.stack), ^bb[[EXIT:[0-9]+]](%[[B1]] : !forth.stack) +\ CHECK: ^bb2(%[[B2:.*]]: !forth.stack): +\ CHECK-NEXT: %[[TRUE:.*]] = arith.constant true +\ CHECK-NEXT: cf.cond_br %[[TRUE]], ^bb[[EXIT:[0-9]+]](%[[B2]] : !forth.stack), ^bb{{[0-9]+}}(%[[B2]] : !forth.stack) +\ CHECK: ^bb[[EXIT]](%[[B3:.*]]: !forth.stack): +\ CHECK-NEXT: return + +10 0 DO LEAVE LOOP diff --git a/test/Translation/Forth/unloop-without-do-error.forth b/test/Translation/Forth/unloop-without-do-error.forth new file mode 100644 index 0000000..b14af8b --- /dev/null +++ b/test/Translation/Forth/unloop-without-do-error.forth @@ -0,0 +1,3 @@ +\ RUN: %not %warpforth-translate --forth-to-mlir %s 2>&1 | %FileCheck %s +\ CHECK: UNLOOP without matching DO +UNLOOP