mirror of https://github.com/llvm/circt.git
[RTG] Add InsertTestToFileMappingPass (#8795)
Add a pass to group tests to output files. Currently, this matches what the emission pass does but can be extended to group tests according to certain requirements demanded from the execution environment. Also add a simple pass that inlines the tests without any gluecode (matching what the emission pass currently does). The emission pass will be simplified in a future PR to only iterate over the file operations and print what's inside
This commit is contained in:
parent
e070aed1f3
commit
1ef47b3cdb
|
@ -13,6 +13,7 @@
|
|||
#ifndef CIRCT_DIALECT_RTG_IR_RTGOPS_H
|
||||
#define CIRCT_DIALECT_RTG_IR_RTGOPS_H
|
||||
|
||||
#include "circt/Dialect/Emit/EmitOpInterfaces.h"
|
||||
#include "circt/Dialect/RTG/IR/RTGAttrInterfaces.h"
|
||||
#include "circt/Dialect/RTG/IR/RTGDialect.h"
|
||||
#include "circt/Dialect/RTG/IR/RTGISAAssemblyAttrInterfaces.h"
|
||||
|
|
|
@ -21,6 +21,7 @@ include "mlir/Interfaces/SideEffectInterfaces.td"
|
|||
include "mlir/Interfaces/InferTypeOpInterface.td"
|
||||
include "circt/Dialect/RTG/IR/RTGInterfaces.td"
|
||||
include "circt/Dialect/RTG/IR/RTGISAAssemblyInterfaces.td"
|
||||
include "circt/Dialect/Emit/EmitOpInterfaces.td"
|
||||
|
||||
// Base class for the operation in this dialect.
|
||||
class RTGOp<string mnemonic, list<Trait> traits = []> :
|
||||
|
@ -696,6 +697,7 @@ def TestOp : RTGOp<"test", [
|
|||
Symbol,
|
||||
SingleBlock,
|
||||
NoTerminator,
|
||||
Emittable,
|
||||
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmBlockArgumentNames"]>,
|
||||
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
|
||||
HasParent<"mlir::ModuleOp">,
|
||||
|
|
|
@ -197,4 +197,39 @@ def PrintTestNamesPass : Pass<"rtg-print-test-names", "mlir::ModuleOp"> {
|
|||
];
|
||||
}
|
||||
|
||||
def InsertTestToFileMappingPass : Pass<"rtg-insert-test-to-file-mapping",
|
||||
"mlir::ModuleOp"> {
|
||||
let summary = "insert Emit dialect ops to prepare for emission";
|
||||
let description = [{
|
||||
This pass inserts emit dialect operations to group tests to output files.
|
||||
All tests can be put in a single output file, each test in its own file, or
|
||||
tests can be grouped according to some properties (e.g., machine mode vs.
|
||||
user mode tests) (TODO).
|
||||
}];
|
||||
|
||||
let options = [
|
||||
Option<"splitOutput", "split-output", "bool", /*default=*/"false",
|
||||
"If 'true' emits one file per 'rtg.test' in the IR. The name of the "
|
||||
"file matches the test name and is placed in 'path'. Otherwise, path "
|
||||
"is interpreted as the full file path including filename.">,
|
||||
Option<"path", "path", "std::string", /*default=*/"",
|
||||
"The directory or file path in which the output files should be "
|
||||
"created. If empty is is emitted to stderr (not allowed if "
|
||||
"'split-output' is set to 'true')">,
|
||||
];
|
||||
|
||||
let dependentDialects = ["emit::EmitDialect"];
|
||||
}
|
||||
|
||||
def SimpleTestInlinerPass : Pass<"rtg-simple-test-inliner", "mlir::ModuleOp"> {
|
||||
let summary = "inline test contents";
|
||||
let description = [{
|
||||
This is a simple pass to inline test contents into 'emit.file' operations
|
||||
in which they are referenced. No "glue code" is inserted between tests
|
||||
added to the same file. Thus this pass is not intended to be used in a
|
||||
production pipeline but just to bring the IR into a structure understood by
|
||||
the RTG ISA assembly emission pass to avoid making that pass more complex.
|
||||
}];
|
||||
}
|
||||
|
||||
#endif // CIRCT_DIALECT_RTG_TRANSFORMS_RTGPASSES_TD
|
||||
|
|
|
@ -3,12 +3,14 @@ add_circt_dialect_library(CIRCTRTGTransforms
|
|||
EmbedValidationValuesPass.cpp
|
||||
EmitRTGISAAssemblyPass.cpp
|
||||
InlineSequencesPass.cpp
|
||||
InsertTestToFileMappingPass.cpp
|
||||
LinearScanRegisterAllocationPass.cpp
|
||||
LowerUniqueLabelsPass.cpp
|
||||
LowerValidateToLabelsPass.cpp
|
||||
MemoryAllocationPass.cpp
|
||||
PrintTestNamesPass.cpp
|
||||
RTGPassPipelines.cpp
|
||||
SimpleTestInlinerPass.cpp
|
||||
UniqueValidateOpsPass.cpp
|
||||
|
||||
DEPENDS
|
||||
|
@ -19,6 +21,7 @@ add_circt_dialect_library(CIRCTRTGTransforms
|
|||
|
||||
LINK_LIBS PRIVATE
|
||||
CIRCTRTGDialect
|
||||
CIRCTEmit
|
||||
CIRCTSupport
|
||||
MLIRArithDialect
|
||||
MLIRIndexDialect
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Dialect/Emit/EmitOps.h"
|
||||
#include "circt/Dialect/RTG/IR/RTGOps.h"
|
||||
#include "circt/Dialect/RTG/Transforms/RTGPasses.h"
|
||||
#include "circt/Support/Path.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
|
||||
namespace circt {
|
||||
namespace rtg {
|
||||
#define GEN_PASS_DEF_INSERTTESTTOFILEMAPPINGPASS
|
||||
#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc"
|
||||
} // namespace rtg
|
||||
} // namespace circt
|
||||
|
||||
using namespace mlir;
|
||||
using namespace circt;
|
||||
using namespace circt::rtg;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Insert Test To File Mapping Pass
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
struct InsertTestToFileMappingPass
|
||||
: public rtg::impl::InsertTestToFileMappingPassBase<
|
||||
InsertTestToFileMappingPass> {
|
||||
using Base::Base;
|
||||
void runOnOperation() override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void InsertTestToFileMappingPass::runOnOperation() {
|
||||
SmallVector<TestOp> tests(getOperation().getOps<TestOp>());
|
||||
auto loc = getOperation().getLoc();
|
||||
if (!splitOutput) {
|
||||
OpBuilder builder = OpBuilder::atBlockEnd(getOperation().getBody());
|
||||
auto fileOp = emit::FileOp::create(builder, loc, path);
|
||||
builder.setInsertionPointToStart(fileOp.getBody());
|
||||
for (auto testOp : tests)
|
||||
emit::RefOp::create(builder, loc, testOp.getSymNameAttr());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.empty() || path == "-") {
|
||||
emitError(loc, "path must be specified when split-output is set");
|
||||
return signalPassFailure();
|
||||
}
|
||||
|
||||
for (auto testOp : tests) {
|
||||
OpBuilder builder = OpBuilder::atBlockEnd(getOperation().getBody());
|
||||
llvm::SmallString<128> filename(path.getValue());
|
||||
appendPossiblyAbsolutePath(filename, testOp.getSymName() + ".s");
|
||||
auto fileOp = emit::FileOp::create(builder, loc, filename);
|
||||
OpBuilder::InsertionGuard guard(builder);
|
||||
builder.setInsertionPointToStart(fileOp.getBody());
|
||||
emit::RefOp::create(builder, loc, testOp.getSymNameAttr());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Dialect/Emit/EmitOps.h"
|
||||
#include "circt/Dialect/RTG/IR/RTGOps.h"
|
||||
#include "circt/Dialect/RTG/Transforms/RTGPasses.h"
|
||||
#include "mlir/IR/PatternMatch.h"
|
||||
|
||||
namespace circt {
|
||||
namespace rtg {
|
||||
#define GEN_PASS_DEF_SIMPLETESTINLINERPASS
|
||||
#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc"
|
||||
} // namespace rtg
|
||||
} // namespace circt
|
||||
|
||||
using namespace mlir;
|
||||
using namespace circt;
|
||||
using namespace circt::rtg;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Simple Test Inliner Pass
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
struct SimpleTestInlinerPass
|
||||
: public rtg::impl::SimpleTestInlinerPassBase<SimpleTestInlinerPass> {
|
||||
using Base::Base;
|
||||
void runOnOperation() override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void SimpleTestInlinerPass::runOnOperation() {
|
||||
const auto &symTbl = getAnalysis<SymbolTable>();
|
||||
IRRewriter rewriter(getOperation());
|
||||
|
||||
for (auto fileOp : getOperation().getOps<emit::FileOp>()) {
|
||||
for (auto refOp :
|
||||
llvm::make_early_inc_range(fileOp.getOps<emit::RefOp>())) {
|
||||
auto testOp = symTbl.lookup<TestOp>(refOp.getTargetAttr().getAttr());
|
||||
if (!testOp) {
|
||||
refOp.emitError("invalid symbol reference: ") << refOp.getTargetAttr();
|
||||
return signalPassFailure();
|
||||
}
|
||||
|
||||
bool allArgsUnused =
|
||||
llvm::all_of(testOp.getBody()->getArguments(),
|
||||
[](auto arg) { return arg.use_empty(); });
|
||||
if (!allArgsUnused) {
|
||||
testOp->emitError("cannot inline test with used arguments");
|
||||
return signalPassFailure();
|
||||
}
|
||||
|
||||
testOp.getBody()->eraseArguments(0, testOp.getBody()->getNumArguments());
|
||||
rewriter.setInsertionPoint(refOp);
|
||||
CommentOp::create(rewriter, refOp->getLoc(),
|
||||
rewriter.getStringAttr("Begin of test '" +
|
||||
testOp.getSymName() + "'"));
|
||||
auto newTestOp = cast<TestOp>(testOp->clone());
|
||||
rewriter.inlineBlockBefore(newTestOp.getBody(), refOp, {});
|
||||
CommentOp::create(
|
||||
rewriter, refOp->getLoc(),
|
||||
rewriter.getStringAttr("End of test '" + testOp.getSymName() + "'"));
|
||||
newTestOp.erase();
|
||||
refOp.erase();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &op : llvm::make_early_inc_range(getOperation().getOps()))
|
||||
if (isa<TargetOp, TestOp>(&op))
|
||||
op.erase();
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// RUN: circt-opt --rtg-insert-test-to-file-mapping=split-output=true --verify-diagnostics %s
|
||||
|
||||
// expected-error @below {{path must be specified when split-output is set}}
|
||||
module {
|
||||
rtg.test @test0() {}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// RUN: circt-opt --rtg-insert-test-to-file-mapping="path=dirname split-output=true" %s | FileCheck %s --check-prefix=CHECK-SPLIT
|
||||
// RUN: circt-opt --rtg-insert-test-to-file-mapping="path=filename split-output=false" %s | FileCheck %s
|
||||
|
||||
rtg.test @test0() {}
|
||||
|
||||
rtg.test @test1() {}
|
||||
|
||||
// CHECK-SPLIT-LABEL: emit.file "dirname{{.+}}test0.s" {
|
||||
// CHECK-SPLIT-NEXT: emit.ref @test0
|
||||
// CHECK-SPLIT-NEXT: }
|
||||
|
||||
// CHECK-SPLIT-LABEL: emit.file "dirname{{.+}}test1.s" {
|
||||
// CHECK-SPLIT-NEXT: emit.ref @test1
|
||||
// CHECK-SPLIT-NEXT: }
|
||||
|
||||
// CHECK-LABEL: emit.file "filename" {
|
||||
// CHECK-NEXT: emit.ref @test0
|
||||
// CHECK-NEXT: emit.ref @test1
|
||||
// CHECK-NEXT: }
|
|
@ -0,0 +1,60 @@
|
|||
// RUN: circt-opt --rtg-simple-test-inliner --split-input-file --verify-diagnostics %s | FileCheck %s
|
||||
|
||||
// CHECK-NOT: rtg.target @tgt1
|
||||
rtg.target @tgt1 : !rtg.dict<imm: !rtg.isa.immediate<32>> {
|
||||
%imm = rtg.constant #rtg.isa.immediate<32, 0>
|
||||
rtg.yield %imm : !rtg.isa.immediate<32>
|
||||
}
|
||||
|
||||
rtg.test @test1() {
|
||||
rtg.comment "Inside test1"
|
||||
}
|
||||
|
||||
rtg.test @test2(imm = %imm: !rtg.isa.immediate<32>) target @tgt1 {
|
||||
rtg.comment "Inside test2"
|
||||
}
|
||||
|
||||
rtg.test @test3(imm = %imm: !rtg.isa.immediate<32>) {
|
||||
rtg.comment "Inside test3"
|
||||
}
|
||||
|
||||
// CHECK-LABEL: emit.file "filename"
|
||||
emit.file "filename" {
|
||||
// CHECK-NOT: emit.ref
|
||||
// CHECK-NEXT: rtg.comment "Begin of test 'test1'"
|
||||
// CHECK-NEXT: rtg.comment "Inside test1"
|
||||
// CHECK-NEXT: rtg.comment "End of test 'test1'"
|
||||
emit.ref @test1
|
||||
// CHECK-NOT: emit.ref
|
||||
// CHECK-NEXT: rtg.comment "Begin of test 'test2'"
|
||||
// CHECK-NEXT: rtg.comment "Inside test2"
|
||||
// CHECK-NEXT: rtg.comment "End of test 'test2'"
|
||||
emit.ref @test2
|
||||
// CHECK-NOT: emit.ref
|
||||
// CHECK-NEXT: rtg.comment "Begin of test 'test3'"
|
||||
// CHECK-NEXT: rtg.comment "Inside test3"
|
||||
// CHECK-NEXT: rtg.comment "End of test 'test3'"
|
||||
emit.ref @test3
|
||||
// CHECK-NOT: emit.ref
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// expected-error @below {{cannot inline test with used arguments}}
|
||||
rtg.test @test(imm = %imm: !rtg.isa.immediate<32>) {
|
||||
%reg = rtg.fixed_reg #rtgtest.t0
|
||||
rtgtest.rv32i.lui %reg, %imm : !rtg.isa.immediate<32>
|
||||
}
|
||||
|
||||
emit.file "filename" {
|
||||
emit.ref @test
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
hw.module @mod() {}
|
||||
|
||||
emit.file "filename" {
|
||||
// expected-error @below {{invalid symbol reference: @mod}}
|
||||
emit.ref @mod
|
||||
}
|
Loading…
Reference in New Issue