From 3ae86116082e0d777b60ffdfa7658426b8bab6bb Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 27 Dec 2024 02:54:22 +0000 Subject: [PATCH 01/18] Add the ICE40 dialect. The ICE40 dialect is a synthesis target representing the Lattice ICE40 family of fpgas. --- docs/Dialects/ICE40/RationaleICE40.md | 17 ++++++ include/circt/Dialect/CMakeLists.txt | 1 + include/circt/Dialect/ICE40/CMakeLists.txt | 3 + include/circt/Dialect/ICE40/ICE40.td | 20 +++++++ include/circt/Dialect/ICE40/ICE40Dialect.td | 42 ++++++++++++++ include/circt/Dialect/ICE40/ICE40Ops.td | 61 +++++++++++++++++++++ 6 files changed, 144 insertions(+) create mode 100644 docs/Dialects/ICE40/RationaleICE40.md create mode 100644 include/circt/Dialect/ICE40/CMakeLists.txt create mode 100644 include/circt/Dialect/ICE40/ICE40.td create mode 100644 include/circt/Dialect/ICE40/ICE40Dialect.td create mode 100644 include/circt/Dialect/ICE40/ICE40Ops.td diff --git a/docs/Dialects/ICE40/RationaleICE40.md b/docs/Dialects/ICE40/RationaleICE40.md new file mode 100644 index 000000000000..ee8aaf8920fb --- /dev/null +++ b/docs/Dialects/ICE40/RationaleICE40.md @@ -0,0 +1,17 @@ +# `ice40` Dialect Rationale + +This document describes various design points of the ice40 dialect, a +dialect that matches the technology library used in the Lattice ICE40 family +of FPGAs. + +## Introduction to the `ice40` Dialect + +The `ice40` dialect provides a collection of operations that match the +documented operations for the ICE40 family of FPGAs. The dialect hues closely +to the documentation. + +The intent of the dialect is as a target of synthesis. + +## Type System for `ice40` Dialect + +The dialect uses signless 1-bit integers for all values. diff --git a/include/circt/Dialect/CMakeLists.txt b/include/circt/Dialect/CMakeLists.txt index e5b55dcb068e..62155eaf5b97 100644 --- a/include/circt/Dialect/CMakeLists.txt +++ b/include/circt/Dialect/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(FSM) add_subdirectory(Handshake) add_subdirectory(HW) add_subdirectory(HWArith) +add_subdirectory(ICE40) add_subdirectory(Interop) add_subdirectory(LLHD) add_subdirectory(LoopSchedule) diff --git a/include/circt/Dialect/ICE40/CMakeLists.txt b/include/circt/Dialect/ICE40/CMakeLists.txt new file mode 100644 index 000000000000..8b5453d3a86f --- /dev/null +++ b/include/circt/Dialect/ICE40/CMakeLists.txt @@ -0,0 +1,3 @@ +add_circt_dialect(ICE40 ice40) + +set(LLVM_TARGET_DEFINITIONS ICE40.td) diff --git a/include/circt/Dialect/ICE40/ICE40.td b/include/circt/Dialect/ICE40/ICE40.td new file mode 100644 index 000000000000..4c8a8b804375 --- /dev/null +++ b/include/circt/Dialect/ICE40/ICE40.td @@ -0,0 +1,20 @@ +//===- ICE40.td - ICE40 FPGA dialect definition ------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the top level file for the ICE40 dialect. This dialect targets the +// Lattice ICE40 FPGA technology library. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_ICE40_ICE40_TD +#define CIRCT_DIALECT_ICE40_ICE40_TD + +include "circt/Dialect/ICE40/ICE40Dialect.td" +include "circt/Dialect/ICE40/ICE40Ops.td" + +#endif // CIRCT_DIALECT_ICE40_ICE40_TD diff --git a/include/circt/Dialect/ICE40/ICE40Dialect.td b/include/circt/Dialect/ICE40/ICE40Dialect.td new file mode 100644 index 000000000000..a27b26fe2f05 --- /dev/null +++ b/include/circt/Dialect/ICE40/ICE40Dialect.td @@ -0,0 +1,42 @@ +//===- ICE40Dialect.td - ICE40 dialect definition ----------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This contains the ICE40Dialect definition to be included in other files. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_ICE40_ICE40DIALECT +#define CIRCT_DIALECT_ICE40_ICE40DIALECT + +include "mlir/IR/OpBase.td" + +def ICE40Dialect : Dialect { + let name = "ice40"; + let cppNamespace = "::circt::ice40"; + + let summary = "Types and operations for the ICE40 technology library dialect"; + let description = [{ + This dialect defines the `ice40` dialect, which is a dialect of the technology + library for the Lattice ICE40 family of products. + }]; + + let useDefaultTypePrinterParser = 1; + + let extraClassDeclaration = [{ + /// Register all HW types. + void registerTypes(); + /// Register all HW attributes. + void registerAttributes(); + }]; +} + +// Base class for the operation in this dialect. +class ICE40Op traits = []> : + Op; + +#endif // CIRCT_DIALECT_ICE40_ICE40DIALECT diff --git a/include/circt/Dialect/ICE40/ICE40Ops.td b/include/circt/Dialect/ICE40/ICE40Ops.td new file mode 100644 index 000000000000..77651f649037 --- /dev/null +++ b/include/circt/Dialect/ICE40/ICE40Ops.td @@ -0,0 +1,61 @@ +//===- ICE40Ops.td - ICE40 ops ============-----------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines the ICE40 ops, matching the technology library. Documentation +// is here: https://www.latticesemi.com/-/media/LatticeSemi/Documents/TechnicalBriefs/FPGA-TN-02026-3-2-iCE40-Technology-Library.ashx?document_id=52206 +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_ICE40_ICE40OPS_TD +#define CIRCT_DIALECT_ICE40_ICE40OPS_TD + +include "circt/Dialect/ICE40/ICE40Dialect.td" + +def I1Property : IntProperty<"bool">; + +def SBLut4Op : ICE40Op<"sb_lut4", []> { + let summary = "A basic lut"; + let description = [{ + The basic look up table. f(i0, i1, i2, i3) = lut_init[concat(i3,i2,i1,i0)]. + }]; + + let arguments = (ins I1:$i0, I1:$i1, I1:$i2, I1:$i3, ArrayProperty:$lut_init); + let results = (outs I1:$result); + + let hasVerifier = 1; +} + +def SBCarryOp : ICE40Op<"sb_carry", []> { + let summary = "Dedicated carry Lookahead logic"; + let description = [{ + The basic carry lookahead logic. f(i0, i1, ci) = 1 iff popcount(concat(i0,i1,ci)) >= 2. + There are constraints on the use, as i0 and i1 have to be the i0 and i1 input of a single lut. + This op doesn't check this. There is a combined op that handles that. + }]; + + let arguments = (ins I1:$i0, I1:$i1, I1:$ci); + let results = (outs I1:$result); + + let hasVerifier = 1; +} + +def SBLut4CarryOp : ICE40Op<"sb_lut4_carry", []> { + let summary = "LUT which also utilizes the carry logic"; + let description = [{ + f(i0, i1, ci) = 1 iff popcount(concat(i0,i1,ci)) >= 2. + There are constraints on the use, as i0 and i1 have to be the i0 and i1 input of a single lut. + This op doesn't check this. There is a combined op that handles that. + }]; + + let arguments = (ins I1:$i0, I1:$i1, I1:$i2, I1:$i3, I1:$ci, ArrayProperty:$lut_init); + let results = (outs I1:$result, I1:$carry_out); + + let hasVerifier = 1; +} + +#endif // CIRCT_DIALECT_ICE40_ICE40OPS_TD From 4b71336243c27b51af4f8fe4ab24cb2659e59603 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Fri, 27 Dec 2024 05:28:37 +0000 Subject: [PATCH 02/18] simple dialect test --- include/circt/Dialect/ICE40/ICE40Dialect.h | 23 +++++++++ include/circt/Dialect/ICE40/ICE40Dialect.td | 9 ---- include/circt/Dialect/ICE40/ICE40Ops.h | 23 +++++++++ include/circt/Dialect/ICE40/ICE40Ops.td | 15 +++++- include/circt/InitAllDialects.h | 2 + lib/Dialect/CMakeLists.txt | 1 + lib/Dialect/ICE40/CMakeLists.txt | 19 ++++++++ lib/Dialect/ICE40/ICE40Dialect.cpp | 33 +++++++++++++ lib/Dialect/ICE40/ICE40Ops.cpp | 53 +++++++++++++++++++++ test/Dialect/ICE40/basic.mlir | 11 +++++ 10 files changed, 179 insertions(+), 10 deletions(-) create mode 100644 include/circt/Dialect/ICE40/ICE40Dialect.h create mode 100644 include/circt/Dialect/ICE40/ICE40Ops.h create mode 100644 lib/Dialect/ICE40/CMakeLists.txt create mode 100644 lib/Dialect/ICE40/ICE40Dialect.cpp create mode 100644 lib/Dialect/ICE40/ICE40Ops.cpp create mode 100644 test/Dialect/ICE40/basic.mlir diff --git a/include/circt/Dialect/ICE40/ICE40Dialect.h b/include/circt/Dialect/ICE40/ICE40Dialect.h new file mode 100644 index 000000000000..170184b57c9d --- /dev/null +++ b/include/circt/Dialect/ICE40/ICE40Dialect.h @@ -0,0 +1,23 @@ +//===- ICE40Dialect.h - ICE40 dialect declaration ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines an ICE40 MLIR dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_ICE40_ICE40DIALECT_H +#define CIRCT_DIALECT_ICE40_ICE40DIALECT_H + +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Dialect.h" + +// Pull in the dialect definition. +#include "circt/Dialect/ICE40/ICE40Dialect.h.inc" + +#endif // CIRCT_DIALECT_ICE40_ICE40DIALECT_H diff --git a/include/circt/Dialect/ICE40/ICE40Dialect.td b/include/circt/Dialect/ICE40/ICE40Dialect.td index a27b26fe2f05..a21f9e510eb8 100644 --- a/include/circt/Dialect/ICE40/ICE40Dialect.td +++ b/include/circt/Dialect/ICE40/ICE40Dialect.td @@ -24,15 +24,6 @@ def ICE40Dialect : Dialect { This dialect defines the `ice40` dialect, which is a dialect of the technology library for the Lattice ICE40 family of products. }]; - - let useDefaultTypePrinterParser = 1; - - let extraClassDeclaration = [{ - /// Register all HW types. - void registerTypes(); - /// Register all HW attributes. - void registerAttributes(); - }]; } // Base class for the operation in this dialect. diff --git a/include/circt/Dialect/ICE40/ICE40Ops.h b/include/circt/Dialect/ICE40/ICE40Ops.h new file mode 100644 index 000000000000..c618701dd768 --- /dev/null +++ b/include/circt/Dialect/ICE40/ICE40Ops.h @@ -0,0 +1,23 @@ +//===- ICE40Ops.h - Declare ICE40 dialect operations ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the operation classes for the ICE40 dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_ICE40_ICE40OPS_H +#define CIRCT_DIALECT_ICE40_ICE40OPS_H + +#include "circt/Dialect/ICE40/ICE40Dialect.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/Bytecode/BytecodeOpInterface.h" + +#define GET_OP_CLASSES +#include "circt/Dialect/ICE40/ICE40.h.inc" + +#endif // CIRCT_DIALECT_ICE40_ICE40OPS_H diff --git a/include/circt/Dialect/ICE40/ICE40Ops.td b/include/circt/Dialect/ICE40/ICE40Ops.td index 77651f649037..9fdc6bfcddaf 100644 --- a/include/circt/Dialect/ICE40/ICE40Ops.td +++ b/include/circt/Dialect/ICE40/ICE40Ops.td @@ -28,6 +28,10 @@ def SBLut4Op : ICE40Op<"sb_lut4", []> { let results = (outs I1:$result); let hasVerifier = 1; + + let assemblyFormat = [{ + $i0 `,` $i1 `,` $i2 `,` $i3 `,` $lut_init attr-dict + }]; } def SBCarryOp : ICE40Op<"sb_carry", []> { @@ -42,12 +46,17 @@ def SBCarryOp : ICE40Op<"sb_carry", []> { let results = (outs I1:$result); let hasVerifier = 1; + + let assemblyFormat = [{ + $i0 `,` $i1 `,` $ci attr-dict + }]; } def SBLut4CarryOp : ICE40Op<"sb_lut4_carry", []> { let summary = "LUT which also utilizes the carry logic"; let description = [{ - f(i0, i1, ci) = 1 iff popcount(concat(i0,i1,ci)) >= 2. + carry_out(i0, i1, ci) = 1 iff popcount(concat(i0,i1,ci)) >= 2. + result(i0, i1, i2, i3) = lut_init[concat(i3,i2,i1,i0)]. There are constraints on the use, as i0 and i1 have to be the i0 and i1 input of a single lut. This op doesn't check this. There is a combined op that handles that. }]; @@ -56,6 +65,10 @@ def SBLut4CarryOp : ICE40Op<"sb_lut4_carry", []> { let results = (outs I1:$result, I1:$carry_out); let hasVerifier = 1; + + let assemblyFormat = [{ + $i0 `,` $i1 `,` $i2 `,` $i3 `,` $ci `,` $lut_init attr-dict + }]; } #endif // CIRCT_DIALECT_ICE40_ICE40OPS_TD diff --git a/include/circt/InitAllDialects.h b/include/circt/InitAllDialects.h index 8e53c5fe211a..1825e1ac123e 100644 --- a/include/circt/InitAllDialects.h +++ b/include/circt/InitAllDialects.h @@ -28,6 +28,7 @@ #include "circt/Dialect/HW/HWDialect.h" #include "circt/Dialect/HWArith/HWArithDialect.h" #include "circt/Dialect/Handshake/HandshakeDialect.h" +#include "circt/Dialect/ICE40/ICE40Dialect.h" #include "circt/Dialect/Interop/InteropDialect.h" #include "circt/Dialect/Kanagawa/KanagawaDialect.h" #include "circt/Dialect/LLHD/IR/LLHDDialect.h" @@ -70,6 +71,7 @@ inline void registerAllDialects(mlir::DialectRegistry ®istry) { handshake::HandshakeDialect, hw::HWDialect, hwarith::HWArithDialect, + ice40::ICE40Dialect, interop::InteropDialect, kanagawa::KanagawaDialect, llhd::LLHDDialect, diff --git a/lib/Dialect/CMakeLists.txt b/lib/Dialect/CMakeLists.txt index a7bf7be67e1f..d086f601dd86 100644 --- a/lib/Dialect/CMakeLists.txt +++ b/lib/Dialect/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory(Handshake) add_subdirectory(HW) add_subdirectory(HWArith) add_subdirectory(Kanagawa) +add_subdirectory(ICE40) add_subdirectory(Interop) add_subdirectory(LLHD) add_subdirectory(LoopSchedule) diff --git a/lib/Dialect/ICE40/CMakeLists.txt b/lib/Dialect/ICE40/CMakeLists.txt new file mode 100644 index 000000000000..b9f12eeacf57 --- /dev/null +++ b/lib/Dialect/ICE40/CMakeLists.txt @@ -0,0 +1,19 @@ +add_circt_dialect_library(CIRCTICE40 + ICE40Ops.cpp + ICE40Dialect.cpp + + ADDITIONAL_HEADER_DIRS + ${CIRCT_MAIN_INCLUDE_DIR}/circt/Dialect/ICE40 + + DEPENDS + MLIRICE40IncGen + + LINK_COMPONENTS + Support + + LINK_LIBS PUBLIC + MLIRIR + MLIRInferTypeOpInterface + ) + +add_dependencies(circt-headers MLIRICE40IncGen) diff --git a/lib/Dialect/ICE40/ICE40Dialect.cpp b/lib/Dialect/ICE40/ICE40Dialect.cpp new file mode 100644 index 000000000000..152a91261844 --- /dev/null +++ b/lib/Dialect/ICE40/ICE40Dialect.cpp @@ -0,0 +1,33 @@ +//===- ICE40Dialect.cpp - Implement the ICE40 dialect ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the ICE40 dialect. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/ICE40/ICE40Dialect.h" +#include "circt/Dialect/ICE40/ICE40Ops.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/DialectImplementation.h" + +using namespace circt; +using namespace ice40; + +//===----------------------------------------------------------------------===// +// Dialect specification. +//===----------------------------------------------------------------------===// + +void ICE40Dialect::initialize() { + // Register operations. + addOperations< +#define GET_OP_LIST +#include "circt/Dialect/ICE40/ICE40.cpp.inc" + >(); +} + +#include "circt/Dialect/ICE40/ICE40Dialect.cpp.inc" diff --git a/lib/Dialect/ICE40/ICE40Ops.cpp b/lib/Dialect/ICE40/ICE40Ops.cpp new file mode 100644 index 000000000000..2c321b2da79a --- /dev/null +++ b/lib/Dialect/ICE40/ICE40Ops.cpp @@ -0,0 +1,53 @@ +//===- ICE40Ops.cpp - Implement the ICE40 operations ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements ICE40 ops. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/ICE40/ICE40Ops.h" +#include "mlir/IR/ImplicitLocOpBuilder.h" +#include "mlir/IR/DialectImplementation.h" + +using namespace circt; +using namespace ice40; + +//===----------------------------------------------------------------------===// +// SBLut4Op +//===----------------------------------------------------------------------===// + +LogicalResult SBLut4Op::verify() +{ + return success(); +} + +//===----------------------------------------------------------------------===// +// SBCarryOp +//===----------------------------------------------------------------------===// + +LogicalResult SBCarryOp::verify() +{ + return success(); +} + +//===----------------------------------------------------------------------===// +// SBLut4CarryOp +//===----------------------------------------------------------------------===// + +LogicalResult SBLut4CarryOp::verify() +{ + return success(); +} + +//===----------------------------------------------------------------------===// +// TableGen generated logic. +//===----------------------------------------------------------------------===// + +// Provide the autogenerated implementation guts for the Op classes. +#define GET_OP_CLASSES +#include "circt/Dialect/ICE40/ICE40.cpp.inc" diff --git a/test/Dialect/ICE40/basic.mlir b/test/Dialect/ICE40/basic.mlir new file mode 100644 index 000000000000..28e5a31b5273 --- /dev/null +++ b/test/Dialect/ICE40/basic.mlir @@ -0,0 +1,11 @@ +// RUN: circt-opt %s | FileCheck %s + +func.func @basic(%i : i1) { + // CHECK: ice40.sb_lut4 + %a = ice40.sb_lut4 %i, %i, %i, %i, [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] + // CHECK: ice40.sb_carry + %b = ice40.sb_carry %i, %i, %a + // CHECK: ice40.sb_lut4_carry + %c, %d = ice40.sb_lut4_carry %i, %i, %a, %a, %b, [1,0,1,1,0,1,1,1,0,1,1,1,1,0,1,1] + func.return +} From 6bee1456e109f622ec2230ca1b1fc46a47d6b7cf Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Fri, 27 Dec 2024 15:51:43 +0000 Subject: [PATCH 03/18] formatting --- include/circt/Dialect/ICE40/ICE40Ops.h | 2 +- lib/Dialect/ICE40/ICE40Ops.cpp | 17 ++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/include/circt/Dialect/ICE40/ICE40Ops.h b/include/circt/Dialect/ICE40/ICE40Ops.h index c618701dd768..4e3475583669 100644 --- a/include/circt/Dialect/ICE40/ICE40Ops.h +++ b/include/circt/Dialect/ICE40/ICE40Ops.h @@ -14,8 +14,8 @@ #define CIRCT_DIALECT_ICE40_ICE40OPS_H #include "circt/Dialect/ICE40/ICE40Dialect.h" -#include "mlir/IR/OpImplementation.h" #include "mlir/Bytecode/BytecodeOpInterface.h" +#include "mlir/IR/OpImplementation.h" #define GET_OP_CLASSES #include "circt/Dialect/ICE40/ICE40.h.inc" diff --git a/lib/Dialect/ICE40/ICE40Ops.cpp b/lib/Dialect/ICE40/ICE40Ops.cpp index 2c321b2da79a..227e37e89d1d 100644 --- a/lib/Dialect/ICE40/ICE40Ops.cpp +++ b/lib/Dialect/ICE40/ICE40Ops.cpp @@ -11,8 +11,8 @@ //===----------------------------------------------------------------------===// #include "circt/Dialect/ICE40/ICE40Ops.h" -#include "mlir/IR/ImplicitLocOpBuilder.h" #include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/ImplicitLocOpBuilder.h" using namespace circt; using namespace ice40; @@ -21,28 +21,19 @@ using namespace ice40; // SBLut4Op //===----------------------------------------------------------------------===// -LogicalResult SBLut4Op::verify() -{ - return success(); -} +LogicalResult SBLut4Op::verify() { return success(); } //===----------------------------------------------------------------------===// // SBCarryOp //===----------------------------------------------------------------------===// -LogicalResult SBCarryOp::verify() -{ - return success(); -} +LogicalResult SBCarryOp::verify() { return success(); } //===----------------------------------------------------------------------===// // SBLut4CarryOp //===----------------------------------------------------------------------===// -LogicalResult SBLut4CarryOp::verify() -{ - return success(); -} +LogicalResult SBLut4CarryOp::verify() { return success(); } //===----------------------------------------------------------------------===// // TableGen generated logic. From c509a64ded12050dc07d9c1f926ce503740823b9 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Sun, 29 Dec 2024 00:53:24 +0000 Subject: [PATCH 04/18] Add the BLIF dialect. The BLIF dialect implements the extended berkely logic interchange format. This is a format used by some OSS tools, such as some place-and-route tools. This is the format used by yosys to interchange with nextpnr. --- include/circt/Dialect/BLIF/BLIF.td | 21 +++++++++ include/circt/Dialect/BLIF/BLIFDialect.h | 23 ++++++++++ include/circt/Dialect/BLIF/BLIFDialect.td | 33 ++++++++++++++ include/circt/Dialect/BLIF/BLIFOps.h | 23 ++++++++++ include/circt/Dialect/BLIF/BLIFOps.td | 53 +++++++++++++++++++++++ include/circt/Dialect/BLIF/CMakeLists.txt | 3 ++ include/circt/Dialect/CMakeLists.txt | 1 + 7 files changed, 157 insertions(+) create mode 100644 include/circt/Dialect/BLIF/BLIF.td create mode 100644 include/circt/Dialect/BLIF/BLIFDialect.h create mode 100644 include/circt/Dialect/BLIF/BLIFDialect.td create mode 100644 include/circt/Dialect/BLIF/BLIFOps.h create mode 100644 include/circt/Dialect/BLIF/BLIFOps.td create mode 100644 include/circt/Dialect/BLIF/CMakeLists.txt diff --git a/include/circt/Dialect/BLIF/BLIF.td b/include/circt/Dialect/BLIF/BLIF.td new file mode 100644 index 000000000000..674f4c410bc4 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIF.td @@ -0,0 +1,21 @@ +//===- BLIF.td - BLIF dialect definition -------------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the top level file for the BLIF dialect. This dialect represents +// the (extended) Berkely Logic Interchange Format +// [http://bear.ces.cwru.edu/eecs_cad/sis_blif.pdf]. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIF_TD +#define CIRCT_DIALECT_BLIF_BLIF_TD + +include "circt/Dialect/BLIF/BLIFDialect.td" +include "circt/Dialect/BLIF/BLIFOps.td" + +#endif // CIRCT_DIALECT_BLIF_BLIF_TD diff --git a/include/circt/Dialect/BLIF/BLIFDialect.h b/include/circt/Dialect/BLIF/BLIFDialect.h new file mode 100644 index 000000000000..8b9e0f25d913 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFDialect.h @@ -0,0 +1,23 @@ +//===- BLIFDialect.h - BLIF dialect declaration -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines an BLIF MLIR dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFDIALECT_H +#define CIRCT_DIALECT_BLIF_BLIFDIALECT_H + +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Dialect.h" + +// Pull in the dialect definition. +#include "circt/Dialect/BLIF/BLIFDialect.h.inc" + +#endif // CIRCT_DIALECT_BLIF_BLIFDIALECT_H diff --git a/include/circt/Dialect/BLIF/BLIFDialect.td b/include/circt/Dialect/BLIF/BLIFDialect.td new file mode 100644 index 000000000000..3b94ac29077c --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFDialect.td @@ -0,0 +1,33 @@ +//===- BLIFDialect.td - BLIF dialect definition ------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This contains the BLIFDialect definition to be included in other files. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFDIALECT +#define CIRCT_DIALECT_BLIF_BLIFDIALECT + +include "mlir/IR/OpBase.td" + +def BLIFDialect : Dialect { + let name = "blif"; + let cppNamespace = "::circt::blif"; + + let summary = "Types and operations for the BLIF format dialect"; + let description = [{ + This dialect defines the `BLIF` dialect, which is a representation of + the Berkely Logic Interchange Format. + }]; +} + +// Base class for the operation in this dialect. +class BLIFOp traits = []> : + Op; + +#endif // CIRCT_DIALECT_BLIF_BLIFDIALECT diff --git a/include/circt/Dialect/BLIF/BLIFOps.h b/include/circt/Dialect/BLIF/BLIFOps.h new file mode 100644 index 000000000000..2406d0fa6fe8 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFOps.h @@ -0,0 +1,23 @@ +//===- BLIFOps.h - Declare BLIF dialect operations --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the operation classes for the BLIF dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFOPS_H +#define CIRCT_DIALECT_BLIF_BLIFOPS_H + +#include "circt/Dialect/BLIF/BLIFDialect.h" +#include "mlir/Bytecode/BytecodeOpInterface.h" +#include "mlir/IR/OpImplementation.h" + +#define GET_OP_CLASSES +#include "circt/Dialect/BLIF/BLIF.h.inc" + +#endif // CIRCT_DIALECT_BLIF_BLIFOPS_H diff --git a/include/circt/Dialect/BLIF/BLIFOps.td b/include/circt/Dialect/BLIF/BLIFOps.td new file mode 100644 index 000000000000..7bdb617b3504 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFOps.td @@ -0,0 +1,53 @@ +//===- BLIFOps.td - BLIF ops ============-------------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines the BLIF ops. This defines module-like operations, connections, +// and logic. +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFOPS_TD +#define CIRCT_DIALECT_BLIF_BLIFOPS_TD + +include "circt/Dialect/BLIF/BLIFDialect.td" +include "circt/Dialect/HW/HWTypes.td" +include "mlir/IR/RegionKindInterface.td" + +def ModelOp : BLIFOp<"model", [IsolatedFromAbove, RegionKindInterface, SingleBlockImplicitTerminator<"OutputOp">]> { + let summary = "A model, which is a module"; + let description = [{ + The basic container. Is like a module. + }]; + + let arguments = (ins SymbolNameAttr:$sym_name, + TypeAttrOf:$module_type); + let results = (outs); + let regions = (region SizedRegion<1>:$body); +} + +def OutputOp : BLIFOp<"output", [Terminator]> { + let arguments = (ins Variadic:$inputs); +} + +def I8Property : IntProperty<"int8_t">; + +def LogicGate: BLIFOp<"logic_gate", []> { + let summary = "Combinatorial logic"; + let description = [{ + A logic gate represents a logic function in sum-of-products + form. Each entry in $func is a vector applied to the inputs + where 0 means invert the input, 1 means use the input, and 2 + means don't use the input. + }]; + + let arguments = (ins ArrayProperty>:$func, + Variadic:$inputs); + let results = (outs I1:$result); +} + + +#endif // CIRCT_DIALECT_BLIF_BLIFOPS_TD diff --git a/include/circt/Dialect/BLIF/CMakeLists.txt b/include/circt/Dialect/BLIF/CMakeLists.txt new file mode 100644 index 000000000000..0c7b6927059c --- /dev/null +++ b/include/circt/Dialect/BLIF/CMakeLists.txt @@ -0,0 +1,3 @@ +add_circt_dialect(BLIF blif) + +set(LLVM_TARGET_DEFINITIONS BLIF.td) diff --git a/include/circt/Dialect/CMakeLists.txt b/include/circt/Dialect/CMakeLists.txt index e5b55dcb068e..78a8f0eb0059 100644 --- a/include/circt/Dialect/CMakeLists.txt +++ b/include/circt/Dialect/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory(AIG) add_subdirectory(Arc) +add_subdirectory(BLIF) add_subdirectory(Calyx) add_subdirectory(Comb) add_subdirectory(DC) From aa647728aa6ea387599fd734078ce9720e0b1642 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Sun, 29 Dec 2024 02:51:34 +0000 Subject: [PATCH 05/18] lib --- include/circt/Dialect/BLIF/BLIFOps.h | 4 +++ include/circt/Dialect/BLIF/BLIFOps.td | 8 ++++- lib/Dialect/BLIF/BLIFDialect.cpp | 33 +++++++++++++++++++ lib/Dialect/BLIF/BLIFOps.cpp | 46 +++++++++++++++++++++++++++ lib/Dialect/BLIF/CMakeLists.txt | 19 +++++++++++ lib/Dialect/CMakeLists.txt | 1 + 6 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 lib/Dialect/BLIF/BLIFDialect.cpp create mode 100644 lib/Dialect/BLIF/BLIFOps.cpp create mode 100644 lib/Dialect/BLIF/CMakeLists.txt diff --git a/include/circt/Dialect/BLIF/BLIFOps.h b/include/circt/Dialect/BLIF/BLIFOps.h index 2406d0fa6fe8..04dbca975cfe 100644 --- a/include/circt/Dialect/BLIF/BLIFOps.h +++ b/include/circt/Dialect/BLIF/BLIFOps.h @@ -14,6 +14,7 @@ #define CIRCT_DIALECT_BLIF_BLIFOPS_H #include "circt/Dialect/BLIF/BLIFDialect.h" +#include "circt/Dialect/HW/HWOpInterfaces.h" #include "mlir/Bytecode/BytecodeOpInterface.h" #include "mlir/IR/OpImplementation.h" @@ -21,3 +22,6 @@ #include "circt/Dialect/BLIF/BLIF.h.inc" #endif // CIRCT_DIALECT_BLIF_BLIFOPS_H + +using namespace circt; +using namespace blif; diff --git a/include/circt/Dialect/BLIF/BLIFOps.td b/include/circt/Dialect/BLIF/BLIFOps.td index 7bdb617b3504..8b88e47b41bb 100644 --- a/include/circt/Dialect/BLIF/BLIFOps.td +++ b/include/circt/Dialect/BLIF/BLIFOps.td @@ -27,6 +27,12 @@ def ModelOp : BLIFOp<"model", [IsolatedFromAbove, RegionKindInterface, SingleBlo TypeAttrOf:$module_type); let results = (outs); let regions = (region SizedRegion<1>:$body); + + let extraClassDeclaration = [{ + static mlir::RegionKind getRegionKind(unsigned index) { + return mlir::RegionKind::Graph; + } + }]; } def OutputOp : BLIFOp<"output", [Terminator]> { @@ -44,7 +50,7 @@ def LogicGate: BLIFOp<"logic_gate", []> { means don't use the input. }]; - let arguments = (ins ArrayProperty>:$func, + let arguments = (ins IntArrayProperty:$func, Variadic:$inputs); let results = (outs I1:$result); } diff --git a/lib/Dialect/BLIF/BLIFDialect.cpp b/lib/Dialect/BLIF/BLIFDialect.cpp new file mode 100644 index 000000000000..4a4efdc75eb8 --- /dev/null +++ b/lib/Dialect/BLIF/BLIFDialect.cpp @@ -0,0 +1,33 @@ +//===- BLIFDialect.cpp - Implement the BLIF dialect -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the BLIF dialect. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/BLIF/BLIFDialect.h" +#include "circt/Dialect/BLIF/BLIFOps.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/DialectImplementation.h" + +using namespace circt; +using namespace blif; + +//===----------------------------------------------------------------------===// +// Dialect specification. +//===----------------------------------------------------------------------===// + +void BLIFDialect::initialize() { + // Register operations. + addOperations< +#define GET_OP_LIST +#include "circt/Dialect/BLIF/BLIF.cpp.inc" + >(); +} + +#include "circt/Dialect/BLIF/BLIFDialect.cpp.inc" diff --git a/lib/Dialect/BLIF/BLIFOps.cpp b/lib/Dialect/BLIF/BLIFOps.cpp new file mode 100644 index 000000000000..aa462fc9a403 --- /dev/null +++ b/lib/Dialect/BLIF/BLIFOps.cpp @@ -0,0 +1,46 @@ +//===- BLIFOps.cpp - Implement the BLIF operations ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements BLIF ops. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/BLIF/BLIFOps.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/ImplicitLocOpBuilder.h" + +using namespace circt; +using namespace blif; + +namespace mlir { +LogicalResult +convertFromAttribute(SmallVectorImpl &storage, Attribute attr, + function_ref emitError) { + auto valueAttr = dyn_cast(attr); + if (!valueAttr) { + emitError() << "expected dense array of int8_t for key `value`"; + return failure(); + } + storage.resize_for_overwrite(valueAttr.size()); + llvm::copy(valueAttr.asArrayRef(), storage.begin()); + return success(); +} + +Attribute convertToAttribute(MLIRContext *ctx, ArrayRef storage) { + return DenseI8ArrayAttr::get(ctx, storage); +} + +} // namespace mlir + +//===----------------------------------------------------------------------===// +// TableGen generated logic. +//===----------------------------------------------------------------------===// + +// Provide the autogenerated implementation guts for the Op classes. +#define GET_OP_CLASSES +#include "circt/Dialect/BLIF/BLIF.cpp.inc" diff --git a/lib/Dialect/BLIF/CMakeLists.txt b/lib/Dialect/BLIF/CMakeLists.txt new file mode 100644 index 000000000000..62ac3131f043 --- /dev/null +++ b/lib/Dialect/BLIF/CMakeLists.txt @@ -0,0 +1,19 @@ +add_circt_dialect_library(CIRCTBLIF + BLIFOps.cpp + BLIFDialect.cpp + + ADDITIONAL_HEADER_DIRS + ${CIRCT_MAIN_INCLUDE_DIR}/circt/Dialect/BLIF + + DEPENDS + MLIRBLIFIncGen + + LINK_COMPONENTS + Support + + LINK_LIBS PUBLIC + MLIRIR + MLIRInferTypeOpInterface + ) + +add_dependencies(circt-headers MLIRBLIFIncGen) diff --git a/lib/Dialect/CMakeLists.txt b/lib/Dialect/CMakeLists.txt index a7bf7be67e1f..eeb8b3d03bc0 100644 --- a/lib/Dialect/CMakeLists.txt +++ b/lib/Dialect/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(AIG) add_subdirectory(Arc) +add_subdirectory(BLIF) add_subdirectory(Calyx) add_subdirectory(Comb) add_subdirectory(DC) From b0cd89575dde81b5c2f1375547946cf4585b8506 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Mon, 30 Dec 2024 16:52:01 +0000 Subject: [PATCH 06/18] parser checkpoint --- include/circt/Dialect/BLIF/BLIFOps.td | 4 +- include/circt/Dialect/BLIF/BLIFParser.h | 61 +++ lib/Dialect/BLIF/CMakeLists.txt | 2 + lib/Dialect/BLIF/Import/BLIFLexer.cpp | 531 +++++++++++++++++++++ lib/Dialect/BLIF/Import/BLIFLexer.h | 175 +++++++ lib/Dialect/BLIF/Import/BLIFParser.cpp | 527 ++++++++++++++++++++ lib/Dialect/BLIF/Import/BLIFTokenKinds.def | 126 +++++ lib/Dialect/BLIF/Import/CMakeLists.txt | 11 + 8 files changed, 1436 insertions(+), 1 deletion(-) create mode 100644 include/circt/Dialect/BLIF/BLIFParser.h create mode 100644 lib/Dialect/BLIF/Import/BLIFLexer.cpp create mode 100644 lib/Dialect/BLIF/Import/BLIFLexer.h create mode 100644 lib/Dialect/BLIF/Import/BLIFParser.cpp create mode 100644 lib/Dialect/BLIF/Import/BLIFTokenKinds.def create mode 100644 lib/Dialect/BLIF/Import/CMakeLists.txt diff --git a/include/circt/Dialect/BLIF/BLIFOps.td b/include/circt/Dialect/BLIF/BLIFOps.td index 8b88e47b41bb..90dfb08aa2d4 100644 --- a/include/circt/Dialect/BLIF/BLIFOps.td +++ b/include/circt/Dialect/BLIF/BLIFOps.td @@ -41,7 +41,7 @@ def OutputOp : BLIFOp<"output", [Terminator]> { def I8Property : IntProperty<"int8_t">; -def LogicGate: BLIFOp<"logic_gate", []> { +def LogicGateOp: BLIFOp<"logic_gate", []> { let summary = "Combinatorial logic"; let description = [{ A logic gate represents a logic function in sum-of-products @@ -55,5 +55,7 @@ def LogicGate: BLIFOp<"logic_gate", []> { let results = (outs I1:$result); } +def LatchGateOp: BLIFOp<"latch_gate", []> { +} #endif // CIRCT_DIALECT_BLIF_BLIFOPS_TD diff --git a/include/circt/Dialect/BLIF/BLIFParser.h b/include/circt/Dialect/BLIF/BLIFParser.h new file mode 100644 index 000000000000..3ea8051e6921 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFParser.h @@ -0,0 +1,61 @@ +//===- BLIFParser.h - .blif to BLIF dialect parser --------------*- C++ -*-===// +// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the interface to the .blif file parser. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFPARSER_H +#define CIRCT_DIALECT_BLIF_BLIFPARSER_H + +#include "circt/Support/LLVM.h" +#include +#include +#include + +namespace llvm { +class SourceMgr; +} // namespace llvm + +namespace mlir { +class LocationAttr; +class TimingScope; +} // namespace mlir + +namespace circt { +namespace blif { + +struct BLIFParserOptions { + /// Specify how @info locators should be handled. + enum class InfoLocHandling { + /// If this is set to true, the @info locators are ignored, and the + /// locations are set to the location in the .BLIF file. + IgnoreInfo, + /// Prefer @info locators, fallback to .BLIF locations. + PreferInfo, + /// Attach both @info locators (when present) and .BLIF locations. + FusedInfo + }; + + InfoLocHandling infoLocatorHandling = InfoLocHandling::PreferInfo; + + /// parse strict blif instead of extended blif + bool strictBLIF = false; +}; + +mlir::OwningOpRef +importBLIFFile(llvm::SourceMgr &sourceMgr, mlir::MLIRContext *context, + mlir::TimingScope &ts, BLIFParserOptions options = {}); + +void registerFromBLIFFileTranslation(); + +} // namespace blif +} // namespace circt + +#endif // CIRCT_DIALECT_BLIF_BLIFPARSER_H diff --git a/lib/Dialect/BLIF/CMakeLists.txt b/lib/Dialect/BLIF/CMakeLists.txt index 62ac3131f043..e63b1b125b99 100644 --- a/lib/Dialect/BLIF/CMakeLists.txt +++ b/lib/Dialect/BLIF/CMakeLists.txt @@ -17,3 +17,5 @@ add_circt_dialect_library(CIRCTBLIF ) add_dependencies(circt-headers MLIRBLIFIncGen) + +add_subdirectory(Import) diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.cpp b/lib/Dialect/BLIF/Import/BLIFLexer.cpp new file mode 100644 index 000000000000..814fb9c40af2 --- /dev/null +++ b/lib/Dialect/BLIF/Import/BLIFLexer.cpp @@ -0,0 +1,531 @@ +//===- BLIFLexer.cpp - .blif file lexer implementation --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This implements a .blif file lexer. +// +//===----------------------------------------------------------------------===// + +#include "BLIFLexer.h" +#include "mlir/IR/Diagnostics.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" + +using namespace circt; +using namespace blif; +using llvm::SMLoc; +using llvm::SMRange; +using llvm::SourceMgr; + +#define isdigit(x) DO_NOT_USE_SLOW_CTYPE_FUNCTIONS +#define isalpha(x) DO_NOT_USE_SLOW_CTYPE_FUNCTIONS + +//===----------------------------------------------------------------------===// +// BLIFToken +//===----------------------------------------------------------------------===// + +SMLoc BLIFToken::getLoc() const { + return SMLoc::getFromPointer(spelling.data()); +} + +SMLoc BLIFToken::getEndLoc() const { + return SMLoc::getFromPointer(spelling.data() + spelling.size()); +} + +SMRange BLIFToken::getLocRange() const { return SMRange(getLoc(), getEndLoc()); } + +/// Return true if this is one of the keyword token kinds (e.g. kw_wire). +bool BLIFToken::isKeyword() const { + switch (kind) { + default: + return false; +#define TOK_KEYWORD(SPELLING) \ + case kw_##SPELLING: \ + return true; +#include "BLIFTokenKinds.def" + } +} + +/// Given a token containing a string literal, return its value, including +/// removing the quote characters and unescaping the contents of the string. The +/// lexer has already verified that this token is valid. +std::string BLIFToken::getStringValue() const { + assert(getKind() == string); + return getStringValue(getSpelling()); +} + +std::string BLIFToken::getStringValue(StringRef spelling) { + // Start by dropping the quotes. + StringRef bytes = spelling.drop_front().drop_back(); + + std::string result; + result.reserve(bytes.size()); + for (size_t i = 0, e = bytes.size(); i != e;) { + auto c = bytes[i++]; + if (c != '\\') { + result.push_back(c); + continue; + } + + assert(i + 1 <= e && "invalid string should be caught by lexer"); + auto c1 = bytes[i++]; + switch (c1) { + case '\\': + case '"': + case '\'': + result.push_back(c1); + continue; + case 'b': + result.push_back('\b'); + continue; + case 'n': + result.push_back('\n'); + continue; + case 't': + result.push_back('\t'); + continue; + case 'f': + result.push_back('\f'); + continue; + case 'r': + result.push_back('\r'); + continue; + // TODO: Handle the rest of the escapes (octal and unicode). + default: + break; + } + + assert(i + 1 <= e && "invalid string should be caught by lexer"); + auto c2 = bytes[i++]; + + assert(llvm::isHexDigit(c1) && llvm::isHexDigit(c2) && "invalid escape"); + result.push_back((llvm::hexDigitValue(c1) << 4) | llvm::hexDigitValue(c2)); + } + + return result; +} + +/// Given a token containing a verbatim string, return its value, including +/// removing the quote characters and unescaping the quotes of the string. The +/// lexer has already verified that this token is valid. +std::string BLIFToken::getVerbatimStringValue() const { + assert(getKind() == verbatim_string); + return getVerbatimStringValue(getSpelling()); +} + +std::string BLIFToken::getVerbatimStringValue(StringRef spelling) { + // Start by dropping the quotes. + StringRef bytes = spelling.drop_front().drop_back(); + + std::string result; + result.reserve(bytes.size()); + for (size_t i = 0, e = bytes.size(); i != e;) { + auto c = bytes[i++]; + if (c != '\\') { + result.push_back(c); + continue; + } + + assert(i + 1 <= e && "invalid string should be caught by lexer"); + auto c1 = bytes[i++]; + if (c1 != '\'') { + result.push_back(c); + } + result.push_back(c1); + } + + return result; +} + +//===----------------------------------------------------------------------===// +// BLIFLexer +//===----------------------------------------------------------------------===// + +static StringAttr getMainBufferNameIdentifier(const llvm::SourceMgr &sourceMgr, + MLIRContext *context) { + auto mainBuffer = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID()); + StringRef bufferName = mainBuffer->getBufferIdentifier(); + if (bufferName.empty()) + bufferName = ""; + return StringAttr::get(context, bufferName); +} + +BLIFLexer::BLIFLexer(const llvm::SourceMgr &sourceMgr, MLIRContext *context) + : sourceMgr(sourceMgr), + bufferNameIdentifier(getMainBufferNameIdentifier(sourceMgr, context)), + curBuffer( + sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())->getBuffer()), + curPtr(curBuffer.begin()), + // Prime the BLIFst token. + curToken(lexTokenImpl()) {} + +/// Encode the specified source location information into a Location object +/// for attachment to the IR or error reporting. +Location BLIFLexer::translateLocation(llvm::SMLoc loc) { + assert(loc.isValid()); + unsigned mainFileID = sourceMgr.getMainFileID(); + auto lineAndColumn = sourceMgr.getLineAndColumn(loc, mainFileID); + return FileLineColLoc::get(bufferNameIdentifier, lineAndColumn.first, + lineAndColumn.second); +} + +/// Emit an error message and return a BLIFToken::error token. +BLIFToken BLIFLexer::emitError(const char *loc, const Twine &message) { + mlir::emitError(translateLocation(SMLoc::getFromPointer(loc)), message); + return formToken(BLIFToken::error, loc); +} + +//===----------------------------------------------------------------------===// +// Lexer Implementation Methods +//===----------------------------------------------------------------------===// + +BLIFToken BLIFLexer::lexTokenImpl() { + while (true) { + const char *tokStart = curPtr; + switch (*curPtr++) { + default: + // Handle identifiers. + if (llvm::isAlpha(curPtr[-1])) + return lexIdentifierOrKeyword(tokStart); + + // Unknown character, emit an error. + return emitError(tokStart, "unexpected character"); + + case 0: + // This may either be a nul character in the source file or may be the EOF + // marker that llvm::MemoryBuffer guarantees will be there. + if (curPtr - 1 == curBuffer.end()) + return formToken(BLIFToken::eof, tokStart); + + [[fallthrough]]; // Treat as whitespace. + + case ' ': + case '\t': + case '\n': + case '\r': + // Handle whitespace. + continue; + + case '`': + case '_': + // Handle identifiers. + return lexIdentifierOrKeyword(tokStart); + + case '.': + return formToken(BLIFToken::period, tokStart); + case ',': + return formToken(BLIFToken::comma, tokStart); + case ':': + return formToken(BLIFToken::colon, tokStart); + case '(': + return formToken(BLIFToken::l_paren, tokStart); + case ')': + return formToken(BLIFToken::r_paren, tokStart); + case '{': + if (*curPtr == '|') + return ++curPtr, formToken(BLIFToken::l_brace_bar, tokStart); + return formToken(BLIFToken::l_brace, tokStart); + case '}': + return formToken(BLIFToken::r_brace, tokStart); + case '[': + return formToken(BLIFToken::l_square, tokStart); + case ']': + return formToken(BLIFToken::r_square, tokStart); + case '<': + if (*curPtr == '=') + return ++curPtr, formToken(BLIFToken::less_equal, tokStart); + return formToken(BLIFToken::less, tokStart); + case '>': + return formToken(BLIFToken::greater, tokStart); + case '=': + if (*curPtr == '>') + return ++curPtr, formToken(BLIFToken::equal_greater, tokStart); + return formToken(BLIFToken::equal, tokStart); + case '?': + return formToken(BLIFToken::question, tokStart); + case '@': + if (*curPtr == '[') + return lexFileInfo(tokStart); + // Unknown character, emit an error. + return emitError(tokStart, "unexpected character"); + case '%': + if (*curPtr == '[') + return lexInlineAnnotation(tokStart); + return emitError(tokStart, "unexpected character following '%'"); + case '|': + if (*curPtr == '}') + return ++curPtr, formToken(BLIFToken::r_brace_bar, tokStart); + // Unknown character, emit an error. + return emitError(tokStart, "unexpected character"); + + case ';': + skipComment(); + continue; + + case '"': + return lexString(tokStart, /*isVerbatim=*/false); + case '\'': + return lexString(tokStart, /*isVerbatim=*/true); + + case '-': + case '+': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return lexNumber(tokStart); + } + } +} + +/// Lex a file info specifier. +/// +/// FileInfo ::= '@[' ('\]'|.)* ']' +/// +BLIFToken BLIFLexer::lexFileInfo(const char *tokStart) { + while (1) { + switch (*curPtr++) { + case ']': // This is the end of the fileinfo literal. + return formToken(BLIFToken::fileinfo, tokStart); + case '\\': + // Ignore escaped ']' + if (*curPtr == ']') + ++curPtr; + break; + case 0: + // This could be the end of file in the middle of the fileinfo. If so + // emit an error. + if (curPtr - 1 != curBuffer.end()) + break; + [[fallthrough]]; + case '\n': // Vertical whitespace isn't allowed in a fileinfo. + case '\v': + case '\f': + return emitError(tokStart, "unterminated file info specifier"); + default: + // Skip over other characters. + break; + } + } +} + +/// Lex an identifier or keyword that starts with a letter. +/// +/// LegalStartChar ::= [a-zA-Z_] +/// LegalIdChar ::= LegalStartChar | [0-9] | '$' +/// +/// Id ::= LegalStartChar (LegalIdChar)* +/// LiteralId ::= [a-zA-Z0-9$_]+ +/// +BLIFToken BLIFLexer::lexIdentifierOrKeyword(const char *tokStart) { + // Remember that this is a literalID + bool isLiteralId = *tokStart == '`'; + + // Match the rest of the identifier regex: [0-9a-zA-Z_$-]* + while (llvm::isAlpha(*curPtr) || llvm::isDigit(*curPtr) || *curPtr == '_' || + *curPtr == '$' || *curPtr == '-') + ++curPtr; + + // Consume the trailing '`' in a literal identifier. + if (isLiteralId) { + if (*curPtr != '`') + return emitError(tokStart, "unterminated literal identifier"); + ++curPtr; + } + + StringRef spelling(tokStart, curPtr - tokStart); + + // Check to see if this is a 'primop', which is an identifier juxtaposed with + // a '(' character. + if (*curPtr == '(') { + BLIFToken::Kind kind = llvm::StringSwitch(spelling) +#define TOK_LPKEYWORD(SPELLING) .Case(#SPELLING, BLIFToken::lp_##SPELLING) +#include "BLIFTokenKinds.def" + .Default(BLIFToken::identifier); + if (kind != BLIFToken::identifier) { + ++curPtr; + return formToken(kind, tokStart); + } + } + + // See if the identifier is a keyword. By default, it is an identifier. + BLIFToken::Kind kind = llvm::StringSwitch(spelling) +#define TOK_KEYWORD(SPELLING) .Case(#SPELLING, BLIFToken::kw_##SPELLING) +#include "BLIFTokenKinds.def" + .Default(BLIFToken::identifier); + + // If this has the backticks of a literal identifier and it fell through the + // above switch, indicating that it was not found to e a keyword, then change + // its kind from identifier to literal identifier. + if (isLiteralId && kind == BLIFToken::identifier) + kind = BLIFToken::literal_identifier; + + return BLIFToken(kind, spelling); +} + +/// Skip a comment line, starting with a ';' and going to end of line. +void BLIFLexer::skipComment() { + while (true) { + switch (*curPtr++) { + case '\n': + case '\r': + // Newline is end of comment. + return; + case 0: + // If this is the end of the buffer, end the comment. + if (curPtr - 1 == curBuffer.end()) { + --curPtr; + return; + } + [[fallthrough]]; + default: + // Skip over other characters. + break; + } + } +} + +/// StringLit ::= '"' UnquotedString? '"' +/// VerbatimStringLit ::= '\'' UnquotedString? '\'' +/// UnquotedString ::= ( '\\\'' | '\\"' | ~[\r\n] )+? +/// +BLIFToken BLIFLexer::lexString(const char *tokStart, bool isVerbatim) { + while (1) { + switch (*curPtr++) { + case '"': // This is the end of the string literal. + if (isVerbatim) + break; + return formToken(BLIFToken::string, tokStart); + case '\'': // This is the end of the raw string. + if (!isVerbatim) + break; + return formToken(BLIFToken::verbatim_string, tokStart); + case '\\': + // Ignore escaped '\'' or '"' + if (*curPtr == '\'' || *curPtr == '"' || *curPtr == '\\') + ++curPtr; + else if (*curPtr == 'u' || *curPtr == 'U') + return emitError(tokStart, "unicode escape not supported in string"); + break; + case 0: + // This could be the end of file in the middle of the string. If so + // emit an error. + if (curPtr - 1 != curBuffer.end()) + break; + [[fallthrough]]; + case '\n': // Vertical whitespace isn't allowed in a string. + case '\r': + case '\v': + case '\f': + return emitError(tokStart, "unterminated string"); + default: + if (curPtr[-1] & ~0x7F) + return emitError(tokStart, "string characters must be 7-bit ASCII"); + // Skip over other characters. + break; + } + } +} + +/// Lex a number literal. +/// +/// UnsignedInt ::= '0' | PosInt +/// PosInt ::= [1-9] ([0-9])* +/// DoubleLit ::= +/// ( '+' | '-' )? Digit+ '.' Digit+ ( 'E' ( '+' | '-' )? Digit+ )? +/// TripleLit ::= +/// Digit+ '.' Digit+ '.' Digit+ +/// Radix-specified Integer ::= +/// ( '-' )? '0' ( 'b' | 'o' | 'd' | 'h' ) LegalDigit* +/// +BLIFToken BLIFLexer::lexNumber(const char *tokStart) { + assert(llvm::isDigit(curPtr[-1]) || curPtr[-1] == '+' || curPtr[-1] == '-'); + + // There needs to be at least one digit. + if (!llvm::isDigit(*curPtr) && !llvm::isDigit(curPtr[-1])) + return emitError(tokStart, "unexpected character after sign"); + + // If we encounter a "b", "o", "d", or "h", this is a radix-specified integer + // literal. This is only supported for BLIFRTL 2.4.0 or later. This is always + // lexed, but rejected during parsing if the version is too old. + const char *oldPtr = curPtr; + if (curPtr[-1] == '-' && *curPtr == '0') + ++curPtr; + if (curPtr[-1] == '0') { + switch (*curPtr) { + case 'b': + ++curPtr; + while (*curPtr >= '0' && *curPtr <= '1') + ++curPtr; + return formToken(BLIFToken::radix_specified_integer, tokStart); + case 'o': + ++curPtr; + while (*curPtr >= '0' && *curPtr <= '7') + ++curPtr; + return formToken(BLIFToken::radix_specified_integer, tokStart); + case 'd': + ++curPtr; + while (llvm::isDigit(*curPtr)) + ++curPtr; + return formToken(BLIFToken::radix_specified_integer, tokStart); + case 'h': + ++curPtr; + while (llvm::isHexDigit(*curPtr)) + ++curPtr; + return formToken(BLIFToken::radix_specified_integer, tokStart); + default: + curPtr = oldPtr; + break; + } + } + + while (llvm::isDigit(*curPtr)) + ++curPtr; + + // If we encounter a '.' followed by a digit, then this is a floating point + // literal, otherwise this is an integer or negative integer. + if (*curPtr != '.' || !llvm::isDigit(curPtr[1])) { + if (*tokStart == '-' || *tokStart == '+') + return formToken(BLIFToken::signed_integer, tokStart); + return formToken(BLIFToken::integer, tokStart); + } + + // Lex a floating point literal. + curPtr += 2; + while (llvm::isDigit(*curPtr)) + ++curPtr; + + bool hasE = false; + if (*curPtr == 'E') { + hasE = true; + ++curPtr; + if (*curPtr == '+' || *curPtr == '-') + ++curPtr; + while (llvm::isDigit(*curPtr)) + ++curPtr; + } + + // If we encounter a '.' followed by a digit, again, and there was no + // exponent, then this is a version literal. Otherwise it is a floating point + // literal. + if (*curPtr != '.' || !llvm::isDigit(curPtr[1]) || hasE) + return formToken(BLIFToken::floatingpoint, tokStart); + + // Lex a version literal. + curPtr += 2; + while (llvm::isDigit(*curPtr)) + ++curPtr; + return formToken(BLIFToken::version, tokStart); +} diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.h b/lib/Dialect/BLIF/Import/BLIFLexer.h new file mode 100644 index 000000000000..8217efc837c1 --- /dev/null +++ b/lib/Dialect/BLIF/Import/BLIFLexer.h @@ -0,0 +1,175 @@ +//===- BLIFLexer.h - .blif lexer and token definitions ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the a Lexer and Token interface for .blif files. +// +//===----------------------------------------------------------------------===// + +#ifndef BLIFTOMLIR_BLIFLEXER_H +#define BLIFTOMLIR_BLIFLEXER_H + +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "llvm/Support/SourceMgr.h" + +namespace mlir { +class MLIRContext; +class Location; +} // namespace mlir + +namespace circt { +namespace blif { + +/// This represents a specific token for .fir files. +class BLIFToken { +public: + enum Kind { +#define TOK_MARKER(NAME) NAME, +#define TOK_IDENTIFIER(NAME) NAME, +#define TOK_LITERAL(NAME) NAME, +#define TOK_PUNCTUATION(NAME, SPELLING) NAME, +#define TOK_KEYWORD(SPELLING) kw_##SPELLING, +#define TOK_KEYWORD_DOT(SPELLING) kw_##SPELLING, +#include "BLIFTokenKinds.def" + }; + + BLIFToken(Kind kind, StringRef spelling) : kind(kind), spelling(spelling) {} + + // Return the bytes that make up this token. + StringRef getSpelling() const { return spelling; } + + // Token classification. + Kind getKind() const { return kind; } + bool is(Kind K) const { return kind == K; } + + bool isAny(Kind k1, Kind k2) const { return is(k1) || is(k2); } + + /// Return true if this token is one of the specified kinds. + template + bool isAny(Kind k1, Kind k2, Kind k3, T... others) const { + if (is(k1)) + return true; + return isAny(k2, k3, others...); + } + + bool isNot(Kind k) const { return kind != k; } + + /// Return true if this token isn't one of the specified kinds. + template + bool isNot(Kind k1, Kind k2, T... others) const { + return !isAny(k1, k2, others...); + } + + /// Return true if this is one of the keyword token kinds (e.g. kw_wire). + bool isKeyword() const; + + bool isModelHeaderKeyword() const { + return true; //isAny(kw_inputs, kw_outputs, kw_names, kw_latch, kw_end); + } + + /// Given a token containing a string literal, return its value, including + /// removing the quote characters and unescaping the contents of the string. + /// The lexer has already verified that this token is valid. + std::string getStringValue() const; + static std::string getStringValue(StringRef spelling); + + /// Given a token containing a verbatim string, return its value, including + /// removing the quote characters and unescaping the quotes of the string. The + /// lexer has already verified that this token is valid. + std::string getVerbatimStringValue() const; + static std::string getVerbatimStringValue(StringRef spelling); + + // Location processing. + llvm::SMLoc getLoc() const; + llvm::SMLoc getEndLoc() const; + llvm::SMRange getLocRange() const; + +private: + /// Discriminator that indicates the sort of token this is. + Kind kind; + + /// A reference to the entire token contents; this is always a pointer into + /// a memory buffer owned by the source manager. + StringRef spelling; +}; + +class BLIFLexerCursor; + +/// This implements a lexer for .fir files. +class BLIFLexer { +public: + BLIFLexer(const llvm::SourceMgr &sourceMgr, mlir::MLIRContext *context); + + const llvm::SourceMgr &getSourceMgr() const { return sourceMgr; } + + /// Move to the next valid token. + void lexToken() { curToken = lexTokenImpl(); } + + const BLIFToken &getToken() const { return curToken; } + + mlir::Location translateLocation(llvm::SMLoc loc); + + /// Get an opaque pointer into the lexer state that can be restored later. + BLIFLexerCursor getCursor() const; + +private: + BLIFToken lexTokenImpl(); + + // Helpers. + BLIFToken formToken(BLIFToken::Kind kind, const char *tokStart) { + return BLIFToken(kind, StringRef(tokStart, curPtr - tokStart)); + } + + BLIFToken emitError(const char *loc, const Twine &message); + + // Lexer implementation methods. + BLIFToken lexFileInfo(const char *tokStart); + BLIFToken lexInlineAnnotation(const char *tokStart); + BLIFToken lexIdentifierOrKeyword(const char *tokStart); + BLIFToken lexNumber(const char *tokStart); + void skipComment(); + BLIFToken lexString(const char *tokStart, bool isVerbatim); + + const llvm::SourceMgr &sourceMgr; + const mlir::StringAttr bufferNameIdentifier; + + StringRef curBuffer; + const char *curPtr; + + /// This is the next token that hasn't been consumed yet. + BLIFToken curToken; + + BLIFLexer(const BLIFLexer &) = delete; + void operator=(const BLIFLexer &) = delete; + friend class BLIFLexerCursor; +}; + +/// This is the state captured for a lexer cursor. +class BLIFLexerCursor { +public: + BLIFLexerCursor(const BLIFLexer &lexer) + : state(lexer.curPtr), curToken(lexer.getToken()) {} + + void restore(BLIFLexer &lexer) { + lexer.curPtr = state; + lexer.curToken = curToken; + } + +private: + const char *state; + BLIFToken curToken; +}; + +inline BLIFLexerCursor BLIFLexer::getCursor() const { + return BLIFLexerCursor(*this); +} + +} // namespace blif +} // namespace circt + +#endif // BLIFTOMLIR_BLIFLEXER_H diff --git a/lib/Dialect/BLIF/Import/BLIFParser.cpp b/lib/Dialect/BLIF/Import/BLIFParser.cpp new file mode 100644 index 000000000000..951d15f1838e --- /dev/null +++ b/lib/Dialect/BLIF/Import/BLIFParser.cpp @@ -0,0 +1,527 @@ +//===- FIRParser.cpp - .fir to FIRRTL dialect parser ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This implements a .fir file parser. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/BLIF/BLIFParser.h" +#include "BLIFLexer.h" +#include "circt/Dialect/BLIF/BLIFOps.h" +#include "circt/Dialect/HW/HWAttributes.h" +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/ImplicitLocOpBuilder.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/IR/Threading.h" +#include "mlir/IR/Verifier.h" +#include "mlir/Support/Timing.h" +#include "mlir/Tools/mlir-translate/Translation.h" +#include "llvm/ADT/PointerEmbeddedInt.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/TypeSwitch.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +using namespace circt; +using namespace blif; + +using llvm::SMLoc; +using llvm::SourceMgr; +using mlir::LocationAttr; + +//===----------------------------------------------------------------------===// +// BLIFParser +//===----------------------------------------------------------------------===// + +namespace { +/// This class implements logic common to all levels of the parser, including +/// things like types and helper logic. +struct BLIFParser { + BLIFParser(MLIRContext *ctx, BLIFLexer &lexer) : context(ctx), lexer(lexer) {} + + MLIRContext *getContext() const { return context; } + + BLIFLexer &getLexer() { return lexer; } + + /// Return the current token the parser is inspecting. + const BLIFToken &getToken() const { return lexer.getToken(); } + StringRef getTokenSpelling() const { return getToken().getSpelling(); } + + //===--------------------------------------------------------------------===// + // Error Handling + //===--------------------------------------------------------------------===// + + /// Emit an error and return failure. + InFlightDiagnostic emitError(const Twine &message = {}) { + return emitError(getToken().getLoc(), message); + } + + InFlightDiagnostic emitError(SMLoc loc, const Twine &message = {}); + + /// Emit a warning. + InFlightDiagnostic emitWarning(const Twine &message = {}) { + return emitWarning(getToken().getLoc(), message); + } + + InFlightDiagnostic emitWarning(SMLoc loc, const Twine &message = {}); + + //===--------------------------------------------------------------------===// + // Location Handling + //===--------------------------------------------------------------------===// + + class LocWithInfo; + + /// Encode the specified source location information into an attribute for + /// attachment to the IR. + Location translateLocation(llvm::SMLoc loc) { + return lexer.translateLocation(loc); + } + + //===--------------------------------------------------------------------===// + // Token Parsing + //===--------------------------------------------------------------------===// + + /// If the current token has the specified kind, consume it and return true. + /// If not, return false. + bool consumeIf(BLIFToken::Kind kind) { + if (getToken().isNot(kind)) + return false; + consumeToken(kind); + return true; + } + + /// Advance the current lexer onto the next token. + /// + /// This returns the consumed token. + BLIFToken consumeToken() { + BLIFToken consumedToken = getToken(); + assert(consumedToken.isNot(BLIFToken::eof, BLIFToken::error) && + "shouldn't advance past EOF or errors"); + lexer.lexToken(); + return consumedToken; + } + + /// Advance the current lexer onto the next token, asserting what the expected + /// current token is. This is preferred to the above method because it leads + /// to more self-documenting code with better checking. + /// + /// This returns the consumed token. + BLIFToken consumeToken(BLIFToken::Kind kind) { + BLIFToken consumedToken = getToken(); + assert(consumedToken.is(kind) && "consumed an unexpected token"); + consumeToken(); + return consumedToken; + } + + /// Capture the current token's spelling into the specified value. This + /// always succeeds. + ParseResult parseGetSpelling(StringRef &spelling) { + spelling = getTokenSpelling(); + return success(); + } + + /// Consume the specified token if present and return success. On failure, + /// output a diagnostic and return failure. + ParseResult parseToken(BLIFToken::Kind expectedToken, const Twine &message); + + /// Parse a comma-separated list of elements, terminated with an arbitrary + /// token. + ParseResult parseListUntil(BLIFToken::Kind rightToken, + const std::function &parseElement); + + //===--------------------------------------------------------------------===// + // Common Parser Rules + //===--------------------------------------------------------------------===// + + /// Parse 'intLit' into the specified value. + ParseResult parseIntLit(APInt &result, const Twine &message); + ParseResult parseIntLit(int64_t &result, const Twine &message); + ParseResult parseIntLit(int32_t &result, const Twine &message); + + // Parse the 'id' grammar, which is an identifier or an allowed keyword. + ParseResult parseId(StringRef &result, const Twine &message); + ParseResult parseId(StringAttr &result, const Twine &message); + // Parse the 'id' grammar or the string literal "NIL" + ParseResult parseIdOrNil(StringRef &result, const Twine &message); + ParseResult parseIdOrNil(StringAttr &result, const Twine &message); + + ParseResult parseIdList(SmallVectorImpl &result, + const Twine &message); + +private: + BLIFParser(const BLIFParser &) = delete; + void operator=(const BLIFParser &) = delete; + + /// The context in which we are parsing. + MLIRContext *context; + + /// BLIFParser is subclassed and reinstantiated. Do not add additional + /// non-trivial state here, add it to SharedParserConstants. + BLIFLexer &lexer; +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Error Handling +//===----------------------------------------------------------------------===// + +InFlightDiagnostic BLIFParser::emitError(SMLoc loc, const Twine &message) { + auto diag = mlir::emitError(translateLocation(loc), message); + + // If we hit a parse error in response to a lexer error, then the lexer + // already reported the error. + if (getToken().is(BLIFToken::error)) + diag.abandon(); + return diag; +} + +InFlightDiagnostic BLIFParser::emitWarning(SMLoc loc, const Twine &message) { + return mlir::emitWarning(translateLocation(loc), message); +} + +//===----------------------------------------------------------------------===// +// Token Parsing +//===----------------------------------------------------------------------===// + +/// Consume the specified token if present and return success. On failure, +/// output a diagnostic and return failure. +ParseResult BLIFParser::parseToken(BLIFToken::Kind expectedToken, + const Twine &message) { + if (consumeIf(expectedToken)) + return success(); + return emitError(message); +} + +//===--------------------------------------------------------------------===// +// Common Parser Rules +//===--------------------------------------------------------------------===// + +/// id ::= Id +/// +/// Parse the 'id' grammar, which is a trivial string. On +/// success, this returns the identifier in the result attribute. +ParseResult BLIFParser::parseId(StringRef &result, const Twine &message) { + switch (getToken().getKind()) { + // The most common case is an identifier. + case BLIFToken::identifier: + result = getTokenSpelling(); + consumeToken(); + return success(); + + default: + emitError(message); + return failure(); + } +} + +ParseResult BLIFParser::parseId(StringAttr &result, const Twine &message) { + StringRef name; + if (parseId(name, message)) + return failure(); + + result = StringAttr::get(getContext(), name); + return success(); +} + +//===----------------------------------------------------------------------===// +// BLIFModelParser +//===----------------------------------------------------------------------===// + +namespace { +/// This class implements logic and state for parsing statements, suites, and +/// similar module body constructs. +struct BLIFModelParser : public BLIFParser { + explicit BLIFModelParser(Block &blockToInsertInto, BLIFLexer &lexer) + : BLIFParser(blockToInsertInto.getParentOp()->getContext(), lexer), + builder(UnknownLoc::get(getContext()), getContext()) { + builder.setInsertionPointToEnd(&blockToInsertInto); + } + + ParseResult parseSimpleStmt(); + ParseResult parseSimpleStmtBlock(); + +private: + ParseResult parseSimpleStmtImpl(); + + // Exp Parsing + ParseResult parseExpImpl(Value &result, const Twine &message, + bool isLeadingStmt); + ParseResult parseExp(Value &result, const Twine &message) { + return parseExpImpl(result, message, /*isLeadingStmt:*/ false); + } + + ParseResult parseLogicGate(); + ParseResult parseLatch(); + ParseResult parseLibraryLogicGate(); + ParseResult parseLibraryLatch(); + ParseResult parseModelReference(); + ParseResult parseModelCmdImpl(); + ParseResult parseModelCmd(); + ParseResult parseModelBody(); + + // Helper to fetch a module referenced by an instance-like statement. + Value getReferencedModel(SMLoc loc, StringRef modelName); + + // The builder to build into. + ImplicitLocOpBuilder builder; +}; + +} // end anonymous namespace + +Value BLIFModelParser::getReferencedModel(SMLoc loc, StringRef modelName) { + return {}; +} + +/// logicgate ::= '.names' in* out NEWLINE single_output_cover* +ParseResult BLIFModelParser::parseLogicGate() { + auto startTok = consumeToken(BLIFToken::kw_names); + auto loc = startTok.getLoc(); + SmallVector inputs; + std::string output; + if (parseIdList(inputs, "expected input list")) + return failure(); + output = inputs.back(); + inputs.pop_back(); + // TODO: READ THE TRUTH TABLE + // builder.create(loc, inputs, output); + return success(); +} + +/// latch ::= '.latch' id_in id_out [latch_type [id | "NIL"]]? [ '0' | '1' | '2' +/// | '3']? +ParseResult BLIFModelParser::parseLatch() { + auto startTok = consumeToken(BLIFToken::kw_latch); + auto loc = startTok.getLoc(); + StringRef input; + StringRef output; + // latchType lType; + std::string clock; + int init_val = 3; // default + if (parseId(input, "Expected input signal") || + parseId(output, "Expected output signal")) + return failure(); + // TODO: Latch type + // if (parseOptionalInt(init_val)) + // return failure(); + if (init_val < 0 || init_val > 3) + return emitError("invalid initial latch value '") << init_val << "'", + failure(); + // builder.create(loc, input, output/*, lType*/, clock, + // init_val); + return success(); +} + +// liblogic ::= '.gate' id formal-actual-list +ParseResult BLIFModelParser::parseLibraryLogicGate() { return failure(); } + +// liblatch ::= '.mlatch' id formal-actual-list [id | "NIL"] [ '0' | '1' | '2' | +// '3']? +ParseResult BLIFModelParser::parseLibraryLatch() { return failure(); } + +// mref ::= '.subskt' id formal-actual-list +ParseResult BLIFModelParser::parseModelReference() { return failure(); } + +/// model_cmd ::= stmt +/// +/// stmt ::= .names +/// ::= .latch +/// ::= .gate +/// ::= .mlatch +/// ::= .subskt +/// +ParseResult BLIFModelParser::parseModelCmdImpl() { + auto kind = getToken().getKind(); + switch (kind) { + // Statements. + case BLIFToken::kw_names: + return parseLogicGate(); + case BLIFToken::kw_latch: + return parseLatch(); + case BLIFToken::kw_gate: + return parseLibraryLogicGate(); + case BLIFToken::kw_mlatch: + return parseLibraryLatch(); + case BLIFToken::kw_subskt: + return parseModelReference(); + default: + return emitError("unexpected token in model command"), failure(); + } +} + +ParseResult BLIFModelParser::parseModelCmd() { + // locationProcessor.startStatement(); + auto result = parseModelCmdImpl(); + // locationProcessor.endStatement(*this); + return result; +} + +// Parse the body of this module. +ParseResult BLIFModelParser::parseModelBody() { + // auto &body = moduleOp->getRegion(0).front(); + + while (true) { + // The outer level parser can handle these tokens. + if (getToken().isAny(BLIFToken::eof, BLIFToken::error, BLIFToken::kw_end)) + return success(); + + // Let the statement parser handle this. + if (parseModelCmd()) + return failure(); + } + + return success(); +} + +//===----------------------------------------------------------------------===// +// BLIFFileParser +//===----------------------------------------------------------------------===// + +namespace { +/// This class implements the parser, namely models +struct BLIFFileParser : public BLIFParser { + explicit BLIFFileParser(BLIFLexer &lexer, ModuleOp mlirModule) + : BLIFParser(mlirModule.getContext(), lexer), mlirModule(mlirModule) {} + + ParseResult parseFile(); + +private: + ParseResult parseModel(); + + ModuleOp mlirModule; +}; + +} // end anonymous namespace + +/// model ::= '.model' id '\n' [input_line, output_line, clock_line]* +/// model_cmds* `.end` +ParseResult BLIFFileParser::parseModel() { + StringAttr name; + auto modLoc = getToken().getLoc(); + SmallVector inputs, outputs, clocks; + consumeToken(BLIFToken::kw_model); + if (parseId(name, "expected model name")) + return failure(); + while (getToken().isModelHeaderKeyword()) { + switch (getToken().getKind()) { + case BLIFToken::kw_inputs: + if (parseIdList(inputs, "expected input list")) + return failure(); + break; + case BLIFToken::kw_outputs: + if (parseIdList(outputs, "expected output list")) + return failure(); + break; + case BLIFToken::kw_clock: + if (parseIdList(clocks, "expected clock list")) + return failure(); + break; + default: + return emitError("unexpected token in model header"); + } + } + // Create the model + + if (/*parseModelBody() ||*/ parseToken(BLIFToken::kw_end, "expected .end")) + return failure(); + + return success(); +} + +/// file ::= model+ +ParseResult BLIFFileParser::parseFile() { + + while (!getToken().is(BLIFToken::eof)) { + // Parse the next model in the file + if (getToken().is(BLIFToken::kw_model)) { + if (parseModel()) + return failure(); + } else { + emitError("unexpected token in file"); + return failure(); + } + } + + // Parse any contained modules. + while (true) { + switch (getToken().getKind()) { + // If we got to the end of the file, then we're done. + case BLIFToken::eof: + goto DoneParsing; + + // If we got an error token, then the lexer already emitted an error, + // just stop. We could introduce error recovery if there was demand for + // it. + case BLIFToken::error: + return failure(); + + default: + emitError("unexpected token in circuit"); + return failure(); + + case BLIFToken::kw_model: + if (parseModel()) + return failure(); + break; + } + } + +DoneParsing: + // TODO: Fixup references + return success(); +} + +//===----------------------------------------------------------------------===// +// Driver +//===----------------------------------------------------------------------===// + +// Parse the specified .fir file into the specified MLIR context. +mlir::OwningOpRef +circt::blif::importBLIFFile(SourceMgr &sourceMgr, MLIRContext *context, + mlir::TimingScope &ts, BLIFParserOptions options) { + auto sourceBuf = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID()); + + context->loadDialect(); + + // This is the result module we are parsing into. + mlir::OwningOpRef module(ModuleOp::create( + FileLineColLoc::get(context, sourceBuf->getBufferIdentifier(), + /*line=*/0, + /*column=*/0))); + // SharedParserConstants state(context, options); + BLIFLexer lexer(sourceMgr, context); + if (BLIFFileParser(lexer, *module).parseFile()) + return nullptr; + + // Make sure the parse module has no other structural problems detected by + // the verifier. + auto circuitVerificationTimer = ts.nest("Verify circuit"); + if (failed(verify(*module))) + return {}; + + return module; +} + +void circt::blif::registerFromBLIFFileTranslation() { + static mlir::TranslateToMLIRRegistration fromBLIF( + "import-blif", "import .blif", + [](llvm::SourceMgr &sourceMgr, MLIRContext *context) { + mlir::TimingScope ts; + return importBLIFFile(sourceMgr, context, ts); + }); +} diff --git a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def new file mode 100644 index 000000000000..4acff709daef --- /dev/null +++ b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def @@ -0,0 +1,126 @@ +//===- BLIFTokenKinds.def - .blif file Token Descriptions -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is intended to be #include'd multiple times to extract information +// about tokens for various clients in the lexer. +// +//===----------------------------------------------------------------------===// + +#if !defined(TOK_MARKER) && !defined(TOK_IDENTIFIER) && \ + !defined(TOK_LITERAL) && !defined(TOK_PUNCTUATION) && \ + !defined(TOK_KEYWORD) && !defined(TOK_LPKEYWORD) && \ + !defined(TOK_LPKEYWORD_PRIM) +#error Must define one of the TOK_ macros. +#endif + +#ifndef TOK_MARKER +#define TOK_MARKER(X) +#endif +#ifndef TOK_IDENTIFIER +#define TOK_IDENTIFIER(NAME) +#endif +#ifndef TOK_LITERAL +#define TOK_LITERAL(NAME) +#endif +#ifndef TOK_PUNCTUATION +#define TOK_PUNCTUATION(NAME, SPELLING) +#endif +#ifndef TOK_KEYWORD +#define TOK_KEYWORD(SPELLING) +#endif +#ifndef TOK_KEYWORD_DOT +#define TOK_KEYWORD(SPELLING) +#endif + +// Markers +TOK_MARKER(eof) +TOK_MARKER(error) + +// Identifiers. +TOK_IDENTIFIER(identifier) // foo +TOK_IDENTIFIER(literal_identifier) // `42` + +// Literals +TOK_LITERAL(integer) // 42 +TOK_LITERAL(signed_integer) // -42 and +42 +TOK_LITERAL(radix_specified_integer) // 0b101010, 0o52, 0d42, 0h2a and negations +TOK_LITERAL(floatingpoint) // 42.0 +TOK_LITERAL(version) // 1.2.3 +TOK_LITERAL(string) // "foo" +TOK_LITERAL(verbatim_string) // 'foo' + +TOK_LITERAL(fileinfo) + +// Punctuation. +TOK_PUNCTUATION(period, ".") +TOK_PUNCTUATION(comma, ",") +TOK_PUNCTUATION(colon, ":") +TOK_PUNCTUATION(question, "?") +TOK_PUNCTUATION(l_paren, "(") +TOK_PUNCTUATION(r_paren, ")") +TOK_PUNCTUATION(l_brace, "{") +TOK_PUNCTUATION(l_brace_bar, "{|") +TOK_PUNCTUATION(r_brace, "}") +TOK_PUNCTUATION(r_brace_bar, "|}") +TOK_PUNCTUATION(l_square, "[") +TOK_PUNCTUATION(r_square, "]") +TOK_PUNCTUATION(less, "<") +TOK_PUNCTUATION(less_equal, "<=") +TOK_PUNCTUATION(greater, ">") +TOK_PUNCTUATION(equal, "=") +TOK_PUNCTUATION(equal_greater, "=>") + +// Keywords. These turn "foo" into FIRToken::kw_foo enums. + +// NOTE: Please key these alphabetized to make it easier to find something in +// this list and to cater to OCD. + +TOK_KEYWORD_DOT(area) +TOK_KEYWORD_DOT(cycle) +TOK_KEYWORD_DOT(clock) +TOK_KEYWORD_DOT(clock_event) +TOK_KEYWORD_DOT(default_input_arrival) +TOK_KEYWORD_DOT(default_input_drive) +TOK_KEYWORD_DOT(default_max_input_load) +TOK_KEYWORD_DOT(default_output_load) +TOK_KEYWORD_DOT(default_output_required) +TOK_KEYWORD_DOT(delay) +TOK_KEYWORD_DOT(end) +TOK_KEYWORD_DOT(end_kiss) +TOK_KEYWORD_DOT(inputs) +TOK_KEYWORD_DOT(input_arrival) +TOK_KEYWORD_DOT(input_drive) +TOK_KEYWORD_DOT(max_input_load) +TOK_KEYWORD_DOT(model) +TOK_KEYWORD_DOT(outputs) +TOK_KEYWORD_DOT(output_load) +TOK_KEYWORD_DOT(output_required) +TOK_KEYWORD_DOT(search) +TOK_KEYWORD_DOT(wire) +TOK_KEYWORD_DOT(wire_load_slope) + + + +// These are for KEYWORD cases that correspond to a primitive operation. + +TOK_KEYWORD_DOT(names) +TOK_KEYWORD_DOT(exdc) +TOK_KEYWORD_DOT(latch) +TOK_KEYWORD_DOT(gate) +TOK_KEYWORD_DOT(mlatch) +TOK_KEYWORD_DOT(subskt) +TOK_KEYWORD_DOT(start_kiss) + + +#undef TOK_MARKER +#undef TOK_IDENTIFIER +#undef TOK_LITERAL +#undef TOK_PUNCTUATION +#undef TOK_KEYWORD +#undef TOK_KEYWORD_DOT +#undef TOK_LPKEYWORD_PRIM diff --git a/lib/Dialect/BLIF/Import/CMakeLists.txt b/lib/Dialect/BLIF/Import/CMakeLists.txt new file mode 100644 index 000000000000..d5ee02e985fd --- /dev/null +++ b/lib/Dialect/BLIF/Import/CMakeLists.txt @@ -0,0 +1,11 @@ + +add_circt_translation_library(CIRCTImportBLIFFile + BLIFLexer.cpp + BLIFParser.cpp + + ADDITIONAL_HEADER_DIRS + + LINK_LIBS PUBLIC + CIRCTBLIF + MLIRTranslateLib + ) From bed4465905ca3cdafa63439276ed1aa225df9a1c Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Mon, 30 Dec 2024 17:09:34 +0000 Subject: [PATCH 07/18] formatting --- lib/Dialect/BLIF/Import/BLIFLexer.cpp | 12 +++++++----- lib/Dialect/BLIF/Import/BLIFLexer.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.cpp b/lib/Dialect/BLIF/Import/BLIFLexer.cpp index 814fb9c40af2..03fda407a993 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.cpp +++ b/lib/Dialect/BLIF/Import/BLIFLexer.cpp @@ -38,7 +38,9 @@ SMLoc BLIFToken::getEndLoc() const { return SMLoc::getFromPointer(spelling.data() + spelling.size()); } -SMRange BLIFToken::getLocRange() const { return SMRange(getLoc(), getEndLoc()); } +SMRange BLIFToken::getLocRange() const { + return SMRange(getLoc(), getEndLoc()); +} /// Return true if this is one of the keyword token kinds (e.g. kw_wire). bool BLIFToken::isKeyword() const { @@ -353,7 +355,7 @@ BLIFToken BLIFLexer::lexIdentifierOrKeyword(const char *tokStart) { BLIFToken::Kind kind = llvm::StringSwitch(spelling) #define TOK_LPKEYWORD(SPELLING) .Case(#SPELLING, BLIFToken::lp_##SPELLING) #include "BLIFTokenKinds.def" - .Default(BLIFToken::identifier); + .Default(BLIFToken::identifier); if (kind != BLIFToken::identifier) { ++curPtr; return formToken(kind, tokStart); @@ -364,7 +366,7 @@ BLIFToken BLIFLexer::lexIdentifierOrKeyword(const char *tokStart) { BLIFToken::Kind kind = llvm::StringSwitch(spelling) #define TOK_KEYWORD(SPELLING) .Case(#SPELLING, BLIFToken::kw_##SPELLING) #include "BLIFTokenKinds.def" - .Default(BLIFToken::identifier); + .Default(BLIFToken::identifier); // If this has the backticks of a literal identifier and it fell through the // above switch, indicating that it was not found to e a keyword, then change @@ -458,8 +460,8 @@ BLIFToken BLIFLexer::lexNumber(const char *tokStart) { return emitError(tokStart, "unexpected character after sign"); // If we encounter a "b", "o", "d", or "h", this is a radix-specified integer - // literal. This is only supported for BLIFRTL 2.4.0 or later. This is always - // lexed, but rejected during parsing if the version is too old. + // literal. This is only supported for BLIFRTL 2.4.0 or later. This is + // always lexed, but rejected during parsing if the version is too old. const char *oldPtr = curPtr; if (curPtr[-1] == '-' && *curPtr == '0') ++curPtr; diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.h b/lib/Dialect/BLIF/Import/BLIFLexer.h index 8217efc837c1..ec82969c9b09 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.h +++ b/lib/Dialect/BLIF/Import/BLIFLexer.h @@ -69,7 +69,7 @@ class BLIFToken { bool isKeyword() const; bool isModelHeaderKeyword() const { - return true; //isAny(kw_inputs, kw_outputs, kw_names, kw_latch, kw_end); + return true; // isAny(kw_inputs, kw_outputs, kw_names, kw_latch, kw_end); } /// Given a token containing a string literal, return its value, including From 55542d9659ae3f4b7cc08f166e28f5b385fc1720 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Mon, 30 Dec 2024 17:24:44 +0000 Subject: [PATCH 08/18] fix build --- lib/Dialect/BLIF/Import/BLIFTokenKinds.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def index 4acff709daef..2a797a2e37ac 100644 --- a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def +++ b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def @@ -34,7 +34,7 @@ #define TOK_KEYWORD(SPELLING) #endif #ifndef TOK_KEYWORD_DOT -#define TOK_KEYWORD(SPELLING) +#define TOK_KEYWORD_DOT(SPELLING) TOK_KEYWORD(SPELLING) #endif // Markers From dd2888aab0db7a9218c2603e3666f34a61e3e306 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Tue, 31 Dec 2024 00:16:06 +0000 Subject: [PATCH 09/18] add a test (not working) --- include/circt/InitAllTranslations.h | 2 ++ lib/Dialect/BLIF/Import/BLIFLexer.cpp | 4 --- lib/Dialect/BLIF/Import/BLIFParser.cpp | 38 ++++++++++++++++++++------ test/Dialect/BLIF/parse-basic.blif | 16 +++++++++++ test/lit.cfg.py | 2 +- 5 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 test/Dialect/BLIF/parse-basic.blif diff --git a/include/circt/InitAllTranslations.h b/include/circt/InitAllTranslations.h index 314faa8b175e..1bf8b8db965f 100644 --- a/include/circt/InitAllTranslations.h +++ b/include/circt/InitAllTranslations.h @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "circt/Dialect/Arc/ModelInfoExport.h" +#include "circt/Dialect/BLIF/BLIFParser.h" #include "circt/Dialect/Calyx/CalyxEmitter.h" #include "circt/Dialect/ESI/ESIDialect.h" #include "circt/Dialect/FIRRTL/FIREmitter.h" @@ -32,6 +33,7 @@ namespace circt { inline void registerAllTranslations() { static bool initOnce = []() { arc::registerArcModelInfoTranslation(); + blif::registerFromBLIFFileTranslation(); calyx::registerToCalyxTranslation(); firrtl::registerFromFIRFileTranslation(); firrtl::registerToFIRFileTranslation(); diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.cpp b/lib/Dialect/BLIF/Import/BLIFLexer.cpp index 03fda407a993..99f0ea108bb0 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.cpp +++ b/lib/Dialect/BLIF/Import/BLIFLexer.cpp @@ -256,10 +256,6 @@ BLIFToken BLIFLexer::lexTokenImpl() { return lexFileInfo(tokStart); // Unknown character, emit an error. return emitError(tokStart, "unexpected character"); - case '%': - if (*curPtr == '[') - return lexInlineAnnotation(tokStart); - return emitError(tokStart, "unexpected character following '%'"); case '|': if (*curPtr == '}') return ++curPtr, formToken(BLIFToken::r_brace_bar, tokStart); diff --git a/lib/Dialect/BLIF/Import/BLIFParser.cpp b/lib/Dialect/BLIF/Import/BLIFParser.cpp index 951d15f1838e..823d0e3f882e 100644 --- a/lib/Dialect/BLIF/Import/BLIFParser.cpp +++ b/lib/Dialect/BLIF/Import/BLIFParser.cpp @@ -160,8 +160,8 @@ struct BLIFParser { ParseResult parseIdOrNil(StringRef &result, const Twine &message); ParseResult parseIdOrNil(StringAttr &result, const Twine &message); - ParseResult parseIdList(SmallVectorImpl &result, - const Twine &message); + ParseResult parseIdList(SmallVectorImpl &result, + const Twine &message, unsigned minCount = 0); private: BLIFParser(const BLIFParser &) = delete; @@ -239,6 +239,28 @@ ParseResult BLIFParser::parseId(StringAttr &result, const Twine &message) { return success(); } + ParseResult BLIFParser::parseIdList(SmallVectorImpl &result, + const Twine &message, unsigned minCount) { + while(true) { + switch (getToken().getKind()) { + // The most common case is an identifier. + case BLIFToken::identifier: + result.push_back(getTokenSpelling()); + consumeToken(); + if (minCount) + --minCount; + break; + default: + if (minCount) { + emitError(message); + return failure(); + } + return success(); + } + } +} + + //===----------------------------------------------------------------------===// // BLIFModelParser //===----------------------------------------------------------------------===// @@ -292,9 +314,9 @@ Value BLIFModelParser::getReferencedModel(SMLoc loc, StringRef modelName) { ParseResult BLIFModelParser::parseLogicGate() { auto startTok = consumeToken(BLIFToken::kw_names); auto loc = startTok.getLoc(); - SmallVector inputs; + SmallVector inputs; std::string output; - if (parseIdList(inputs, "expected input list")) + if (parseIdList(inputs, "expected input list", 1)) return failure(); output = inputs.back(); inputs.pop_back(); @@ -413,22 +435,22 @@ struct BLIFFileParser : public BLIFParser { ParseResult BLIFFileParser::parseModel() { StringAttr name; auto modLoc = getToken().getLoc(); - SmallVector inputs, outputs, clocks; + SmallVector inputs, outputs, clocks; consumeToken(BLIFToken::kw_model); if (parseId(name, "expected model name")) return failure(); while (getToken().isModelHeaderKeyword()) { switch (getToken().getKind()) { case BLIFToken::kw_inputs: - if (parseIdList(inputs, "expected input list")) + if (parseIdList(inputs, "expected input list", 1)) return failure(); break; case BLIFToken::kw_outputs: - if (parseIdList(outputs, "expected output list")) + if (parseIdList(outputs, "expected output list", 1)) return failure(); break; case BLIFToken::kw_clock: - if (parseIdList(clocks, "expected clock list")) + if (parseIdList(clocks, "expected clock list", 1)) return failure(); break; default: diff --git a/test/Dialect/BLIF/parse-basic.blif b/test/Dialect/BLIF/parse-basic.blif new file mode 100644 index 000000000000..10ba2ad8b81f --- /dev/null +++ b/test/Dialect/BLIF/parse-basic.blif @@ -0,0 +1,16 @@ +; RUN: circt-translate -import-blif -verify-diagnostics -split-input-file %s | circt-opt | FileCheck %s + +.model simple +.inputs a B +.outputs c +.names a b c +11 1 +.end + +;// ----- + +# unamed model +.names a b \ +c # test \ +11 1 + diff --git a/test/lit.cfg.py b/test/lit.cfg.py index 6ff8c38100cd..791317c1f457 100644 --- a/test/lit.cfg.py +++ b/test/lit.cfg.py @@ -22,7 +22,7 @@ config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) # suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.td', '.mlir', '.ll', '.fir', '.sv'] +config.suffixes = ['.td', '.mlir', '.ll', '.blif', '.fir', '.sv'] # test_source_root: The root path where tests are located. config.test_source_root = os.path.dirname(__file__) From 5a60636f5cc0d9bb9ca40c544166d976fc626d4f Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Tue, 31 Dec 2024 05:02:04 +0000 Subject: [PATCH 10/18] a wild modelop appears --- include/circt/Dialect/BLIF/BLIFOps.td | 11 ++++- include/circt/InitAllDialects.h | 2 + lib/Dialect/BLIF/BLIFOps.cpp | 35 ++++++++++++++++ lib/Dialect/BLIF/Import/BLIFLexer.cpp | 36 ++++++++++++---- lib/Dialect/BLIF/Import/BLIFLexer.h | 3 +- lib/Dialect/BLIF/Import/BLIFParser.cpp | 48 +++++++++++++--------- lib/Dialect/BLIF/Import/BLIFTokenKinds.def | 2 +- test/Dialect/BLIF/parse-basic.blif | 8 ++-- 8 files changed, 112 insertions(+), 33 deletions(-) diff --git a/include/circt/Dialect/BLIF/BLIFOps.td b/include/circt/Dialect/BLIF/BLIFOps.td index 90dfb08aa2d4..9bdae21aaae6 100644 --- a/include/circt/Dialect/BLIF/BLIFOps.td +++ b/include/circt/Dialect/BLIF/BLIFOps.td @@ -24,10 +24,19 @@ def ModelOp : BLIFOp<"model", [IsolatedFromAbove, RegionKindInterface, SingleBlo }]; let arguments = (ins SymbolNameAttr:$sym_name, - TypeAttrOf:$module_type); + TypeAttrOf:$module_type, + APIntAttr:$clocks); let results = (outs); let regions = (region SizedRegion<1>:$body); + let builders = [ + OpBuilder<(ins "StringRef":$name, + "ArrayRef":$inputs, + "ArrayRef":$outputs, + "ArrayRef":$clocks)> + ]; + + let extraClassDeclaration = [{ static mlir::RegionKind getRegionKind(unsigned index) { return mlir::RegionKind::Graph; diff --git a/include/circt/InitAllDialects.h b/include/circt/InitAllDialects.h index 8e53c5fe211a..a6ed7eb8e23b 100644 --- a/include/circt/InitAllDialects.h +++ b/include/circt/InitAllDialects.h @@ -16,6 +16,7 @@ #include "circt/Dialect/AIG/AIGDialect.h" #include "circt/Dialect/Arc/ArcDialect.h" +#include "circt/Dialect/BLIF/BLIFDialect.h" #include "circt/Dialect/Calyx/CalyxDialect.h" #include "circt/Dialect/Comb/CombDialect.h" #include "circt/Dialect/DC/DCDialect.h" @@ -58,6 +59,7 @@ inline void registerAllDialects(mlir::DialectRegistry ®istry) { registry.insert< aig::AIGDialect, arc::ArcDialect, + blif::BLIFDialect, calyx::CalyxDialect, chirrtl::CHIRRTLDialect, comb::CombDialect, diff --git a/lib/Dialect/BLIF/BLIFOps.cpp b/lib/Dialect/BLIF/BLIFOps.cpp index aa462fc9a403..f4c02746cb70 100644 --- a/lib/Dialect/BLIF/BLIFOps.cpp +++ b/lib/Dialect/BLIF/BLIFOps.cpp @@ -17,6 +17,41 @@ using namespace circt; using namespace blif; +void ModelOp::build(mlir::OpBuilder &odsBuilder, mlir::OperationState &odsState, + StringRef name, ArrayRef inputs, + ArrayRef outputs, ArrayRef clocks) { + SmallVector modulePorts; + auto I1 = odsBuilder.getIntegerType(1); + auto uLoc = odsBuilder.getUnknownLoc(); + auto *r = odsState.addRegion(); + Block *b = new Block(); + r->push_back(b); + + for (auto input : inputs) { + modulePorts.push_back( + {odsBuilder.getStringAttr(input), I1, hw::ModulePort::Input}); + b->addArgument(I1, uLoc); + } + for (auto output : outputs) + modulePorts.push_back( + {odsBuilder.getStringAttr(output), I1, hw::ModulePort::Output}); + for (auto clock : clocks) { + modulePorts.push_back( + {odsBuilder.getStringAttr(clock), I1, hw::ModulePort::Input}); + b->addArgument(I1, uLoc); + } + auto moduleType = hw::ModuleType::get(odsBuilder.getContext(), modulePorts); + APInt clockVec(inputs.size() + outputs.size() + clocks.size(), 0); + for (size_t i = inputs.size() + outputs.size(); + i < inputs.size() + outputs.size() + clocks.size(); i++) + clockVec.setBit(i); + auto clockAttr = odsBuilder.getIntegerAttr( + odsBuilder.getIntegerType(clockVec.getBitWidth()), clockVec); + odsState.addAttribute("sym_name", odsBuilder.getStringAttr(name)); + odsState.addAttribute("module_type", TypeAttr::get(moduleType)); + odsState.addAttribute("clocks", clockAttr); +} + namespace mlir { LogicalResult convertFromAttribute(SmallVectorImpl &storage, Attribute attr, diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.cpp b/lib/Dialect/BLIF/Import/BLIFLexer.cpp index 99f0ea108bb0..f215bb6b1669 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.cpp +++ b/lib/Dialect/BLIF/Import/BLIFLexer.cpp @@ -214,13 +214,8 @@ BLIFToken BLIFLexer::lexTokenImpl() { // Handle whitespace. continue; - case '`': - case '_': - // Handle identifiers. - return lexIdentifierOrKeyword(tokStart); - case '.': - return formToken(BLIFToken::period, tokStart); + return lexPeriodOrKeyword(tokStart); case ',': return formToken(BLIFToken::comma, tokStart); case ':': @@ -262,7 +257,7 @@ BLIFToken BLIFLexer::lexTokenImpl() { // Unknown character, emit an error. return emitError(tokStart, "unexpected character"); - case ';': + case '#': skipComment(); continue; @@ -319,6 +314,33 @@ BLIFToken BLIFLexer::lexFileInfo(const char *tokStart) { } } +/// Lex a period or a keyword that starts with a period. +/// +/// Period ::= '.' +/// +BLIFToken BLIFLexer::lexPeriodOrKeyword(const char *tokStart) { + + // Match the rest of the identifier regex: [a-zA-Z_$-]* + while (llvm::isAlpha(*curPtr) || llvm::isDigit(*curPtr) || *curPtr == '_' || + *curPtr == '$' || *curPtr == '-') + ++curPtr; + + StringRef spelling(tokStart, curPtr - tokStart); + + // See if the identifier is a keyword. By default, it is a period. + BLIFToken::Kind kind = llvm::StringSwitch(spelling) +#define TOK_KEYWORD_DOT(SPELLING) .Case("." #SPELLING, BLIFToken::kw_##SPELLING) +#include "BLIFTokenKinds.def" + .Default(BLIFToken::period); + if (kind != BLIFToken::period) { + ++curPtr; + return formToken(kind, tokStart); + } + + // Otherwise, this is a period. + return formToken(BLIFToken::period, tokStart); +} + /// Lex an identifier or keyword that starts with a letter. /// /// LegalStartChar ::= [a-zA-Z_] diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.h b/lib/Dialect/BLIF/Import/BLIFLexer.h index ec82969c9b09..10dd159b67cd 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.h +++ b/lib/Dialect/BLIF/Import/BLIFLexer.h @@ -69,7 +69,7 @@ class BLIFToken { bool isKeyword() const; bool isModelHeaderKeyword() const { - return true; // isAny(kw_inputs, kw_outputs, kw_names, kw_latch, kw_end); + return isAny(kw_inputs, kw_outputs, kw_clock); } /// Given a token containing a string literal, return its value, including @@ -134,6 +134,7 @@ class BLIFLexer { BLIFToken lexNumber(const char *tokStart); void skipComment(); BLIFToken lexString(const char *tokStart, bool isVerbatim); + BLIFToken lexPeriodOrKeyword(const char *tokStart); const llvm::SourceMgr &sourceMgr; const mlir::StringAttr bufferNameIdentifier; diff --git a/lib/Dialect/BLIF/Import/BLIFParser.cpp b/lib/Dialect/BLIF/Import/BLIFParser.cpp index 823d0e3f882e..03411272b6b7 100644 --- a/lib/Dialect/BLIF/Import/BLIFParser.cpp +++ b/lib/Dialect/BLIF/Import/BLIFParser.cpp @@ -239,28 +239,27 @@ ParseResult BLIFParser::parseId(StringAttr &result, const Twine &message) { return success(); } - ParseResult BLIFParser::parseIdList(SmallVectorImpl &result, - const Twine &message, unsigned minCount) { - while(true) { - switch (getToken().getKind()) { - // The most common case is an identifier. - case BLIFToken::identifier: - result.push_back(getTokenSpelling()); - consumeToken(); - if (minCount) +ParseResult BLIFParser::parseIdList(SmallVectorImpl &result, + const Twine &message, unsigned minCount) { + while (true) { + switch (getToken().getKind()) { + // The most common case is an identifier. + case BLIFToken::identifier: + result.push_back(getTokenSpelling()); + consumeToken(); + if (minCount) --minCount; - break; - default: - if (minCount) { - emitError(message); - return failure(); + break; + default: + if (minCount) { + emitError(message); + return failure(); + } + return success(); } - return success(); - } } } - //===----------------------------------------------------------------------===// // BLIFModelParser //===----------------------------------------------------------------------===// @@ -418,7 +417,10 @@ namespace { /// This class implements the parser, namely models struct BLIFFileParser : public BLIFParser { explicit BLIFFileParser(BLIFLexer &lexer, ModuleOp mlirModule) - : BLIFParser(mlirModule.getContext(), lexer), mlirModule(mlirModule) {} + : BLIFParser(mlirModule.getContext(), lexer), mlirModule(mlirModule), + builder(UnknownLoc::get(getContext()), getContext()) { + builder.setInsertionPointToStart(mlirModule.getBody()); + } ParseResult parseFile(); @@ -426,6 +428,7 @@ struct BLIFFileParser : public BLIFParser { ParseResult parseModel(); ModuleOp mlirModule; + ImplicitLocOpBuilder builder; }; } // end anonymous namespace @@ -442,22 +445,29 @@ ParseResult BLIFFileParser::parseModel() { while (getToken().isModelHeaderKeyword()) { switch (getToken().getKind()) { case BLIFToken::kw_inputs: + consumeToken(BLIFToken::kw_inputs); if (parseIdList(inputs, "expected input list", 1)) return failure(); break; case BLIFToken::kw_outputs: + consumeToken(BLIFToken::kw_outputs); if (parseIdList(outputs, "expected output list", 1)) return failure(); break; case BLIFToken::kw_clock: + consumeToken(BLIFToken::kw_clock); if (parseIdList(clocks, "expected clock list", 1)) return failure(); break; default: - return emitError("unexpected token in model header"); + break; } } // Create the model + auto m = builder.create(name, inputs, outputs, clocks); + OpBuilder::InsertionGuard guard(builder); + builder.setInsertionPointToEnd(&m.getBody().back()); + builder.create(ValueRange()); if (/*parseModelBody() ||*/ parseToken(BLIFToken::kw_end, "expected .end")) return failure(); diff --git a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def index 2a797a2e37ac..3179b522333f 100644 --- a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def +++ b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def @@ -14,7 +14,7 @@ #if !defined(TOK_MARKER) && !defined(TOK_IDENTIFIER) && \ !defined(TOK_LITERAL) && !defined(TOK_PUNCTUATION) && \ !defined(TOK_KEYWORD) && !defined(TOK_LPKEYWORD) && \ - !defined(TOK_LPKEYWORD_PRIM) + !defined(TOK_KEYWORD_DOT) #error Must define one of the TOK_ macros. #endif diff --git a/test/Dialect/BLIF/parse-basic.blif b/test/Dialect/BLIF/parse-basic.blif index 10ba2ad8b81f..1de0b6f84f9b 100644 --- a/test/Dialect/BLIF/parse-basic.blif +++ b/test/Dialect/BLIF/parse-basic.blif @@ -1,13 +1,13 @@ -; RUN: circt-translate -import-blif -verify-diagnostics -split-input-file %s | circt-opt | FileCheck %s +# RUN: circt-translate -import-blif -verify-diagnostics -split-input-file %s | circt-opt | FileCheck %s .model simple .inputs a B .outputs c -.names a b c -11 1 +#.names a b c +#11 1 .end -;// ----- +#// ----- # unamed model .names a b \ From 58f041c0fa61ae2ff58d3e68127aaa56ad4b3f7b Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Tue, 31 Dec 2024 16:13:52 +0000 Subject: [PATCH 11/18] partially parse a .names --- include/circt/Dialect/BLIF/BLIFOps.td | 2 +- lib/Dialect/BLIF/Import/BLIFLexer.h | 3 +- lib/Dialect/BLIF/Import/BLIFParser.cpp | 98 ++++++++++++++++++++-- lib/Dialect/BLIF/Import/BLIFTokenKinds.def | 3 + test/Dialect/BLIF/parse-basic.blif | 10 ++- 5 files changed, 102 insertions(+), 14 deletions(-) diff --git a/include/circt/Dialect/BLIF/BLIFOps.td b/include/circt/Dialect/BLIF/BLIFOps.td index 9bdae21aaae6..1731786beb70 100644 --- a/include/circt/Dialect/BLIF/BLIFOps.td +++ b/include/circt/Dialect/BLIF/BLIFOps.td @@ -59,7 +59,7 @@ def LogicGateOp: BLIFOp<"logic_gate", []> { means don't use the input. }]; - let arguments = (ins IntArrayProperty:$func, + let arguments = (ins /* IntArrayProperty:$func, */ Variadic:$inputs); let results = (outs I1:$result); } diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.h b/lib/Dialect/BLIF/Import/BLIFLexer.h index 10dd159b67cd..cd778b6e55e6 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.h +++ b/lib/Dialect/BLIF/Import/BLIFLexer.h @@ -69,7 +69,8 @@ class BLIFToken { bool isKeyword() const; bool isModelHeaderKeyword() const { - return isAny(kw_inputs, kw_outputs, kw_clock); + return isAny(kw_inputs, kw_outputs, kw_clocks, kw_input, kw_output, + kw_clock); } /// Given a token containing a string literal, return its value, including diff --git a/lib/Dialect/BLIF/Import/BLIFParser.cpp b/lib/Dialect/BLIF/Import/BLIFParser.cpp index 03411272b6b7..8879c0702f3e 100644 --- a/lib/Dialect/BLIF/Import/BLIFParser.cpp +++ b/lib/Dialect/BLIF/Import/BLIFParser.cpp @@ -271,11 +271,14 @@ struct BLIFModelParser : public BLIFParser { explicit BLIFModelParser(Block &blockToInsertInto, BLIFLexer &lexer) : BLIFParser(blockToInsertInto.getParentOp()->getContext(), lexer), builder(UnknownLoc::get(getContext()), getContext()) { - builder.setInsertionPointToEnd(&blockToInsertInto); + builder.setInsertionPoint(&blockToInsertInto, blockToInsertInto.begin()); + unresolved = builder + .create( + builder.getIntegerType(1), ValueRange()) + .getResult(0); } - ParseResult parseSimpleStmt(); - ParseResult parseSimpleStmtBlock(); + ParseResult parseModelBody(); private: ParseResult parseSimpleStmtImpl(); @@ -294,17 +297,54 @@ struct BLIFModelParser : public BLIFParser { ParseResult parseModelReference(); ParseResult parseModelCmdImpl(); ParseResult parseModelCmd(); - ParseResult parseModelBody(); // Helper to fetch a module referenced by an instance-like statement. Value getReferencedModel(SMLoc loc, StringRef modelName); + /// Find the value corrosponding to the named identifier. + Value getNamedValue(StringRef name); + + /// Set the value corrosponding to the named identifier. + void setNamedValue(StringRef name, Value value); + + /// Remember to fix an op whose value could not be resolved + void addFixup(Operation *op, unsigned idx, StringRef name); + // The builder to build into. ImplicitLocOpBuilder builder; + + /// Keep track of names; + DenseMap namedValues; + + struct fixup { + Operation *op; + unsigned idx; + StringRef name; + }; + + DenseMap> fixups; + + Value unresolved; }; } // end anonymous namespace +Value BLIFModelParser::getNamedValue(StringRef name) { + auto &val = namedValues[name]; + if (!val) + val = unresolved; + return val; +} + +void BLIFModelParser::setNamedValue(StringRef name, Value value) { + assert(!namedValues[name] && "name already exists"); + namedValues[name] = value; +} + +void BLIFModelParser::addFixup(Operation *op, unsigned idx, StringRef name) { + fixups[name].push_back({op, idx, name}); +} + Value BLIFModelParser::getReferencedModel(SMLoc loc, StringRef modelName) { return {}; } @@ -320,7 +360,19 @@ ParseResult BLIFModelParser::parseLogicGate() { output = inputs.back(); inputs.pop_back(); // TODO: READ THE TRUTH TABLE - // builder.create(loc, inputs, output); + // Resolve Inputs + SmallVector inputValues; + for (auto input : inputs) + inputValues.push_back(getNamedValue(input)); + // BuildOp + auto op = builder.create(builder.getIntegerType(1), inputValues); + // Record output name + setNamedValue(output, op.getResult()); + // Record Fixups + for (auto [idx, name, val] : llvm::enumerate(inputs, inputValues)) { + if (val == unresolved) + addFixup(op, idx, name); + } return success(); } @@ -394,7 +446,6 @@ ParseResult BLIFModelParser::parseModelCmd() { // Parse the body of this module. ParseResult BLIFModelParser::parseModelBody() { - // auto &body = moduleOp->getRegion(0).front(); while (true) { // The outer level parser can handle these tokens. @@ -449,16 +500,40 @@ ParseResult BLIFFileParser::parseModel() { if (parseIdList(inputs, "expected input list", 1)) return failure(); break; + case BLIFToken::kw_input: { + consumeToken(BLIFToken::kw_input); + StringRef tmp; + if (parseId(tmp, "expected input")) + return failure(); + inputs.push_back(tmp); + break; + } case BLIFToken::kw_outputs: consumeToken(BLIFToken::kw_outputs); if (parseIdList(outputs, "expected output list", 1)) return failure(); break; - case BLIFToken::kw_clock: - consumeToken(BLIFToken::kw_clock); + case BLIFToken::kw_output: { + consumeToken(BLIFToken::kw_output); + StringRef tmp; + if (parseId(tmp, "expected output")) + return failure(); + outputs.push_back(tmp); + break; + } + case BLIFToken::kw_clocks: + consumeToken(BLIFToken::kw_clocks); if (parseIdList(clocks, "expected clock list", 1)) return failure(); break; + case BLIFToken::kw_clock: { + consumeToken(BLIFToken::kw_clock); + StringRef tmp; + if (parseId(tmp, "expected clock")) + return failure(); + clocks.push_back(tmp); + break; + } default: break; } @@ -469,9 +544,14 @@ ParseResult BLIFFileParser::parseModel() { builder.setInsertionPointToEnd(&m.getBody().back()); builder.create(ValueRange()); - if (/*parseModelBody() ||*/ parseToken(BLIFToken::kw_end, "expected .end")) + BLIFModelParser bodyParser(m.getBody().back(), getLexer()); + + if (bodyParser.parseModelBody() || + parseToken(BLIFToken::kw_end, "expected .end")) return failure(); + // Fix up outputs + return success(); } diff --git a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def index 3179b522333f..0d06ac0218f1 100644 --- a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def +++ b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def @@ -83,6 +83,7 @@ TOK_PUNCTUATION(equal_greater, "=>") TOK_KEYWORD_DOT(area) TOK_KEYWORD_DOT(cycle) TOK_KEYWORD_DOT(clock) +TOK_KEYWORD_DOT(clocks) TOK_KEYWORD_DOT(clock_event) TOK_KEYWORD_DOT(default_input_arrival) TOK_KEYWORD_DOT(default_input_drive) @@ -92,11 +93,13 @@ TOK_KEYWORD_DOT(default_output_required) TOK_KEYWORD_DOT(delay) TOK_KEYWORD_DOT(end) TOK_KEYWORD_DOT(end_kiss) +TOK_KEYWORD_DOT(input) TOK_KEYWORD_DOT(inputs) TOK_KEYWORD_DOT(input_arrival) TOK_KEYWORD_DOT(input_drive) TOK_KEYWORD_DOT(max_input_load) TOK_KEYWORD_DOT(model) +TOK_KEYWORD_DOT(output) TOK_KEYWORD_DOT(outputs) TOK_KEYWORD_DOT(output_load) TOK_KEYWORD_DOT(output_required) diff --git a/test/Dialect/BLIF/parse-basic.blif b/test/Dialect/BLIF/parse-basic.blif index 1de0b6f84f9b..7b4fa472bdb1 100644 --- a/test/Dialect/BLIF/parse-basic.blif +++ b/test/Dialect/BLIF/parse-basic.blif @@ -1,9 +1,13 @@ # RUN: circt-translate -import-blif -verify-diagnostics -split-input-file %s | circt-opt | FileCheck %s .model simple -.inputs a B -.outputs c -#.names a b c +.input a +.output b +.clock clk1 +.inputs c d +.outputs e f +.clocks clk2 clk3 +.names a b c #11 1 .end From 3d6fc1c9f7d22475940f1afa0ba47df0c1b75454 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Wed, 1 Jan 2025 17:27:36 +0000 Subject: [PATCH 12/18] fewer vestages --- lib/Dialect/BLIF/Import/BLIFLexer.cpp | 230 ++------------------- lib/Dialect/BLIF/Import/BLIFLexer.h | 5 +- lib/Dialect/BLIF/Import/BLIFParser.cpp | 64 ++++-- lib/Dialect/BLIF/Import/BLIFTokenKinds.def | 53 +---- test/Dialect/BLIF/parse-basic.blif | 6 +- 5 files changed, 87 insertions(+), 271 deletions(-) diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.cpp b/lib/Dialect/BLIF/Import/BLIFLexer.cpp index f215bb6b1669..10c3b97c65d1 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.cpp +++ b/lib/Dialect/BLIF/Import/BLIFLexer.cpp @@ -57,11 +57,6 @@ bool BLIFToken::isKeyword() const { /// Given a token containing a string literal, return its value, including /// removing the quote characters and unescaping the contents of the string. The /// lexer has already verified that this token is valid. -std::string BLIFToken::getStringValue() const { - assert(getKind() == string); - return getStringValue(getSpelling()); -} - std::string BLIFToken::getStringValue(StringRef spelling) { // Start by dropping the quotes. StringRef bytes = spelling.drop_front().drop_back(); @@ -113,14 +108,6 @@ std::string BLIFToken::getStringValue(StringRef spelling) { return result; } -/// Given a token containing a verbatim string, return its value, including -/// removing the quote characters and unescaping the quotes of the string. The -/// lexer has already verified that this token is valid. -std::string BLIFToken::getVerbatimStringValue() const { - assert(getKind() == verbatim_string); - return getVerbatimStringValue(getSpelling()); -} - std::string BLIFToken::getVerbatimStringValue(StringRef spelling) { // Start by dropping the quotes. StringRef bytes = spelling.drop_front().drop_back(); @@ -194,7 +181,7 @@ BLIFToken BLIFLexer::lexTokenImpl() { default: // Handle identifiers. if (llvm::isAlpha(curPtr[-1])) - return lexIdentifierOrKeyword(tokStart); + return lexIdentifier(tokStart); // Unknown character, emit an error. return emitError(tokStart, "unexpected character"); @@ -215,57 +202,12 @@ BLIFToken BLIFLexer::lexTokenImpl() { continue; case '.': - return lexPeriodOrKeyword(tokStart); - case ',': - return formToken(BLIFToken::comma, tokStart); - case ':': - return formToken(BLIFToken::colon, tokStart); - case '(': - return formToken(BLIFToken::l_paren, tokStart); - case ')': - return formToken(BLIFToken::r_paren, tokStart); - case '{': - if (*curPtr == '|') - return ++curPtr, formToken(BLIFToken::l_brace_bar, tokStart); - return formToken(BLIFToken::l_brace, tokStart); - case '}': - return formToken(BLIFToken::r_brace, tokStart); - case '[': - return formToken(BLIFToken::l_square, tokStart); - case ']': - return formToken(BLIFToken::r_square, tokStart); - case '<': - if (*curPtr == '=') - return ++curPtr, formToken(BLIFToken::less_equal, tokStart); - return formToken(BLIFToken::less, tokStart); - case '>': - return formToken(BLIFToken::greater, tokStart); - case '=': - if (*curPtr == '>') - return ++curPtr, formToken(BLIFToken::equal_greater, tokStart); - return formToken(BLIFToken::equal, tokStart); - case '?': - return formToken(BLIFToken::question, tokStart); - case '@': - if (*curPtr == '[') - return lexFileInfo(tokStart); - // Unknown character, emit an error. - return emitError(tokStart, "unexpected character"); - case '|': - if (*curPtr == '}') - return ++curPtr, formToken(BLIFToken::r_brace_bar, tokStart); - // Unknown character, emit an error. - return emitError(tokStart, "unexpected character"); + return lexCommand(tokStart); case '#': skipComment(); continue; - case '"': - return lexString(tokStart, /*isVerbatim=*/false); - case '\'': - return lexString(tokStart, /*isVerbatim=*/true); - case '-': case '+': case '0': @@ -283,116 +225,47 @@ BLIFToken BLIFLexer::lexTokenImpl() { } } -/// Lex a file info specifier. -/// -/// FileInfo ::= '@[' ('\]'|.)* ']' -/// -BLIFToken BLIFLexer::lexFileInfo(const char *tokStart) { - while (1) { - switch (*curPtr++) { - case ']': // This is the end of the fileinfo literal. - return formToken(BLIFToken::fileinfo, tokStart); - case '\\': - // Ignore escaped ']' - if (*curPtr == ']') - ++curPtr; - break; - case 0: - // This could be the end of file in the middle of the fileinfo. If so - // emit an error. - if (curPtr - 1 != curBuffer.end()) - break; - [[fallthrough]]; - case '\n': // Vertical whitespace isn't allowed in a fileinfo. - case '\v': - case '\f': - return emitError(tokStart, "unterminated file info specifier"); - default: - // Skip over other characters. - break; - } - } -} - /// Lex a period or a keyword that starts with a period. /// -/// Period ::= '.' +/// Command :== '.' [a-zA-Z_]+ /// -BLIFToken BLIFLexer::lexPeriodOrKeyword(const char *tokStart) { +BLIFToken BLIFLexer::lexCommand(const char *tokStart) { - // Match the rest of the identifier regex: [a-zA-Z_$-]* - while (llvm::isAlpha(*curPtr) || llvm::isDigit(*curPtr) || *curPtr == '_' || - *curPtr == '$' || *curPtr == '-') + // Match the rest of the command regex: [a-zA-Z_]* + while (llvm::isAlpha(*curPtr) || llvm::isDigit(*curPtr) || *curPtr == '_') ++curPtr; StringRef spelling(tokStart, curPtr - tokStart); - // See if the identifier is a keyword. By default, it is a period. + // See if the identifier is a keyword. BLIFToken::Kind kind = llvm::StringSwitch(spelling) #define TOK_KEYWORD_DOT(SPELLING) .Case("." #SPELLING, BLIFToken::kw_##SPELLING) #include "BLIFTokenKinds.def" - .Default(BLIFToken::period); - if (kind != BLIFToken::period) { + .Default(BLIFToken::error); + if (kind != BLIFToken::error) { ++curPtr; return formToken(kind, tokStart); } // Otherwise, this is a period. - return formToken(BLIFToken::period, tokStart); + return emitError(tokStart, "unexpected character after period"); } -/// Lex an identifier or keyword that starts with a letter. +/// Lex an identifier. /// -/// LegalStartChar ::= [a-zA-Z_] -/// LegalIdChar ::= LegalStartChar | [0-9] | '$' +/// LegalStartChar ::= [a-zA-Z] +/// LegalIdChar ::= LegalStartChar | [0-9] | '$' | '_' /// /// Id ::= LegalStartChar (LegalIdChar)* -/// LiteralId ::= [a-zA-Z0-9$_]+ /// -BLIFToken BLIFLexer::lexIdentifierOrKeyword(const char *tokStart) { - // Remember that this is a literalID - bool isLiteralId = *tokStart == '`'; +BLIFToken BLIFLexer::lexIdentifier(const char *tokStart) { - // Match the rest of the identifier regex: [0-9a-zA-Z_$-]* + // Match the rest of the identifier regex: [0-9a-zA-Z$_]* while (llvm::isAlpha(*curPtr) || llvm::isDigit(*curPtr) || *curPtr == '_' || - *curPtr == '$' || *curPtr == '-') - ++curPtr; - - // Consume the trailing '`' in a literal identifier. - if (isLiteralId) { - if (*curPtr != '`') - return emitError(tokStart, "unterminated literal identifier"); + *curPtr == '$') ++curPtr; - } - StringRef spelling(tokStart, curPtr - tokStart); - - // Check to see if this is a 'primop', which is an identifier juxtaposed with - // a '(' character. - if (*curPtr == '(') { - BLIFToken::Kind kind = llvm::StringSwitch(spelling) -#define TOK_LPKEYWORD(SPELLING) .Case(#SPELLING, BLIFToken::lp_##SPELLING) -#include "BLIFTokenKinds.def" - .Default(BLIFToken::identifier); - if (kind != BLIFToken::identifier) { - ++curPtr; - return formToken(kind, tokStart); - } - } - - // See if the identifier is a keyword. By default, it is an identifier. - BLIFToken::Kind kind = llvm::StringSwitch(spelling) -#define TOK_KEYWORD(SPELLING) .Case(#SPELLING, BLIFToken::kw_##SPELLING) -#include "BLIFTokenKinds.def" - .Default(BLIFToken::identifier); - - // If this has the backticks of a literal identifier and it fell through the - // above switch, indicating that it was not found to e a keyword, then change - // its kind from identifier to literal identifier. - if (isLiteralId && kind == BLIFToken::identifier) - kind = BLIFToken::literal_identifier; - - return BLIFToken(kind, spelling); + return formToken(BLIFToken::identifier, tokStart); } /// Skip a comment line, starting with a ';' and going to end of line. @@ -417,48 +290,6 @@ void BLIFLexer::skipComment() { } } -/// StringLit ::= '"' UnquotedString? '"' -/// VerbatimStringLit ::= '\'' UnquotedString? '\'' -/// UnquotedString ::= ( '\\\'' | '\\"' | ~[\r\n] )+? -/// -BLIFToken BLIFLexer::lexString(const char *tokStart, bool isVerbatim) { - while (1) { - switch (*curPtr++) { - case '"': // This is the end of the string literal. - if (isVerbatim) - break; - return formToken(BLIFToken::string, tokStart); - case '\'': // This is the end of the raw string. - if (!isVerbatim) - break; - return formToken(BLIFToken::verbatim_string, tokStart); - case '\\': - // Ignore escaped '\'' or '"' - if (*curPtr == '\'' || *curPtr == '"' || *curPtr == '\\') - ++curPtr; - else if (*curPtr == 'u' || *curPtr == 'U') - return emitError(tokStart, "unicode escape not supported in string"); - break; - case 0: - // This could be the end of file in the middle of the string. If so - // emit an error. - if (curPtr - 1 != curBuffer.end()) - break; - [[fallthrough]]; - case '\n': // Vertical whitespace isn't allowed in a string. - case '\r': - case '\v': - case '\f': - return emitError(tokStart, "unterminated string"); - default: - if (curPtr[-1] & ~0x7F) - return emitError(tokStart, "string characters must be 7-bit ASCII"); - // Skip over other characters. - break; - } - } -} - /// Lex a number literal. /// /// UnsignedInt ::= '0' | PosInt @@ -521,31 +352,4 @@ BLIFToken BLIFLexer::lexNumber(const char *tokStart) { return formToken(BLIFToken::signed_integer, tokStart); return formToken(BLIFToken::integer, tokStart); } - - // Lex a floating point literal. - curPtr += 2; - while (llvm::isDigit(*curPtr)) - ++curPtr; - - bool hasE = false; - if (*curPtr == 'E') { - hasE = true; - ++curPtr; - if (*curPtr == '+' || *curPtr == '-') - ++curPtr; - while (llvm::isDigit(*curPtr)) - ++curPtr; - } - - // If we encounter a '.' followed by a digit, again, and there was no - // exponent, then this is a version literal. Otherwise it is a floating point - // literal. - if (*curPtr != '.' || !llvm::isDigit(curPtr[1]) || hasE) - return formToken(BLIFToken::floatingpoint, tokStart); - - // Lex a version literal. - curPtr += 2; - while (llvm::isDigit(*curPtr)) - ++curPtr; - return formToken(BLIFToken::version, tokStart); } diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.h b/lib/Dialect/BLIF/Import/BLIFLexer.h index cd778b6e55e6..783c9a8bf582 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.h +++ b/lib/Dialect/BLIF/Import/BLIFLexer.h @@ -130,12 +130,11 @@ class BLIFLexer { // Lexer implementation methods. BLIFToken lexFileInfo(const char *tokStart); - BLIFToken lexInlineAnnotation(const char *tokStart); - BLIFToken lexIdentifierOrKeyword(const char *tokStart); + BLIFToken lexIdentifier(const char *tokStart); BLIFToken lexNumber(const char *tokStart); void skipComment(); BLIFToken lexString(const char *tokStart, bool isVerbatim); - BLIFToken lexPeriodOrKeyword(const char *tokStart); + BLIFToken lexCommand(const char *tokStart); const llvm::SourceMgr &sourceMgr; const mlir::StringAttr bufferNameIdentifier; diff --git a/lib/Dialect/BLIF/Import/BLIFParser.cpp b/lib/Dialect/BLIF/Import/BLIFParser.cpp index 8879c0702f3e..b7f92a243048 100644 --- a/lib/Dialect/BLIF/Import/BLIFParser.cpp +++ b/lib/Dialect/BLIF/Import/BLIFParser.cpp @@ -276,9 +276,18 @@ struct BLIFModelParser : public BLIFParser { .create( builder.getIntegerType(1), ValueRange()) .getResult(0); + ModelOp model = cast(blockToInsertInto.getParentOp()); + int idx = 0; + for (auto port : model.getModuleType().getPorts()) { + if (port.dir == hw::ModulePort::Direction::Input) + setNamedValue(port.name.getValue(), + blockToInsertInto.getArgument(idx++)); + } } ParseResult parseModelBody(); + ParseResult fixupUnresolvedValues(); + ParseResult fixupOutput(); private: ParseResult parseSimpleStmtImpl(); @@ -290,6 +299,8 @@ struct BLIFModelParser : public BLIFParser { return parseExpImpl(result, message, /*isLeadingStmt:*/ false); } + ParseResult parseTruthLine(); + ParseResult parseTruthTable(); ParseResult parseLogicGate(); ParseResult parseLatch(); ParseResult parseLibraryLogicGate(); @@ -319,7 +330,6 @@ struct BLIFModelParser : public BLIFParser { struct fixup { Operation *op; unsigned idx; - StringRef name; }; DenseMap> fixups; @@ -330,10 +340,8 @@ struct BLIFModelParser : public BLIFParser { } // end anonymous namespace Value BLIFModelParser::getNamedValue(StringRef name) { - auto &val = namedValues[name]; - if (!val) - val = unresolved; - return val; + auto val = namedValues.lookup(name); + return val ? val : unresolved; } void BLIFModelParser::setNamedValue(StringRef name, Value value) { @@ -342,19 +350,23 @@ void BLIFModelParser::setNamedValue(StringRef name, Value value) { } void BLIFModelParser::addFixup(Operation *op, unsigned idx, StringRef name) { - fixups[name].push_back({op, idx, name}); + fixups[name].push_back({op, idx}); } Value BLIFModelParser::getReferencedModel(SMLoc loc, StringRef modelName) { return {}; } +ParseResult BLIFModelParser::parseTruthLine() { return failure(); } + +ParseResult BLIFModelParser::parseTruthTable() { return failure(); } + /// logicgate ::= '.names' in* out NEWLINE single_output_cover* ParseResult BLIFModelParser::parseLogicGate() { auto startTok = consumeToken(BLIFToken::kw_names); auto loc = startTok.getLoc(); SmallVector inputs; - std::string output; + StringRef output; if (parseIdList(inputs, "expected input list", 1)) return failure(); output = inputs.back(); @@ -460,6 +472,37 @@ ParseResult BLIFModelParser::parseModelBody() { return success(); } +ParseResult BLIFModelParser::fixupUnresolvedValues() { + for (auto &fixup : fixups) { + Value val = getNamedValue(fixup.first); + if (val == unresolved) + return emitError("unresolved value '") << fixup.first << "'"; + for (auto [op, idx] : fixup.second) { + op->setOperand(idx, val); + } + } + return success(); +} + +ParseResult BLIFModelParser::fixupOutput() { + auto model = cast(builder.getBlock()->getParentOp()); + SmallVector outputs; + for (auto port : model.getModuleType().getPorts()) { + if (port.dir == hw::ModulePort::Direction::Output) { + auto val = getNamedValue(port.name.getValue()); + if (val == unresolved) + emitWarning("unresolved output '") << port.name.getValue() << "'"; + outputs.push_back(val); + } + } + builder.create(outputs); + if (unresolved && unresolved.use_empty()) { + unresolved.getDefiningOp()->erase(); + unresolved = nullptr; + } + return success(); +} + //===----------------------------------------------------------------------===// // BLIFFileParser //===----------------------------------------------------------------------===// @@ -541,12 +584,9 @@ ParseResult BLIFFileParser::parseModel() { // Create the model auto m = builder.create(name, inputs, outputs, clocks); OpBuilder::InsertionGuard guard(builder); - builder.setInsertionPointToEnd(&m.getBody().back()); - builder.create(ValueRange()); - BLIFModelParser bodyParser(m.getBody().back(), getLexer()); - - if (bodyParser.parseModelBody() || + if (bodyParser.parseModelBody() || bodyParser.fixupUnresolvedValues() || + bodyParser.fixupOutput() || parseToken(BLIFToken::kw_end, "expected .end")) return failure(); diff --git a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def index 0d06ac0218f1..22c531bef095 100644 --- a/lib/Dialect/BLIF/Import/BLIFTokenKinds.def +++ b/lib/Dialect/BLIF/Import/BLIFTokenKinds.def @@ -13,8 +13,7 @@ #if !defined(TOK_MARKER) && !defined(TOK_IDENTIFIER) && \ !defined(TOK_LITERAL) && !defined(TOK_PUNCTUATION) && \ - !defined(TOK_KEYWORD) && !defined(TOK_LPKEYWORD) && \ - !defined(TOK_KEYWORD_DOT) + !defined(TOK_KEYWORD) && !defined(TOK_KEYWORD_DOT) #error Must define one of the TOK_ macros. #endif @@ -43,43 +42,20 @@ TOK_MARKER(error) // Identifiers. TOK_IDENTIFIER(identifier) // foo -TOK_IDENTIFIER(literal_identifier) // `42` // Literals TOK_LITERAL(integer) // 42 TOK_LITERAL(signed_integer) // -42 and +42 TOK_LITERAL(radix_specified_integer) // 0b101010, 0o52, 0d42, 0h2a and negations -TOK_LITERAL(floatingpoint) // 42.0 -TOK_LITERAL(version) // 1.2.3 -TOK_LITERAL(string) // "foo" -TOK_LITERAL(verbatim_string) // 'foo' - -TOK_LITERAL(fileinfo) - -// Punctuation. -TOK_PUNCTUATION(period, ".") -TOK_PUNCTUATION(comma, ",") -TOK_PUNCTUATION(colon, ":") -TOK_PUNCTUATION(question, "?") -TOK_PUNCTUATION(l_paren, "(") -TOK_PUNCTUATION(r_paren, ")") -TOK_PUNCTUATION(l_brace, "{") -TOK_PUNCTUATION(l_brace_bar, "{|") -TOK_PUNCTUATION(r_brace, "}") -TOK_PUNCTUATION(r_brace_bar, "|}") -TOK_PUNCTUATION(l_square, "[") -TOK_PUNCTUATION(r_square, "]") -TOK_PUNCTUATION(less, "<") -TOK_PUNCTUATION(less_equal, "<=") -TOK_PUNCTUATION(greater, ">") -TOK_PUNCTUATION(equal, "=") -TOK_PUNCTUATION(equal_greater, "=>") +TOK_LITERAL(cover) // 0--1- +TOK_LITERAL(bit) // 0 || 1 // Keywords. These turn "foo" into FIRToken::kw_foo enums. // NOTE: Please key these alphabetized to make it easier to find something in // this list and to cater to OCD. +// Keywords which start with '.'. These are separated as the kw_foo must be formed from '.foo'. TOK_KEYWORD_DOT(area) TOK_KEYWORD_DOT(cycle) TOK_KEYWORD_DOT(clock) @@ -93,37 +69,30 @@ TOK_KEYWORD_DOT(default_output_required) TOK_KEYWORD_DOT(delay) TOK_KEYWORD_DOT(end) TOK_KEYWORD_DOT(end_kiss) +TOK_KEYWORD_DOT(exdc) +TOK_KEYWORD_DOT(gate) TOK_KEYWORD_DOT(input) TOK_KEYWORD_DOT(inputs) TOK_KEYWORD_DOT(input_arrival) TOK_KEYWORD_DOT(input_drive) +TOK_KEYWORD_DOT(latch) TOK_KEYWORD_DOT(max_input_load) +TOK_KEYWORD_DOT(mlatch) TOK_KEYWORD_DOT(model) +TOK_KEYWORD_DOT(names) TOK_KEYWORD_DOT(output) TOK_KEYWORD_DOT(outputs) TOK_KEYWORD_DOT(output_load) TOK_KEYWORD_DOT(output_required) TOK_KEYWORD_DOT(search) +TOK_KEYWORD_DOT(start_kiss) +TOK_KEYWORD_DOT(subskt) TOK_KEYWORD_DOT(wire) TOK_KEYWORD_DOT(wire_load_slope) - - -// These are for KEYWORD cases that correspond to a primitive operation. - -TOK_KEYWORD_DOT(names) -TOK_KEYWORD_DOT(exdc) -TOK_KEYWORD_DOT(latch) -TOK_KEYWORD_DOT(gate) -TOK_KEYWORD_DOT(mlatch) -TOK_KEYWORD_DOT(subskt) -TOK_KEYWORD_DOT(start_kiss) - - #undef TOK_MARKER #undef TOK_IDENTIFIER #undef TOK_LITERAL #undef TOK_PUNCTUATION #undef TOK_KEYWORD #undef TOK_KEYWORD_DOT -#undef TOK_LPKEYWORD_PRIM diff --git a/test/Dialect/BLIF/parse-basic.blif b/test/Dialect/BLIF/parse-basic.blif index 7b4fa472bdb1..a4f89bd7bfdb 100644 --- a/test/Dialect/BLIF/parse-basic.blif +++ b/test/Dialect/BLIF/parse-basic.blif @@ -7,8 +7,12 @@ .inputs c d .outputs e f .clocks clk2 clk3 -.names a b c +.names a c b #11 1 +.names foo1 foo2 e # use before def +.names c d foo1 +.names clk1 clk2 clk3 foo2 +.names foo1 foo2 f .end #// ----- From 604b9bcd9f2513e21c2f99c4c451db2025f81e0f Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Fri, 3 Jan 2025 16:42:06 +0000 Subject: [PATCH 13/18] handle truth tables --- include/circt/Dialect/BLIF/BLIFOps.td | 2 +- lib/Dialect/BLIF/Import/BLIFLexer.cpp | 86 +++++++++++--------------- lib/Dialect/BLIF/Import/BLIFLexer.h | 1 + lib/Dialect/BLIF/Import/BLIFParser.cpp | 75 ++++++++++++++++++++-- test/Dialect/BLIF/parse-basic.blif | 19 +++--- 5 files changed, 119 insertions(+), 64 deletions(-) diff --git a/include/circt/Dialect/BLIF/BLIFOps.td b/include/circt/Dialect/BLIF/BLIFOps.td index 1731786beb70..7a8100ad15aa 100644 --- a/include/circt/Dialect/BLIF/BLIFOps.td +++ b/include/circt/Dialect/BLIF/BLIFOps.td @@ -59,7 +59,7 @@ def LogicGateOp: BLIFOp<"logic_gate", []> { means don't use the input. }]; - let arguments = (ins /* IntArrayProperty:$func, */ + let arguments = (ins ArrayProperty>:$func, Variadic:$inputs); let results = (outs I1:$result); } diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.cpp b/lib/Dialect/BLIF/Import/BLIFLexer.cpp index 10c3b97c65d1..881628533242 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.cpp +++ b/lib/Dialect/BLIF/Import/BLIFLexer.cpp @@ -194,9 +194,21 @@ BLIFToken BLIFLexer::lexTokenImpl() { [[fallthrough]]; // Treat as whitespace. + case '\\': + // Handle line continuations. + if (*curPtr == '\r') { + ++curPtr; + } + if (*curPtr == '\n') { + ++curPtr; + continue; + } + + case '\n': + // return formToken(BLIFToken::newline, tokStart); + case ' ': case '\t': - case '\n': case '\r': // Handle whitespace. continue; @@ -209,9 +221,10 @@ BLIFToken BLIFLexer::lexTokenImpl() { continue; case '-': - case '+': case '0': case '1': + return lexNumberOrCover(tokStart); + case '2': case '3': case '4': @@ -268,7 +281,7 @@ BLIFToken BLIFLexer::lexIdentifier(const char *tokStart) { return formToken(BLIFToken::identifier, tokStart); } -/// Skip a comment line, starting with a ';' and going to end of line. +/// Skip a comment line, starting with a '#' and going to end of line. void BLIFLexer::skipComment() { while (true) { switch (*curPtr++) { @@ -294,62 +307,37 @@ void BLIFLexer::skipComment() { /// /// UnsignedInt ::= '0' | PosInt /// PosInt ::= [1-9] ([0-9])* -/// DoubleLit ::= -/// ( '+' | '-' )? Digit+ '.' Digit+ ( 'E' ( '+' | '-' )? Digit+ )? -/// TripleLit ::= -/// Digit+ '.' Digit+ '.' Digit+ -/// Radix-specified Integer ::= -/// ( '-' )? '0' ( 'b' | 'o' | 'd' | 'h' ) LegalDigit* /// BLIFToken BLIFLexer::lexNumber(const char *tokStart) { - assert(llvm::isDigit(curPtr[-1]) || curPtr[-1] == '+' || curPtr[-1] == '-'); + assert(llvm::isDigit(curPtr[-1]) || curPtr[-1] == '-'); // There needs to be at least one digit. if (!llvm::isDigit(*curPtr) && !llvm::isDigit(curPtr[-1])) return emitError(tokStart, "unexpected character after sign"); - // If we encounter a "b", "o", "d", or "h", this is a radix-specified integer - // literal. This is only supported for BLIFRTL 2.4.0 or later. This is - // always lexed, but rejected during parsing if the version is too old. - const char *oldPtr = curPtr; - if (curPtr[-1] == '-' && *curPtr == '0') + while (llvm::isDigit(*curPtr)) ++curPtr; - if (curPtr[-1] == '0') { - switch (*curPtr) { - case 'b': - ++curPtr; - while (*curPtr >= '0' && *curPtr <= '1') - ++curPtr; - return formToken(BLIFToken::radix_specified_integer, tokStart); - case 'o': - ++curPtr; - while (*curPtr >= '0' && *curPtr <= '7') - ++curPtr; - return formToken(BLIFToken::radix_specified_integer, tokStart); - case 'd': - ++curPtr; - while (llvm::isDigit(*curPtr)) - ++curPtr; - return formToken(BLIFToken::radix_specified_integer, tokStart); - case 'h': - ++curPtr; - while (llvm::isHexDigit(*curPtr)) - ++curPtr; - return formToken(BLIFToken::radix_specified_integer, tokStart); - default: - curPtr = oldPtr; - break; - } - } - while (llvm::isDigit(*curPtr)) + return formToken(BLIFToken::integer, tokStart); +} + +/// Lex a number literal or a cover literal +/// +/// +/// Cover ::= [0-9\-]* +/// +BLIFToken BLIFLexer::lexNumberOrCover(const char *tokStart) { + while (llvm::isDigit(*curPtr) || *curPtr == '-') ++curPtr; - // If we encounter a '.' followed by a digit, then this is a floating point - // literal, otherwise this is an integer or negative integer. - if (*curPtr != '.' || !llvm::isDigit(curPtr[1])) { - if (*tokStart == '-' || *tokStart == '+') - return formToken(BLIFToken::signed_integer, tokStart); - return formToken(BLIFToken::integer, tokStart); + StringRef spelling(tokStart, curPtr - tokStart); + if (spelling.contains('2') || spelling.contains('3') || + spelling.contains('4') || spelling.contains('5') || + spelling.contains('6') || spelling.contains('7') || + spelling.contains('8') || spelling.contains('9') || + !spelling.contains('-')) { + curPtr = tokStart + 1; + return lexNumber(tokStart); } + return formToken(BLIFToken::integer, tokStart); } diff --git a/lib/Dialect/BLIF/Import/BLIFLexer.h b/lib/Dialect/BLIF/Import/BLIFLexer.h index 783c9a8bf582..0742c2d8746a 100644 --- a/lib/Dialect/BLIF/Import/BLIFLexer.h +++ b/lib/Dialect/BLIF/Import/BLIFLexer.h @@ -132,6 +132,7 @@ class BLIFLexer { BLIFToken lexFileInfo(const char *tokStart); BLIFToken lexIdentifier(const char *tokStart); BLIFToken lexNumber(const char *tokStart); + BLIFToken lexNumberOrCover(const char *tokStart); void skipComment(); BLIFToken lexString(const char *tokStart, bool isVerbatim); BLIFToken lexCommand(const char *tokStart); diff --git a/lib/Dialect/BLIF/Import/BLIFParser.cpp b/lib/Dialect/BLIF/Import/BLIFParser.cpp index b7f92a243048..b663ce5e8fec 100644 --- a/lib/Dialect/BLIF/Import/BLIFParser.cpp +++ b/lib/Dialect/BLIF/Import/BLIFParser.cpp @@ -299,8 +299,9 @@ struct BLIFModelParser : public BLIFParser { return parseExpImpl(result, message, /*isLeadingStmt:*/ false); } - ParseResult parseTruthLine(); - ParseResult parseTruthTable(); + ParseResult parseOptionalTruthLine(StringRef &input, bool &output); + ParseResult parseTruthTable(SmallVectorImpl &inputs, + SmallVectorImpl &outputs); ParseResult parseLogicGate(); ParseResult parseLatch(); ParseResult parseLibraryLogicGate(); @@ -357,9 +358,49 @@ Value BLIFModelParser::getReferencedModel(SMLoc loc, StringRef modelName) { return {}; } -ParseResult BLIFModelParser::parseTruthLine() { return failure(); } +ParseResult BLIFModelParser::parseOptionalTruthLine(StringRef &input, + bool &output) { + switch (getToken().getKind()) { + case BLIFToken::cover: + case BLIFToken::integer: + input = getTokenSpelling(); + consumeToken(); + break; + default: + return failure(); + } + + switch (getToken().getKind()) { + case BLIFToken::integer: + if (getTokenSpelling() == "0") { + consumeToken(); + output = false; + return success(); + } + if (getTokenSpelling() == "1") { + consumeToken(); + output = true; + return success(); + } + break; + default: + break; + } -ParseResult BLIFModelParser::parseTruthTable() { return failure(); } + return failure(); +} + +ParseResult BLIFModelParser::parseTruthTable(SmallVectorImpl &inputs, + SmallVectorImpl &outputs) { + StringRef input; + bool output; + while (succeeded(parseOptionalTruthLine(input, output))) { + inputs.push_back(input); + outputs.push_back(output); + } + + return success(); +} /// logicgate ::= '.names' in* out NEWLINE single_output_cover* ParseResult BLIFModelParser::parseLogicGate() { @@ -371,13 +412,35 @@ ParseResult BLIFModelParser::parseLogicGate() { return failure(); output = inputs.back(); inputs.pop_back(); - // TODO: READ THE TRUTH TABLE + // Deal with truth table + SmallVector covers; + SmallVector sets; + if (parseTruthTable(covers, sets)) + return failure(); + + SmallVector> truthTable; + for (auto [i, o] : llvm::zip_equal(covers, sets)) { + SmallVector row; + for (auto c : i) + if (c == '0') + row.push_back(0); + else if (c == '1') + row.push_back(1); + else if (c == '-') + row.push_back(2); + else + return emitError("invalid truth table value '") << c << "'"; + row.push_back(o); + truthTable.push_back(row); + } + // Resolve Inputs SmallVector inputValues; for (auto input : inputs) inputValues.push_back(getNamedValue(input)); // BuildOp - auto op = builder.create(builder.getIntegerType(1), inputValues); + auto op = builder.create(builder.getIntegerType(1), truthTable, + inputValues); // Record output name setNamedValue(output, op.getResult()); // Record Fixups diff --git a/test/Dialect/BLIF/parse-basic.blif b/test/Dialect/BLIF/parse-basic.blif index a4f89bd7bfdb..0cf9136a1f66 100644 --- a/test/Dialect/BLIF/parse-basic.blif +++ b/test/Dialect/BLIF/parse-basic.blif @@ -1,5 +1,6 @@ -# RUN: circt-translate -import-blif -verify-diagnostics -split-input-file %s | circt-opt | FileCheck %s +# RUN: circt-translate -import-blif -verify-diagnostics %s | circt-opt | FileCheck %s +# CHECK: blif.model .model simple .input a .output b @@ -7,18 +8,20 @@ .inputs c d .outputs e f .clocks clk2 clk3 +# CHECK: blif.logic_gate +# CHECK-SAME: func = [array, array, array] .names a c b -#11 1 +11 1 +0- 1 +-0 1 .names foo1 foo2 e # use before def +00 1 .names c d foo1 +-0 1 .names clk1 clk2 clk3 foo2 +1-0 0 .names foo1 foo2 f +-1 1 .end -#// ----- - -# unamed model -.names a b \ -c # test \ -11 1 From 2e7d28ef1822eccfcc49a0273b6f023aee6f5a00 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Fri, 3 Jan 2025 20:07:21 +0000 Subject: [PATCH 14/18] parse latches --- include/circt/Dialect/BLIF/BLIFDialect.h | 1 + include/circt/Dialect/BLIF/BLIFOps.td | 16 +++++ include/circt/Dialect/BLIF/CMakeLists.txt | 4 ++ lib/Dialect/BLIF/BLIFDialect.cpp | 3 + lib/Dialect/BLIF/CMakeLists.txt | 6 +- lib/Dialect/BLIF/Import/BLIFParser.cpp | 84 +++++++++++++++++------ test/Dialect/BLIF/parse-basic.blif | 9 ++- 7 files changed, 97 insertions(+), 26 deletions(-) diff --git a/include/circt/Dialect/BLIF/BLIFDialect.h b/include/circt/Dialect/BLIF/BLIFDialect.h index 8b9e0f25d913..4cf98d7d63a3 100644 --- a/include/circt/Dialect/BLIF/BLIFDialect.h +++ b/include/circt/Dialect/BLIF/BLIFDialect.h @@ -19,5 +19,6 @@ // Pull in the dialect definition. #include "circt/Dialect/BLIF/BLIFDialect.h.inc" +#include "circt/Dialect/BLIF/BLIFEnums.h.inc" #endif // CIRCT_DIALECT_BLIF_BLIFDIALECT_H diff --git a/include/circt/Dialect/BLIF/BLIFOps.td b/include/circt/Dialect/BLIF/BLIFOps.td index 7a8100ad15aa..6d0989dfbe41 100644 --- a/include/circt/Dialect/BLIF/BLIFOps.td +++ b/include/circt/Dialect/BLIF/BLIFOps.td @@ -15,6 +15,7 @@ include "circt/Dialect/BLIF/BLIFDialect.td" include "circt/Dialect/HW/HWTypes.td" +include "mlir/IR/EnumAttr.td" include "mlir/IR/RegionKindInterface.td" def ModelOp : BLIFOp<"model", [IsolatedFromAbove, RegionKindInterface, SingleBlockImplicitTerminator<"OutputOp">]> { @@ -64,7 +65,22 @@ def LogicGateOp: BLIFOp<"logic_gate", []> { let results = (outs I1:$result); } +def LatchMode : I32EnumAttr< + "LatchModeEnum", "Latch Mode", [ + I32EnumAttrCase<"Unspecified", 0, "un">, + I32EnumAttrCase<"FallingEdge", 1, "fe">, + I32EnumAttrCase<"RisingEdge", 2, "re">, + I32EnumAttrCase<"ActiveHigh", 3, "ah">, + I32EnumAttrCase<"ActiveLow", 4, "al">, + I32EnumAttrCase<"Asynchronous", 5, "as"> + ]> { + let cppNamespace = "circt::blif"; + } + def LatchGateOp: BLIFOp<"latch_gate", []> { + + let arguments = (ins I1:$input, LatchMode:$mode, Optional:$clock, I32Property:$initVal); + let results = (outs I1:$output); } #endif // CIRCT_DIALECT_BLIF_BLIFOPS_TD diff --git a/include/circt/Dialect/BLIF/CMakeLists.txt b/include/circt/Dialect/BLIF/CMakeLists.txt index 0c7b6927059c..dda7b7fe9c89 100644 --- a/include/circt/Dialect/BLIF/CMakeLists.txt +++ b/include/circt/Dialect/BLIF/CMakeLists.txt @@ -1,3 +1,7 @@ add_circt_dialect(BLIF blif) set(LLVM_TARGET_DEFINITIONS BLIF.td) +mlir_tablegen(BLIFEnums.h.inc -gen-enum-decls) +mlir_tablegen(BLIFEnums.cpp.inc -gen-enum-defs) +add_public_tablegen_target(CIRCTBLIFEnumsIncGen) +add_dependencies(circt-headers CIRCTBLIFEnumsIncGen) diff --git a/lib/Dialect/BLIF/BLIFDialect.cpp b/lib/Dialect/BLIF/BLIFDialect.cpp index 4a4efdc75eb8..1de1e0e35961 100644 --- a/lib/Dialect/BLIF/BLIFDialect.cpp +++ b/lib/Dialect/BLIF/BLIFDialect.cpp @@ -30,4 +30,7 @@ void BLIFDialect::initialize() { >(); } +// Provide implementations for the enums we use. +#include "circt/Dialect/BLIF/BLIFEnums.cpp.inc" + #include "circt/Dialect/BLIF/BLIFDialect.cpp.inc" diff --git a/lib/Dialect/BLIF/CMakeLists.txt b/lib/Dialect/BLIF/CMakeLists.txt index e63b1b125b99..c0b6e873d15d 100644 --- a/lib/Dialect/BLIF/CMakeLists.txt +++ b/lib/Dialect/BLIF/CMakeLists.txt @@ -7,6 +7,7 @@ add_circt_dialect_library(CIRCTBLIF DEPENDS MLIRBLIFIncGen + CIRCTBLIFEnumsIncGen LINK_COMPONENTS Support @@ -16,6 +17,9 @@ add_circt_dialect_library(CIRCTBLIF MLIRInferTypeOpInterface ) -add_dependencies(circt-headers MLIRBLIFIncGen) +add_dependencies(circt-headers + MLIRBLIFIncGen + CIRCTBLIFEnumsIncGen + ) add_subdirectory(Import) diff --git a/lib/Dialect/BLIF/Import/BLIFParser.cpp b/lib/Dialect/BLIF/Import/BLIFParser.cpp index b663ce5e8fec..56e5026a8821 100644 --- a/lib/Dialect/BLIF/Import/BLIFParser.cpp +++ b/lib/Dialect/BLIF/Import/BLIFParser.cpp @@ -148,14 +148,10 @@ struct BLIFParser { // Common Parser Rules //===--------------------------------------------------------------------===// - /// Parse 'intLit' into the specified value. - ParseResult parseIntLit(APInt &result, const Twine &message); - ParseResult parseIntLit(int64_t &result, const Twine &message); - ParseResult parseIntLit(int32_t &result, const Twine &message); - // Parse the 'id' grammar, which is an identifier or an allowed keyword. ParseResult parseId(StringRef &result, const Twine &message); ParseResult parseId(StringAttr &result, const Twine &message); + // Parse the 'id' grammar or the string literal "NIL" ParseResult parseIdOrNil(StringRef &result, const Twine &message); ParseResult parseIdOrNil(StringAttr &result, const Twine &message); @@ -163,6 +159,8 @@ struct BLIFParser { ParseResult parseIdList(SmallVectorImpl &result, const Twine &message, unsigned minCount = 0); + ParseResult parseOptionalInt(int &result); + private: BLIFParser(const BLIFParser &) = delete; void operator=(const BLIFParser &) = delete; @@ -260,6 +258,22 @@ ParseResult BLIFParser::parseIdList(SmallVectorImpl &result, } } +/// id ::= Id +/// +/// Parse the 'id' grammar, which is a trivial string. On +/// success, this returns the identifier in the result attribute. +ParseResult BLIFParser::parseOptionalInt(int &result) { + if (getToken().getKind() == BLIFToken::integer) { + if (getTokenSpelling().getAsInteger(10, result)) { + emitError("invalid integer value"); + return failure(); + } + consumeToken(); + return success(); + } + return failure(); +} + //===----------------------------------------------------------------------===// // BLIFModelParser //===----------------------------------------------------------------------===// @@ -292,17 +306,11 @@ struct BLIFModelParser : public BLIFParser { private: ParseResult parseSimpleStmtImpl(); - // Exp Parsing - ParseResult parseExpImpl(Value &result, const Twine &message, - bool isLeadingStmt); - ParseResult parseExp(Value &result, const Twine &message) { - return parseExpImpl(result, message, /*isLeadingStmt:*/ false); - } - ParseResult parseOptionalTruthLine(StringRef &input, bool &output); ParseResult parseTruthTable(SmallVectorImpl &inputs, SmallVectorImpl &outputs); ParseResult parseLogicGate(); + ParseResult parseOptionalLatchType(LatchModeEnum &lType, StringRef &name); ParseResult parseLatch(); ParseResult parseLibraryLogicGate(); ParseResult parseLibraryLatch(); @@ -404,8 +412,7 @@ ParseResult BLIFModelParser::parseTruthTable(SmallVectorImpl &inputs, /// logicgate ::= '.names' in* out NEWLINE single_output_cover* ParseResult BLIFModelParser::parseLogicGate() { - auto startTok = consumeToken(BLIFToken::kw_names); - auto loc = startTok.getLoc(); + consumeToken(BLIFToken::kw_names); SmallVector inputs; StringRef output; if (parseIdList(inputs, "expected input list", 1)) @@ -451,11 +458,29 @@ ParseResult BLIFModelParser::parseLogicGate() { return success(); } +ParseResult BLIFModelParser::parseOptionalLatchType(LatchModeEnum &lType, + StringRef &clkName) { + if (getToken().getKind() == BLIFToken::identifier) { + lType = llvm::StringSwitch(getToken().getSpelling()) + .Case("fe", LatchModeEnum::FallingEdge) + .Case("re", LatchModeEnum::RisingEdge) + .Case("ah", LatchModeEnum::ActiveHigh) + .Case("al", LatchModeEnum::ActiveLow) + .Case("as", LatchModeEnum::Asynchronous) + .Default(LatchModeEnum::Unspecified); + if (lType == LatchModeEnum::Unspecified) { + return success(); + } + consumeToken(); + return parseId(clkName, "Expected clock signal"); + } + return success(); +} + /// latch ::= '.latch' id_in id_out [latch_type [id | "NIL"]]? [ '0' | '1' | '2' /// | '3']? ParseResult BLIFModelParser::parseLatch() { - auto startTok = consumeToken(BLIFToken::kw_latch); - auto loc = startTok.getLoc(); + consumeToken(BLIFToken::kw_latch); StringRef input; StringRef output; // latchType lType; @@ -464,14 +489,30 @@ ParseResult BLIFModelParser::parseLatch() { if (parseId(input, "Expected input signal") || parseId(output, "Expected output signal")) return failure(); - // TODO: Latch type - // if (parseOptionalInt(init_val)) - // return failure(); + + LatchModeEnum lType = LatchModeEnum::Unspecified; + StringRef clkName; + if (parseOptionalLatchType(lType, clkName)) + return emitError("invalid latch type"), failure(); + parseOptionalInt(init_val); + if (init_val < 0 || init_val > 3) return emitError("invalid initial latch value '") << init_val << "'", failure(); - // builder.create(loc, input, output/*, lType*/, clock, - // init_val); + + Value inVal = getNamedValue(input); + Value clkVal = clkName == "NIL" | lType == LatchModeEnum::Unspecified + ? Value() + : getNamedValue(clkName); + + auto op = builder.create(builder.getIntegerType(1), inVal, lType, + clkVal, init_val); + // Record output name + setNamedValue(output, op.getResult()); + // Record Fixups + if (inVal == unresolved) + addFixup(op, 0, input); + return success(); } @@ -594,7 +635,6 @@ struct BLIFFileParser : public BLIFParser { /// model_cmds* `.end` ParseResult BLIFFileParser::parseModel() { StringAttr name; - auto modLoc = getToken().getLoc(); SmallVector inputs, outputs, clocks; consumeToken(BLIFToken::kw_model); if (parseId(name, "expected model name")) diff --git a/test/Dialect/BLIF/parse-basic.blif b/test/Dialect/BLIF/parse-basic.blif index 0cf9136a1f66..e45deab80d24 100644 --- a/test/Dialect/BLIF/parse-basic.blif +++ b/test/Dialect/BLIF/parse-basic.blif @@ -20,8 +20,11 @@ -0 1 .names clk1 clk2 clk3 foo2 1-0 0 -.names foo1 foo2 f +.latch foo2 foo3 +.latch foo3 foo4 2 +.latch foo4 foo5 al NIL +.latch foo5 foo6 fe a +.latch foo6 foo7 re a 1 +.names foo1 foo7 f -1 1 .end - - From 625503604b2ee31e3d73406fdfffe25187471639 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Fri, 3 Jan 2025 21:57:01 +0000 Subject: [PATCH 15/18] warnings --- lib/Dialect/BLIF/Import/BLIFParser.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/Dialect/BLIF/Import/BLIFParser.cpp b/lib/Dialect/BLIF/Import/BLIFParser.cpp index 56e5026a8821..643f2b772493 100644 --- a/lib/Dialect/BLIF/Import/BLIFParser.cpp +++ b/lib/Dialect/BLIF/Import/BLIFParser.cpp @@ -271,7 +271,7 @@ ParseResult BLIFParser::parseOptionalInt(int &result) { consumeToken(); return success(); } - return failure(); + return success(); } //===----------------------------------------------------------------------===// @@ -492,16 +492,15 @@ ParseResult BLIFModelParser::parseLatch() { LatchModeEnum lType = LatchModeEnum::Unspecified; StringRef clkName; - if (parseOptionalLatchType(lType, clkName)) + if (parseOptionalLatchType(lType, clkName) || parseOptionalInt(init_val)) return emitError("invalid latch type"), failure(); - parseOptionalInt(init_val); if (init_val < 0 || init_val > 3) return emitError("invalid initial latch value '") << init_val << "'", failure(); Value inVal = getNamedValue(input); - Value clkVal = clkName == "NIL" | lType == LatchModeEnum::Unspecified + Value clkVal = ((clkName == "NIL") | (lType == LatchModeEnum::Unspecified)) ? Value() : getNamedValue(clkName); From f618e0acdc6cfc4eec7201e953a1cfeda276d6bb Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Sat, 4 Jan 2025 02:54:54 +0000 Subject: [PATCH 16/18] missing dependency --- lib/Dialect/BLIF/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Dialect/BLIF/CMakeLists.txt b/lib/Dialect/BLIF/CMakeLists.txt index c0b6e873d15d..51e9d1c78cf3 100644 --- a/lib/Dialect/BLIF/CMakeLists.txt +++ b/lib/Dialect/BLIF/CMakeLists.txt @@ -13,6 +13,7 @@ add_circt_dialect_library(CIRCTBLIF Support LINK_LIBS PUBLIC + CIRCTHW MLIRIR MLIRInferTypeOpInterface ) From 90088c2aa41377ba68917c34fe25e9d8cfcb4bd2 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Sat, 4 Jan 2025 17:41:09 +0000 Subject: [PATCH 17/18] blif emission --- include/circt/Dialect/BLIF/BLIFEmitter.h | 30 +++ include/circt/InitAllTranslations.h | 2 + lib/Dialect/BLIF/CMakeLists.txt | 3 +- lib/Dialect/BLIF/Export/BLIFEmitter.cpp | 225 +++++++++++++++++++++++ lib/Dialect/BLIF/Export/CMakeLists.txt | 7 + 5 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 include/circt/Dialect/BLIF/BLIFEmitter.h create mode 100644 lib/Dialect/BLIF/Export/BLIFEmitter.cpp create mode 100644 lib/Dialect/BLIF/Export/CMakeLists.txt diff --git a/include/circt/Dialect/BLIF/BLIFEmitter.h b/include/circt/Dialect/BLIF/BLIFEmitter.h new file mode 100644 index 000000000000..a4ef6f1b61d3 --- /dev/null +++ b/include/circt/Dialect/BLIF/BLIFEmitter.h @@ -0,0 +1,30 @@ +//===- BLIFEmitter.h - BLIF dialect to .blif emitter ------------*- C++ -*-===// +// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the interface to the .blif file emitter. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_BLIF_BLIFEMITTER_H +#define CIRCT_DIALECT_BLIF_BLIFEMITTER_H + +#include "circt/Support/LLVM.h" + +namespace circt { +namespace blif { + +mlir::LogicalResult exportBLIFFile(mlir::ModuleOp module, + llvm::raw_ostream &os); + +void registerToBLIFFileTranslation(); + +} // namespace blif +} // namespace circt + +#endif // CIRCT_DIALECT_BLIF_BLIFEMITTER_H diff --git a/include/circt/InitAllTranslations.h b/include/circt/InitAllTranslations.h index 1bf8b8db965f..4a96563d6d73 100644 --- a/include/circt/InitAllTranslations.h +++ b/include/circt/InitAllTranslations.h @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "circt/Dialect/Arc/ModelInfoExport.h" +#include "circt/Dialect/BLIF/BLIFEmitter.h" #include "circt/Dialect/BLIF/BLIFParser.h" #include "circt/Dialect/Calyx/CalyxEmitter.h" #include "circt/Dialect/ESI/ESIDialect.h" @@ -34,6 +35,7 @@ inline void registerAllTranslations() { static bool initOnce = []() { arc::registerArcModelInfoTranslation(); blif::registerFromBLIFFileTranslation(); + blif::registerToBLIFFileTranslation(); calyx::registerToCalyxTranslation(); firrtl::registerFromFIRFileTranslation(); firrtl::registerToFIRFileTranslation(); diff --git a/lib/Dialect/BLIF/CMakeLists.txt b/lib/Dialect/BLIF/CMakeLists.txt index 51e9d1c78cf3..bd4ed5bebc7a 100644 --- a/lib/Dialect/BLIF/CMakeLists.txt +++ b/lib/Dialect/BLIF/CMakeLists.txt @@ -23,4 +23,5 @@ add_dependencies(circt-headers CIRCTBLIFEnumsIncGen ) -add_subdirectory(Import) + add_subdirectory(Export) + add_subdirectory(Import) diff --git a/lib/Dialect/BLIF/Export/BLIFEmitter.cpp b/lib/Dialect/BLIF/Export/BLIFEmitter.cpp new file mode 100644 index 000000000000..010bb01242a7 --- /dev/null +++ b/lib/Dialect/BLIF/Export/BLIFEmitter.cpp @@ -0,0 +1,225 @@ +//===- BLIFEmitter.cpp - BLIF dialect to .blif emitter --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This implements a .blif file emitter. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/BLIF/BLIFEmitter.h" +#include "circt/Dialect/BLIF/BLIFDialect.h" +#include "circt/Dialect/BLIF/BLIFOps.h" +#include "circt/Dialect/HW/HWDialect.h" +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/Tools/mlir-translate/Translation.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/TypeSwitch.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "export-blif" + +using namespace circt; +using namespace blif; + +//===----------------------------------------------------------------------===// +// Emitter +//===----------------------------------------------------------------------===// + +// NOLINTBEGIN(misc-no-recursion) +namespace { + +/// An emitter for FIRRTL dialect operations to .fir output. +struct Emitter { + Emitter(llvm::raw_ostream &os) : os(os) {} + LogicalResult finalize(); + + // Circuit/module emission + void emitModel(ModelOp op); + void emitModuleParameters(Operation *op, ArrayAttr parameters); + + // Statement emission + void emitCommand(LogicGateOp op); + void emitCommand(LatchGateOp op); + +private: + /// Emit an error and remark that emission failed. + InFlightDiagnostic emitError(Operation *op, const Twine &message) { + encounteredError = true; + return op->emitError(message); + } + + /// Emit an error and remark that emission failed. + InFlightDiagnostic emitOpError(Operation *op, const Twine &message) { + encounteredError = true; + return op->emitOpError(message); + } + +private: + /// Pretty printer. + llvm::raw_ostream &os; + + /// Whether we have encountered any errors during emission. + bool encounteredError = false; + + /// The names used to emit values already encountered. Anything that gets a + /// name in the output FIR is listed here, such that future expressions can + /// reference it. + DenseMap valueNames; + StringSet<> valueNamesStorage; + int nameCounter = 0; + + /// Return the name used during emission of a `Value`, or none if the value + /// has not yet been emitted or it was emitted inline. + StringRef lookupEmittedName(Value value) { + auto it = valueNames.find(value); + if (it != valueNames.end()) + return {it->second}; + std::string name = "v" + std::to_string(nameCounter++); + while (valueNamesStorage.count(name)) { + name = "v" + std::to_string(nameCounter++); + } + addValueName(value, name); + return valueNames.find(value)->second; + } + + void addValueName(Value value, StringAttr attr) { + valueNames.insert({value, attr.getValue()}); + } + void addValueName(Value value, StringRef str) { + auto it = valueNamesStorage.insert(str); + valueNames.insert({value, it.first->getKey()}); + } +}; +} // namespace + +LogicalResult Emitter::finalize() { return failure(encounteredError); } + +/// Emit an entire circuit. +void Emitter::emitModel(ModelOp op) { + os << ".model " << op.getSymName() << "\n"; + auto mType = op.getModuleType(); + auto clockMask = op.getClocks(); + + int inIdx = 0; + int outIdx = 0; + auto &body = op.getBody().front(); + auto term = body.getTerminator(); + for (auto [idx, port] : llvm::enumerate(mType.getPorts())) { + switch (port.dir) { + case hw::ModulePort::Direction::Input: + if (clockMask.extractBits(idx, 1).isZero()) + os << ".input " << port.name.getValue() << "\n"; + else + os << ".clock " << port.name.getValue() << "\n"; + addValueName(body.getArgument(inIdx++), port.name); + break; + case hw::ModulePort::Direction::Output: + os << ".output " << port.name.getValue() << "\n"; + addValueName(term->getOperand(outIdx++), port.name); + break; + case hw::ModulePort::Direction::InOut: + emitOpError(op, "inout ports are not expected"); + break; + } + } + + // Emit the model body. + for (auto &bodyOp : body) { + if (encounteredError) + return; + TypeSwitch(&bodyOp) + .Case([&](auto op) { emitCommand(op); }) + .Case([&](auto op) { emitCommand(op); }) + .Case([&](auto op) {}) + .Default([&](auto op) { + emitOpError(op, "not supported for emission inside model definition"); + }); + } + + os << ".end\n"; +} + +void Emitter::emitCommand(LogicGateOp op) { + os << ".names"; + for (auto input : op.getInputs()) + os << " " << lookupEmittedName(input); + os << " " << lookupEmittedName(op.getResult()) << "\n"; + + for (auto row : op.getFunc()) { + const char *names[] = {"0", "1", "-"}; + for (auto input : ArrayRef(row).drop_back()) + os << names[input]; + if (row.size() > 1) + os << " "; + os << names[row.back()] << "\n"; + } +} + +void Emitter::emitCommand(LatchGateOp op) { + os << ".latch"; + os << " " << lookupEmittedName(op.getInput()); + os << " " << lookupEmittedName(op.getOutput()); + bool emitClk = true; + switch (op.getMode()) { + case LatchModeEnum::Unspecified: + emitClk = false; + break; + case LatchModeEnum::FallingEdge: + os << " fe "; + break; + case LatchModeEnum::RisingEdge: + os << " re "; + break; + case LatchModeEnum::ActiveHigh: + os << " ah "; + break; + case LatchModeEnum::ActiveLow: + os << " al "; + break; + case LatchModeEnum::Asynchronous: + os << " as "; + break; + } + if (emitClk) { + if (op.getClock()) + os << lookupEmittedName(op.getClock()); + else + os << "NIL"; + } + if (op.getInitVal() != 3) + os << " " << op.getInitVal(); + os << "\n"; +} + +//===----------------------------------------------------------------------===// +// Driver +//===----------------------------------------------------------------------===// + +// Emit the specified BLIF circuit into the given output stream. +mlir::LogicalResult circt::blif::exportBLIFFile(mlir::ModuleOp module, + llvm::raw_ostream &os) { + Emitter emitter(os); + for (auto &op : *module.getBody()) { + if (auto circuitOp = dyn_cast(op)) + emitter.emitModel(circuitOp); + } + return emitter.finalize(); +} + +void circt::blif::registerToBLIFFileTranslation() { + static mlir::TranslateFromMLIRRegistration toBLIF( + "export-blif", "emit BLIF dialect operations to .blif output", + [](ModuleOp module, llvm::raw_ostream &os) { + return exportBLIFFile(module, os); + }, + [](mlir::DialectRegistry ®istry) { + registry.insert(); + registry.insert(); + }); +} diff --git a/lib/Dialect/BLIF/Export/CMakeLists.txt b/lib/Dialect/BLIF/Export/CMakeLists.txt new file mode 100644 index 000000000000..eb340d1626a5 --- /dev/null +++ b/lib/Dialect/BLIF/Export/CMakeLists.txt @@ -0,0 +1,7 @@ +add_circt_translation_library(CIRCTExportBLIF + BLIFEmitter.cpp + + LINK_LIBS PUBLIC + CIRCTBLIF + MLIRTranslateLib +) From 0bbc4d635c785cabf60809d831dff2eb06818b25 Mon Sep 17 00:00:00 2001 From: Andrew Lenharth Date: Sun, 5 Jan 2025 05:26:40 +0000 Subject: [PATCH 18/18] Convert ICE40 to blif format targeting the ice40 library. Match output to yosys and input to nextpnr. Incomplete. --- include/circt/Conversion/ICE40ToBLIF.h | 24 +++++ include/circt/Conversion/Passes.h | 1 + include/circt/Conversion/Passes.td | 14 +++ include/circt/Dialect/BLIF/BLIFOps.td | 11 +++ lib/Conversion/CMakeLists.txt | 1 + lib/Conversion/ICE40ToBLIF/CMakeLists.txt | 16 ++++ lib/Conversion/ICE40ToBLIF/ICE40ToBLIF.cpp | 106 +++++++++++++++++++++ test/Dialect/ICE40/basic.mlir | 9 ++ 8 files changed, 182 insertions(+) create mode 100644 include/circt/Conversion/ICE40ToBLIF.h create mode 100644 lib/Conversion/ICE40ToBLIF/CMakeLists.txt create mode 100644 lib/Conversion/ICE40ToBLIF/ICE40ToBLIF.cpp diff --git a/include/circt/Conversion/ICE40ToBLIF.h b/include/circt/Conversion/ICE40ToBLIF.h new file mode 100644 index 000000000000..faf8297e4850 --- /dev/null +++ b/include/circt/Conversion/ICE40ToBLIF.h @@ -0,0 +1,24 @@ +//===- ICE40ToBLIF.h - ICE40 to BLIF dialect conversion ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_CONVERSION_ICE40TOBLIF_H +#define CIRCT_CONVERSION_ICE40TOBLIF_H + +#include "circt/Support/LLVM.h" +#include + +namespace circt { + +std::unique_ptr createICE40ToBLIFPass(); + +#define GEN_PASS_DECL_ICE40TOBLIF +#include "circt/Conversion/Passes.h.inc" + +} // namespace circt + +#endif // CIRCT_CONVERSION_ICE40TOBLIF_H diff --git a/include/circt/Conversion/Passes.h b/include/circt/Conversion/Passes.h index 30722ab98991..9f5e8a91910b 100644 --- a/include/circt/Conversion/Passes.h +++ b/include/circt/Conversion/Passes.h @@ -37,6 +37,7 @@ #include "circt/Conversion/HWToSystemC.h" #include "circt/Conversion/HandshakeToDC.h" #include "circt/Conversion/HandshakeToHW.h" +#include "circt/Conversion/ICE40ToBLIF.h" #include "circt/Conversion/LTLToCore.h" #include "circt/Conversion/LoopScheduleToCalyx.h" #include "circt/Conversion/MooreToCore.h" diff --git a/include/circt/Conversion/Passes.td b/include/circt/Conversion/Passes.td index 5f6ab39d4c23..475bbb023a15 100644 --- a/include/circt/Conversion/Passes.td +++ b/include/circt/Conversion/Passes.td @@ -517,6 +517,20 @@ def HandshakeToHW : Pass<"lower-handshake-to-hw", "mlir::ModuleOp"> { "seq::SeqDialect"]; } +//===----------------------------------------------------------------------===// +// ICE40ToBLIF +//===----------------------------------------------------------------------===// + +def ICE40ToBLIF : Pass<"lower-ice40-to-blif", "mlir::ModuleOp"> { + let summary = "Lower ICE40 to BLIF"; + let description = [{ + This pass lowers ICE40 to BLIF. + }]; + let constructor = "circt::createICE40ToBLIFPass()"; + let dependentDialects = ["ice40::ICE40Dialect", "hw::HWDialect", + "blif::BLIFDialect"]; +} + //===----------------------------------------------------------------------===// // Moore to Comb/HW/LLHD //===----------------------------------------------------------------------===// diff --git a/include/circt/Dialect/BLIF/BLIFOps.td b/include/circt/Dialect/BLIF/BLIFOps.td index 6d0989dfbe41..d8057ecf6db8 100644 --- a/include/circt/Dialect/BLIF/BLIFOps.td +++ b/include/circt/Dialect/BLIF/BLIFOps.td @@ -83,4 +83,15 @@ def LatchGateOp: BLIFOp<"latch_gate", []> { let results = (outs I1:$output); } +def LibraryGateOp: BLIFOp<"library_gate", []> { + let summary = "Library logic"; + let description = [{ + }]; + + let arguments = (ins FlatSymbolRefAttr:$libraryName, + Variadic:$inputs); + let results = (outs I1:$result); +} + + #endif // CIRCT_DIALECT_BLIF_BLIFOPS_TD diff --git a/lib/Conversion/CMakeLists.txt b/lib/Conversion/CMakeLists.txt index 61ddd4965cab..c8bddb38371a 100644 --- a/lib/Conversion/CMakeLists.txt +++ b/lib/Conversion/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(HWToLLVM) add_subdirectory(HWToSMT) add_subdirectory(HWToSV) add_subdirectory(HWToSystemC) +add_subdirectory(ICE40ToBLIF) add_subdirectory(LoopScheduleToCalyx) add_subdirectory(MooreToCore) add_subdirectory(PipelineToHW) diff --git a/lib/Conversion/ICE40ToBLIF/CMakeLists.txt b/lib/Conversion/ICE40ToBLIF/CMakeLists.txt new file mode 100644 index 000000000000..f9c8a17e9b3e --- /dev/null +++ b/lib/Conversion/ICE40ToBLIF/CMakeLists.txt @@ -0,0 +1,16 @@ +add_circt_conversion_library(CIRCTICE40ToBLIF + ICE40ToBLIF.cpp + + DEPENDS + CIRCTConversionPassIncGen + + LINK_COMPONENTS + Core + + LINK_LIBS PUBLIC + CIRCTBLIF + CIRCTHW + CIRCTICE40 + CIRCTSupport + MLIRTransforms +) diff --git a/lib/Conversion/ICE40ToBLIF/ICE40ToBLIF.cpp b/lib/Conversion/ICE40ToBLIF/ICE40ToBLIF.cpp new file mode 100644 index 000000000000..4dcab127c406 --- /dev/null +++ b/lib/Conversion/ICE40ToBLIF/ICE40ToBLIF.cpp @@ -0,0 +1,106 @@ +#include "circt/Conversion/ICE40ToBLIF.h" +#include "circt/Dialect/BLIF/BLIFDialect.h" +#include "circt/Dialect/BLIF/BLIFOps.h" +#include "circt/Dialect/ICE40/ICE40Dialect.h" +#include "circt/Dialect/ICE40/ICE40Ops.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/DialectConversion.h" + +namespace circt { +#define GEN_PASS_DEF_ICE40TOBLIF +#include "circt/Conversion/Passes.h.inc" +} // namespace circt + +using namespace circt; +using namespace mlir; + +namespace { +struct ICE40ToBLIFPass + : public ::circt::impl::ICE40ToBLIFBase { + void populateICE40ToBLIFPatterns(RewritePatternSet &patterns); + + void runOnOperation() override { + ModuleOp module = getOperation(); + + // Define the conversion target + ConversionTarget target(getContext()); + target.addLegalDialect(); + target.addIllegalDialect(); + + // Define the type converter + TypeConverter typeConverter; + + // Define the rewrite patterns + RewritePatternSet patterns(&getContext()); + populateICE40ToBLIFPatterns(patterns); + + // Apply the conversion + if (failed(applyPartialConversion(module, target, std::move(patterns)))) + signalPassFailure(); + } +}; + +// Define the patterns to convert ICE40 operations to BLIF operations + +namespace { +class SBLut4OpLowering : public OpConversionPattern { +public: + SBLut4OpLowering(MLIRContext *context) : OpConversionPattern(context) {} + + LogicalResult + matchAndRewrite(ice40::SBLut4Op op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + // Create the new BLIF LibraryGateOp with the same operands and attributes + rewriter.replaceOpWithNewOp( + op, rewriter.getIntegerType(1), "SB_LUT4", adaptor.getOperands()); + return success(); + } +}; + +class SBCarryOpLowering : public OpConversionPattern { +public: + SBCarryOpLowering(MLIRContext *context) : OpConversionPattern(context) {} + + LogicalResult + matchAndRewrite(ice40::SBCarryOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + // Create the new BLIF LibraryGateOp with the same operands and attributes + rewriter.replaceOpWithNewOp( + op, rewriter.getIntegerType(1), "SB_CARRY", adaptor.getOperands()); + return success(); + } +}; + +class SBLut4CarryOpLowering : public OpConversionPattern { +public: + SBLut4CarryOpLowering(MLIRContext *context) : OpConversionPattern(context) {} + + LogicalResult + matchAndRewrite(ice40::SBLut4CarryOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + // Create the new BLIF LibraryGateOp with the same operands and attributes + auto newFunc = rewriter.create( + op.getLoc(), rewriter.getIntegerType(1), "SB_LUT4", + adaptor.getOperands().take_front(4)); + Value carry[] = {adaptor.getOperands()[0], adaptor.getOperands()[1], + adaptor.getOperands()[2], adaptor.getOperands()[4]}; + auto newCarry = rewriter.create( + op.getLoc(), rewriter.getIntegerType(1), "SB_CARRY", carry); + rewriter.replaceOpWithMultiple(op, + {newFunc.getResult(), newCarry.getResult()}); + return success(); + } +}; +} // end anonymous namespace + +void ICE40ToBLIFPass::populateICE40ToBLIFPatterns(RewritePatternSet &patterns) { + patterns.add( + &getContext()); +} + +} // end anonymous namespace + +std::unique_ptr circt::createICE40ToBLIFPass() { + return std::make_unique(); +} diff --git a/test/Dialect/ICE40/basic.mlir b/test/Dialect/ICE40/basic.mlir index 28e5a31b5273..c7edcd82aedb 100644 --- a/test/Dialect/ICE40/basic.mlir +++ b/test/Dialect/ICE40/basic.mlir @@ -1,10 +1,19 @@ // RUN: circt-opt %s | FileCheck %s +// RUN: circt-opt %s --lower-ice40-to-blif | FileCheck --check-prefix=LOWER %s func.func @basic(%i : i1) { + // LOWER: blif.library_gate + // LOWER-SAME: SB_LUT4 // CHECK: ice40.sb_lut4 %a = ice40.sb_lut4 %i, %i, %i, %i, [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] + // LOWER: blif.library_gate + // LOWER-SAME: SB_CARRY // CHECK: ice40.sb_carry %b = ice40.sb_carry %i, %i, %a + // LOWER: blif.library_gate + // LOWER-SAME: SB_LUT4 + // LOWER: blif.library_gate + // LOWER-SAME: SB_CARRY // CHECK: ice40.sb_lut4_carry %c, %d = ice40.sb_lut4_carry %i, %i, %a, %a, %b, [1,0,1,1,0,1,1,1,0,1,1,1,1,0,1,1] func.return