From 7d89311c07cc72264b0bc02621aa56973572e9d7 Mon Sep 17 00:00:00 2001 From: Julian Oppermann Date: Thu, 15 Jul 2021 09:49:43 +0200 Subject: [PATCH] [Scheduling] Add an algorithm-independent problem test. (#1413) This allows testing the `check()`/`verify()` facilities in the Problem class by reading a given schedule from the test case. Tests like this are useful because they let us test new problem models in case a suitable algorithm is still being implemented, or can only live out-of-tree. I also separated normal and error tests, as per convention. --- test/Scheduling/TestPasses.cpp | 88 ++++++++++++++++++++++------- test/Scheduling/asap-errors.mlir | 13 +++++ test/Scheduling/problem-errors.mlir | 47 +++++++++++++++ test/Scheduling/problems.mlir | 66 ++++++++++++++++++++++ test/Scheduling/test_asap.mlir | 71 ----------------------- 5 files changed, 194 insertions(+), 91 deletions(-) create mode 100644 test/Scheduling/asap-errors.mlir create mode 100644 test/Scheduling/problem-errors.mlir create mode 100644 test/Scheduling/problems.mlir delete mode 100644 test/Scheduling/test_asap.mlir diff --git a/test/Scheduling/TestPasses.cpp b/test/Scheduling/TestPasses.cpp index ced6b3606b..ab424b068a 100644 --- a/test/Scheduling/TestPasses.cpp +++ b/test/Scheduling/TestPasses.cpp @@ -1,4 +1,4 @@ -//===- TestPasses.cpp - Test passes for scheduling algorithms -===============// +//===- TestPasses.cpp - Test passes for the scheduling infrastructure -----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This file implements test passes for scheduling algorithms. +// This file implements test passes for scheduling problems and algorithms. // //===----------------------------------------------------------------------===// @@ -20,22 +20,10 @@ using namespace circt; using namespace circt::scheduling; //===----------------------------------------------------------------------===// -// ASAPScheduler +// Construction helper methods //===----------------------------------------------------------------------===// -namespace { -struct TestASAPSchedulerPass - : public PassWrapper { - void runOnFunction() override; -}; -} // anonymous namespace - -void TestASAPSchedulerPass::runOnFunction() { - auto func = getFunction(); - OpBuilder builder(func.getContext()); - - Problem prob(func); - +static LogicalResult constructProblem(Problem &prob, FuncOp func) { // set up catch-all operator type with unit latency auto unitOpr = prob.getOrInsertOperatorType("unit"); prob.setLatency(unitOpr, 1); @@ -84,12 +72,69 @@ void TestASAPSchedulerPass::runOnFunction() { // finally, we have two integer indices in range of the operations list if (failed(prob.insertDependence( std::make_pair(ops[fromIdx], ops[toIdx])))) { - func->emitError("inserting aux dependence failed"); - return signalPassFailure(); + return func->emitError("inserting aux dependence failed"); } } } + return success(); +} + +//===----------------------------------------------------------------------===// +// (Basic) Problem +//===----------------------------------------------------------------------===// + +namespace { +struct TestProblemPass : public PassWrapper { + void runOnFunction() override; +}; +} // namespace + +void TestProblemPass::runOnFunction() { + auto func = getFunction(); + + Problem prob(func); + if (failed(constructProblem(prob, func))) { + func->emitError("problem construction failed"); + return signalPassFailure(); + } + + if (failed(prob.check())) { + func->emitError("problem check failed"); + return signalPassFailure(); + } + + // get schedule from the test case + for (auto *op : prob.getOperations()) + if (auto startTimeAttr = op->getAttrOfType("problemStartTime")) + prob.setStartTime(op, startTimeAttr.getInt()); + + if (failed(prob.verify())) { + func->emitError("problem verification failed"); + return signalPassFailure(); + } +} + +//===----------------------------------------------------------------------===// +// ASAPScheduler +//===----------------------------------------------------------------------===// + +namespace { +struct TestASAPSchedulerPass + : public PassWrapper { + void runOnFunction() override; +}; +} // anonymous namespace + +void TestASAPSchedulerPass::runOnFunction() { + auto func = getFunction(); + + Problem prob(func); + if (failed(constructProblem(prob, func))) { + func->emitError("problem construction failed"); + return signalPassFailure(); + } + if (failed(prob.check())) { func->emitError("problem check failed"); return signalPassFailure(); @@ -105,9 +150,10 @@ void TestASAPSchedulerPass::runOnFunction() { return signalPassFailure(); } + OpBuilder builder(func.getContext()); for (auto *op : prob.getOperations()) { unsigned startTime = *prob.getStartTime(op); - op->emitRemark("start time = " + std::to_string(startTime)); + op->setAttr("asapStartTime", builder.getI32IntegerAttr(startTime)); } } @@ -118,8 +164,10 @@ void TestASAPSchedulerPass::runOnFunction() { namespace circt { namespace test { void registerSchedulingTestPasses() { + PassRegistration problemTester( + "test-scheduling-problem", "Import a schedule encoded as attributes"); PassRegistration asapTester( - "test-asap-scheduler", "Emit ASAP scheduler's solution as remarks"); + "test-asap-scheduler", "Emit ASAP scheduler's solution as attributes"); } } // namespace test } // namespace circt diff --git a/test/Scheduling/asap-errors.mlir b/test/Scheduling/asap-errors.mlir new file mode 100644 index 0000000000..569507ea3d --- /dev/null +++ b/test/Scheduling/asap-errors.mlir @@ -0,0 +1,13 @@ +// RUN: circt-opt %s -test-asap-scheduler -verify-diagnostics -split-input-file + +// expected-error@+2 {{dependence cycle detected}} +// expected-error@+1 {{scheduling failed}} +func @cyclic_graph() attributes { + auxdeps = [ [0,1], [1,2], [2,3], [3,1] ] + } { + %0 = constant 0 : i32 + %1 = constant 1 : i32 + %2 = constant 2 : i32 + %3 = constant 3 : i32 + return +} diff --git a/test/Scheduling/problem-errors.mlir b/test/Scheduling/problem-errors.mlir new file mode 100644 index 0000000000..03ad88b567 --- /dev/null +++ b/test/Scheduling/problem-errors.mlir @@ -0,0 +1,47 @@ +// RUN: circt-opt %s -test-scheduling-problem -verify-diagnostics -split-input-file + +// expected-error@+2 {{Operator type 'foo' has no latency}} +// expected-error@+1 {{problem check failed}} +func @no_latency() { + %0 = constant 0 : i32 + %1 = constant { problemStartTime = 0, opr = "foo" } 1 : i32 + return +} + +// ----- + +// expected-error@+1 {{problem verification failed}} +func @no_starttime() { + %0 = constant { problemStartTime = 0 } 0 : i32 + %1 = constant 1 : i32 // expected-error {{Operation has no start time}} + return { problemStartTime = 0 } +} + +// ----- + +// expected-error@+2 {{Precedence violated for dependence}} +// expected-error@+1 {{problem verification failed}} +func @ssa_dep_violated(%a0 : i32, %a1 : i32, %a2 : i32) -> i32 attributes { + operatortypes = [ + { name = "_0", latency = 0 }, + { name = "_1", latency = 1 }, + { name = "_3", latency = 3 } + ] } { + %0 = addi %a0, %a0 { opr = "_3", problemStartTime = 0 } : i32 + %1 = addi %a1, %0 { opr = "_1", problemStartTime = 3 } : i32 + %2 = addi %1, %a2 { opr = "_0", problemStartTime = 3 } : i32 + return { opr = "_1", problemStartTime = 4 } %2 : i32 +} + +// ----- + +// expected-error@+2 {{Precedence violated for dependence}} +// expected-error@+1 {{problem verification failed}} +func @aux_dep_violated() attributes { + auxdeps = [ [0,1], [1,2], [2,3] ] + } { + %0 = constant { problemStartTime = 123 } 0 : i32 + %1 = constant { problemStartTime = 456 } 1 : i32 + %2 = constant { problemStartTime = 123 } 2 : i32 + return { problemStartTime = 456 } +} diff --git a/test/Scheduling/problems.mlir b/test/Scheduling/problems.mlir new file mode 100644 index 0000000000..e4a53c04c0 --- /dev/null +++ b/test/Scheduling/problems.mlir @@ -0,0 +1,66 @@ +// RUN: circt-opt %s -test-scheduling-problem -allow-unregistered-dialect +// RUN: circt-opt %s -test-asap-scheduler -allow-unregistered-dialect | FileCheck %s -check-prefix=ASAP + +// ASAP-LABEL: unit_latencies +func @unit_latencies(%a1 : i32, %a2 : i32, %a3 : i32, %a4 : i32) -> i32 { + // ASAP-NEXT: asapStartTime = 0 + %0 = addi %a1, %a2 { problemStartTime = 0 } : i32 + // ASAP-NEXT: asapStartTime = 1 + %1 = addi %0, %a3 { problemStartTime = 1 } : i32 + // ASAP-NEXT: asapStartTime = 2 + %2:3 = "more.results"(%0, %1) { problemStartTime = 2 } : (i32, i32) -> (i32, i32, i32) + // ASAP-NEXT: asapStartTime = 3 + %3 = addi %a4, %2#1 { problemStartTime = 3 } : i32 + // ASAP-NEXT: asapStartTime = 3 + %4 = addi %2#0, %2#2 { problemStartTime = 4 } : i32 + // ASAP-NEXT: asapStartTime = 4 + %5 = addi %3, %3 { problemStartTime = 4 } : i32 + // ASAP-NEXT: asapStartTime = 5 + %6 = "more.operands"(%3, %4, %5) { problemStartTime = 6 } : (i32, i32, i32) -> i32 + // ASAP-NEXT: asapStartTime = 6 + return { problemStartTime = 7 } %6 : i32 +} + +// ASAP-LABEL: arbitrary_latencies +func @arbitrary_latencies(%v : complex) -> f32 attributes { + operatortypes = [ + { name = "extr", latency = 0 }, + { name = "add", latency = 3 }, + { name = "mult", latency = 6 }, + { name = "sqrt", latency = 10 } + ] } { + // ASAP-NEXT: asapStartTime = 0 + %0 = "complex.re"(%v) { opr = "extr", problemStartTime = 0 } : (complex) -> f32 + // ASAP-NEXT: asapStartTime = 0 + %1 = "complex.im"(%v) { opr = "extr", problemStartTime = 10 } : (complex) -> f32 + // ASAP-NEXT: asapStartTime = 0 + %2 = mulf %0, %0 { opr = "mult", problemStartTime = 20 } : f32 + // ASAP-NEXT: asapStartTime = 0 + %3 = mulf %1, %1 { opr = "mult", problemStartTime = 30 } : f32 + // ASAP-NEXT: asapStartTime = 6 + %4 = addf %2, %3 { opr = "add", problemStartTime = 40 } : f32 + // ASAP-NEXT: asapStartTime = 9 + %5 = "math.sqrt"(%4) { opr = "sqrt", problemStartTime = 50 } : (f32) -> f32 + // ASAP-NEXT: asapStartTime = 19 + return { problemStartTime = 60 } %5 : f32 +} + +// ASAP-LABEL: auxiliary_dependences +func @auxiliary_dependences() attributes { auxdeps = [ + [0,1], [0,2], [2,3], [3,4], [3,6], [4,5], [5,6] + ] } { + // ASAP-NEXT: asapStartTime = 0 + %0 = constant { problemStartTime = 0 } 0 : i32 + // ASAP-NEXT: asapStartTime = 1 + %1 = constant { problemStartTime = 1 } 1 : i32 + // ASAP-NEXT: asapStartTime = 1 + %2 = constant { problemStartTime = 2 } 2 : i32 + // ASAP-NEXT: asapStartTime = 2 + %3 = constant { problemStartTime = 3 } 3 : i32 + // ASAP-NEXT: asapStartTime = 3 + %4 = constant { problemStartTime = 4 } 4 : i32 + // ASAP-NEXT: asapStartTime = 4 + %5 = constant { problemStartTime = 5 } 5 : i32 + // ASAP-NEXT: asapStartTime = 5 + return { problemStartTime = 6 } +} diff --git a/test/Scheduling/test_asap.mlir b/test/Scheduling/test_asap.mlir deleted file mode 100644 index 05e882094d..0000000000 --- a/test/Scheduling/test_asap.mlir +++ /dev/null @@ -1,71 +0,0 @@ -// RUN: circt-opt -test-asap-scheduler -verify-diagnostics -allow-unregistered-dialect %s - -func @test_asap1(%a1 : i32, %a2 : i32, %a3 : i32, %a4 : i32) -> i32 { - // expected-remark@+1 {{start time = 0}} - %0 = addi %a1, %a2 : i32 - // expected-remark@+1 {{start time = 1}} - %1 = addi %0, %a3 : i32 - // expected-remark@+1 {{start time = 1}} - %2 = addi %a4, %0 : i32 - // expected-remark@+1 {{start time = 2}} - %3 = addi %2, %1 : i32 - // expected-remark@+1 {{start time = 3}} - %4 = addi %3, %3 : i32 - // expected-remark@+1 {{start time = 4}} - %5 = "more.operands"(%0, %1, %2, %3, %4) : (i32, i32, i32, i32, i32) -> i32 - // expected-remark@+1 {{start time = 5}} - return %5 : i32 -} - -func @test_asap2(%v : complex) -> f32 attributes { operatortypes = [ - { name = "extr", latency = 0 }, - { name = "add", latency = 3 }, - { name = "mult", latency = 6 }, - { name = "sqrt", latency = 10 } - ] } { - // expected-remark@+1 {{start time = 0}} - %0 = "complex.re"(%v) { opr = "extr" } : (complex) -> f32 - // expected-remark@+1 {{start time = 0}} - %1 = "complex.im"(%v) { opr = "extr" } : (complex) -> f32 - // expected-remark@+1 {{start time = 0}} - %2 = mulf %0, %0 { opr = "mult" } : f32 - // expected-remark@+1 {{start time = 0}} - %3 = mulf %1, %1 { opr = "mult" } : f32 - // expected-remark@+1 {{start time = 6}} - %4 = addf %2, %3 { opr = "add" } : f32 - // expected-remark@+1 {{start time = 9}} - %5 = "math.sqrt"(%4) { opr = "sqrt" } : (f32) -> f32 - // expected-remark@+1 {{start time = 19}} - return %5 : f32 -} - -func @test_asap3() attributes { auxdeps = [ - [0,1], [0,2], [2,3], [3,4], [3,6], [4,5], [5,6] - ] } { - // expected-remark@+1 {{start time = 0}} - %0 = constant 0 : i32 - // expected-remark@+1 {{start time = 1}} - %1 = constant 1 : i32 - // expected-remark@+1 {{start time = 1}} - %2 = constant 2 : i32 - // expected-remark@+1 {{start time = 2}} - %3 = constant 3 : i32 - // expected-remark@+1 {{start time = 3}} - %4 = constant 4 : i32 - // expected-remark@+1 {{start time = 4}} - %5 = constant 5 : i32 - // expected-remark@+1 {{start time = 5}} - return -} - -// expected-error@+2 {{dependence cycle detected}} -// expected-error@+1 {{scheduling failed}} -func @test_asap4() attributes { auxdeps = [ - [0,1], [1,2], [2,3], [3,1] - ] } { - %0 = constant 0 : i32 - %1 = constant 1 : i32 - %2 = constant 2 : i32 - %3 = constant 3 : i32 - return -}