Skip to content
223 changes: 220 additions & 3 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,8 @@ def StoreOp : CIR_Op<"store", [
//===----------------------------------------------------------------------===//

def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp", "IfOp",
"DoWhileOp", "WhileOp", "ForOp"]>,
"SwitchOp", "DoWhileOp","WhileOp",
"ForOp", "CaseOp"]>,
Terminator]> {
let summary = "Return from function";
let description = [{
Expand Down Expand Up @@ -609,8 +610,9 @@ def ConditionOp : CIR_Op<"condition", [
//===----------------------------------------------------------------------===//

def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
ParentOneOf<["IfOp", "ScopeOp", "WhileOp",
"ForOp", "DoWhileOp"]>]> {
ParentOneOf<["IfOp", "ScopeOp", "SwitchOp",
"WhileOp", "ForOp", "CaseOp",
"DoWhileOp"]>]> {
let summary = "Represents the default branching behaviour of a region";
let description = [{
The `cir.yield` operation terminates regions on different CIR operations,
Expand Down Expand Up @@ -753,6 +755,221 @@ def ScopeOp : CIR_Op<"scope", [
];
}

//===----------------------------------------------------------------------===//
// SwitchOp
//===----------------------------------------------------------------------===//

def CaseOpKind_DT : I32EnumAttrCase<"Default", 1, "default">;
def CaseOpKind_EQ : I32EnumAttrCase<"Equal", 2, "equal">;
def CaseOpKind_AO : I32EnumAttrCase<"Anyof", 3, "anyof">;
def CaseOpKind_RG : I32EnumAttrCase<"Range", 4, "range">;

def CaseOpKind : I32EnumAttr<
"CaseOpKind",
"case kind",
[CaseOpKind_DT, CaseOpKind_EQ, CaseOpKind_AO, CaseOpKind_RG]> {
let cppNamespace = "::cir";
}

def CaseOp : CIR_Op<"case", [
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
RecursivelySpeculatable, AutomaticAllocationScope]> {
let summary = "Case operation";
let description = [{
The `cir.case` operation represents a case within a C/C++ switch.
The `cir.case` operation must be in a `cir.switch` operation directly
or indirectly.

The `cir.case` have 4 kinds:
- `equal, <constant>`: equality of the second case operand against the
condition.
- `anyof, [constant-list]`: equals to any of the values in a subsequent
following list.
- `range, [lower-bound, upper-bound]`: the condition is within the closed
interval.
- `default`: any other value.

Each case region must be explicitly terminated.
}];

let arguments = (ins ArrayAttr:$value, CaseOpKind:$kind);
let regions = (region AnyRegion:$caseRegion);

let assemblyFormat = "`(` $kind `,` $value `)` $caseRegion attr-dict";

let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins "mlir::ArrayAttr":$value,
"CaseOpKind":$kind,
"mlir::OpBuilder::InsertPoint &":$insertPoint)>
];
}

def SwitchOp : CIR_Op<"switch",
[SameVariadicOperandSize,
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments]> {
let summary = "Switch operation";
let description = [{
The `cir.switch` operation represents C/C++ switch functionality for
conditionally executing multiple regions of code. The operand to an switch
is an integral condition value.

The set of `cir.case` operations and their enclosing `cir.switch`
represents the semantics of a C/C++ switch statement. Users can use
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
represents the semantics of a C/C++ switch statement. Users can use
represent the semantics of a C/C++ switch statement. Users can use

Sorry I missed this one in my earlier grammar corrections.

`collectCases(llvm::SmallVector<CaseOp> &cases)` to collect the `cir.case`
operation in the `cir.switch` operation easily.

The `cir.case` operations doesn't have to be in the region of `cir.switch`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `cir.case` operations doesn't have to be in the region of `cir.switch`
The `cir.case` operations don't have to be in the region of `cir.switch`

This one too.

directly. However, when all the `cir.case` operations lives in the region
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
directly. However, when all the `cir.case` operations lives in the region
directly. However, when all the `cir.case` operations live in the region

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

of `cir.switch` directly and there is no other operations except the ending
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
of `cir.switch` directly and there is no other operations except the ending
of `cir.switch` directly and there are no other operations except the ending

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

`cir.yield` operation in the region of `cir.switch` directly, we call the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`cir.yield` operation in the region of `cir.switch` directly, we call the
`cir.yield` operation in the region of `cir.switch` directly, we say the

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

`cir.switch` operation is in a simple form. Users can use
`bool isSimpleForm(llvm::SmallVector<CaseOp> &cases)` member function to
detect if the `cir.switch` operation is in a simple form. The simple form
makes analysis easier to handle the `cir.switch` operation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
makes analysis easier to handle the `cir.switch` operation
makes it easier for analyses to handle the `cir.switch` operation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

and makes the boundary to give up pretty clear.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
and makes the boundary to give up pretty clear.
and makes the boundary to give up clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


To make the simple form as common as possible, CIR code generation attaches
operations corresponding to the statements that lives between top level
cases into the closest `cir.case` operation.

For example,

```
switch(int cond) {
case 4:
a++;

b++;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the blank line and the difference in indentation necessary for this example? I'd be less confused if it was just this:

      case 4:
        a++;
        b++;
      case 5:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

case 5;
c++;

...
}
```

The statement `b++` is not a sub-statement of the case statement `case 4`.
But to make the generated `cir.switch` a simple form, we will attach the
statement `b++` into the closest `cir.case` operation. So that the generated
code will be like:

```
cir.switch(int cond) {
cir.case(equal, 4) {
a++;
b++;
cir.yield
}
cir.case(equal, 5) {
c++;
cir.yield
}
...
}
```

For the same reason, we will hoist the case statement as the substatement
of another case statement so that they will be in the same level. For
example,

```
switch(int cond) {
case 4:
default;
case 5;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
case 5;
case 5:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

a++;
...
}
```

will be generated as

```
cir.switch(int cond) {
cir.case(equal, 4) {
cir.yield
}
cir.case(default) {
cir.yield
}
cir.case(equal, 5) {
a++;
cir.yield
}
...
}
```

The cir.switch might not be considered "simple" if any of the following is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The cir.switch might not be considered "simple" if any of the following is
The cir.switch is not be considered "simple" if any of the following is

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

true:
- There are case statements of the switch statement lives in other scopes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- There are case statements of the switch statement lives in other scopes
- There are case statements of the switch statement that are in a scope

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

other than the top level compound statement scope. Note that a case
statement itself doesn't form a scope.
- The sub-statement of the switch statement is not a compound statement.
- There are codes before the first case statement. For example,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- There are codes before the first case statement. For example,
- There is any code before the first case statement. For example,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


```
switch(int cond) {
l:
b++;

case 4:
a++;
break;

case 5:
goto l;
...
}
```

the generated CIR for this non-simple switch would be:

```
cir.switch(int cond) {
cir.label "l"
b++;
cir.case(4) {
a++;
cir.break
}
cir.case(5) {
goto "l"
}
cir.yield
}
```
}];

let arguments = (ins CIR_IntType:$condition);

let regions = (region AnyRegion:$body);

let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins "mlir::Value":$condition,
"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location, mlir::OperationState &)>":$switchBuilder)>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you reformat this to fit within 80 characters? Maybe by defining an alias similar to BuilderCallbackRef (as defined in CIRDialect.h)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

];

let assemblyFormat = [{
custom<SwitchOp>(
$body, $condition, type($condition)
)
attr-dict
}];

let extraClassDeclaration = [{
// Collect cases in the switch.
void collectCases(llvm::SmallVector<CaseOp> &cases);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void collectCases(llvm::SmallVector<CaseOp> &cases);
void collectCases(llvm::SmallVectorImpl<CaseOp> &cases);

I believe that using SmallVector directly gets the default size template argument and will cause a copy if the function is called with an object that was constructed with any other size argument. See the note here: https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// Check if the switch is in a simple form.
// If yes, collect the cases to \param cases.
// This is an expensive and need to be used with caution.
bool isSimpleForm(llvm::SmallVector<CaseOp> &cases);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool isSimpleForm(llvm::SmallVector<CaseOp> &cases);
bool isSimpleForm(llvm::SmallVectorImpl<CaseOp> &cases);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}];
}

//===----------------------------------------------------------------------===//
// BrOp
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ struct MissingFeatures {
static bool targetSpecificCXXABI() { return false; }
static bool moduleNameHash() { return false; }
static bool setDSOLocal() { return false; }
static bool foldCaseStmt() { return false; }

// Missing types
static bool dataMemberType() { return false; }
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class CIRGenFunction : public CIRGenTypeCache {
/// declarations.
DeclMapTy localDeclMap;

/// The type of the condition for the emitting switch statement.
llvm::SmallVector<mlir::Type, 2> condTypeStack;

clang::ASTContext &getContext() const { return cgm.getASTContext(); }

CIRGenBuilderTy &getBuilder() { return builder; }
Expand Down Expand Up @@ -469,6 +472,16 @@ class CIRGenFunction : public CIRGenTypeCache {
ReturnValueSlot returnValue = ReturnValueSlot());
CIRGenCallee emitCallee(const clang::Expr *e);

template <typename T>
mlir::LogicalResult emitCaseDefaultCascade(const T *stmt, mlir::Type condType,
mlir::ArrayAttr value,
cir::CaseOpKind kind,
bool buildingTopLevelCase);

mlir::LogicalResult emitCaseStmt(const clang::CaseStmt &s,
mlir::Type condType,
bool buildingTopLevelCase);

mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s);
mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);

Expand Down Expand Up @@ -595,6 +608,11 @@ class CIRGenFunction : public CIRGenTypeCache {

mlir::Value emitStoreThroughBitfieldLValue(RValue src, LValue dstresult);

mlir::LogicalResult emitSwitchBody(const clang::Stmt *s);
mlir::LogicalResult emitSwitchCase(const clang::SwitchCase &s,
bool buildingTopLevelCase);
mlir::LogicalResult emitSwitchStmt(const clang::SwitchStmt &s);

/// Given a value and its clang type, returns the value casted to its memory
/// representation.
/// Note: CIR defers most of the special casting to the final lowering passes
Expand Down
Loading
Loading