Skip to content

Commit 36bff18

Browse files
jupvfrancocopybara-github
authored andcommitted
New pass to remove redundant eproc.
Given the name of a top-proc, and in a module with 2 eprocs, if the top proc is empty, erase it and make the other eproc the top one, by renaming it. This is conditional on a name being passes as top proc to the pass. PiperOrigin-RevId: 842868194
1 parent 51cbe8e commit 36bff18

File tree

6 files changed

+229
-0
lines changed

6 files changed

+229
-0
lines changed

xls/contrib/mlir/BUILD

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ cc_library(
329329
":procify_loops", # buildcleaner: keep
330330
":scalarize", # buildcleaner: keep
331331
":scf_to_xls", # buildcleaner: keep
332+
":skip_empty_top_eproc", # buildcleaner: keep
332333
":xls_transforms_passes",
333334
"@llvm-project//llvm:Support",
334335
"@llvm-project//mlir:Pass",
@@ -450,6 +451,20 @@ cc_library(
450451
],
451452
)
452453

454+
cc_library(
455+
name = "skip_empty_top_eproc",
456+
srcs = ["transforms/skip_empty_top_eproc.cc"],
457+
deps = [
458+
":mlir_xls",
459+
":xls_transforms_passes",
460+
":xls_transforms_passes_inc_gen",
461+
"@llvm-project//llvm:Support",
462+
"@llvm-project//mlir:IR",
463+
"@llvm-project//mlir:Pass",
464+
"@llvm-project//mlir:Support",
465+
],
466+
)
467+
453468
cc_binary(
454469
name = "xls_opt",
455470
srcs = ["tools/xls_opt/xls_opt.cc"],
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// RUN: xls_opt -skip-empty-top-eproc=top-proc-name=bar -split-input-file %s 2>&1 | FileCheck %s
2+
3+
4+
// CHECK-LABEL: module
5+
// CHECK-NEXT: xls.eproc @bar(%arg0: i1, %arg1: i32) zeroinitializer {
6+
// CHECK-NEXT: xls.next_value [%arg0, %arg1], [%arg0, %arg1] : (i32, i32) -> i32
7+
// CHECK-NEXT: xls.yield
8+
// CHECK-NEXT: }
9+
// CHECK: xls.instantiate_eproc @bar (@x as @pred, @y as @arg)
10+
// CHECK-NOT: @foo
11+
12+
module {
13+
14+
xls.eproc @foo(%pred: i1, %arg: i32) zeroinitializer {
15+
%0 = xls.next_value [%pred, %arg], [%pred, %arg] : (i32, i32) -> i32
16+
xls.yield %pred, %0 : i1, i32
17+
}
18+
19+
xls.chan @x : i1
20+
xls.chan @pred : i1
21+
xls.chan @y : i32
22+
xls.chan @arg : i32
23+
xls.instantiate_eproc @foo (@x as @pred, @y as @arg)
24+
25+
xls.eproc @bar() zeroinitializer {
26+
xls.yield
27+
}
28+
xls.instantiate_eproc @bar ()
29+
30+
}
31+
32+
// -----
33+
34+
// CHECK-LABEL: module
35+
// CHECK-NEXT: xls.eproc @bar(%arg0: i1, %arg1: i32) zeroinitializer {
36+
// CHECK-NEXT: xls.next_value [%arg0, %arg1], [%arg0, %arg1] : (i32, i32) -> i32
37+
// CHECK-NEXT: xls.yield
38+
// CHECK-NEXT: }
39+
// CHECK: xls.instantiate_eproc @bar (@x as @pred, @y as @arg)
40+
// CHECK-NOT: @foo
41+
42+
module {
43+
44+
xls.eproc @bar() zeroinitializer {
45+
xls.yield
46+
}
47+
xls.instantiate_eproc @bar ()
48+
49+
xls.eproc @foo(%pred: i1, %arg: i32) zeroinitializer {
50+
%0 = xls.next_value [%pred, %arg], [%pred, %arg] : (i32, i32) -> i32
51+
xls.yield %pred, %0 : i1, i32
52+
}
53+
54+
xls.chan @x : i1
55+
xls.chan @pred : i1
56+
xls.chan @y : i32
57+
xls.chan @arg : i32
58+
xls.instantiate_eproc @foo (@x as @pred, @y as @arg)
59+
60+
}
61+
62+
// -----
63+
64+
65+
// This test won't be rewritten, since the top eproc isn't the empty one.
66+
// CHECK-LABEL: module
67+
// CHECK: xls.eproc @foo()
68+
// CHECK: xls.eproc @bar(%{{.*}}: i1, %{{.*}}: i32)
69+
70+
module {
71+
72+
xls.eproc @foo() zeroinitializer {
73+
xls.yield
74+
}
75+
xls.instantiate_eproc @foo ()
76+
77+
xls.eproc @bar(%pred: i1, %arg: i32) zeroinitializer {
78+
%0 = xls.next_value [%pred, %arg], [%pred, %arg] : (i32, i32) -> i32
79+
xls.yield %pred, %0 : i1, i32
80+
}
81+
82+
xls.chan @x : i1
83+
xls.chan @pred : i1
84+
xls.chan @y : i32
85+
xls.chan @arg : i32
86+
xls.instantiate_eproc @bar (@x as @pred, @y as @arg)
87+
88+
}

xls/contrib/mlir/transforms/passes.td

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,26 @@ def OptimizeUsingXlsPass : Pass<"optimize-using-xls", "::mlir::ModuleOp"> {
217217
];
218218
}
219219

220+
def SkipEmptyTopEprocPass : Pass<"skip-empty-top-eproc", "::mlir::ModuleOp"> {
221+
let summary = "Skips empty top eprocs.";
222+
let description = [{
223+
In a module with two eprocs, if the second one is empty, removes the
224+
empty eproc and renames the other one to have the same name.
225+
}];
226+
227+
let dependentDialects = [
228+
"mlir::xls::XlsDialect",
229+
"mlir::func::FuncDialect",
230+
];
231+
232+
let options = [
233+
Option<"top_proc_name", "top-proc-name", "std::optional<std::string>",
234+
/*default=*/"std::nullopt",
235+
"When defined, and in a module with exactly two eprocs, if the eproc with"
236+
" this name is empty, we erase it and give this name to the other eproc."
237+
>
238+
];
239+
}
240+
241+
220242
#endif // MLIR_XLS_TRANSFORMS_PASSES
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2024 The XLS Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <cassert>
16+
#include <optional>
17+
#include <utility>
18+
19+
#include "llvm/include/llvm/ADT/SmallVector.h"
20+
#include "mlir/include/mlir/IR/BuiltinOps.h"
21+
#include "mlir/include/mlir/IR/Value.h"
22+
#include "mlir/include/mlir/IR/Visitors.h"
23+
#include "mlir/include/mlir/Pass/Pass.h" // IWYU pragma: keep
24+
#include "mlir/include/mlir/Support/LLVM.h"
25+
#include "xls/contrib/mlir/IR/xls_ops.h"
26+
#include "xls/contrib/mlir/transforms/passes.h" // IWYU pragma: keep
27+
28+
namespace mlir::xls {
29+
30+
#define GEN_PASS_DEF_SKIPEMPTYTOPEPROCPASS
31+
#include "xls/contrib/mlir/transforms/passes.h.inc"
32+
33+
namespace {
34+
35+
bool IsEmptyEproc(EprocOp eproc) {
36+
return eproc.getBody().front().without_terminator().empty();
37+
}
38+
39+
class SkipEmptyTopEprocPass
40+
: public impl::SkipEmptyTopEprocPassBase<SkipEmptyTopEprocPass> {
41+
using SkipEmptyTopEprocPassBase::SkipEmptyTopEprocPassBase;
42+
43+
public:
44+
void runOnOperation() override {
45+
if (!top_proc_name.has_value()) {
46+
return;
47+
}
48+
49+
ModuleOp module_op = getOperation();
50+
auto eprocs = llvm::to_vector(module_op.getOps<EprocOp>());
51+
if (eprocs.size() != 2) {
52+
return;
53+
}
54+
55+
EprocOp new_top_eproc = eprocs[0];
56+
EprocOp current_empty_top_eproc = eprocs[1];
57+
assert(!(new_top_eproc.getSymName() == *top_proc_name &&
58+
current_empty_top_eproc.getSymName() == *top_proc_name) &&
59+
"Only one eproc must have the top-level name");
60+
if (new_top_eproc.getSymName() == *top_proc_name) {
61+
std::swap(new_top_eproc, current_empty_top_eproc);
62+
} else if (current_empty_top_eproc.getSymName() != *top_proc_name) {
63+
// Nothing to do here then.
64+
return;
65+
}
66+
67+
if (!IsEmptyEproc(current_empty_top_eproc)) {
68+
// Nothing to do here. This top proc is not empty.
69+
return;
70+
}
71+
72+
// Step 1: Erase all instances of this empty eproc.
73+
auto uses = *current_empty_top_eproc.getSymbolUses(module_op);
74+
for (auto& use : uses) {
75+
use.getUser()->erase();
76+
}
77+
78+
// Step 2: Change the name of the non-empty non-top eproc to be the name of
79+
// the empty one. Both eproc and instances need to be updated.
80+
uses = *new_top_eproc.getSymbolUses(module_op);
81+
for (auto& use : uses) {
82+
cast<InstantiateEprocOp>(use.getUser())
83+
.setEproc(current_empty_top_eproc.getSymName());
84+
}
85+
new_top_eproc.setSymName(current_empty_top_eproc.getSymName());
86+
87+
// Step 3: Erase the empty eproc.
88+
current_empty_top_eproc.erase();
89+
}
90+
};
91+
92+
} // namespace
93+
} // namespace mlir::xls

xls/contrib/mlir/transforms/xls_lower.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ void XlsLowerPassPipeline(OpPassManager& pm,
3030
pm.addNestedPass<xls::SprocOp>(createOptimizeSpawnsPass());
3131
pm.addPass(mlir::createCanonicalizerPass());
3232
pm.addPass(createProcElaborationPass());
33+
pm.addPass(
34+
createSkipEmptyTopEprocPass({.top_proc_name = options.top_proc_name}));
3335
if (options.instantiate_eprocs) {
3436
pm.addPass(createInstantiateEprocsPass());
3537
// Removes discardable eprocs.

xls/contrib/mlir/transforms/xls_lower.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#ifndef GDM_HW_MLIR_XLS_TRANSFORMS_XLS_LOWER_H_
1616
#define GDM_HW_MLIR_XLS_TRANSFORMS_XLS_LOWER_H_
1717

18+
#include <optional>
19+
#include <string>
20+
1821
#include "llvm/include/llvm/Support/CommandLine.h"
1922
#include "mlir/include/mlir/Pass/PassManager.h"
2023
#include "mlir/include/mlir/Pass/PassOptions.h"
@@ -50,6 +53,12 @@ struct XlsLowerPassPipelineOptions
5053
llvm::cl::desc(
5154
"If true, procify-loops is applied by default to all scf.for ops"),
5255
llvm::cl::init(false)};
56+
PassOptions::Option<std::optional<std::string>> top_proc_name{
57+
*this, "top-proc-name",
58+
llvm::cl::desc("When defined, and in a module with exactly two eprocs, "
59+
"if the eproc with this name is empty, we erase it and "
60+
"give this name to the other eproc."),
61+
llvm::cl::init(std::nullopt)};
5362
};
5463

5564
// A Pass pipeline that lowers to a form that can be translated to XLS.

0 commit comments

Comments
 (0)