-
Notifications
You must be signed in to change notification settings - Fork 313
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[RTG] Add ISA assembly emission pass
- Loading branch information
Showing
8 changed files
with
375 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
//===- EmitRTGISAAssemblyPass.cpp - RTG Assembly 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 is the main ISA Assembly emitter implementation for the RTG dialect. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "circt/Dialect/RTG/IR/RTGISAAssemblyOpInterfaces.h" | ||
#include "circt/Dialect/RTG/IR/RTGOps.h" | ||
#include "circt/Dialect/RTG/Transforms/RTGPasses.h" | ||
#include "circt/Support/Path.h" | ||
#include "mlir/IR/Threading.h" | ||
#include "mlir/Support/FileUtilities.h" | ||
#include "llvm/ADT/SmallString.h" | ||
#include "llvm/ADT/StringRef.h" | ||
#include "llvm/ADT/StringSet.h" | ||
#include "llvm/Support/FileSystem.h" | ||
#include "llvm/Support/Path.h" | ||
#include "llvm/Support/ToolOutputFile.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
#include <fstream> | ||
|
||
namespace circt { | ||
namespace rtg { | ||
#define GEN_PASS_DEF_EMITRTGISAASSEMBLYPASS | ||
#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc" | ||
} // namespace rtg | ||
} // namespace circt | ||
|
||
using namespace circt; | ||
using namespace rtg; | ||
|
||
#define DEBUG_TYPE "emit-rtg-isa-assembly" | ||
|
||
namespace { | ||
|
||
class Emitter { | ||
public: | ||
Emitter(llvm::raw_ostream &os, | ||
const llvm::StringSet<llvm::MallocAllocator> &unsupportedInstr) | ||
: os(os), unsupportedInstr(unsupportedInstr) {} | ||
|
||
LogicalResult emitInstruction(InstructionOpInterface instr) { | ||
os << llvm::indent(4); | ||
bool useBinary = unsupportedInstr.contains(instr->getName().getStringRef()); | ||
|
||
// TODO: we cannot just assume that double-slash is the way to do a line | ||
// comment | ||
if (useBinary) | ||
os << "// "; | ||
|
||
SmallVector<Attribute> operands; | ||
for (auto operand : instr->getOperands()) { | ||
auto attr = state.lookup(operand); | ||
if (!attr) | ||
return failure(); | ||
|
||
operands.push_back(attr); | ||
} | ||
|
||
instr.printInstructionAssembly(os, operands); | ||
os << "\n"; | ||
|
||
if (!useBinary) | ||
return success(); | ||
|
||
os << llvm::indent(4); | ||
// TODO: don't hardcode '.word' | ||
os << ".word 0x"; | ||
instr.printInstructionBinary(os, operands); | ||
os << "\n"; | ||
|
||
return success(); | ||
} | ||
|
||
LogicalResult emitTest(rtg::TestOp test, bool emitHeaderFooter = false) { | ||
if (emitHeaderFooter) | ||
os << "// Begin of " << test.getSymName() << "\n\n"; | ||
|
||
for (auto &op : *test.getBody()) { | ||
if (op.hasTrait<OpTrait::ConstantLike>()) { | ||
SmallVector<OpFoldResult> results; | ||
if (failed(op.fold(results))) | ||
return failure(); | ||
|
||
for (auto [val, res] : llvm::zip(op.getResults(), results)) { | ||
auto attr = res.dyn_cast<Attribute>(); | ||
if (!attr) | ||
return failure(); | ||
|
||
state[val] = attr; | ||
} | ||
|
||
continue; | ||
} | ||
|
||
if (auto instr = dyn_cast<InstructionOpInterface>(&op)) { | ||
if (failed(emitInstruction(instr))) | ||
return failure(); | ||
|
||
continue; | ||
} | ||
|
||
return op.emitError("emitter unknown RTG operation"); | ||
} | ||
|
||
state.clear(); | ||
|
||
if (emitHeaderFooter) | ||
os << "\n// End of " << test.getSymName() << "\n\n"; | ||
|
||
return success(); | ||
} | ||
|
||
private: | ||
/// Output Stream. | ||
llvm::raw_ostream &os; | ||
|
||
/// Instructions to emit in binary. | ||
const llvm::StringSet<llvm::MallocAllocator> &unsupportedInstr; | ||
|
||
/// Evaluated values. | ||
DenseMap<Value, Attribute> state; | ||
}; | ||
|
||
} // namespace | ||
|
||
static void parseUnsupportedInstructionsFile( | ||
const std::string &unsupportedInstructionsFile, | ||
llvm::StringSet<llvm::MallocAllocator> &unsupportedInstrs) { | ||
if (!unsupportedInstructionsFile.empty()) { | ||
std::ifstream input(unsupportedInstructionsFile); | ||
std::string token; | ||
while (std::getline(input, token, ',')) { | ||
auto trimmed = StringRef(token).trim(); | ||
if (!trimmed.empty()) | ||
unsupportedInstrs.insert(trimmed); | ||
} | ||
} | ||
} | ||
|
||
static std::unique_ptr<llvm::ToolOutputFile> | ||
createOutputFile(StringRef filename, StringRef dirname, | ||
function_ref<InFlightDiagnostic()> emitError) { | ||
// Determine the output path from the output directory and filename. | ||
SmallString<128> outputFilename(dirname); | ||
appendPossiblyAbsolutePath(outputFilename, filename); | ||
auto outputDir = llvm::sys::path::parent_path(outputFilename); | ||
|
||
// Create the output directory if needed. | ||
std::error_code error = llvm::sys::fs::create_directories(outputDir); | ||
if (error) { | ||
emitError() << "cannot create output directory \"" << outputDir | ||
<< "\": " << error.message(); | ||
return {}; | ||
} | ||
|
||
// Open the output file. | ||
std::string errorMessage; | ||
auto output = mlir::openOutputFile(outputFilename, &errorMessage); | ||
if (!output) | ||
emitError() << errorMessage; | ||
return output; | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
// EmitRTGISAAssemblyPass | ||
//===----------------------------------------------------------------------===// | ||
|
||
namespace { | ||
struct EmitRTGISAAssemblyPass | ||
: public rtg::impl::EmitRTGISAAssemblyPassBase<EmitRTGISAAssemblyPass> { | ||
using Base::Base; | ||
|
||
void runOnOperation() override; | ||
/// Emit each 'rtg.test' into a separate file using the test's name as the | ||
/// filename. | ||
LogicalResult | ||
emitSplit(const llvm::StringSet<llvm::MallocAllocator> &unsupportedInstr); | ||
/// Emit all tests into a single file (or print them to stderr if no file path | ||
/// is given). | ||
LogicalResult | ||
emit(const llvm::StringSet<llvm::MallocAllocator> &unsupportedInstr); | ||
}; | ||
} // namespace | ||
|
||
void EmitRTGISAAssemblyPass::runOnOperation() { | ||
if ((!path.hasValue() || path.empty()) && splitOutput) { | ||
getOperation().emitError("'split-output' option only valid in combination " | ||
"with a valid 'path' argument"); | ||
return signalPassFailure(); | ||
} | ||
|
||
llvm::StringSet<llvm::MallocAllocator> unsupportedInstr( | ||
unsupportedInstructions); | ||
parseUnsupportedInstructionsFile(unsupportedInstructionsFile.getValue(), | ||
unsupportedInstr); | ||
|
||
if (splitOutput) { | ||
if (failed(emitSplit(unsupportedInstr))) | ||
return signalPassFailure(); | ||
|
||
return; | ||
} | ||
|
||
if (failed(emit(unsupportedInstr))) | ||
return signalPassFailure(); | ||
} | ||
|
||
LogicalResult EmitRTGISAAssemblyPass::emit( | ||
const llvm::StringSet<llvm::MallocAllocator> &unsupportedInstr) { | ||
std::unique_ptr<llvm::ToolOutputFile> file; | ||
bool emitToFile = path.hasValue() && !path.empty(); | ||
if (emitToFile) { | ||
file = createOutputFile(path, std::string(), | ||
[&]() { return getOperation().emitError(); }); | ||
if (!file) | ||
return failure(); | ||
|
||
file->keep(); | ||
} | ||
|
||
Emitter emitter(emitToFile ? file->os() : llvm::errs(), unsupportedInstr); | ||
for (auto test : getOperation().getOps<TestOp>()) | ||
if (failed(emitter.emitTest(test, true))) | ||
return failure(); | ||
|
||
return success(); | ||
} | ||
|
||
LogicalResult EmitRTGISAAssemblyPass::emitSplit( | ||
const llvm::StringSet<llvm::MallocAllocator> &unsupportedInstr) { | ||
auto tests = getOperation().getOps<TestOp>(); | ||
return failableParallelForEach( | ||
&getContext(), tests.begin(), tests.end(), [&](rtg::TestOp test) { | ||
auto res = createOutputFile(test.getSymName().str() + ".s", path, | ||
[&]() { return test.emitError(); }); | ||
if (!res) | ||
return failure(); | ||
|
||
res->keep(); | ||
return Emitter(res->os(), unsupportedInstr).emitTest(test); | ||
}); | ||
} |
6 changes: 6 additions & 0 deletions
6
test/Dialect/RTG/Transform/emit-rtg-isa-assembly-split-invalid.mlir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// RUN: circt-opt --rtg-emit-isa-assembly=split-output=true --verify-diagnostics %s | ||
|
||
// expected-error @below {{'split-output' option only valid in combination with a valid 'path' argument}} | ||
module { | ||
rtg.test @test0 : !rtg.dict<> {} | ||
} |
24 changes: 24 additions & 0 deletions
24
test/Dialect/RTG/Transform/emit-rtg-isa-assembly-split.mlir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// RUN: circt-opt --rtg-emit-isa-assembly="path=%T split-output=true" %s && FileCheck %s --input-file=%T/test0.s --check-prefix=CHECK-TEST0 && FileCheck %s --input-file=%T/test1.s --check-prefix=CHECK-TEST1 | ||
// RUN: circt-opt --rtg-emit-isa-assembly="path=%t split-output=false" %s && FileCheck %s --input-file=%t --check-prefixes=CHECK,CHECK-TEST0,CHECK-TEST1 | ||
|
||
// CHECK: Begin of test0 | ||
// CHECK-EMPTY: | ||
|
||
rtg.test @test0 : !rtg.dict<> { | ||
// CHECK-TEST0: ebreak | ||
rtgtest.rv32i.ebreak | ||
} | ||
|
||
// CHECK-EMPTY: | ||
// CHECK: End of test0 | ||
// CHECK-EMPTY: | ||
// CHECK-NEXT: Begin of test1 | ||
// CHECK-EMPTY: | ||
|
||
rtg.test @test1 : !rtg.dict<> { | ||
// CHECK-TEST1: ecall | ||
rtgtest.rv32i.ecall | ||
} | ||
|
||
// CHECK-EMPTY: | ||
// CHECK-NEXT: End of test1 |
Oops, something went wrong.