[Handshake] Add sync op (#3912)

A `SyncOp` will make its results available once all inputs have arrived.
This commit is contained in:
Christian Ulmann 2022-09-16 13:27:39 +02:00 committed by GitHub
parent 39cefff2e6
commit d18142046b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 2 deletions

View File

@ -810,6 +810,33 @@ def JoinOp : Handshake_Op<"join", [
let hasCustomAssemblyFormat = 1;
}
def SyncOp : Handshake_Op<"sync", [
HasClock, DeclareOpInterfaceMethods<ExecutableOpInterface>,
DeclareOpInterfaceMethods<GeneralOpInterface>,
AllTypesMatch<["operands", "results"]>
]> {
let summary = "sync operation";
let description = [{
Synchronizes an arbitrary set of inputs. Synchronization implies applying
join semantics in between all in- and output ports.
Example:
```mlir
%aSynced, %bSynced, %cSynced = sync %a, %b, %c : i32, i1, none
```
}];
let arguments = (ins Variadic<AnyType> : $operands);
let results = (outs Variadic<AnyType> : $results);
let skipDefaultBuilders = 1;
let builders = [OpBuilder<
(ins "ValueRange":$operands), [{
$_state.addOperands(operands);
$_state.addTypes(operands.getTypes());
}]>];
let assemblyFormat = [{ $operands attr-dict `:` type($operands)}];
}
def I4 : I<4>;
def I4Attr : SignlessIntegerAttrBase<I4, "4-bit integer attribute">;

View File

@ -34,8 +34,8 @@ public:
BranchOp, BufferOp, ConditionalBranchOp, ConstantOp, ControlMergeOp,
EndOp, ForkOp, FuncOp, InstanceOp, JoinOp, LazyForkOp, LoadOp,
MemoryOp, ExternalMemoryOp, MergeOp, MuxOp, ReturnOp, SinkOp,
handshake::SelectOp, SourceOp, StartOp, StoreOp, TerminatorOp,
PackOp, UnpackOp>([&](auto opNode) -> ResultType {
handshake::SelectOp, SourceOp, StartOp, StoreOp, SyncOp,
TerminatorOp, PackOp, UnpackOp>([&](auto opNode) -> ResultType {
return thisCast->visitHandshake(opNode, args...);
})
.Default([&](auto opNode) -> ResultType {
@ -83,6 +83,7 @@ public:
HANDLE(SourceOp);
HANDLE(StartOp);
HANDLE(StoreOp);
HANDLE(SyncOp);
HANDLE(TerminatorOp);
HANDLE(PackOp);
HANDLE(UnpackOp);

View File

@ -0,0 +1,10 @@
// RUN: handshake-runner %s | FileCheck %s
// CHECK: 0 42
handshake.func @main(%ctrl: none) -> (i64, i64, none) {
%ctrlF:3 = fork [3] %ctrl : none
%c0 = constant %ctrlF#0 {value = 0 : i64} : i64
%c42 = constant %ctrlF#1 {value = 42 : i64} : i64
%out:3 = sync %c0, %c42, %ctrlF#2 : i64, i64, none
return %out#0, %out#1, %out#2 : i64, i64, none
}

View File

@ -438,6 +438,19 @@ bool JoinOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 1);
}
void SyncOp::execute(std::vector<llvm::Any> &ins,
std::vector<llvm::Any> &outs) {
outs = ins;
}
bool SyncOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
llvm::DenseMap<mlir::Value, double> &timeMap,
std::vector<std::vector<llvm::Any>> & /*store*/,
std::vector<mlir::Value> &scheduleList) {
return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 1);
}
void StoreOp::execute(std::vector<llvm::Any> &ins,
std::vector<llvm::Any> &outs) {
// Forward the address and data to the memory op.

View File

@ -0,0 +1,41 @@
// RUN: circt-opt -split-input-file %s | circt-opt | FileCheck %s
// CHECK-LABEL: handshake.func @single_in(
// CHECK-SAME: %[[VAL_0:.*]]: none, ...) -> none
// CHECK: %[[VAL_1:.*]] = sync %[[VAL_0]] : none
// CHECK: return %[[VAL_1]] : none
// CHECK: }
handshake.func @single_in(%in0: none) -> none {
%ctrlOut = sync %in0 : none
return %ctrlOut : none
}
// -----
// CHECK-LABEL: handshake.func @multi_in(
// CHECK-SAME: %[[VAL_0:.*]]: none,
// CHECK-SAME: %[[VAL_1:.*]]: i32,
// CHECK-SAME: %[[VAL_2:.*]]: i512, ...) -> (none, i32, i512)
// CHECK: %[[VAL_3:.*]]:3 = sync %[[VAL_0]], %[[VAL_1]], %[[VAL_2]] : none, i32, i512
// CHECK: return %[[VAL_3]]#0, %[[VAL_3]]#1, %[[VAL_3]]#2 : none, i32, i512
// CHECK: }
handshake.func @multi_in(%in0: none, %in1: i32, %in2: i512) -> (none, i32, i512) {
%out:3 = sync %in0, %in1, %in2 : none, i32, i512
return %out#0, %out#1, %out#2 : none, i32, i512
}
// -----
// CHECK-LABEL: handshake.func @attr(
// CHECK-SAME: %[[VAL_0:.*]]: i64,
// CHECK-SAME: %[[VAL_1:.*]]: i32, ...) -> (i64, i32)
// CHECK: %[[VAL_2:.*]]:2 = sync %[[VAL_0]], %[[VAL_1]] {test_attr = "attribute"} : i64, i32
// CHECK: return %[[VAL_2]]#0, %[[VAL_2]]#1 : i64, i32
// CHECK: }
handshake.func @attr(%in0: i64, %in1: i32) -> (i64, i32) {
%out:2 = sync %in0, %in1 {test_attr = "attribute"} : i64, i32
return %out#0, %out#1 : i64, i32
}