|
11 | 11 | ///
|
12 | 12 | //===----------------------------------------------------------------------===//
|
13 | 13 |
|
14 |
| -#ifndef LLVM_CLANG_CIR_DIALECT_IR_CIROPS |
15 |
| -#define LLVM_CLANG_CIR_DIALECT_IR_CIROPS |
| 14 | +#ifndef CLANG_CIR_DIALECT_IR_CIROPS_TD |
| 15 | +#define CLANG_CIR_DIALECT_IR_CIROPS_TD |
16 | 16 |
|
17 | 17 | include "clang/CIR/Dialect/IR/CIRDialect.td"
|
18 | 18 | include "clang/CIR/Dialect/IR/CIRTypes.td"
|
@@ -115,6 +115,165 @@ def ConstantOp : CIR_Op<"const",
|
115 | 115 | let hasFolder = 1;
|
116 | 116 | }
|
117 | 117 |
|
| 118 | +//===----------------------------------------------------------------------===// |
| 119 | +// ReturnOp |
| 120 | +//===----------------------------------------------------------------------===// |
| 121 | + |
| 122 | +def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp"]>, |
| 123 | + Terminator]> { |
| 124 | + let summary = "Return from function"; |
| 125 | + let description = [{ |
| 126 | + The "return" operation represents a return operation within a function. |
| 127 | + The operation takes an optional operand and produces no results. |
| 128 | + The operand type must match the signature of the function that contains |
| 129 | + the operation. |
| 130 | + |
| 131 | + ```mlir |
| 132 | + func @foo() -> i32 { |
| 133 | + ... |
| 134 | + cir.return %0 : i32 |
| 135 | + } |
| 136 | + ``` |
| 137 | + }]; |
| 138 | + |
| 139 | + // The return operation takes an optional input operand to return. This |
| 140 | + // value must match the return type of the enclosing function. |
| 141 | + let arguments = (ins Variadic<CIR_AnyType>:$input); |
| 142 | + |
| 143 | + // The return operation only emits the input in the format if it is present. |
| 144 | + let assemblyFormat = "($input^ `:` type($input))? attr-dict "; |
| 145 | + |
| 146 | + // Allow building a ReturnOp with no return operand. |
| 147 | + let builders = [ |
| 148 | + OpBuilder<(ins), [{ build($_builder, $_state, std::nullopt); }]> |
| 149 | + ]; |
| 150 | + |
| 151 | + // Provide extra utility definitions on the c++ operation class definition. |
| 152 | + let extraClassDeclaration = [{ |
| 153 | + bool hasOperand() { return getNumOperands() != 0; } |
| 154 | + }]; |
| 155 | + |
| 156 | + let hasVerifier = 1; |
| 157 | +} |
| 158 | + |
| 159 | +//===----------------------------------------------------------------------===// |
| 160 | +// YieldOp |
| 161 | +//===----------------------------------------------------------------------===// |
| 162 | + |
| 163 | +def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator, |
| 164 | + ParentOneOf<["ScopeOp"]>]> { |
| 165 | + let summary = "Represents the default branching behaviour of a region"; |
| 166 | + let description = [{ |
| 167 | + The `cir.yield` operation terminates regions on different CIR operations, |
| 168 | + and it is used to represent the default branching behaviour of a region. |
| 169 | + Said branching behaviour is determinted by the parent operation. For |
| 170 | + example, a yield in a `switch-case` region implies a fallthrough, while |
| 171 | + a yield in a `cir.if` region implies a branch to the exit block, and so |
| 172 | + on. |
| 173 | + |
| 174 | + In some cases, it might yield an SSA value and the semantics of how the |
| 175 | + values are yielded is defined by the parent operation. For example, a |
| 176 | + `cir.ternary` operation yields a value from one of its regions. |
| 177 | + |
| 178 | + As a general rule, `cir.yield` must be explicitly used whenever a region has |
| 179 | + more than one block and no terminator, or within `cir.switch` regions not |
| 180 | + `cir.return` terminated. |
| 181 | + |
| 182 | + Examples: |
| 183 | + ```mlir |
| 184 | + cir.if %4 { |
| 185 | + ... |
| 186 | + cir.yield |
| 187 | + } |
| 188 | + |
| 189 | + cir.switch (%5) [ |
| 190 | + case (equal, 3) { |
| 191 | + ... |
| 192 | + cir.yield |
| 193 | + }, ... |
| 194 | + ] |
| 195 | + |
| 196 | + cir.scope { |
| 197 | + ... |
| 198 | + cir.yield |
| 199 | + } |
| 200 | + |
| 201 | + %x = cir.scope { |
| 202 | + ... |
| 203 | + cir.yield %val |
| 204 | + } |
| 205 | + |
| 206 | + %y = cir.ternary { |
| 207 | + ... |
| 208 | + cir.yield %val : i32 |
| 209 | + } : i32 |
| 210 | + ``` |
| 211 | + }]; |
| 212 | + |
| 213 | + let arguments = (ins Variadic<CIR_AnyType>:$args); |
| 214 | + let assemblyFormat = "($args^ `:` type($args))? attr-dict"; |
| 215 | + let builders = [ |
| 216 | + OpBuilder<(ins), [{ /* nothing to do */ }]>, |
| 217 | + ]; |
| 218 | +} |
| 219 | + |
| 220 | +//===----------------------------------------------------------------------===// |
| 221 | +// ScopeOp |
| 222 | +//===----------------------------------------------------------------------===// |
| 223 | + |
| 224 | +def ScopeOp : CIR_Op<"scope", [ |
| 225 | + DeclareOpInterfaceMethods<RegionBranchOpInterface>, |
| 226 | + RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments]> { |
| 227 | + let summary = "Represents a C/C++ scope"; |
| 228 | + let description = [{ |
| 229 | + `cir.scope` contains one region and defines a strict "scope" for all new |
| 230 | + values produced within its blocks. |
| 231 | + |
| 232 | + The region can contain an arbitrary number of blocks but usually defaults |
| 233 | + to one and can optionally return a value (useful for representing values |
| 234 | + coming out of C++ full-expressions) via `cir.yield`: |
| 235 | + |
| 236 | + |
| 237 | + ```mlir |
| 238 | + %rvalue = cir.scope { |
| 239 | + ... |
| 240 | + cir.yield %value |
| 241 | + } |
| 242 | + ``` |
| 243 | + |
| 244 | + The blocks can be terminated by `cir.yield`, `cir.return` or `cir.throw`. |
| 245 | + If `cir.scope` yields no value, the `cir.yield` can be left out, and |
| 246 | + will be inserted implicitly. |
| 247 | + }]; |
| 248 | + |
| 249 | + let results = (outs Optional<CIR_AnyType>:$results); |
| 250 | + let regions = (region AnyRegion:$scopeRegion); |
| 251 | + |
| 252 | + let hasVerifier = 1; |
| 253 | + let skipDefaultBuilders = 1; |
| 254 | + let assemblyFormat = [{ |
| 255 | + custom<OmittedTerminatorRegion>($scopeRegion) (`:` type($results)^)? attr-dict |
| 256 | + }]; |
| 257 | + |
| 258 | + let extraClassDeclaration = [{ |
| 259 | + /// Determine whether the scope is empty, meaning it contains a single block |
| 260 | + /// terminated by a cir.yield. |
| 261 | + bool isEmpty() { |
| 262 | + auto &entry = getRegion().front(); |
| 263 | + return getRegion().hasOneBlock() && |
| 264 | + llvm::isa<YieldOp>(entry.front()); |
| 265 | + } |
| 266 | + }]; |
| 267 | + |
| 268 | + let builders = [ |
| 269 | + // Scopes for yielding values. |
| 270 | + OpBuilder<(ins |
| 271 | + "llvm::function_ref<void(mlir::OpBuilder &, mlir::Type &, mlir::Location)>":$scopeBuilder)>, |
| 272 | + // Scopes without yielding values. |
| 273 | + OpBuilder<(ins "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$scopeBuilder)> |
| 274 | + ]; |
| 275 | +} |
| 276 | + |
118 | 277 | //===----------------------------------------------------------------------===//
|
119 | 278 | // GlobalOp
|
120 | 279 | //===----------------------------------------------------------------------===//
|
@@ -158,25 +317,86 @@ def GlobalOp : CIR_Op<"global"> {
|
158 | 317 | // FuncOp
|
159 | 318 | //===----------------------------------------------------------------------===//
|
160 | 319 |
|
161 |
| -// TODO(CIR): For starters, cir.func has only name, nothing else. The other |
162 |
| -// properties of a function will be added over time as more of ClangIR is |
163 |
| -// upstreamed. |
| 320 | +// TODO(CIR): FuncOp is still a tiny shell of what it will become. Many more |
| 321 | +// properties and attributes will be added as upstreaming continues. |
164 | 322 |
|
165 |
| -def FuncOp : CIR_Op<"func"> { |
| 323 | +def FuncOp : CIR_Op<"func", [ |
| 324 | + AutomaticAllocationScope, CallableOpInterface, FunctionOpInterface, |
| 325 | + IsolatedFromAbove |
| 326 | +]> { |
166 | 327 | let summary = "Declare or define a function";
|
167 | 328 | let description = [{
|
168 | 329 | The `cir.func` operation defines a function, similar to the `mlir::FuncOp`
|
169 | 330 | built-in.
|
170 | 331 | }];
|
171 | 332 |
|
172 |
| - let arguments = (ins SymbolNameAttr:$sym_name); |
| 333 | + let arguments = (ins SymbolNameAttr:$sym_name, |
| 334 | + TypeAttrOf<CIR_FuncType>:$function_type, |
| 335 | + OptionalAttr<DictArrayAttr>:$arg_attrs, |
| 336 | + OptionalAttr<DictArrayAttr>:$res_attrs); |
| 337 | + |
| 338 | + let regions = (region AnyRegion:$body); |
173 | 339 |
|
174 | 340 | let skipDefaultBuilders = 1;
|
175 | 341 |
|
176 |
| - let builders = [OpBuilder<(ins "llvm::StringRef":$sym_name)>]; |
| 342 | + let builders = [OpBuilder<(ins "llvm::StringRef":$sym_name, |
| 343 | + "FuncType":$type)>]; |
| 344 | + |
| 345 | + let extraClassDeclaration = [{ |
| 346 | + /// Returns the region on the current operation that is callable. This may |
| 347 | + /// return null in the case of an external callable object, e.g. an external |
| 348 | + /// function. |
| 349 | + ::mlir::Region *getCallableRegion(); |
| 350 | + |
| 351 | + /// Returns the results types that the callable region produces when |
| 352 | + /// executed. |
| 353 | + llvm::ArrayRef<mlir::Type> getCallableResults() { |
| 354 | + return getFunctionType().getReturnTypes(); |
| 355 | + } |
| 356 | + |
| 357 | + /// Returns the argument types of this function. |
| 358 | + llvm::ArrayRef<mlir::Type> getArgumentTypes() { |
| 359 | + return getFunctionType().getInputs(); |
| 360 | + } |
| 361 | + |
| 362 | + /// Returns 0 or 1 result type of this function (0 in the case of a function |
| 363 | + /// returing void) |
| 364 | + llvm::ArrayRef<mlir::Type> getResultTypes() { |
| 365 | + return getFunctionType().getReturnTypes(); |
| 366 | + } |
| 367 | + |
| 368 | + /// Hook for OpTrait::FunctionOpInterfaceTrait, called after verifying that |
| 369 | + /// the 'type' attribute is present and checks if it holds a function type. |
| 370 | + /// Ensures getType, getNumFuncArguments, and getNumFuncResults can be |
| 371 | + /// called safely. |
| 372 | + llvm::LogicalResult verifyType(); |
| 373 | + |
| 374 | + //===------------------------------------------------------------------===// |
| 375 | + // SymbolOpInterface Methods |
| 376 | + //===------------------------------------------------------------------===// |
| 377 | + |
| 378 | + bool isDeclaration(); |
| 379 | + }]; |
177 | 380 |
|
178 | 381 | let hasCustomAssemblyFormat = 1;
|
179 | 382 | let hasVerifier = 1;
|
180 | 383 | }
|
181 | 384 |
|
182 |
| -#endif // LLVM_CLANG_CIR_DIALECT_IR_CIROPS |
| 385 | +//===----------------------------------------------------------------------===// |
| 386 | +// TrapOp |
| 387 | +//===----------------------------------------------------------------------===// |
| 388 | + |
| 389 | +def TrapOp : CIR_Op<"trap", [Terminator]> { |
| 390 | + let summary = "Exit the program abnormally"; |
| 391 | + let description = [{ |
| 392 | + The cir.trap operation causes the program to exit abnormally. The |
| 393 | + implementations may implement this operation with different mechanisms. For |
| 394 | + example, an implementation may implement this operation by calling abort, |
| 395 | + while another implementation may implement this operation by executing an |
| 396 | + illegal instruction. |
| 397 | + }]; |
| 398 | + |
| 399 | + let assemblyFormat = "attr-dict"; |
| 400 | +} |
| 401 | + |
| 402 | +#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD |
0 commit comments