Add the NsOp to represent namespaces

This commit is contained in:
Sameer Rahmani 2022-04-27 21:49:57 +01:00
parent 0d7f51da8a
commit 97b31e8cb0
11 changed files with 209 additions and 16 deletions

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "serene/passes.h"
#include "serene/slir/dialect.h"
#include <mlir/Dialect/Arithmetic/IR/Arithmetic.h>
@ -28,7 +29,9 @@ int main(int argc, char **argv) {
serene::slir::registerTo(registry);
registry.insert<mlir::arith::ArithmeticDialect, mlir::func::FuncDialect>();
// TODO: Register passes here
serene::passes::registerAllPasses();
return static_cast<int>(
mlir::failed(mlir::MlirOptMain(argc, argv, "slir-opt", registry)));
}

View File

@ -1,13 +1,14 @@
module @some.ns {
serene.ns @some.ns {
// Value operator ----
%0 = "serene.value"(){value= 3} : () -> i64
// compact form
%1 = serene.value 3 : i32
%x = serene.symbol "some.ns" "x"
// Def operator ----
%foo = "serene.def"(%0){sym_name = "foo"}: (i64) -> !serene.symbol
%foo = "serene.define"(%0){sym_name = "foo"}: (i64) -> !serene.symbol
// compact form
%bar = serene.def "bar", %0 : i64
%bar = serene.define "bar", %0 : i64
// Fn operator ----
%f1 = "serene.fn"()({
@ -15,7 +16,7 @@ module @some.ns {
%2 = serene.value 3 : i32
// Def operator ----
%baz = "serene.def"(%fnarg1){sym_name = "baz"}: (i1) -> !serene.symbol
%baz = "serene.define"(%fnarg1){sym_name = "baz"}: (i1) -> !serene.symbol
serene.ret %baz : !serene.symbol
},
{
@ -23,10 +24,10 @@ module @some.ns {
%3 = serene.value 4 : i32
// Def operator ----
%baz1 = "serene.def"(%3){sym_name = "baz"}: (i32) -> !serene.symbol
%baz1 = "serene.define"(%3){sym_name = "baz"}: (i32) -> !serene.symbol
serene.ret %baz1 : !serene.symbol
^b2:
%baz2 = "serene.def"(%3){sym_name = "baz"}: (i32) -> !serene.symbol
%baz2 = "serene.define"(%3){sym_name = "baz"}: (i32) -> !serene.symbol
serene.ret %baz2 : !serene.symbol
}){name = "some-fn", return_type = i32} : () -> !serene.fn

View File

@ -1,2 +1,3 @@
add_subdirectory("serene/slir/")
add_subdirectory("serene/passes/")
add_subdirectory("serene/errors/")

View File

@ -19,10 +19,16 @@
#ifndef SERENE_PASSES_H
#define SERENE_PASSES_H
#include "serene/export.h"
#include <mlir/Pass/Pass.h>
namespace serene::passes {
/// Return a pass to lower the serene.symbol op
SERENE_EXPORT std::unique_ptr<mlir::Pass> createLowerSymbol();
SERENE_EXPORT void registerAllPasses();
/// Return a pass to convert SLIR dialect to built-in dialects
/// of MLIR.
std::unique_ptr<mlir::Pass> createSLIRLowerToMLIRPass();

View File

@ -0,0 +1,9 @@
set(LLVM_TARGET_DEFINITIONS passes.td)
mlir_tablegen(passes.h.inc -gen-pass-decls)
if(SERENE_ENABLE_DOCS)
mlir_tablegen(passes.md.inc -gen-pass-doc)
endif()
add_public_tablegen_target(SerenePassGen)

View File

@ -0,0 +1,49 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2022 Sameer Rahmani <lxsameer@gnu.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SERENE_PASSES_TD
#define SERENE_PASSES_TD
include "mlir/Pass/PassBase.td"
def LowerSymbol : Pass<"lower-symbol", "slir::SymbolOp"> {
let summary = "Lowers `serene.symbol` operation to LLVM dialect";
let description = [{Lowers `serene.symbol` operation to LLVM dialect as an struct}];
// This is how we can create the default instance of this pass.
// via `createLowerSymbol` function
let constructor = "serene::passes::createLowerSymbol()";
}
def LowerSLIR : Pass<"lower-slir", "slir::NsOp"> {
let summary = "Lowers the SLIR dialect to LLVM IR indirectly";
let description = [{
Lowers the SLIR dialect to LLVM IR indirectly meaning
that it will lower SLIR to other dialects that in turn will
eventually lower to llvm ir.
}];
// This is how we can create the default instance of this pass.
// via `createLowerSymbol` function
let constructor = "serene::passes::createLowerSLIR()";
let dependentDialects = ["slir::SereneDialect", "mlir::func::FuncDialect"];
}
#endif // SERENE_PASSES_TD

View File

@ -1,4 +1,4 @@
/* -*- C++ -*-
/*
* Serene Programming Language
*
* Copyright (c) 2019-2022 Sameer Rahmani <lxsameer@gnu.org>
@ -21,7 +21,9 @@
include "mlir/IR/OpBase.td"
include "mlir/Interfaces/CallInterfaces.td"
include "mlir/IR/RegionKindInterface.td"
include "mlir/IR/SymbolInterfaces.td"
include "mlir/IR/OpAsmInterface.td"
// Base class for Serene dialect operations. This operation inherits from the base
// `Op` class in OpBase.td, and provides:
@ -75,21 +77,48 @@ def ValueOp : Serene_Op<"value", [
let assemblyFormat = "attr-dict $value";
}
// Symbol Op
def SymbolOp : Serene_Op<"symbol", [
ConstantLike, NoSideEffect, IsolatedFromAbove
]> {
// Def
def DefOp: Serene_Op<"def", [Symbol]> {
let summary = "This operation is the compile time contructor for symbol type";
let description = [{
The `symbol` operation produces an SSA value that holds a value of type
symbol at compile time.
Example:
```
%1 = serene.symbol "some.ns" "symbol_name"
// Equivalent generic form
%1 = "serene.symbol"() {ns = "some.ns", name = "symbol_name"} : () -> i32
```
}];
let arguments = (ins StrAttr:$ns, StrAttr:$name);
let results = (outs SereneSymbol:$result);
let assemblyFormat = "attr-dict $ns $name";
}
// Define
def DefnieOp: Serene_Op<"define", [Symbol]> {
let summary = "This operation defines a global binding in the current namespace";
let description = [{
`def` defines a global binding in the current namespace. It always return a
`define` defines a global binding in the current namespace. It always return a
symbol type.
Examples:
```mlir
%foo = "serene.def"(%0){name = "foo"}: (i64) -> !serene.symbol
%foo = "serene.define"(%0){name = "foo"}: (i64) -> !serene.symbol
// compact form
%bar = serene.def "bar", %0 : i64
%bar = serene.define "bar", %0 : i64
```
}];
@ -174,4 +203,63 @@ def CallOp : Serene_Op<"call",
}];
}
def NsOp : Serene_Op<"ns", [
AffineScope, IsolatedFromAbove, NoRegionArguments, SymbolTable, Symbol,
OpAsmOpInterface
] # GraphRegionNoTerminator.traits> {
let summary = "This operation represents a Serene namespace.";
let description = [{
The `serene.ns` operation represents a namespace that will eventually
lowers to a `mlir::ModuleOp`.
Example:
```mlir
serene.ns @some.ns {
....
}
```
}];
// TODO: Create a new Attr type that is an array to represent
// required namespaces
let arguments = (ins SymbolNameAttr:$sym_name,
OptionalAttr<StrAttr>:$sym_visibility);
let regions = (region SizedRegion<1>:$body);
let assemblyFormat = "$sym_name attr-dict-with-keyword $body";
let builders = [
OpBuilder<(ins CArg<"llvm::Optional<llvm::StringRef>", "{}">:$name)>
];
let extraClassDeclaration = [{
/// Construct a namespace from the given location with an optional name.
// static NsOp create(slir::reader::LocationRange loc, Optional<StringRef> name = llvm::None);
/// Return the name of this module if present.
llvm::StringRef getName() { return sym_name(); }
//===------------------------------------------------------------------===//
// SymbolOpInterface Methods
//===------------------------------------------------------------------===//
/// A ModuleOp may optionally define a symbol.
bool isOptionalSymbol() { return false; }
//===------------------------------------------------------------------===//
// DataLayoutOpInterface Methods
//===------------------------------------------------------------------===//
mlir::DataLayoutSpecInterface getDataLayoutSpec();
//===------------------------------------------------------------------===//
// OpAsmOpInterface Methods
//===------------------------------------------------------------------===//
static ::llvm::StringRef getDefaultDialect() {
return "builtin";
}
}];
}
#endif // SERENE_DIALECT_OPS

View File

@ -1,4 +1,4 @@
/* -*- C++ -*-
/*
* Serene Programming Language
*
* Copyright (c) 2019-2022 Sameer Rahmani <lxsameer@gnu.org>

View File

@ -108,7 +108,7 @@ else()
endif()
# Generate the tablegen ODS files before this target
add_dependencies(serene SereneTablegen SereneDialectGen SereneErrorGen)
add_dependencies(serene SereneTablegen SereneDialectGen SerenePassGen SereneErrorGen)
# We need this directory, and users of our library will need it too
target_include_directories(serene PUBLIC "$<BUILD_INTERFACE:${INCLUDE_DIR}>")

View File

@ -21,6 +21,7 @@
#include "serene/utils.h"
#include <llvm/Support/Casting.h>
#include <llvm/Support/raw_ostream.h>
#include <mlir/Dialect/Arithmetic/IR/Arithmetic.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
@ -34,6 +35,27 @@
#include <cstdint>
namespace serene::passes {
#define GEN_PASS_CLASSES
#include "serene/passes/passes.h.inc"
class LowerSymbol : public LowerSymbolBase<LowerSymbol> {
void runOnOperation() override { llvm::outs() << "here\n"; }
};
std::unique_ptr<mlir::Pass> createLowerSymbol() {
return std::make_unique<LowerSymbol>();
}
class LowerSLIR : public LowerSLIRBase<LowerSLIR> {
void runOnOperation() override { llvm::outs() << "here\n"; }
};
std::unique_ptr<mlir::Pass> createLowerSLIR() {
return std::make_unique<LowerSLIR>();
}
#define GEN_PASS_REGISTRATION
#include "serene/passes/passes.h.inc"
// ----------------------------------------------------------------------------
// ValueOp lowering to constant op
@ -191,4 +213,6 @@ void SLIRToMLIRPass::runOnModule() {
std::unique_ptr<mlir::Pass> createSLIRLowerToMLIRPass() {
return std::make_unique<SLIRToMLIRPass>();
};
void registerAllPasses() { registerPasses(); }
} // namespace serene::passes

View File

@ -24,4 +24,16 @@
#include <mlir/IR/BuiltinAttributes.h>
#include <mlir/IR/OperationSupport.h>
namespace serene::slir {} // namespace serene::slir
namespace serene::slir {
mlir::DataLayoutSpecInterface NsOp::getDataLayoutSpec() {
// Take the first and only (if present) attribute that implements the
// interface. This needs a linear search, but is called only once per data
// layout object construction that is used for repeated queries.
for (mlir::NamedAttribute attr : getOperation()->getAttrs())
if (auto spec = attr.getValue().dyn_cast<mlir::DataLayoutSpecInterface>())
return spec;
return {};
}
} // namespace serene::slir