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
30 changes: 30 additions & 0 deletions gpu_test/test_kernels.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -154,6 +154,36 @@ def test_do_loop(kernel_runner: KernelRunner) -> None:
assert result == [0, 1, 2, 3, 4]


def test_multi_while(kernel_runner: KernelRunner) -> None:
"""Multi-WHILE: two exit conditions from the same loop (interleaved CF).

20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN
Decrements while >10 AND even. 20→19 (odd, WHILE(2) exit) → result 19.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n"
"20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN\n"
"0 CELLS DATA + !"
),
)
assert result[0] == 19


def test_while_until(kernel_runner: KernelRunner) -> None:
"""WHILE+UNTIL: two different exit mechanisms from the same loop (interleaved CF).

10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN
Decrements while >0, stops early at 5. 10→9→…→5 (UNTIL exit) → result 5.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN\n0 CELLS DATA + !"
),
)
assert result[0] == 5


# --- GPU Indexing ---


Expand Down
2 changes: 1 addition & 1 deletion include/warpforth/Conversion/Passes.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,7 +28,7 @@ def ConvertForthToMemRef
let dependentDialects = ["mlir::memref::MemRefDialect",
"mlir::arith::ArithDialect",
"mlir::LLVM::LLVMDialect",
"mlir::scf::SCFDialect"];
"mlir::cf::ControlFlowDialect"];
}

def ConvertForthToGPU : Pass<"convert-forth-to-gpu", "mlir::ModuleOp"> {
Expand Down
103 changes: 24 additions & 79 deletions include/warpforth/Dialect/Forth/ForthOps.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,6 @@
#define FORTH_OPS

include "warpforth/Dialect/Forth/ForthDialect.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"

//===----------------------------------------------------------------------===//
Expand DownExpand Up@@ -446,102 +445,48 @@ def Forth_ZeroEqOp : Forth_StackOpBase<"zero_eq"> {
}

//===----------------------------------------------------------------------===//
// Control flow operations.
// Control flow support operations.
//===----------------------------------------------------------------------===//

def Forth_YieldOp : Forth_Op<"yield", [Pure, Terminator, ReturnLike,
ParentOneOf<["IfOp", "BeginUntilOp", "BeginWhileRepeatOp", "DoLoopOp"]>]> {
let summary = "Yield stack from control flow region";
def Forth_PopFlagOp : Forth_Op<"pop_flag", [Pure]> {
let summary = "Pop top of stack as boolean flag";
let description = [{
Yields the current stack state from a control flow region back to
the parent operation. Acts as a region terminator.
When the optional `while_cond` attribute is present, the yield acts as
a WHILE condition (continue when flag is non-zero) rather than
UNTIL (exit when flag is non-zero).
Pops the top value from the stack and returns it as an i1 flag
(non-zero = true, zero = false). Used by IF, UNTIL, WHILE.
Forth semantics: ( flag -- )
}];
let arguments = (ins Forth_StackType:$result, OptionalAttr<UnitAttr>:$while_cond);
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack, I1:$flag);
let assemblyFormat = [{
$result (`while_cond` $while_cond^)? attr-dict `:` type($result)
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($flag)
}];
}

def Forth_IfOp : Forth_Op<"if", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Conditional execution";
def Forth_PopOp : Forth_Op<"pop", [Pure]> {
let summary = "Pop top of stack as i64 value";
let description = [{
Conditional execution. If the flag is non-zero, the then region executes;
otherwise the else region executes. Each region must yield the resulting
stack.
Forth semantics: flag IF then-body ELSE else-body THEN
Pops the top value from the stack and returns it as an i64.
Used by DO to pop start and limit.
Forth semantics: ( x -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$then_region,
SizedRegion<1>:$else_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginUntilOp : Forth_Op<"begin_until", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Post-test loop (do-while)";
let description = [{
BEGIN/UNTIL loop. Executes body, pops flag. If flag is zero, loops back.
If non-zero, exits. Stack effect: ( -- ) with flag consumed each iteration.
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_DoLoopOp : Forth_Op<"do_loop", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Counted loop (DO/LOOP)";
let description = [{
Pops start and limit from the stack, iterates from start to limit-1.
Use forth.loop_index (I word) inside to access the current loop index.
Stack effect: ( limit start -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginWhileRepeatOp : Forth_Op<"begin_while_repeat",
[RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Pre-test loop (BEGIN/WHILE/REPEAT)";
let description = [{
BEGIN/WHILE/REPEAT loop. The condition region runs first, WHILE pops flag.
If flag is non-zero, the body region executes and loops back to condition.
If flag is zero, the loop exits.
Stack effect: ( -- ) with flag consumed each iteration.
let results = (outs Forth_StackType:$output_stack, I64:$value);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($value)
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$condition_region,
SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_LoopIndexOp : Forth_Op<"loop_index", [Pure]> {
let summary = "Push loop index onto stack (I/J/K words)";
def Forth_PushValueOp : Forth_Op<"push_value", [Pure]> {
let summary = "Push dynamic i64 value onto stack";
let description = [{
Pushes the loop index at the given nesting depth onto the stack.
depth=0 is I (innermost), depth=1 is J, depth=2 is K.
Only valid inside nested forth.do_loop bodies at sufficient depth.
( -- index )
Pushes a dynamic i64 value onto the stack. Used by I/J/K to push
the loop counter value.
Forth semantics: ( -- x )
}];
let arguments = (ins Forth_StackType:$input_stack,
DefaultValuedAttr<I64Attr, "0">:$depth);
let arguments = (ins Forth_StackType:$input_stack, I64:$value);
let results = (outs Forth_StackType:$output_stack);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack)
$input_stack `,` $value attr-dict `:` type($input_stack) `,` type($value) `->` type($output_stack)
}];
}

Expand Down
1 change: 0 additions & 1 deletion lib/Conversion/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@ add_mlir_library(MLIRConversionPasses
MLIRGPUToNVVMTransforms
MLIRGPUTransforms
MLIRReconcileUnrealizedCasts
MLIRSCFToControlFlow
MLIRTransforms
)

Expand Down
2 changes: 1 addition & 1 deletion lib/Conversion/ForthToMemRef/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,6 @@ add_mlir_conversion_library(MLIRForthToMemRefConversion
MLIRArithDialect
MLIRLLVMDialect
MLIRFuncDialect
MLIRSCFDialect
MLIRControlFlowDialect
MLIRForth
)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
30 changes: 30 additions & 0 deletions gpu_test/test_kernels.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -154,6 +154,36 @@ def test_do_loop(kernel_runner: KernelRunner) -> None:
assert result == [0, 1, 2, 3, 4]


def test_multi_while(kernel_runner: KernelRunner) -> None:
"""Multi-WHILE: two exit conditions from the same loop (interleaved CF).

20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN
Decrements while >10 AND even. 20→19 (odd, WHILE(2) exit) → result 19.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n"
"20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN\n"
"0 CELLS DATA + !"
),
)
assert result[0] == 19


def test_while_until(kernel_runner: KernelRunner) -> None:
"""WHILE+UNTIL: two different exit mechanisms from the same loop (interleaved CF).

10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN
Decrements while >0, stops early at 5. 10→9→…→5 (UNTIL exit) → result 5.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN\n0 CELLS DATA + !"
),
)
assert result[0] == 5


# --- GPU Indexing ---


Expand Down
2 changes: 1 addition & 1 deletion include/warpforth/Conversion/Passes.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,7 +28,7 @@ def ConvertForthToMemRef
let dependentDialects = ["mlir::memref::MemRefDialect",
"mlir::arith::ArithDialect",
"mlir::LLVM::LLVMDialect",
"mlir::scf::SCFDialect"];
"mlir::cf::ControlFlowDialect"];
}

def ConvertForthToGPU : Pass<"convert-forth-to-gpu", "mlir::ModuleOp"> {
Expand Down
103 changes: 24 additions & 79 deletions include/warpforth/Dialect/Forth/ForthOps.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,6 @@
#define FORTH_OPS

include "warpforth/Dialect/Forth/ForthDialect.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"

//===----------------------------------------------------------------------===//
Expand DownExpand Up@@ -446,102 +445,48 @@ def Forth_ZeroEqOp : Forth_StackOpBase<"zero_eq"> {
}

//===----------------------------------------------------------------------===//
// Control flow operations.
// Control flow support operations.
//===----------------------------------------------------------------------===//

def Forth_YieldOp : Forth_Op<"yield", [Pure, Terminator, ReturnLike,
ParentOneOf<["IfOp", "BeginUntilOp", "BeginWhileRepeatOp", "DoLoopOp"]>]> {
let summary = "Yield stack from control flow region";
def Forth_PopFlagOp : Forth_Op<"pop_flag", [Pure]> {
let summary = "Pop top of stack as boolean flag";
let description = [{
Yields the current stack state from a control flow region back to
the parent operation. Acts as a region terminator.
When the optional `while_cond` attribute is present, the yield acts as
a WHILE condition (continue when flag is non-zero) rather than
UNTIL (exit when flag is non-zero).
Pops the top value from the stack and returns it as an i1 flag
(non-zero = true, zero = false). Used by IF, UNTIL, WHILE.
Forth semantics: ( flag -- )
}];
let arguments = (ins Forth_StackType:$result, OptionalAttr<UnitAttr>:$while_cond);
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack, I1:$flag);
let assemblyFormat = [{
$result (`while_cond` $while_cond^)? attr-dict `:` type($result)
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($flag)
}];
}

def Forth_IfOp : Forth_Op<"if", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Conditional execution";
def Forth_PopOp : Forth_Op<"pop", [Pure]> {
let summary = "Pop top of stack as i64 value";
let description = [{
Conditional execution. If the flag is non-zero, the then region executes;
otherwise the else region executes. Each region must yield the resulting
stack.
Forth semantics: flag IF then-body ELSE else-body THEN
Pops the top value from the stack and returns it as an i64.
Used by DO to pop start and limit.
Forth semantics: ( x -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$then_region,
SizedRegion<1>:$else_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginUntilOp : Forth_Op<"begin_until", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Post-test loop (do-while)";
let description = [{
BEGIN/UNTIL loop. Executes body, pops flag. If flag is zero, loops back.
If non-zero, exits. Stack effect: ( -- ) with flag consumed each iteration.
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_DoLoopOp : Forth_Op<"do_loop", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Counted loop (DO/LOOP)";
let description = [{
Pops start and limit from the stack, iterates from start to limit-1.
Use forth.loop_index (I word) inside to access the current loop index.
Stack effect: ( limit start -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginWhileRepeatOp : Forth_Op<"begin_while_repeat",
[RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Pre-test loop (BEGIN/WHILE/REPEAT)";
let description = [{
BEGIN/WHILE/REPEAT loop. The condition region runs first, WHILE pops flag.
If flag is non-zero, the body region executes and loops back to condition.
If flag is zero, the loop exits.
Stack effect: ( -- ) with flag consumed each iteration.
let results = (outs Forth_StackType:$output_stack, I64:$value);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($value)
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$condition_region,
SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_LoopIndexOp : Forth_Op<"loop_index", [Pure]> {
let summary = "Push loop index onto stack (I/J/K words)";
def Forth_PushValueOp : Forth_Op<"push_value", [Pure]> {
let summary = "Push dynamic i64 value onto stack";
let description = [{
Pushes the loop index at the given nesting depth onto the stack.
depth=0 is I (innermost), depth=1 is J, depth=2 is K.
Only valid inside nested forth.do_loop bodies at sufficient depth.
( -- index )
Pushes a dynamic i64 value onto the stack. Used by I/J/K to push
the loop counter value.
Forth semantics: ( -- x )
}];
let arguments = (ins Forth_StackType:$input_stack,
DefaultValuedAttr<I64Attr, "0">:$depth);
let arguments = (ins Forth_StackType:$input_stack, I64:$value);
let results = (outs Forth_StackType:$output_stack);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack)
$input_stack `,` $value attr-dict `:` type($input_stack) `,` type($value) `->` type($output_stack)
}];
}

Expand Down
1 change: 0 additions & 1 deletion lib/Conversion/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@ add_mlir_library(MLIRConversionPasses
MLIRGPUToNVVMTransforms
MLIRGPUTransforms
MLIRReconcileUnrealizedCasts
MLIRSCFToControlFlow
MLIRTransforms
)

Expand Down
2 changes: 1 addition & 1 deletion lib/Conversion/ForthToMemRef/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,6 @@ add_mlir_conversion_library(MLIRForthToMemRefConversion
MLIRArithDialect
MLIRLLVMDialect
MLIRFuncDialect
MLIRSCFDialect
MLIRControlFlowDialect
MLIRForth
)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
30 changes: 30 additions & 0 deletions gpu_test/test_kernels.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -154,6 +154,36 @@ def test_do_loop(kernel_runner: KernelRunner) -> None:
assert result == [0, 1, 2, 3, 4]


def test_multi_while(kernel_runner: KernelRunner) -> None:
"""Multi-WHILE: two exit conditions from the same loop (interleaved CF).

20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN
Decrements while >10 AND even. 20→19 (odd, WHILE(2) exit) → result 19.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n"
"20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN\n"
"0 CELLS DATA + !"
),
)
assert result[0] == 19


def test_while_until(kernel_runner: KernelRunner) -> None:
"""WHILE+UNTIL: two different exit mechanisms from the same loop (interleaved CF).

10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN
Decrements while >0, stops early at 5. 10→9→…→5 (UNTIL exit) → result 5.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN\n0 CELLS DATA + !"
),
)
assert result[0] == 5


# --- GPU Indexing ---


Expand Down
2 changes: 1 addition & 1 deletion include/warpforth/Conversion/Passes.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,7 +28,7 @@ def ConvertForthToMemRef
let dependentDialects = ["mlir::memref::MemRefDialect",
"mlir::arith::ArithDialect",
"mlir::LLVM::LLVMDialect",
"mlir::scf::SCFDialect"];
"mlir::cf::ControlFlowDialect"];
}

def ConvertForthToGPU : Pass<"convert-forth-to-gpu", "mlir::ModuleOp"> {
Expand Down
103 changes: 24 additions & 79 deletions include/warpforth/Dialect/Forth/ForthOps.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,6 @@
#define FORTH_OPS

include "warpforth/Dialect/Forth/ForthDialect.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"

//===----------------------------------------------------------------------===//
Expand DownExpand Up@@ -446,102 +445,48 @@ def Forth_ZeroEqOp : Forth_StackOpBase<"zero_eq"> {
}

//===----------------------------------------------------------------------===//
// Control flow operations.
// Control flow support operations.
//===----------------------------------------------------------------------===//

def Forth_YieldOp : Forth_Op<"yield", [Pure, Terminator, ReturnLike,
ParentOneOf<["IfOp", "BeginUntilOp", "BeginWhileRepeatOp", "DoLoopOp"]>]> {
let summary = "Yield stack from control flow region";
def Forth_PopFlagOp : Forth_Op<"pop_flag", [Pure]> {
let summary = "Pop top of stack as boolean flag";
let description = [{
Yields the current stack state from a control flow region back to
the parent operation. Acts as a region terminator.
When the optional `while_cond` attribute is present, the yield acts as
a WHILE condition (continue when flag is non-zero) rather than
UNTIL (exit when flag is non-zero).
Pops the top value from the stack and returns it as an i1 flag
(non-zero = true, zero = false). Used by IF, UNTIL, WHILE.
Forth semantics: ( flag -- )
}];
let arguments = (ins Forth_StackType:$result, OptionalAttr<UnitAttr>:$while_cond);
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack, I1:$flag);
let assemblyFormat = [{
$result (`while_cond` $while_cond^)? attr-dict `:` type($result)
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($flag)
}];
}

def Forth_IfOp : Forth_Op<"if", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Conditional execution";
def Forth_PopOp : Forth_Op<"pop", [Pure]> {
let summary = "Pop top of stack as i64 value";
let description = [{
Conditional execution. If the flag is non-zero, the then region executes;
otherwise the else region executes. Each region must yield the resulting
stack.
Forth semantics: flag IF then-body ELSE else-body THEN
Pops the top value from the stack and returns it as an i64.
Used by DO to pop start and limit.
Forth semantics: ( x -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$then_region,
SizedRegion<1>:$else_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginUntilOp : Forth_Op<"begin_until", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Post-test loop (do-while)";
let description = [{
BEGIN/UNTIL loop. Executes body, pops flag. If flag is zero, loops back.
If non-zero, exits. Stack effect: ( -- ) with flag consumed each iteration.
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_DoLoopOp : Forth_Op<"do_loop", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Counted loop (DO/LOOP)";
let description = [{
Pops start and limit from the stack, iterates from start to limit-1.
Use forth.loop_index (I word) inside to access the current loop index.
Stack effect: ( limit start -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginWhileRepeatOp : Forth_Op<"begin_while_repeat",
[RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Pre-test loop (BEGIN/WHILE/REPEAT)";
let description = [{
BEGIN/WHILE/REPEAT loop. The condition region runs first, WHILE pops flag.
If flag is non-zero, the body region executes and loops back to condition.
If flag is zero, the loop exits.
Stack effect: ( -- ) with flag consumed each iteration.
let results = (outs Forth_StackType:$output_stack, I64:$value);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($value)
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$condition_region,
SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_LoopIndexOp : Forth_Op<"loop_index", [Pure]> {
let summary = "Push loop index onto stack (I/J/K words)";
def Forth_PushValueOp : Forth_Op<"push_value", [Pure]> {
let summary = "Push dynamic i64 value onto stack";
let description = [{
Pushes the loop index at the given nesting depth onto the stack.
depth=0 is I (innermost), depth=1 is J, depth=2 is K.
Only valid inside nested forth.do_loop bodies at sufficient depth.
( -- index )
Pushes a dynamic i64 value onto the stack. Used by I/J/K to push
the loop counter value.
Forth semantics: ( -- x )
}];
let arguments = (ins Forth_StackType:$input_stack,
DefaultValuedAttr<I64Attr, "0">:$depth);
let arguments = (ins Forth_StackType:$input_stack, I64:$value);
let results = (outs Forth_StackType:$output_stack);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack)
$input_stack `,` $value attr-dict `:` type($input_stack) `,` type($value) `->` type($output_stack)
}];
}

Expand Down
1 change: 0 additions & 1 deletion lib/Conversion/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@ add_mlir_library(MLIRConversionPasses
MLIRGPUToNVVMTransforms
MLIRGPUTransforms
MLIRReconcileUnrealizedCasts
MLIRSCFToControlFlow
MLIRTransforms
)

Expand Down
2 changes: 1 addition & 1 deletion lib/Conversion/ForthToMemRef/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,6 @@ add_mlir_conversion_library(MLIRForthToMemRefConversion
MLIRArithDialect
MLIRLLVMDialect
MLIRFuncDialect
MLIRSCFDialect
MLIRControlFlowDialect
MLIRForth
)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
30 changes: 30 additions & 0 deletions gpu_test/test_kernels.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -154,6 +154,36 @@ def test_do_loop(kernel_runner: KernelRunner) -> None:
assert result == [0, 1, 2, 3, 4]


def test_multi_while(kernel_runner: KernelRunner) -> None:
"""Multi-WHILE: two exit conditions from the same loop (interleaved CF).

20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN
Decrements while >10 AND even. 20→19 (odd, WHILE(2) exit) → result 19.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n"
"20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN\n"
"0 CELLS DATA + !"
),
)
assert result[0] == 19


def test_while_until(kernel_runner: KernelRunner) -> None:
"""WHILE+UNTIL: two different exit mechanisms from the same loop (interleaved CF).

10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN
Decrements while >0, stops early at 5. 10→9→…→5 (UNTIL exit) → result 5.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN\n0 CELLS DATA + !"
),
)
assert result[0] == 5


# --- GPU Indexing ---


Expand Down
2 changes: 1 addition & 1 deletion include/warpforth/Conversion/Passes.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,7 +28,7 @@ def ConvertForthToMemRef
let dependentDialects = ["mlir::memref::MemRefDialect",
"mlir::arith::ArithDialect",
"mlir::LLVM::LLVMDialect",
"mlir::scf::SCFDialect"];
"mlir::cf::ControlFlowDialect"];
}

def ConvertForthToGPU : Pass<"convert-forth-to-gpu", "mlir::ModuleOp"> {
Expand Down
103 changes: 24 additions & 79 deletions include/warpforth/Dialect/Forth/ForthOps.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,6 @@
#define FORTH_OPS

include "warpforth/Dialect/Forth/ForthDialect.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"

//===----------------------------------------------------------------------===//
Expand DownExpand Up@@ -446,102 +445,48 @@ def Forth_ZeroEqOp : Forth_StackOpBase<"zero_eq"> {
}

//===----------------------------------------------------------------------===//
// Control flow operations.
// Control flow support operations.
//===----------------------------------------------------------------------===//

def Forth_YieldOp : Forth_Op<"yield", [Pure, Terminator, ReturnLike,
ParentOneOf<["IfOp", "BeginUntilOp", "BeginWhileRepeatOp", "DoLoopOp"]>]> {
let summary = "Yield stack from control flow region";
def Forth_PopFlagOp : Forth_Op<"pop_flag", [Pure]> {
let summary = "Pop top of stack as boolean flag";
let description = [{
Yields the current stack state from a control flow region back to
the parent operation. Acts as a region terminator.
When the optional `while_cond` attribute is present, the yield acts as
a WHILE condition (continue when flag is non-zero) rather than
UNTIL (exit when flag is non-zero).
Pops the top value from the stack and returns it as an i1 flag
(non-zero = true, zero = false). Used by IF, UNTIL, WHILE.
Forth semantics: ( flag -- )
}];
let arguments = (ins Forth_StackType:$result, OptionalAttr<UnitAttr>:$while_cond);
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack, I1:$flag);
let assemblyFormat = [{
$result (`while_cond` $while_cond^)? attr-dict `:` type($result)
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($flag)
}];
}

def Forth_IfOp : Forth_Op<"if", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Conditional execution";
def Forth_PopOp : Forth_Op<"pop", [Pure]> {
let summary = "Pop top of stack as i64 value";
let description = [{
Conditional execution. If the flag is non-zero, the then region executes;
otherwise the else region executes. Each region must yield the resulting
stack.
Forth semantics: flag IF then-body ELSE else-body THEN
Pops the top value from the stack and returns it as an i64.
Used by DO to pop start and limit.
Forth semantics: ( x -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$then_region,
SizedRegion<1>:$else_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginUntilOp : Forth_Op<"begin_until", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Post-test loop (do-while)";
let description = [{
BEGIN/UNTIL loop. Executes body, pops flag. If flag is zero, loops back.
If non-zero, exits. Stack effect: ( -- ) with flag consumed each iteration.
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_DoLoopOp : Forth_Op<"do_loop", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Counted loop (DO/LOOP)";
let description = [{
Pops start and limit from the stack, iterates from start to limit-1.
Use forth.loop_index (I word) inside to access the current loop index.
Stack effect: ( limit start -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginWhileRepeatOp : Forth_Op<"begin_while_repeat",
[RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Pre-test loop (BEGIN/WHILE/REPEAT)";
let description = [{
BEGIN/WHILE/REPEAT loop. The condition region runs first, WHILE pops flag.
If flag is non-zero, the body region executes and loops back to condition.
If flag is zero, the loop exits.
Stack effect: ( -- ) with flag consumed each iteration.
let results = (outs Forth_StackType:$output_stack, I64:$value);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($value)
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$condition_region,
SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_LoopIndexOp : Forth_Op<"loop_index", [Pure]> {
let summary = "Push loop index onto stack (I/J/K words)";
def Forth_PushValueOp : Forth_Op<"push_value", [Pure]> {
let summary = "Push dynamic i64 value onto stack";
let description = [{
Pushes the loop index at the given nesting depth onto the stack.
depth=0 is I (innermost), depth=1 is J, depth=2 is K.
Only valid inside nested forth.do_loop bodies at sufficient depth.
( -- index )
Pushes a dynamic i64 value onto the stack. Used by I/J/K to push
the loop counter value.
Forth semantics: ( -- x )
}];
let arguments = (ins Forth_StackType:$input_stack,
DefaultValuedAttr<I64Attr, "0">:$depth);
let arguments = (ins Forth_StackType:$input_stack, I64:$value);
let results = (outs Forth_StackType:$output_stack);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack)
$input_stack `,` $value attr-dict `:` type($input_stack) `,` type($value) `->` type($output_stack)
}];
}

Expand Down
1 change: 0 additions & 1 deletion lib/Conversion/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@ add_mlir_library(MLIRConversionPasses
MLIRGPUToNVVMTransforms
MLIRGPUTransforms
MLIRReconcileUnrealizedCasts
MLIRSCFToControlFlow
MLIRTransforms
)

Expand Down
2 changes: 1 addition & 1 deletion lib/Conversion/ForthToMemRef/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,6 @@ add_mlir_conversion_library(MLIRForthToMemRefConversion
MLIRArithDialect
MLIRLLVMDialect
MLIRFuncDialect
MLIRSCFDialect
MLIRControlFlowDialect
MLIRForth
)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
30 changes: 30 additions & 0 deletions gpu_test/test_kernels.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -154,6 +154,36 @@ def test_do_loop(kernel_runner: KernelRunner) -> None:
assert result == [0, 1, 2, 3, 4]


def test_multi_while(kernel_runner: KernelRunner) -> None:
"""Multi-WHILE: two exit conditions from the same loop (interleaved CF).

20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN
Decrements while >10 AND even. 20→19 (odd, WHILE(2) exit) → result 19.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n"
"20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN\n"
"0 CELLS DATA + !"
),
)
assert result[0] == 19


def test_while_until(kernel_runner: KernelRunner) -> None:
"""WHILE+UNTIL: two different exit mechanisms from the same loop (interleaved CF).

10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN
Decrements while >0, stops early at 5. 10→9→…→5 (UNTIL exit) → result 5.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN\n0 CELLS DATA + !"
),
)
assert result[0] == 5


# --- GPU Indexing ---


Expand Down
2 changes: 1 addition & 1 deletion include/warpforth/Conversion/Passes.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,7 +28,7 @@ def ConvertForthToMemRef
let dependentDialects = ["mlir::memref::MemRefDialect",
"mlir::arith::ArithDialect",
"mlir::LLVM::LLVMDialect",
"mlir::scf::SCFDialect"];
"mlir::cf::ControlFlowDialect"];
}

def ConvertForthToGPU : Pass<"convert-forth-to-gpu", "mlir::ModuleOp"> {
Expand Down
103 changes: 24 additions & 79 deletions include/warpforth/Dialect/Forth/ForthOps.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,6 @@
#define FORTH_OPS

include "warpforth/Dialect/Forth/ForthDialect.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"

//===----------------------------------------------------------------------===//
Expand DownExpand Up@@ -446,102 +445,48 @@ def Forth_ZeroEqOp : Forth_StackOpBase<"zero_eq"> {
}

//===----------------------------------------------------------------------===//
// Control flow operations.
// Control flow support operations.
//===----------------------------------------------------------------------===//

def Forth_YieldOp : Forth_Op<"yield", [Pure, Terminator, ReturnLike,
ParentOneOf<["IfOp", "BeginUntilOp", "BeginWhileRepeatOp", "DoLoopOp"]>]> {
let summary = "Yield stack from control flow region";
def Forth_PopFlagOp : Forth_Op<"pop_flag", [Pure]> {
let summary = "Pop top of stack as boolean flag";
let description = [{
Yields the current stack state from a control flow region back to
the parent operation. Acts as a region terminator.
When the optional `while_cond` attribute is present, the yield acts as
a WHILE condition (continue when flag is non-zero) rather than
UNTIL (exit when flag is non-zero).
Pops the top value from the stack and returns it as an i1 flag
(non-zero = true, zero = false). Used by IF, UNTIL, WHILE.
Forth semantics: ( flag -- )
}];
let arguments = (ins Forth_StackType:$result, OptionalAttr<UnitAttr>:$while_cond);
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack, I1:$flag);
let assemblyFormat = [{
$result (`while_cond` $while_cond^)? attr-dict `:` type($result)
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($flag)
}];
}

def Forth_IfOp : Forth_Op<"if", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Conditional execution";
def Forth_PopOp : Forth_Op<"pop", [Pure]> {
let summary = "Pop top of stack as i64 value";
let description = [{
Conditional execution. If the flag is non-zero, the then region executes;
otherwise the else region executes. Each region must yield the resulting
stack.
Forth semantics: flag IF then-body ELSE else-body THEN
Pops the top value from the stack and returns it as an i64.
Used by DO to pop start and limit.
Forth semantics: ( x -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$then_region,
SizedRegion<1>:$else_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginUntilOp : Forth_Op<"begin_until", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Post-test loop (do-while)";
let description = [{
BEGIN/UNTIL loop. Executes body, pops flag. If flag is zero, loops back.
If non-zero, exits. Stack effect: ( -- ) with flag consumed each iteration.
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_DoLoopOp : Forth_Op<"do_loop", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Counted loop (DO/LOOP)";
let description = [{
Pops start and limit from the stack, iterates from start to limit-1.
Use forth.loop_index (I word) inside to access the current loop index.
Stack effect: ( limit start -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginWhileRepeatOp : Forth_Op<"begin_while_repeat",
[RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Pre-test loop (BEGIN/WHILE/REPEAT)";
let description = [{
BEGIN/WHILE/REPEAT loop. The condition region runs first, WHILE pops flag.
If flag is non-zero, the body region executes and loops back to condition.
If flag is zero, the loop exits.
Stack effect: ( -- ) with flag consumed each iteration.
let results = (outs Forth_StackType:$output_stack, I64:$value);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($value)
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$condition_region,
SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_LoopIndexOp : Forth_Op<"loop_index", [Pure]> {
let summary = "Push loop index onto stack (I/J/K words)";
def Forth_PushValueOp : Forth_Op<"push_value", [Pure]> {
let summary = "Push dynamic i64 value onto stack";
let description = [{
Pushes the loop index at the given nesting depth onto the stack.
depth=0 is I (innermost), depth=1 is J, depth=2 is K.
Only valid inside nested forth.do_loop bodies at sufficient depth.
( -- index )
Pushes a dynamic i64 value onto the stack. Used by I/J/K to push
the loop counter value.
Forth semantics: ( -- x )
}];
let arguments = (ins Forth_StackType:$input_stack,
DefaultValuedAttr<I64Attr, "0">:$depth);
let arguments = (ins Forth_StackType:$input_stack, I64:$value);
let results = (outs Forth_StackType:$output_stack);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack)
$input_stack `,` $value attr-dict `:` type($input_stack) `,` type($value) `->` type($output_stack)
}];
}

Expand Down
1 change: 0 additions & 1 deletion lib/Conversion/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@ add_mlir_library(MLIRConversionPasses
MLIRGPUToNVVMTransforms
MLIRGPUTransforms
MLIRReconcileUnrealizedCasts
MLIRSCFToControlFlow
MLIRTransforms
)

Expand Down
2 changes: 1 addition & 1 deletion lib/Conversion/ForthToMemRef/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,6 @@ add_mlir_conversion_library(MLIRForthToMemRefConversion
MLIRArithDialect
MLIRLLVMDialect
MLIRFuncDialect
MLIRSCFDialect
MLIRControlFlowDialect
MLIRForth
)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
30 changes: 30 additions & 0 deletions gpu_test/test_kernels.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -154,6 +154,36 @@ def test_do_loop(kernel_runner: KernelRunner) -> None:
assert result == [0, 1, 2, 3, 4]


def test_multi_while(kernel_runner: KernelRunner) -> None:
"""Multi-WHILE: two exit conditions from the same loop (interleaved CF).

20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN
Decrements while >10 AND even. 20→19 (odd, WHILE(2) exit) → result 19.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n"
"20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN\n"
"0 CELLS DATA + !"
),
)
assert result[0] == 19


def test_while_until(kernel_runner: KernelRunner) -> None:
"""WHILE+UNTIL: two different exit mechanisms from the same loop (interleaved CF).

10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN
Decrements while >0, stops early at 5. 10→9→…→5 (UNTIL exit) → result 5.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN\n0 CELLS DATA + !"
),
)
assert result[0] == 5


# --- GPU Indexing ---


Expand Down
2 changes: 1 addition & 1 deletion include/warpforth/Conversion/Passes.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,7 +28,7 @@ def ConvertForthToMemRef
let dependentDialects = ["mlir::memref::MemRefDialect",
"mlir::arith::ArithDialect",
"mlir::LLVM::LLVMDialect",
"mlir::scf::SCFDialect"];
"mlir::cf::ControlFlowDialect"];
}

def ConvertForthToGPU : Pass<"convert-forth-to-gpu", "mlir::ModuleOp"> {
Expand Down
103 changes: 24 additions & 79 deletions include/warpforth/Dialect/Forth/ForthOps.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,6 @@
#define FORTH_OPS

include "warpforth/Dialect/Forth/ForthDialect.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"

//===----------------------------------------------------------------------===//
Expand DownExpand Up@@ -446,102 +445,48 @@ def Forth_ZeroEqOp : Forth_StackOpBase<"zero_eq"> {
}

//===----------------------------------------------------------------------===//
// Control flow operations.
// Control flow support operations.
//===----------------------------------------------------------------------===//

def Forth_YieldOp : Forth_Op<"yield", [Pure, Terminator, ReturnLike,
ParentOneOf<["IfOp", "BeginUntilOp", "BeginWhileRepeatOp", "DoLoopOp"]>]> {
let summary = "Yield stack from control flow region";
def Forth_PopFlagOp : Forth_Op<"pop_flag", [Pure]> {
let summary = "Pop top of stack as boolean flag";
let description = [{
Yields the current stack state from a control flow region back to
the parent operation. Acts as a region terminator.
When the optional `while_cond` attribute is present, the yield acts as
a WHILE condition (continue when flag is non-zero) rather than
UNTIL (exit when flag is non-zero).
Pops the top value from the stack and returns it as an i1 flag
(non-zero = true, zero = false). Used by IF, UNTIL, WHILE.
Forth semantics: ( flag -- )
}];
let arguments = (ins Forth_StackType:$result, OptionalAttr<UnitAttr>:$while_cond);
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack, I1:$flag);
let assemblyFormat = [{
$result (`while_cond` $while_cond^)? attr-dict `:` type($result)
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($flag)
}];
}

def Forth_IfOp : Forth_Op<"if", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Conditional execution";
def Forth_PopOp : Forth_Op<"pop", [Pure]> {
let summary = "Pop top of stack as i64 value";
let description = [{
Conditional execution. If the flag is non-zero, the then region executes;
otherwise the else region executes. Each region must yield the resulting
stack.
Forth semantics: flag IF then-body ELSE else-body THEN
Pops the top value from the stack and returns it as an i64.
Used by DO to pop start and limit.
Forth semantics: ( x -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$then_region,
SizedRegion<1>:$else_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginUntilOp : Forth_Op<"begin_until", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Post-test loop (do-while)";
let description = [{
BEGIN/UNTIL loop. Executes body, pops flag. If flag is zero, loops back.
If non-zero, exits. Stack effect: ( -- ) with flag consumed each iteration.
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_DoLoopOp : Forth_Op<"do_loop", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Counted loop (DO/LOOP)";
let description = [{
Pops start and limit from the stack, iterates from start to limit-1.
Use forth.loop_index (I word) inside to access the current loop index.
Stack effect: ( limit start -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginWhileRepeatOp : Forth_Op<"begin_while_repeat",
[RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Pre-test loop (BEGIN/WHILE/REPEAT)";
let description = [{
BEGIN/WHILE/REPEAT loop. The condition region runs first, WHILE pops flag.
If flag is non-zero, the body region executes and loops back to condition.
If flag is zero, the loop exits.
Stack effect: ( -- ) with flag consumed each iteration.
let results = (outs Forth_StackType:$output_stack, I64:$value);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($value)
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$condition_region,
SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_LoopIndexOp : Forth_Op<"loop_index", [Pure]> {
let summary = "Push loop index onto stack (I/J/K words)";
def Forth_PushValueOp : Forth_Op<"push_value", [Pure]> {
let summary = "Push dynamic i64 value onto stack";
let description = [{
Pushes the loop index at the given nesting depth onto the stack.
depth=0 is I (innermost), depth=1 is J, depth=2 is K.
Only valid inside nested forth.do_loop bodies at sufficient depth.
( -- index )
Pushes a dynamic i64 value onto the stack. Used by I/J/K to push
the loop counter value.
Forth semantics: ( -- x )
}];
let arguments = (ins Forth_StackType:$input_stack,
DefaultValuedAttr<I64Attr, "0">:$depth);
let arguments = (ins Forth_StackType:$input_stack, I64:$value);
let results = (outs Forth_StackType:$output_stack);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack)
$input_stack `,` $value attr-dict `:` type($input_stack) `,` type($value) `->` type($output_stack)
}];
}

Expand Down
1 change: 0 additions & 1 deletion lib/Conversion/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@ add_mlir_library(MLIRConversionPasses
MLIRGPUToNVVMTransforms
MLIRGPUTransforms
MLIRReconcileUnrealizedCasts
MLIRSCFToControlFlow
MLIRTransforms
)

Expand Down
2 changes: 1 addition & 1 deletion lib/Conversion/ForthToMemRef/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,6 @@ add_mlir_conversion_library(MLIRForthToMemRefConversion
MLIRArithDialect
MLIRLLVMDialect
MLIRFuncDialect
MLIRSCFDialect
MLIRControlFlowDialect
MLIRForth
)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
30 changes: 30 additions & 0 deletions gpu_test/test_kernels.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -154,6 +154,36 @@ def test_do_loop(kernel_runner: KernelRunner) -> None:
assert result == [0, 1, 2, 3, 4]


def test_multi_while(kernel_runner: KernelRunner) -> None:
"""Multi-WHILE: two exit conditions from the same loop (interleaved CF).

20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN
Decrements while >10 AND even. 20→19 (odd, WHILE(2) exit) → result 19.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n"
"20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN\n"
"0 CELLS DATA + !"
),
)
assert result[0] == 19


def test_while_until(kernel_runner: KernelRunner) -> None:
"""WHILE+UNTIL: two different exit mechanisms from the same loop (interleaved CF).

10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN
Decrements while >0, stops early at 5. 10→9→…→5 (UNTIL exit) → result 5.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN\n0 CELLS DATA + !"
),
)
assert result[0] == 5


# --- GPU Indexing ---


Expand Down
2 changes: 1 addition & 1 deletion include/warpforth/Conversion/Passes.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,7 +28,7 @@ def ConvertForthToMemRef
let dependentDialects = ["mlir::memref::MemRefDialect",
"mlir::arith::ArithDialect",
"mlir::LLVM::LLVMDialect",
"mlir::scf::SCFDialect"];
"mlir::cf::ControlFlowDialect"];
}

def ConvertForthToGPU : Pass<"convert-forth-to-gpu", "mlir::ModuleOp"> {
Expand Down
103 changes: 24 additions & 79 deletions include/warpforth/Dialect/Forth/ForthOps.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,6 @@
#define FORTH_OPS

include "warpforth/Dialect/Forth/ForthDialect.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"

//===----------------------------------------------------------------------===//
Expand DownExpand Up@@ -446,102 +445,48 @@ def Forth_ZeroEqOp : Forth_StackOpBase<"zero_eq"> {
}

//===----------------------------------------------------------------------===//
// Control flow operations.
// Control flow support operations.
//===----------------------------------------------------------------------===//

def Forth_YieldOp : Forth_Op<"yield", [Pure, Terminator, ReturnLike,
ParentOneOf<["IfOp", "BeginUntilOp", "BeginWhileRepeatOp", "DoLoopOp"]>]> {
let summary = "Yield stack from control flow region";
def Forth_PopFlagOp : Forth_Op<"pop_flag", [Pure]> {
let summary = "Pop top of stack as boolean flag";
let description = [{
Yields the current stack state from a control flow region back to
the parent operation. Acts as a region terminator.
When the optional `while_cond` attribute is present, the yield acts as
a WHILE condition (continue when flag is non-zero) rather than
UNTIL (exit when flag is non-zero).
Pops the top value from the stack and returns it as an i1 flag
(non-zero = true, zero = false). Used by IF, UNTIL, WHILE.
Forth semantics: ( flag -- )
}];
let arguments = (ins Forth_StackType:$result, OptionalAttr<UnitAttr>:$while_cond);
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack, I1:$flag);
let assemblyFormat = [{
$result (`while_cond` $while_cond^)? attr-dict `:` type($result)
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($flag)
}];
}

def Forth_IfOp : Forth_Op<"if", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Conditional execution";
def Forth_PopOp : Forth_Op<"pop", [Pure]> {
let summary = "Pop top of stack as i64 value";
let description = [{
Conditional execution. If the flag is non-zero, the then region executes;
otherwise the else region executes. Each region must yield the resulting
stack.
Forth semantics: flag IF then-body ELSE else-body THEN
Pops the top value from the stack and returns it as an i64.
Used by DO to pop start and limit.
Forth semantics: ( x -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$then_region,
SizedRegion<1>:$else_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginUntilOp : Forth_Op<"begin_until", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Post-test loop (do-while)";
let description = [{
BEGIN/UNTIL loop. Executes body, pops flag. If flag is zero, loops back.
If non-zero, exits. Stack effect: ( -- ) with flag consumed each iteration.
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_DoLoopOp : Forth_Op<"do_loop", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Counted loop (DO/LOOP)";
let description = [{
Pops start and limit from the stack, iterates from start to limit-1.
Use forth.loop_index (I word) inside to access the current loop index.
Stack effect: ( limit start -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginWhileRepeatOp : Forth_Op<"begin_while_repeat",
[RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Pre-test loop (BEGIN/WHILE/REPEAT)";
let description = [{
BEGIN/WHILE/REPEAT loop. The condition region runs first, WHILE pops flag.
If flag is non-zero, the body region executes and loops back to condition.
If flag is zero, the loop exits.
Stack effect: ( -- ) with flag consumed each iteration.
let results = (outs Forth_StackType:$output_stack, I64:$value);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($value)
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$condition_region,
SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_LoopIndexOp : Forth_Op<"loop_index", [Pure]> {
let summary = "Push loop index onto stack (I/J/K words)";
def Forth_PushValueOp : Forth_Op<"push_value", [Pure]> {
let summary = "Push dynamic i64 value onto stack";
let description = [{
Pushes the loop index at the given nesting depth onto the stack.
depth=0 is I (innermost), depth=1 is J, depth=2 is K.
Only valid inside nested forth.do_loop bodies at sufficient depth.
( -- index )
Pushes a dynamic i64 value onto the stack. Used by I/J/K to push
the loop counter value.
Forth semantics: ( -- x )
}];
let arguments = (ins Forth_StackType:$input_stack,
DefaultValuedAttr<I64Attr, "0">:$depth);
let arguments = (ins Forth_StackType:$input_stack, I64:$value);
let results = (outs Forth_StackType:$output_stack);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack)
$input_stack `,` $value attr-dict `:` type($input_stack) `,` type($value) `->` type($output_stack)
}];
}

Expand Down
1 change: 0 additions & 1 deletion lib/Conversion/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@ add_mlir_library(MLIRConversionPasses
MLIRGPUToNVVMTransforms
MLIRGPUTransforms
MLIRReconcileUnrealizedCasts
MLIRSCFToControlFlow
MLIRTransforms
)

Expand Down
2 changes: 1 addition & 1 deletion lib/Conversion/ForthToMemRef/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,6 @@ add_mlir_conversion_library(MLIRForthToMemRefConversion
MLIRArithDialect
MLIRLLVMDialect
MLIRFuncDialect
MLIRSCFDialect
MLIRControlFlowDialect
MLIRForth
)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
30 changes: 30 additions & 0 deletions gpu_test/test_kernels.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -154,6 +154,36 @@ def test_do_loop(kernel_runner: KernelRunner) -> None:
assert result == [0, 1, 2, 3, 4]


def test_multi_while(kernel_runner: KernelRunner) -> None:
"""Multi-WHILE: two exit conditions from the same loop (interleaved CF).

20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN
Decrements while >10 AND even. 20→19 (odd, WHILE(2) exit) → result 19.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n"
"20 BEGIN DUP 10 > WHILE DUP 2 MOD 0= WHILE 1 - REPEAT THEN\n"
"0 CELLS DATA + !"
),
)
assert result[0] == 19


def test_while_until(kernel_runner: KernelRunner) -> None:
"""WHILE+UNTIL: two different exit mechanisms from the same loop (interleaved CF).

10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN
Decrements while >0, stops early at 5. 10→9→…→5 (UNTIL exit) → result 5.
"""
result = kernel_runner.run(
forth_source=(
"PARAM DATA 256\n10 BEGIN DUP 0 > WHILE 1 - DUP 5 = UNTIL THEN\n0 CELLS DATA + !"
),
)
assert result[0] == 5


# --- GPU Indexing ---


Expand Down
2 changes: 1 addition & 1 deletion include/warpforth/Conversion/Passes.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,7 +28,7 @@ def ConvertForthToMemRef
let dependentDialects = ["mlir::memref::MemRefDialect",
"mlir::arith::ArithDialect",
"mlir::LLVM::LLVMDialect",
"mlir::scf::SCFDialect"];
"mlir::cf::ControlFlowDialect"];
}

def ConvertForthToGPU : Pass<"convert-forth-to-gpu", "mlir::ModuleOp"> {
Expand Down
103 changes: 24 additions & 79 deletions include/warpforth/Dialect/Forth/ForthOps.td
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,6 @@
#define FORTH_OPS

include "warpforth/Dialect/Forth/ForthDialect.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"

//===----------------------------------------------------------------------===//
Expand DownExpand Up@@ -446,102 +445,48 @@ def Forth_ZeroEqOp : Forth_StackOpBase<"zero_eq"> {
}

//===----------------------------------------------------------------------===//
// Control flow operations.
// Control flow support operations.
//===----------------------------------------------------------------------===//

def Forth_YieldOp : Forth_Op<"yield", [Pure, Terminator, ReturnLike,
ParentOneOf<["IfOp", "BeginUntilOp", "BeginWhileRepeatOp", "DoLoopOp"]>]> {
let summary = "Yield stack from control flow region";
def Forth_PopFlagOp : Forth_Op<"pop_flag", [Pure]> {
let summary = "Pop top of stack as boolean flag";
let description = [{
Yields the current stack state from a control flow region back to
the parent operation. Acts as a region terminator.
When the optional `while_cond` attribute is present, the yield acts as
a WHILE condition (continue when flag is non-zero) rather than
UNTIL (exit when flag is non-zero).
Pops the top value from the stack and returns it as an i1 flag
(non-zero = true, zero = false). Used by IF, UNTIL, WHILE.
Forth semantics: ( flag -- )
}];
let arguments = (ins Forth_StackType:$result, OptionalAttr<UnitAttr>:$while_cond);
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack, I1:$flag);
let assemblyFormat = [{
$result (`while_cond` $while_cond^)? attr-dict `:` type($result)
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($flag)
}];
}

def Forth_IfOp : Forth_Op<"if", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Conditional execution";
def Forth_PopOp : Forth_Op<"pop", [Pure]> {
let summary = "Pop top of stack as i64 value";
let description = [{
Conditional execution. If the flag is non-zero, the then region executes;
otherwise the else region executes. Each region must yield the resulting
stack.
Forth semantics: flag IF then-body ELSE else-body THEN
Pops the top value from the stack and returns it as an i64.
Used by DO to pop start and limit.
Forth semantics: ( x -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$then_region,
SizedRegion<1>:$else_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginUntilOp : Forth_Op<"begin_until", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Post-test loop (do-while)";
let description = [{
BEGIN/UNTIL loop. Executes body, pops flag. If flag is zero, loops back.
If non-zero, exits. Stack effect: ( -- ) with flag consumed each iteration.
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_DoLoopOp : Forth_Op<"do_loop", [RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Counted loop (DO/LOOP)";
let description = [{
Pops start and limit from the stack, iterates from start to limit-1.
Use forth.loop_index (I word) inside to access the current loop index.
Stack effect: ( limit start -- )
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_BeginWhileRepeatOp : Forth_Op<"begin_while_repeat",
[RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
let summary = "Pre-test loop (BEGIN/WHILE/REPEAT)";
let description = [{
BEGIN/WHILE/REPEAT loop. The condition region runs first, WHILE pops flag.
If flag is non-zero, the body region executes and loops back to condition.
If flag is zero, the loop exits.
Stack effect: ( -- ) with flag consumed each iteration.
let results = (outs Forth_StackType:$output_stack, I64:$value);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack) `,` type($value)
}];
let arguments = (ins Forth_StackType:$input_stack);
let results = (outs Forth_StackType:$output_stack);
let regions = (region SizedRegion<1>:$condition_region,
SizedRegion<1>:$body_region);
let hasCustomAssemblyFormat = 1;
}

def Forth_LoopIndexOp : Forth_Op<"loop_index", [Pure]> {
let summary = "Push loop index onto stack (I/J/K words)";
def Forth_PushValueOp : Forth_Op<"push_value", [Pure]> {
let summary = "Push dynamic i64 value onto stack";
let description = [{
Pushes the loop index at the given nesting depth onto the stack.
depth=0 is I (innermost), depth=1 is J, depth=2 is K.
Only valid inside nested forth.do_loop bodies at sufficient depth.
( -- index )
Pushes a dynamic i64 value onto the stack. Used by I/J/K to push
the loop counter value.
Forth semantics: ( -- x )
}];
let arguments = (ins Forth_StackType:$input_stack,
DefaultValuedAttr<I64Attr, "0">:$depth);
let arguments = (ins Forth_StackType:$input_stack, I64:$value);
let results = (outs Forth_StackType:$output_stack);
let assemblyFormat = [{
$input_stack attr-dict `:` type($input_stack) `->` type($output_stack)
$input_stack `,` $value attr-dict `:` type($input_stack) `,` type($value) `->` type($output_stack)
}];
}

Expand Down
1 change: 0 additions & 1 deletion lib/Conversion/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@ add_mlir_library(MLIRConversionPasses
MLIRGPUToNVVMTransforms
MLIRGPUTransforms
MLIRReconcileUnrealizedCasts
MLIRSCFToControlFlow
MLIRTransforms
)

Expand Down
2 changes: 1 addition & 1 deletion lib/Conversion/ForthToMemRef/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,6 @@ add_mlir_conversion_library(MLIRForthToMemRefConversion
MLIRArithDialect
MLIRLLVMDialect
MLIRFuncDialect
MLIRSCFDialect
MLIRControlFlowDialect
MLIRForth
)
Loading