diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e3c70d0d..181c399d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,8 +64,6 @@ jobs: - name: setup dataflow tool-chain working-directory: ${{github.workspace}} run: | - git clone https://github.com/coredac/dataflow.git - cd dataflow mkdir build && cd build cmake -G Ninja .. \ -DLLVM_DIR=${{github.workspace}}/llvm-project/build/lib/cmake/llvm \ @@ -102,6 +100,6 @@ jobs: - name: run test working-directory: ${{github.workspace}} run: | - cd ${{github.workspace}}/dataflow/test + cd ${{github.workspace}}/test ${{github.workspace}}/llvm-project/build/bin/llvm-lit * -v diff --git a/include/Common/AcceleratorAttrs.h b/include/Common/AcceleratorAttrs.h new file mode 100644 index 00000000..744db0f1 --- /dev/null +++ b/include/Common/AcceleratorAttrs.h @@ -0,0 +1,20 @@ +#ifndef COMMON_ACCELERATOR_ATTRS_H +#define COMMON_ACCELERATOR_ATTRS_H + +#include "llvm/ADT/StringRef.h" + +namespace mlir { +namespace accel { + +// Common attribute key. +constexpr llvm::StringRef kAcceleratorAttr = "accelerator"; + +// Common accelerator targets. +constexpr llvm::StringRef kNeuraTarget = "neura"; +constexpr llvm::StringRef kGpuTarget = "gpu"; +constexpr llvm::StringRef kTpuTarget = "tpu"; + +} // namespace accel +} // namespace mlir + +#endif // COMMON_ACCELERATOR_ATTRS_H diff --git a/include/NeuraDialect/NeuraOps.td b/include/NeuraDialect/NeuraOps.td index ccf407db..06866449 100644 --- a/include/NeuraDialect/NeuraOps.td +++ b/include/NeuraDialect/NeuraOps.td @@ -2,6 +2,15 @@ include "NeuraDialect/NeuraDialect.td" +// ---------------------------------------------------- +// Defines basic scalar operations. + +def Neura_ConstantOp : Op { + let arguments = (ins AnyAttr:$value); + let results = (outs AnyType:$result); + // let assemblyFormat = "attr-dict `:` type($result)"; +} + // Defines an addition operation. def Neura_AddOp : Op { let summary = "Integer addition operation"; @@ -12,7 +21,7 @@ def Neura_AddOp : Op { let traits = [SameOperandsAndResultElementType]; } -// Defines an addition operation. +// Defines a floating-point addition operation. def Neura_FAddOp : Op { let summary = "Floating addition operation"; let opName = "fadd"; @@ -22,7 +31,7 @@ def Neura_FAddOp : Op { let traits = [SameOperandsAndResultElementType]; } -// Defines a multiplication operation. +// Defines a floating-point multiplication operation. def Neura_FMulOp : Op { let summary = "Floating multiplication operation"; let opName = "fmul"; @@ -32,6 +41,69 @@ def Neura_FMulOp : Op { let traits = [SameOperandsAndResultElementType]; } +def Neura_OrOp : Op { + let summary = "Bitwise OR operation"; + let arguments = (ins AnySignlessInteger:$lhs, AnySignlessInteger:$rhs); + let results = (outs AnySignlessInteger:$result); + let traits = [SameOperandsAndResultElementType]; + // let assemblyFormat = "$lhs `,` $rhs attr-dict `:` type($result)"; +} + +// Defines a move operation for data communication. +def Neura_MovOp : Op { + let summary = "Move operation"; + let opName = "mov"; + let arguments = (ins AnyType:$lhs); + let results = (outs AnyType:$result); + // let assemblyFormat = "$lhs attr-dict `:` type($lhs) `->` type($result)"; + // let traits = [Pure]; +} + +def Neura_ICmpOp : Op { + let summary = "Integer compare operation"; + let opName = "icmp"; + let arguments = (ins AnyInteger:$lhs, AnyInteger:$rhs, + StrAttr:$predicate); + let results = (outs I1:$result); + // let assemblyFormat = "$lhs `,` $rhs `,` $predicate attr-dict `:` type($result)"; + // let traits = [SameOperandsAndResultElementType]; +} + +def Neura_LoadOp : Op { + let arguments = (ins AnyType:$addr); + let results = (outs AnyType:$value); + // let assemblyFormat = "$addr attr-dict `:` type($value)"; +} + +def Neura_StoreOp : Op { + let arguments = (ins AnyType:$value, AnyType:$addr); + let results = (outs); + // let assemblyFormat = "$value `,` $addr attr-dict"; +} + +def Neura_GEP : Op { + let summary = "Pointer computation using offset indices"; + let arguments = (ins AnyType:$base, Variadic:$indices); + let results = (outs AnyType:$result); + // let assemblyFormat = "$base `[` $indices `]` attr-dict"; +} + +def Neura_CondBr : Op { + let arguments = (ins I1:$condition, + Variadic:$trueArgs, + Variadic:$falseArgs); + let successors = (successor AnySuccessor:$trueDest, AnySuccessor:$falseDest); + let assemblyFormat = "$condition `then` $trueArgs `:` type($trueArgs) `to` $trueDest `else` $falseArgs `:` type($falseArgs) `to` $falseDest attr-dict"; +} + +def Neura_ReturnOp : Op { + let arguments = (ins Variadic:$values); + // let assemblyFormat = "($values^)? attr-dict"; +} + +// ---------------------------------------------------- +// Defines vector operations. + def VectorOfAnyFloat : TypeConstraint< CPred< @@ -51,6 +123,9 @@ def Neura_VFMulOp : Op { let traits = [SameOperandsAndResultElementType]; } +// ---------------------------------------------------- +// Defines fused operations. + def Neura_FAddFAddOp : Op { let summary = "Fused fadd(fadd(a, b), c)"; let arguments = (ins AnyFloat:$a, AnyFloat:$b, AnyFloat:$c); @@ -67,12 +142,4 @@ def Neura_FMulFAddOp : Op { let traits = [SameOperandsAndResultElementType]; } -// Defines a move operation for data communication. -def Neura_MovOp : Op { - let summary = "Move operation"; - let opName = "mov"; - let arguments = (ins AnyType:$lhs); - let results = (outs AnyType:$result); - let assemblyFormat = "$lhs attr-dict `:` type($lhs) `->` type($result)"; - // let traits = [Pure]; -} + diff --git a/include/Transforms/AssignAcceleratorPass.h b/include/Transforms/AssignAcceleratorPass.h new file mode 100644 index 00000000..ae803f4b --- /dev/null +++ b/include/Transforms/AssignAcceleratorPass.h @@ -0,0 +1,13 @@ +#ifndef NEURA_TRANSFORMS_ASSIGN_ACCELERATORPASS_H +#define NEURA_TRANSFORMS_ASSIGN_ACCELERATORPASS_H + +#include "mlir/Pass/Pass.h" + +namespace mlir { +namespace neura { + std::unique_ptr createAssignAcceleratorPass(); +} // namespace neura +} // namespace mlir + +#endif // NEURA_TRANSFORMS_ASSIGN_ACCELERATORPASS_H + diff --git a/lib/Conversion/LlvmToNeura/LlvmToNeuraPass.cpp b/lib/Conversion/LlvmToNeura/LlvmToNeuraPass.cpp index 39b8d6f8..c1a5854b 100644 --- a/lib/Conversion/LlvmToNeura/LlvmToNeuraPass.cpp +++ b/lib/Conversion/LlvmToNeura/LlvmToNeuraPass.cpp @@ -1,4 +1,5 @@ #include "Conversion/LlvmToNeura/LlvmToNeura.h" +#include "Common/AcceleratorAttrs.h" #include "NeuraDialect/NeuraDialect.h" #include "NeuraDialect/NeuraOps.h" #include "mlir/Dialect/LLVMIR/LLVMAttrs.h" @@ -75,6 +76,110 @@ struct LlvmVFMulToNeuraVFMul: public OpRewritePattern { } }; +struct LlvmICmpToNeuraICmp : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(LLVM::ICmpOp op, + PatternRewriter &rewriter) const override { + auto pred = op.getPredicate(); + auto lhs = op.getLhs(); + auto rhs = op.getRhs(); + auto resultType = op.getType(); + + rewriter.replaceOpWithNewOp( + op, resultType, lhs, rhs, rewriter.getStringAttr(LLVM::stringifyICmpPredicate(pred))); + return success(); + } +}; + +struct LlvmGEPToNeuraGEP : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(mlir::LLVM::GEPOp op, + PatternRewriter &rewriter) const override { + Value base = op.getBase(); + SmallVector indexValues; + + for (auto gepIndex : op.getIndices()) { + if (auto val = gepIndex.dyn_cast()) { + indexValues.push_back(val); + } else if (auto intAttr = gepIndex.dyn_cast()) { + auto cst = rewriter.create( + op.getLoc(), rewriter.getIndexType(), intAttr); + indexValues.push_back(cst); + } else { + return op.emitOpError("Unsupported GEP index kind"); + } + } + + rewriter.replaceOpWithNewOp(op, op.getType(), base, indexValues); + return success(); + } +}; + +struct LlvmLoadToNeuraLoad : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(mlir::LLVM::LoadOp op, + PatternRewriter &rewriter) const override { + Value ptr = op.getAddr(); // getPointer() is deprecated + Type resultType = op.getResult().getType(); + rewriter.replaceOpWithNewOp(op, resultType, ptr); + return success(); + } +}; + +struct LlvmStoreToNeuraStore : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(mlir::LLVM::StoreOp op, + PatternRewriter &rewriter) const override { + Value value = op.getValue(); + Value addr = op.getAddr(); // getPointer() is deprecated + rewriter.replaceOpWithNewOp(op, value, addr); + return success(); + } +}; + +struct LlvmCondBrToNeuraCondBr : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + LogicalResult matchAndRewrite(LLVM::CondBrOp op, + PatternRewriter &rewriter) const override { + // Get the source operation's successors (basic blocks) + Block *trueDest = op.getTrueDest(); + Block *falseDest = op.getFalseDest(); + + // Get the operands for each destination + ValueRange trueOperands = op.getTrueDestOperands(); + ValueRange falseOperands = op.getFalseDestOperands(); + + // Create the new operation with proper successors + auto newOp = rewriter.create( + op.getLoc(), // Location + op.getCondition(), // Condition + trueOperands, // True destination operands + falseOperands, // False destination operands + trueDest, // True destination block + falseDest // False destination block + ); + + // Replace the old op with the new one + rewriter.replaceOp(op, newOp->getResults()); + + return success(); + } +}; + +struct LlvmReturnToNeuraReturn : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(LLVM::ReturnOp op, + PatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp(op, op.getOperands()); + return success(); + } +}; + struct LowerLlvmToNeuraPass : public PassWrapper> { @@ -96,17 +201,27 @@ struct LowerLlvmToNeuraPass patterns.add(&getContext()); patterns.add(&getContext()); patterns.add(&getContext()); + patterns.add(&getContext()); + patterns.add(&getContext()); + patterns.add(&getContext()); + patterns.add(&getContext()); + patterns.add(&getContext()); + patterns.add(&getContext()); + FrozenRewritePatternSet frozen(std::move(patterns)); ModuleOp module_op = getOperation(); // Applies to every region inside the module (regardless of func type, // e.g., mlir func or llvm func). - module_op.walk([&](Operation *op) { - if (!op->getRegions().empty()) { - for (Region ®ion : op->getRegions()) { - if (failed(applyPatternsAndFoldGreedily(region, frozen))) { - signalPassFailure(); + module_op.walk([&](FunctionOpInterface func) { + if (func->hasAttr(mlir::accel::kAcceleratorAttr)) { + auto target = func->getAttrOfType(mlir::accel::kAcceleratorAttr); + if (target && target.getValue() == mlir::accel::kNeuraTarget) { + for (Region ®ion : func->getRegions()) { + if (failed(applyPatternsAndFoldGreedily(region, frozen))) { + signalPassFailure(); + } } } } diff --git a/lib/Conversion/LlvmToNeura/LlvmToNeuraPatterns.td b/lib/Conversion/LlvmToNeura/LlvmToNeuraPatterns.td index 1053ae91..7daf5b08 100644 --- a/lib/Conversion/LlvmToNeura/LlvmToNeuraPatterns.td +++ b/lib/Conversion/LlvmToNeura/LlvmToNeuraPatterns.td @@ -8,3 +8,13 @@ def : Pat< (Neura_FAddOp $lhs, $rhs) >; +def : Pat< + (LLVM_ConstantOp $value), + (Neura_ConstantOp $value) +>; + +def : Pat< + (LLVM_OrOp $lhs, $rhs), + (Neura_OrOp $lhs, $rhs) +>; + diff --git a/lib/Transforms/AssignAcceleratorPass.cpp b/lib/Transforms/AssignAcceleratorPass.cpp new file mode 100644 index 00000000..bbd53e04 --- /dev/null +++ b/lib/Transforms/AssignAcceleratorPass.cpp @@ -0,0 +1,42 @@ +#include "Common/AcceleratorAttrs.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Pass/Pass.h" + +using namespace mlir; + +namespace { +struct AssignAcceleratorPass : public PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AssignAcceleratorPass) + + StringRef getArgument() const override { return "assign-accelerator"; } + StringRef getDescription() const override { return "Tags non-main functions as neura.kernel."; } + + void runOnOperation() override { + ModuleOp module = getOperation(); + Builder builder(&getContext()); + + module.walk([&](Operation *op) { + if (auto func = dyn_cast(op)) { + if (func.getName() != "main" && + !func.isExternal() && + !func->hasAttr(mlir::accel::kAcceleratorAttr)) { + func->setAttr(mlir::accel::kAcceleratorAttr, builder.getStringAttr(mlir::accel::kNeuraTarget)); + } + } + }); + } +}; +} // namespace + +/// Register the pass +namespace mlir { +namespace neura { +std::unique_ptr createAssignAcceleratorPass() { + return std::make_unique(); +} +} // namespace neura +} // namespace mlir + diff --git a/lib/Transforms/CMakeLists.txt b/lib/Transforms/CMakeLists.txt index 1e99239b..f61f87a5 100644 --- a/lib/Transforms/CMakeLists.txt +++ b/lib/Transforms/CMakeLists.txt @@ -1,15 +1,5 @@ -# set(LLVM_TARGET_DEFINITIONS ${CMAKE_CURRENT_SOURCE_DIR}/FusePatterns.td) -# -# mlir_tablegen(FusePatterns.inc -gen-rewriters -# -I ${MLIR_SOURCE_DIR}/include -# -I ${MLIR_BINARY_DIR}/include -# -I ${CMAKE_SOURCE_DIR}/include -# -I ${CMAKE_CURRENT_SOURCE_DIR} -# ) -# -# add_public_tablegen_target(NeuraFusePatternsGen) - add_mlir_library(NeuraTransforms + AssignAcceleratorPass.cpp InsertMovPass.cpp FusePatternsPass.cpp diff --git a/test/neura/arith_add.mlir b/test/neura/arith_add.mlir index 9cdc6977..5c605b77 100644 --- a/test/neura/arith_add.mlir +++ b/test/neura/arith_add.mlir @@ -3,8 +3,8 @@ func.func @test(%a: f32) -> f32 { %b = arith.constant 2.0 : f32 %res = arith.addf %a, %b : f32 - // CHECK: neura.mov %arg0 : f32 -> f32 - // CHECK: neura.mov %cst : f32 -> f32 + // CHECK: neura.mov + // CHECK: neura.mov // CHECK: neura.fadd return %res : f32 } diff --git a/test/neura/for_loop/test.mlir b/test/neura/for_loop/test.mlir index c2c7506c..1c72782d 100644 --- a/test/neura/for_loop/test.mlir +++ b/test/neura/for_loop/test.mlir @@ -2,17 +2,15 @@ // RUN: clang++ -S -emit-llvm -O2 -o %t-kernel.ll kernel.cpp // RUN: mlir-translate --import-llvm %t-kernel.ll -o %t-kernel.mlir +// TODO: Enable --insert-mov once the backward ctrl flow mov is supported. // Lowers to neura. // RUN: mlir-neura-opt \ +// RUN: --assign-accelerator \ // RUN: --lower-llvm-to-neura \ // RUN: --fuse-patterns \ -// RUN: --insert-mov \ +// RN: --insert-mov \ // RUN: %t-kernel.mlir | FileCheck %s // Verifies the neura ops are generated. And fusion happens. -// CHECK: "neura.vfmul" -// CHECK: "neura.add" -// CHECK: "neura.fmul_fadd" -// CHECK: [[LHS:%.*]] = neura.mov %{{.*}} -// CHECK-NEXT: [[RHS:%.*]] = neura.mov %{{.*}} -// CHECK-NEXT: [[RES:%.*]] = "neura.add"([[LHS]], [[RHS]]) +// CHECK: accelerator = "neura" +// CHECK-NOT: = llvm. diff --git a/test/neura/interpreter/interpreter.mlir b/test/neura/interpreter/interpreter.mlir index 16682095..b9efeb87 100644 --- a/test/neura/interpreter/interpreter.mlir +++ b/test/neura/interpreter/interpreter.mlir @@ -4,8 +4,8 @@ module { func.func @test() -> f32 { %arg0 = arith.constant 9.0 : f32 %cst = arith.constant 2.0 : f32 - %0 = neura.mov %arg0 : f32 -> f32 - %1 = neura.mov %cst : f32 -> f32 + %0 = "neura.mov"(%arg0) : (f32) -> f32 + %1 = "neura.mov"(%cst) : (f32) -> f32 %2 = "neura.fadd"(%0, %1) : (f32, f32) -> f32 return %2 : f32 // CHECK: 11.0 diff --git a/test/neura/llvm_add.mlir b/test/neura/llvm_add.mlir index 4c37a665..e761bcbf 100644 --- a/test/neura/llvm_add.mlir +++ b/test/neura/llvm_add.mlir @@ -1,10 +1,10 @@ -// RUN: mlir-neura-opt --lower-llvm-to-neura --insert-mov %s | FileCheck %s +// RUN: mlir-neura-opt --assign-accelerator --lower-llvm-to-neura --insert-mov %s | FileCheck %s func.func @test(%a: f32) -> f32 { %b = llvm.mlir.constant(2.0 : f32) : f32 %res = llvm.fadd %a, %b : f32 - // CHECK: [[LHS:%.*]] = neura.mov %{{.*}} : f32 -> f32 - // CHECK: [[RHS:%.*]] = neura.mov %{{.*}} : f32 -> f32 + // CHECK: [[LHS:%.*]] = "neura.mov"(%{{.*}}) : (f32) -> f32 + // CHECK: [[RHS:%.*]] = "neura.mov"(%{{.*}}) : (f32) -> f32 // CHECK: [[RES:%.*]] = "neura.fadd"([[LHS]], [[RHS]]) return %res : f32 } diff --git a/tools/mlir-neura-opt/mlir-neura-opt.cpp b/tools/mlir-neura-opt/mlir-neura-opt.cpp index 1de2c2d7..ecfa835b 100644 --- a/tools/mlir-neura-opt/mlir-neura-opt.cpp +++ b/tools/mlir-neura-opt/mlir-neura-opt.cpp @@ -10,6 +10,7 @@ #include "Conversion/ArithToNeura/ArithToNeura.h" #include "Conversion/LlvmToNeura/LlvmToNeura.h" #include "NeuraDialect/NeuraDialect.h" +#include "Transforms/AssignAcceleratorPass.h" #include "Transforms/InsertMovPass.h" #include "Transforms/FusePatternsPass.h" @@ -25,6 +26,9 @@ int main(int argc, char **argv) { mlir::registerPass([]() -> std::unique_ptr { return mlir::neura::createLowerArithToNeuraPass(); }); + mlir::registerPass([]() -> std::unique_ptr { + return mlir::neura::createAssignAcceleratorPass(); + }); mlir::registerPass([]() -> std::unique_ptr { return mlir::neura::createLowerLlvmToNeuraPass(); });