Skip to content

Commit 24998fc

Browse files
Rebased to main
1 parent e48a6a8 commit 24998fc

6 files changed

Lines changed: 765 additions & 13 deletions

File tree

docs/CoreDSLDialect.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,72 @@ Traits: `HasParent<InstructionOp>`, `NoRegionArguments`, `SingleBlock`, `Termina
849849

850850

851851

852+
### `coredsl.switch` (coredsl::SwitchOp)
853+
854+
_Switch-case operation on arbitrary integers_
855+
856+
Syntax:
857+
858+
```
859+
operation ::= `coredsl.switch` $arg attr-dict `:` type($arg) (`->` type($results)^)?
860+
custom<SwitchCases>($cases, $caseRegions) `\n`
861+
`` `default` $defaultRegion
862+
```
863+
864+
`coredsl.switch` is a modified version of `scf.index_switch` that branches
865+
to one of the given regions based on the values of the argument and the
866+
cases. As opposed to `scf.index_switch`, this operation accepts any integer
867+
as argument.
868+
869+
The operation always has a "default" region and any number of case regions
870+
denoted by integer constants. Control-flow transfers to the case region
871+
whose constant value equals the value of the argument. If the argument does
872+
not equal any of the case values, control-flow transfer to the "default"
873+
region.
874+
875+
Example:
876+
877+
```mlir
878+
%0 = coredsl.switch %arg0 : ui32 -> ui32
879+
case 2 {
880+
%1 = hwarith.constant 10 : ui32
881+
coredsl.yield %1 : ui32
882+
}
883+
case 5 {
884+
%2 = hwarith.constant 20 : ui32
885+
coredsl.yield %2 : ui32
886+
}
887+
default {
888+
%3 = hwarith.constant 30 : ui32
889+
coredsl.yield %3 : ui32
890+
}
891+
```
892+
893+
Traits: `RecursiveMemoryEffects`, `SingleBlockImplicitTerminator<coredsl::YieldOp>`, `SingleBlock`
894+
895+
Interfaces: `RegionBranchOpInterface`
896+
897+
#### Attributes:
898+
899+
<table>
900+
<tr><th>Attribute</th><th>MLIR Type</th><th>Description</th></tr>
901+
<tr><td><code>cases</code></td><td>::mlir::ArrayAttr</td><td>array attribute</td></tr>
902+
</table>
903+
904+
#### Operands:
905+
906+
| Operand | Description |
907+
| :-----: | ----------- |
908+
| `arg` | integer |
909+
910+
#### Results:
911+
912+
| Result | Description |
913+
| :----: | ----------- |
914+
| `results` | variadic of any type |
915+
916+
917+
852918
### `coredsl.xor` (coredsl::XorOp)
853919

854920
_Bitwise XOR operator._
@@ -890,6 +956,39 @@ Effects: `MemoryEffects::Effect{}`
890956

891957

892958

959+
### `coredsl.yield` (coredsl::YieldOp)
960+
961+
_Loop yield and termination operation_
962+
963+
Syntax:
964+
965+
```
966+
operation ::= `coredsl.yield` attr-dict ($results^ `:` type($results))?
967+
```
968+
969+
The `coredsl.yield` operation is equivalent to the `scf.yield` operation,
970+
but only works for `coredsl.switch`
971+
If `coredsl.yield` has any operands, the operands must match the parent
972+
operation's results.
973+
If the parent operation defines no values, then the `coredsl.yield` may be
974+
left out in the custom syntax and the builders will insert one implicitly.
975+
Otherwise, it has to be present in the syntax to indicate which values are
976+
yielded.
977+
978+
Traits: `AlwaysSpeculatableImplTrait`, `HasParent<SwitchOp>`, `ReturnLike`, `Terminator`
979+
980+
Interfaces: `ConditionallySpeculatable`, `NoMemoryEffect (MemoryEffectOpInterface)`, `RegionBranchTerminatorOpInterface`
981+
982+
Effects: `MemoryEffects::Effect{}`
983+
984+
#### Operands:
985+
986+
| Operand | Description |
987+
| :-----: | ----------- |
988+
| `results` | variadic of any type |
989+
990+
991+
893992
## Enums
894993

895994
### AddressSpaceAccessMode

include/shortnail/Dialect/CoreDSL/CoreDSLOps.td

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,4 +685,92 @@ def CastOp : CoreDSL_Op<"cast", [Pure]> {
685685
let hasVerifier = 1;
686686
}
687687

688+
//===----------------------------------------------------------------------===//
689+
// YieldOp
690+
//===----------------------------------------------------------------------===//
691+
692+
def YieldOp : CoreDSL_Op<"yield", [Pure, ReturnLike, Terminator,
693+
ParentOneOf<["SwitchOp"]>]> {
694+
let summary = "loop yield and termination operation";
695+
let description = [{
696+
The `coredsl.yield` operation is equivalent to the `scf.yield` operation,
697+
but only works for `coredsl.switch`
698+
If `coredsl.yield` has any operands, the operands must match the parent
699+
operation's results.
700+
If the parent operation defines no values, then the `coredsl.yield` may be
701+
left out in the custom syntax and the builders will insert one implicitly.
702+
Otherwise, it has to be present in the syntax to indicate which values are
703+
yielded.
704+
}];
705+
706+
let arguments = (ins Variadic<AnyType>:$results);
707+
let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
708+
709+
let assemblyFormat =
710+
[{ attr-dict ($results^ `:` type($results))? }];
711+
}
712+
713+
def SwitchOp : CoreDSL_Op<"switch", [RecursiveMemoryEffects,
714+
SingleBlockImplicitTerminator<"coredsl::YieldOp">,
715+
DeclareOpInterfaceMethods<RegionBranchOpInterface,
716+
["getRegionInvocationBounds",
717+
"getEntrySuccessorRegions"]>]> {
718+
let summary = "switch-case operation on arbitrary integers";
719+
let description = [{
720+
`coredsl.switch` is a modified version of `scf.index_switch` that branches
721+
to one of the given regions based on the values of the argument and the
722+
cases. As opposed to `scf.index_switch`, this operation accepts any integer
723+
as argument.
724+
725+
The operation always has a "default" region and any number of case regions
726+
denoted by integer constants. Control-flow transfers to the case region
727+
whose constant value equals the value of the argument. If the argument does
728+
not equal any of the case values, control-flow transfer to the "default"
729+
region.
730+
731+
Example:
732+
733+
```mlir
734+
%0 = coredsl.switch %arg0 : ui32 -> ui32
735+
case 2 {
736+
%1 = hwarith.constant 10 : ui32
737+
coredsl.yield %1 : ui32
738+
}
739+
case 5 {
740+
%2 = hwarith.constant 20 : ui32
741+
coredsl.yield %2 : ui32
742+
}
743+
default {
744+
%3 = hwarith.constant 30 : ui32
745+
coredsl.yield %3 : ui32
746+
}
747+
```
748+
}];
749+
750+
let arguments = (ins AnyInteger:$arg, ArrayAttr:$cases);
751+
let results = (outs Variadic<AnyType>:$results);
752+
let regions = (region SizedRegion<1>:$defaultRegion,
753+
VariadicRegion<SizedRegion<1>>:$caseRegions);
754+
755+
let assemblyFormat = [{
756+
$arg attr-dict `:` type($arg) (`->` type($results)^)?
757+
custom<SwitchCases>($cases, $caseRegions) `\n`
758+
`` `default` $defaultRegion
759+
}];
760+
761+
let extraClassDeclaration = [{
762+
/// Get the number of cases.
763+
unsigned getNumCases();
764+
765+
/// Get the default region body.
766+
Block &getDefaultBlock();
767+
768+
/// Get the body of a case region.
769+
Block &getCaseBlock(unsigned idx);
770+
}];
771+
772+
let hasCanonicalizer = 1;
773+
let hasVerifier = 1;
774+
}
775+
688776
#endif // SHORTNAIL_DIALECT_COREDSL_COREDSLOPS_TD

lib/Conversion/CoreDSLToPy/CoreDSLToPy.cpp

Lines changed: 100 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,26 @@ handleWordWiseAccess(Op op,
153153
}
154154
}
155155

156+
// Handles both scf::YieldOp and coredsl::YieldOp, as their handling is
157+
// identical
158+
template <typename YieldOp>
159+
static WalkResult emitYieldOp(mlir::raw_indented_ostream &os, YieldOp yieldOp) {
160+
os << "return ";
161+
bool first = true;
162+
for (auto res : yieldOp.getResults()) {
163+
if (!first)
164+
os << ", ";
165+
166+
valToPy(os, res);
167+
first = false;
168+
}
169+
os << "\n";
170+
return WalkResult::advance();
171+
}
172+
156173
static WalkResult emitCoreDSLOp(mlir::raw_indented_ostream &os, Operation *op) {
157174
assert(isa<coredsl::CoreDSLDialect>(op->getDialect()));
175+
static unsigned uniqueSwitchNumber = 0;
158176

159177
return TypeSwitch<Operation *, WalkResult>(op)
160178
.Case<coredsl::SetOp>([&](auto setOp) {
@@ -468,6 +486,86 @@ static WalkResult emitCoreDSLOp(mlir::raw_indented_ostream &os, Operation *op) {
468486
<< (targetType.isSigned() ? "True" : "False") << ")\n";
469487
return WalkResult::advance();
470488
})
489+
.Case<coredsl::SwitchOp>([&](coredsl::SwitchOp switchOp) {
490+
const unsigned switchId = uniqueSwitchNumber++;
491+
for (unsigned i = 0, end = switchOp.getNumCases(); i < end; ++i) {
492+
Block &caseBlock = switchOp.getCaseBlock(i);
493+
// Emit each case as a helper function. This way each
494+
// switch has the form
495+
// 'ret1, ret2, ..., retn = helper_function', thereby
496+
// making handling of the results easier and allowing
497+
// us to just emit a return for every yield op
498+
os << "def helper_function_coredsl_switch_" << switchId << "_case_"
499+
<< i << "():\n";
500+
os.indent();
501+
auto res = caseBlock.template walk<WalkOrder::PreOrder>(
502+
[&os](Operation *op) { return emitOp(os, op); });
503+
if (res.wasInterrupted())
504+
return res;
505+
os.unindent();
506+
}
507+
os << "def helper_function_coresl_switch_" << switchId
508+
<< "_default_case():\n";
509+
os.indent();
510+
auto res = switchOp.getDefaultBlock().walk<WalkOrder::PreOrder>(
511+
[&os](Operation *op) { return emitOp(os, op); });
512+
if (res.wasInterrupted())
513+
return res;
514+
os.unindent();
515+
bool first = true;
516+
for (const auto &[idx, attr] : llvm::enumerate(switchOp.getCases())) {
517+
const IntegerAttr &intAttr = cast<IntegerAttr>(attr);
518+
const APInt &caseValAPInt = intAttr.getValue();
519+
const unsigned bitWidth = switchOp.getArg().getType().getWidth();
520+
const auto signedness = switchOp.getArg().getType().getSignedness();
521+
assert(signedness != IntegerType::Signless);
522+
const APSInt caseVal = APSInt(caseValAPInt.sextOrTrunc(bitWidth),
523+
signedness == IntegerType::Signed);
524+
if (first)
525+
os << "if ";
526+
else
527+
os << "elif ";
528+
first = false;
529+
valToPy(os, switchOp.getArg());
530+
os << "." << hwarith::ICmpPredicate::eq << "(";
531+
os << intToPy(caseVal) << "):\n";
532+
os.indent();
533+
if (switchOp.getNumResults() > 0) {
534+
bool first = true;
535+
for (auto res : switchOp.getResults()) {
536+
if (!first) {
537+
os << ", ";
538+
}
539+
first = false;
540+
valToPy(os, res);
541+
}
542+
os << " = ";
543+
}
544+
os << "helper_function_coredsl_switch_" << switchId << "_case_" << idx
545+
<< "()\n";
546+
os.unindent();
547+
}
548+
os << "else:\n";
549+
os.indent();
550+
if (switchOp.getNumResults() > 0) {
551+
bool first = true;
552+
for (auto res : switchOp.getResults()) {
553+
if (!first) {
554+
os << ", ";
555+
}
556+
first = false;
557+
valToPy(os, res);
558+
}
559+
os << " = ";
560+
}
561+
os << "helper_function_coredsl_switch_" << switchId
562+
<< "_default_case()\n";
563+
os.unindent();
564+
// Skip the child scopes, as we already visited them
565+
return WalkResult::skip();
566+
})
567+
.Case<coredsl::YieldOp>(
568+
[&](coredsl::YieldOp yieldOp) { return emitYieldOp(os, yieldOp); })
471569
.Default([&](auto _) {
472570
op->emitError() << "CoreDSLToPy::emitCoreDSLOp lacks emission code for "
473571
"this operation!";
@@ -646,19 +744,8 @@ static WalkResult emitSCFOp(mlir::raw_indented_ostream &os, Operation *op) {
646744

647745
return WalkResult::skip();
648746
})
649-
.Case<scf::YieldOp>([&](auto yieldOp) {
650-
os << "return ";
651-
bool first = true;
652-
for (auto res : yieldOp.getResults()) {
653-
if (!first)
654-
os << ", ";
655-
656-
valToPy(os, res);
657-
first = false;
658-
}
659-
os << "\n";
660-
return WalkResult::advance();
661-
})
747+
.Case<scf::YieldOp>(
748+
[&](auto yieldOp) { return emitYieldOp(os, yieldOp); })
662749
// .Case<scf::WhileOp>([&](auto whileOp) {
663750
// // TODO PITA
664751
// return WalkResult::interrupt();

0 commit comments

Comments
 (0)