From bfb683474f5b59a382a538e54c308d5b4c565016 Mon Sep 17 00:00:00 2001 From: Alex Cameron Date: Mon, 16 Feb 2026 00:47:01 +1100 Subject: [PATCH] feat: add bitwise operations AND, OR, XOR, NOT, LSHIFT, RSHIFT --- include/warpforth/Dialect/Forth/ForthOps.td | 94 +++++++++++++++++++ .../ForthToMemRef/ForthToMemRef.cpp | 58 ++++++++++-- lib/Translation/ForthToMLIR/ForthToMLIR.cpp | 14 +++ test/Conversion/ForthToMemRef/bitwise.mlir | 64 +++++++++++++ test/Translation/Forth/bitwise-ops.forth | 22 +++++ 5 files changed, 243 insertions(+), 9 deletions(-) create mode 100644 test/Conversion/ForthToMemRef/bitwise.mlir create mode 100644 test/Translation/Forth/bitwise-ops.forth diff --git a/include/warpforth/Dialect/Forth/ForthOps.td b/include/warpforth/Dialect/Forth/ForthOps.td index 056a98d..11bac48 100644 --- a/include/warpforth/Dialect/Forth/ForthOps.td +++ b/include/warpforth/Dialect/Forth/ForthOps.td @@ -201,6 +201,100 @@ def Forth_ModOp : Forth_Op<"mod", [Pure]> { }]; } +//===----------------------------------------------------------------------===// +// Bitwise operations. +//===----------------------------------------------------------------------===// + +def Forth_AndOp : Forth_Op<"and", [Pure]> { + let summary = "Bitwise AND of top two stack elements"; + let description = [{ + Pops the top two elements, performs bitwise AND, and pushes the result. + Forth semantics: ( a b -- a&b ) + }]; + + let arguments = (ins Forth_StackType:$input_stack); + let results = (outs Forth_StackType:$output_stack); + + let assemblyFormat = [{ + $input_stack attr-dict `:` type($input_stack) `->` type($output_stack) + }]; +} + +def Forth_OrOp : Forth_Op<"or", [Pure]> { + let summary = "Bitwise OR of top two stack elements"; + let description = [{ + Pops the top two elements, performs bitwise OR, and pushes the result. + Forth semantics: ( a b -- a|b ) + }]; + + let arguments = (ins Forth_StackType:$input_stack); + let results = (outs Forth_StackType:$output_stack); + + let assemblyFormat = [{ + $input_stack attr-dict `:` type($input_stack) `->` type($output_stack) + }]; +} + +def Forth_XorOp : Forth_Op<"xor", [Pure]> { + let summary = "Bitwise XOR of top two stack elements"; + let description = [{ + Pops the top two elements, performs bitwise XOR, and pushes the result. + Forth semantics: ( a b -- a^b ) + }]; + + let arguments = (ins Forth_StackType:$input_stack); + let results = (outs Forth_StackType:$output_stack); + + let assemblyFormat = [{ + $input_stack attr-dict `:` type($input_stack) `->` type($output_stack) + }]; +} + +def Forth_NotOp : Forth_Op<"not", [Pure]> { + let summary = "Bitwise NOT of top stack element"; + let description = [{ + Pops the top element, performs bitwise NOT (complement), and pushes the result. + Forth semantics: ( a -- ~a ) + }]; + + let arguments = (ins Forth_StackType:$input_stack); + let results = (outs Forth_StackType:$output_stack); + + let assemblyFormat = [{ + $input_stack attr-dict `:` type($input_stack) `->` type($output_stack) + }]; +} + +def Forth_LshiftOp : Forth_Op<"lshift", [Pure]> { + let summary = "Left shift"; + let description = [{ + Pops shift amount and value, shifts value left, and pushes the result. + Forth semantics: ( a n -- a<` type($output_stack) + }]; +} + +def Forth_RshiftOp : Forth_Op<"rshift", [Pure]> { + let summary = "Logical right shift"; + let description = [{ + Pops shift amount and value, shifts value right (logical/unsigned), and pushes the result. + Forth semantics: ( a n -- a>>n ) + }]; + + let arguments = (ins Forth_StackType:$input_stack); + let results = (outs Forth_StackType:$output_stack); + + let assemblyFormat = [{ + $input_stack attr-dict `:` type($input_stack) `->` type($output_stack) + }]; +} + //===----------------------------------------------------------------------===// // Memory operations. //===----------------------------------------------------------------------===// diff --git a/lib/Conversion/ForthToMemRef/ForthToMemRef.cpp b/lib/Conversion/ForthToMemRef/ForthToMemRef.cpp index 638f151..d2ca260 100644 --- a/lib/Conversion/ForthToMemRef/ForthToMemRef.cpp +++ b/lib/Conversion/ForthToMemRef/ForthToMemRef.cpp @@ -298,6 +298,13 @@ using SubOpConversion = BinaryArithOpConversion; using MulOpConversion = BinaryArithOpConversion; using DivOpConversion = BinaryArithOpConversion; using ModOpConversion = BinaryArithOpConversion; +using AndOpConversion = BinaryArithOpConversion; +using OrOpConversion = BinaryArithOpConversion; +using XorOpConversion = BinaryArithOpConversion; +using LshiftOpConversion = + BinaryArithOpConversion; +using RshiftOpConversion = + BinaryArithOpConversion; /// Base template for binary comparison operations. /// Pops two values, compares, pushes -1 (true) or 0 (false): (a b -- flag) @@ -348,6 +355,37 @@ using LtOpConversion = using GtOpConversion = BinaryCmpOpConversion; +/// Conversion pattern for forth.not operation (bitwise NOT). +/// Unary: pops one value, XORs with -1 (all bits set), pushes result: (a -- ~a) +struct NotOpConversion : public OpConversionPattern { + NotOpConversion(const TypeConverter &typeConverter, MLIRContext *context) + : OpConversionPattern(typeConverter, context) {} + using OneToNOpAdaptor = OpConversionPattern::OneToNOpAdaptor; + + LogicalResult + matchAndRewrite(forth::NotOp op, OneToNOpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + auto loc = op.getLoc(); + ValueRange inputStack = adaptor.getOperands()[0]; + Value memref = inputStack[0]; + Value stackPtr = inputStack[1]; + + // Load top value + Value a = rewriter.create(loc, memref, stackPtr); + + // XOR with -1 (all bits set) to flip all bits + Value allOnes = rewriter.create( + loc, rewriter.getI64Type(), rewriter.getI64IntegerAttr(-1)); + Value result = rewriter.create(loc, a, allOnes); + + // Store result at same position (SP unchanged) + rewriter.create(loc, result, memref, stackPtr); + + rewriter.replaceOpWithMultiple(op, {{memref, stackPtr}}); + return success(); + } +}; + /// Conversion pattern for forth.zero_eq operation (0=). /// Unary: pops one value, pushes -1 if zero, 0 otherwise: (a -- flag) struct ZeroEqOpConversion : public OpConversionPattern { @@ -891,15 +929,17 @@ struct ConvertForthToMemRefPass RewritePatternSet patterns(context); // Add Forth operation conversion patterns - patterns.add( - typeConverter, context); + patterns + .add( + typeConverter, context); // Add GPU indexing op conversion patterns patterns.add>(typeConverter, diff --git a/lib/Translation/ForthToMLIR/ForthToMLIR.cpp b/lib/Translation/ForthToMLIR/ForthToMLIR.cpp index ffa6320..8a56c74 100644 --- a/lib/Translation/ForthToMLIR/ForthToMLIR.cpp +++ b/lib/Translation/ForthToMLIR/ForthToMLIR.cpp @@ -271,6 +271,20 @@ Value ForthParser::emitOperation(StringRef word, Value inputStack, return builder.create(loc, stackType, inputStack).getResult(); } else if (word == "MOD") { return builder.create(loc, stackType, inputStack).getResult(); + } else if (word == "AND") { + return builder.create(loc, stackType, inputStack).getResult(); + } else if (word == "OR") { + return builder.create(loc, stackType, inputStack).getResult(); + } else if (word == "XOR") { + return builder.create(loc, stackType, inputStack).getResult(); + } else if (word == "NOT") { + return builder.create(loc, stackType, inputStack).getResult(); + } else if (word == "LSHIFT") { + return builder.create(loc, stackType, inputStack) + .getResult(); + } else if (word == "RSHIFT") { + return builder.create(loc, stackType, inputStack) + .getResult(); } else if (word == "@") { return builder.create(loc, stackType, inputStack) .getResult(); diff --git a/test/Conversion/ForthToMemRef/bitwise.mlir b/test/Conversion/ForthToMemRef/bitwise.mlir new file mode 100644 index 0000000..be8ea2c --- /dev/null +++ b/test/Conversion/ForthToMemRef/bitwise.mlir @@ -0,0 +1,64 @@ +// RUN: %warpforth-opt --convert-forth-to-memref %s | %FileCheck %s + +// CHECK-LABEL: func.func private @main + +// and: pop two, arith.andi, store result +// CHECK: memref.load +// CHECK: arith.subi +// CHECK: memref.load +// CHECK: arith.andi %{{.*}}, %{{.*}} : i64 +// CHECK: memref.store + +// or: pop two, arith.ori, store result +// CHECK: memref.load +// CHECK: memref.load +// CHECK: arith.ori %{{.*}}, %{{.*}} : i64 +// CHECK: memref.store + +// xor: pop two, arith.xori, store result +// CHECK: memref.load +// CHECK: memref.load +// CHECK: arith.xori %{{.*}}, %{{.*}} : i64 +// CHECK: memref.store + +// not: load one value, xori with -1, store at same SP +// CHECK: memref.load +// CHECK: arith.constant -1 : i64 +// CHECK: arith.xori %{{.*}}, %{{.*}} : i64 +// CHECK: memref.store + +// lshift: pop two, arith.shli, store result +// CHECK: memref.load +// CHECK: memref.load +// CHECK: arith.shli %{{.*}}, %{{.*}} : i64 +// CHECK: memref.store + +// rshift: pop two, arith.shrui, store result +// CHECK: memref.load +// CHECK: memref.load +// CHECK: arith.shrui %{{.*}}, %{{.*}} : i64 +// CHECK: memref.store + +module { + func.func private @main() { + %0 = forth.stack !forth.stack + %1 = forth.literal %0 3 : !forth.stack -> !forth.stack + %2 = forth.literal %1 5 : !forth.stack -> !forth.stack + %3 = forth.and %2 : !forth.stack -> !forth.stack + %4 = forth.literal %3 7 : !forth.stack -> !forth.stack + %5 = forth.literal %4 8 : !forth.stack -> !forth.stack + %6 = forth.or %5 : !forth.stack -> !forth.stack + %7 = forth.literal %6 15 : !forth.stack -> !forth.stack + %8 = forth.literal %7 3 : !forth.stack -> !forth.stack + %9 = forth.xor %8 : !forth.stack -> !forth.stack + %10 = forth.literal %9 42 : !forth.stack -> !forth.stack + %11 = forth.not %10 : !forth.stack -> !forth.stack + %12 = forth.literal %11 1 : !forth.stack -> !forth.stack + %13 = forth.literal %12 4 : !forth.stack -> !forth.stack + %14 = forth.lshift %13 : !forth.stack -> !forth.stack + %15 = forth.literal %14 256 : !forth.stack -> !forth.stack + %16 = forth.literal %15 2 : !forth.stack -> !forth.stack + %17 = forth.rshift %16 : !forth.stack -> !forth.stack + return + } +} diff --git a/test/Translation/Forth/bitwise-ops.forth b/test/Translation/Forth/bitwise-ops.forth new file mode 100644 index 0000000..c2d4742 --- /dev/null +++ b/test/Translation/Forth/bitwise-ops.forth @@ -0,0 +1,22 @@ +\ RUN: %warpforth-translate --forth-to-mlir %s | %FileCheck %s + +\ Verify bitwise operations parse correctly with SSA chaining +\ CHECK: %[[S0:.*]] = forth.stack +\ CHECK: %[[S1:.*]] = forth.literal %[[S0]] +\ CHECK: %[[S2:.*]] = forth.literal %[[S1]] +\ CHECK: %[[S3:.*]] = forth.and %[[S2]] +\ CHECK: %[[S4:.*]] = forth.literal %[[S3]] +\ CHECK: %[[S5:.*]] = forth.literal %[[S4]] +\ CHECK: %[[S6:.*]] = forth.or %[[S5]] +\ CHECK: %[[S7:.*]] = forth.literal %[[S6]] +\ CHECK: %[[S8:.*]] = forth.literal %[[S7]] +\ CHECK: %[[S9:.*]] = forth.xor %[[S8]] +\ CHECK: %[[S10:.*]] = forth.literal %[[S9]] +\ CHECK: %[[S11:.*]] = forth.not %[[S10]] +\ CHECK: %[[S12:.*]] = forth.literal %[[S11]] +\ CHECK: %[[S13:.*]] = forth.literal %[[S12]] +\ CHECK: %[[S14:.*]] = forth.lshift %[[S13]] +\ CHECK: %[[S15:.*]] = forth.literal %[[S14]] +\ CHECK: %[[S16:.*]] = forth.literal %[[S15]] +\ CHECK: %{{.*}} = forth.rshift %[[S16]] +3 5 AND 7 8 OR 15 3 XOR 42 NOT 1 4 LSHIFT 256 2 RSHIFT