1//===- Ops.td - Standard operation definitions -------------*- tablegen -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// 9// Defines some MLIR standard operations. 10// 11//===----------------------------------------------------------------------===// 12 13#ifndef STANDARD_OPS 14#define STANDARD_OPS 15 16include "mlir/Dialect/StandardOps/IR/StandardOpsBase.td" 17include "mlir/IR/OpAsmInterface.td" 18include "mlir/IR/SymbolInterfaces.td" 19include "mlir/Interfaces/CallInterfaces.td" 20include "mlir/Interfaces/ControlFlowInterfaces.td" 21include "mlir/Interfaces/SideEffectInterfaces.td" 22include "mlir/Interfaces/VectorInterfaces.td" 23include "mlir/Interfaces/ViewLikeInterface.td" 24 25def StandardOps_Dialect : Dialect { 26 let name = "std"; 27 let cppNamespace = ""; 28 let hasConstantMaterializer = 1; 29} 30 31// Base class for Standard dialect ops. 32class Std_Op<string mnemonic, list<OpTrait> traits = []> : 33 Op<StandardOps_Dialect, mnemonic, traits> { 34 // For every standard op, there needs to be a: 35 // * void print(OpAsmPrinter &p, ${C++ class of Op} op) 36 // * LogicalResult verify(${C++ class of Op} op) 37 // * ParseResult parse${C++ class of Op}(OpAsmParser &parser, 38 // OperationState &result) 39 // functions. 40 let printer = [{ return ::print(p, *this); }]; 41 let verifier = [{ return ::verify(*this); }]; 42 let parser = [{ return ::parse$cppClass(parser, result); }]; 43} 44 45// Base class for standard cast operations. Requires single operand and result, 46// but does not constrain them to specific types. 47class CastOp<string mnemonic, list<OpTrait> traits = []> : 48 Std_Op<mnemonic, 49 !listconcat(traits, [NoSideEffect, SameOperandsAndResultShape])> { 50 51 let results = (outs AnyType); 52 53 let builders = [ 54 OpBuilderDAG<(ins "Value":$source, "Type":$destType), [{ 55 impl::buildCastOp($_builder, $_state, source, destType); 56 }]> 57 ]; 58 59 let parser = [{ 60 return impl::parseCastOp(parser, result); 61 }]; 62 let printer = [{ 63 return printStandardCastOp(this->getOperation(), p); 64 }]; 65 let verifier = [{ return ::verifyCastOp(*this); }]; 66 67 let hasFolder = 1; 68} 69 70// Base class for arithmetic cast operations. 71class ArithmeticCastOp<string mnemonic, list<OpTrait> traits = []> : 72 CastOp<mnemonic, !listconcat(traits, [ElementwiseMappable])> { 73} 74 75// Base class for unary ops. Requires single operand and result. Individual 76// classes will have `operand` accessor. 77class UnaryOp<string mnemonic, list<OpTrait> traits = []> : 78 Op<StandardOps_Dialect, mnemonic, !listconcat(traits, [NoSideEffect])> { 79 let results = (outs AnyType); 80 let printer = [{ 81 return printStandardUnaryOp(this->getOperation(), p); 82 }]; 83} 84 85class UnaryOpSameOperandAndResultType<string mnemonic, 86 list<OpTrait> traits = []> : 87 UnaryOp<mnemonic, !listconcat(traits, [SameOperandsAndResultType])> { 88 let parser = [{ 89 return impl::parseOneResultSameOperandTypeOp(parser, result); 90 }]; 91} 92 93class FloatUnaryOp<string mnemonic, list<OpTrait> traits = []> : 94 UnaryOpSameOperandAndResultType<mnemonic, 95 !listconcat(traits, 96 [DeclareOpInterfaceMethods<VectorUnrollOpInterface>, 97 ElementwiseMappable])>, 98 Arguments<(ins FloatLike:$operand)>; 99 100// Base class for standard arithmetic operations. Requires operands and 101// results to be of the same type, but does not constrain them to specific 102// types. Individual classes will have `lhs` and `rhs` accessor to operands. 103class ArithmeticOp<string mnemonic, list<OpTrait> traits = []> : 104 Op<StandardOps_Dialect, mnemonic, 105 !listconcat(traits, [NoSideEffect, 106 SameOperandsAndResultType, 107 ElementwiseMappable])> { 108 109 let results = (outs AnyType:$result); 110 111 let parser = [{ 112 return impl::parseOneResultSameOperandTypeOp(parser, result); 113 }]; 114 115 let printer = [{ 116 return printStandardBinaryOp(this->getOperation(), p); 117 }]; 118} 119 120// Base class for standard arithmetic operations on integers, vectors and 121// tensors thereof. This operation takes two operands and returns one result, 122// each of these is required to be of the same type. This type may be an 123// integer scalar type, a vector whose element type is an integer type, or an 124// integer tensor. The custom assembly form of the operation is as follows 125// 126// <op>i %0, %1 : i32 127// 128class IntArithmeticOp<string mnemonic, list<OpTrait> traits = []> : 129 ArithmeticOp<mnemonic, 130 !listconcat(traits, 131 [DeclareOpInterfaceMethods<VectorUnrollOpInterface>])>, 132 Arguments<(ins SignlessIntegerLike:$lhs, SignlessIntegerLike:$rhs)>; 133 134// Base class for standard arithmetic binary operations on floats, vectors and 135// tensors thereof. This operation has two operands and returns one result, 136// each of these is required to be of the same type. This type may be a 137// floating point scalar type, a vector whose element type is a floating point 138// type, or a floating point tensor. The custom assembly form of the operation 139// is as follows 140// 141// <op>f %0, %1 : f32 142// 143class FloatArithmeticOp<string mnemonic, list<OpTrait> traits = []> : 144 ArithmeticOp<mnemonic, 145 !listconcat(traits, 146 [DeclareOpInterfaceMethods<VectorUnrollOpInterface>])>, 147 Arguments<(ins FloatLike:$lhs, FloatLike:$rhs)>; 148 149// Base class for standard arithmetic operations on complex numbers with a 150// floating-point element type. 151// These operations take two operands and return one result, all of which must 152// be complex numbers of the same type. 153// The assembly format is as follows 154// 155// <op>cf %0, %1 : complex<f32> 156// 157class ComplexFloatArithmeticOp<string mnemonic, list<OpTrait> traits = []> : 158 ArithmeticOp<mnemonic, traits>, 159 Arguments<(ins Complex<AnyFloat>:$lhs, Complex<AnyFloat>:$rhs)>; 160 161// Base class for memref allocating ops: alloca and alloc. 162// 163// %0 = alloclike(%m)[%s] : memref<8x?xf32, (d0, d1)[s0] -> ((d0 + s0), d1)> 164// 165class AllocLikeOp<string mnemonic, 166 Resource resource, 167 list<OpTrait> traits = []> : 168 Std_Op<mnemonic, 169 !listconcat([ 170 MemoryEffects<[MemAlloc<resource>]>, 171 AttrSizedOperandSegments 172 ], traits)> { 173 174 let arguments = (ins Variadic<Index>:$dynamicSizes, 175 // The symbolic operands (the ones in square brackets) bind 176 // to the symbols of the memref's layout map. 177 Variadic<Index>:$symbolOperands, 178 Confined<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$alignment); 179 let results = (outs Res<AnyMemRef, "", [MemAlloc<resource>]>:$memref); 180 181 let builders = [ 182 OpBuilderDAG<(ins "MemRefType":$memrefType, 183 CArg<"IntegerAttr", "IntegerAttr()">:$alignment), [{ 184 return build($_builder, $_state, memrefType, {}, alignment); 185 }]>, 186 OpBuilderDAG<(ins "MemRefType":$memrefType, "ValueRange":$dynamicSizes, 187 CArg<"IntegerAttr", "IntegerAttr()">:$alignment), [{ 188 return build($_builder, $_state, memrefType, dynamicSizes, {}, alignment); 189 }]>, 190 OpBuilderDAG<(ins "MemRefType":$memrefType, "ValueRange":$dynamicSizes, 191 "ValueRange":$symbolOperands, 192 CArg<"IntegerAttr", "{}">:$alignment), [{ 193 $_state.types.push_back(memrefType); 194 $_state.addOperands(dynamicSizes); 195 $_state.addOperands(symbolOperands); 196 $_state.addAttribute(getOperandSegmentSizeAttr(), 197 $_builder.getI32VectorAttr({ 198 static_cast<int32_t>(dynamicSizes.size()), 199 static_cast<int32_t>(symbolOperands.size())})); 200 if (alignment) 201 $_state.addAttribute(getAlignmentAttrName(), alignment); 202 }]>]; 203 204 let extraClassDeclaration = [{ 205 static StringRef getAlignmentAttrName() { return "alignment"; } 206 207 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 208 209 /// Returns the dynamic sizes for this alloc operation if specified. 210 operand_range getDynamicSizes() { return dynamicSizes(); } 211 }]; 212 213 let assemblyFormat = [{ 214 `(`$dynamicSizes`)` (`` `[` $symbolOperands^ `]`)? attr-dict `:` type($memref) 215 }]; 216 217 let hasCanonicalizer = 1; 218} 219 220// Base class for ops with static/dynamic offset, sizes and strides 221// attributes/arguments. 222class BaseOpWithOffsetSizesAndStrides<string mnemonic, list<OpTrait> traits = []> : 223 Std_Op<mnemonic, 224 !listconcat(traits, [NoSideEffect, AttrSizedOperandSegments])> { 225 code extraBaseClassDeclaration = [{ 226 /// Returns the dynamic sizes for this subview operation if specified. 227 operand_range getDynamicSizes() { return sizes(); } 228 229 /// Return the list of Range (i.e. offset, size, stride). Each 230 /// Range entry contains either the dynamic value or a ConstantIndexOp 231 /// constructed with `b` at location `loc`. 232 SmallVector<Range, 8> getOrCreateRanges(OpBuilder &b, Location loc) { 233 return mlir::getOrCreateRanges(*this, b, loc); 234 } 235 }]; 236} 237 238//===----------------------------------------------------------------------===// 239// AbsFOp 240//===----------------------------------------------------------------------===// 241 242def AbsFOp : FloatUnaryOp<"absf"> { 243 let summary = "floating point absolute-value operation"; 244 let description = [{ 245 The `absf` operation computes the absolute value. It takes one operand and 246 returns one result of the same type. This type may be a float scalar type, 247 a vector whose element type is float, or a tensor of floats. 248 249 Example: 250 251 ```mlir 252 // Scalar absolute value. 253 %a = absf %b : f64 254 255 // SIMD vector element-wise absolute value. 256 %f = absf %g : vector<4xf32> 257 258 // Tensor element-wise absolute value. 259 %x = absf %y : tensor<4x?xf8> 260 ``` 261 }]; 262} 263 264//===----------------------------------------------------------------------===// 265// AddCFOp 266//===----------------------------------------------------------------------===// 267 268def AddCFOp : ComplexFloatArithmeticOp<"addcf"> { 269 let summary = "complex number addition"; 270 let description = [{ 271 The `addcf` operation takes two complex number operands and returns their 272 sum, a single complex number. 273 All operands and result must be of the same type, a complex number with a 274 floating-point element type. 275 276 Example: 277 278 ```mlir 279 %a = addcf %b, %c : complex<f32> 280 ``` 281 }]; 282} 283 284//===----------------------------------------------------------------------===// 285// AddFOp 286//===----------------------------------------------------------------------===// 287 288def AddFOp : FloatArithmeticOp<"addf"> { 289 let summary = "floating point addition operation"; 290 let description = [{ 291 Syntax: 292 293 ``` 294 operation ::= ssa-id `=` `std.addf` ssa-use `,` ssa-use `:` type 295 ``` 296 297 The `addf` operation takes two operands and returns one result, each of 298 these is required to be the same type. This type may be a floating point 299 scalar type, a vector whose element type is a floating point type, or a 300 floating point tensor. 301 302 Example: 303 304 ```mlir 305 // Scalar addition. 306 %a = addf %b, %c : f64 307 308 // SIMD vector addition, e.g. for Intel SSE. 309 %f = addf %g, %h : vector<4xf32> 310 311 // Tensor addition. 312 %x = addf %y, %z : tensor<4x?xbf16> 313 ``` 314 315 TODO: In the distant future, this will accept optional attributes for fast 316 math, contraction, rounding mode, and other controls. 317 }]; 318 let hasFolder = 1; 319} 320 321//===----------------------------------------------------------------------===// 322// AddIOp 323//===----------------------------------------------------------------------===// 324 325def AddIOp : IntArithmeticOp<"addi", [Commutative]> { 326 let summary = "integer addition operation"; 327 let description = [{ 328 Syntax: 329 330 ``` 331 operation ::= ssa-id `=` `std.addi` ssa-use `,` ssa-use `:` type 332 ``` 333 334 The `addi` operation takes two operands and returns one result, each of 335 these is required to be the same type. This type may be an integer scalar 336 type, a vector whose element type is integer, or a tensor of integers. It 337 has no standard attributes. 338 339 Example: 340 341 ```mlir 342 // Scalar addition. 343 %a = addi %b, %c : i64 344 345 // SIMD vector element-wise addition, e.g. for Intel SSE. 346 %f = addi %g, %h : vector<4xi32> 347 348 // Tensor element-wise addition. 349 %x = addi %y, %z : tensor<4x?xi8> 350 ``` 351 }]; 352 let hasFolder = 1; 353} 354 355//===----------------------------------------------------------------------===// 356// AllocOp 357//===----------------------------------------------------------------------===// 358 359def AllocOp : AllocLikeOp<"alloc", DefaultResource> { 360 let summary = "memory allocation operation"; 361 let description = [{ 362 The `alloc` operation allocates a region of memory, as specified by its 363 memref type. 364 365 Example: 366 367 ```mlir 368 %0 = alloc() : memref<8x64xf32, 1> 369 ``` 370 371 The optional list of dimension operands are bound to the dynamic dimensions 372 specified in its memref type. In the example below, the ssa value '%d' is 373 bound to the second dimension of the memref (which is dynamic). 374 375 ```mlir 376 %0 = alloc(%d) : memref<8x?xf32, 1> 377 ``` 378 379 The optional list of symbol operands are bound to the symbols of the 380 memrefs affine map. In the example below, the ssa value '%s' is bound to 381 the symbol 's0' in the affine map specified in the allocs memref type. 382 383 ```mlir 384 %0 = alloc()[%s] : memref<8x64xf32, 385 affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1> 386 ``` 387 388 This operation returns a single ssa value of memref type, which can be used 389 by subsequent load and store operations. 390 391 The optional `alignment` attribute may be specified to ensure that the 392 region of memory that will be indexed is aligned at the specified byte 393 boundary. 394 395 ```mlir 396 %0 = alloc()[%s] {alignment = 8} : 397 memref<8x64xf32, affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1> 398 ``` 399 }]; 400} 401 402//===----------------------------------------------------------------------===// 403// AllocaOp 404//===----------------------------------------------------------------------===// 405 406def AllocaOp : AllocLikeOp<"alloca", AutomaticAllocationScopeResource> { 407 let summary = "stack memory allocation operation"; 408 let description = [{ 409 The `alloca` operation allocates memory on the stack, to be automatically 410 released when control transfers back from the region of its closest 411 surrounding operation with an 412 [`AutomaticAllocationScope`](../Traits.md#automaticallocationscope) trait. 413 The amount of memory allocated is specified by its memref and additional 414 operands. For example: 415 416 ```mlir 417 %0 = alloca() : memref<8x64xf32> 418 ``` 419 420 The optional list of dimension operands are bound to the dynamic dimensions 421 specified in its memref type. In the example below, the SSA value '%d' is 422 bound to the second dimension of the memref (which is dynamic). 423 424 ```mlir 425 %0 = alloca(%d) : memref<8x?xf32> 426 ``` 427 428 The optional list of symbol operands are bound to the symbols of the 429 memref's affine map. In the example below, the SSA value '%s' is bound to 430 the symbol 's0' in the affine map specified in the allocs memref type. 431 432 ```mlir 433 %0 = alloca()[%s] : memref<8x64xf32, 434 affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>> 435 ``` 436 437 This operation returns a single SSA value of memref type, which can be used 438 by subsequent load and store operations. An optional alignment attribute, if 439 specified, guarantees alignment at least to that boundary. If not specified, 440 an alignment on any convenient boundary compatible with the type will be 441 chosen. 442 }]; 443} 444 445//===----------------------------------------------------------------------===// 446// AndOp 447//===----------------------------------------------------------------------===// 448 449def AndOp : IntArithmeticOp<"and", [Commutative]> { 450 let summary = "integer binary and"; 451 let description = [{ 452 Syntax: 453 454 ``` 455 operation ::= ssa-id `=` `std.and` ssa-use `,` ssa-use `:` type 456 ``` 457 458 The `and` operation takes two operands and returns one result, each of these 459 is required to be the same type. This type may be an integer scalar type, a 460 vector whose element type is integer, or a tensor of integers. It has no 461 standard attributes. 462 463 Example: 464 465 ```mlir 466 // Scalar integer bitwise and. 467 %a = and %b, %c : i64 468 469 // SIMD vector element-wise bitwise integer and. 470 %f = and %g, %h : vector<4xi32> 471 472 // Tensor element-wise bitwise integer and. 473 %x = and %y, %z : tensor<4x?xi8> 474 ``` 475 }]; 476 let hasFolder = 1; 477} 478 479//===----------------------------------------------------------------------===// 480// AssertOp 481//===----------------------------------------------------------------------===// 482 483def AssertOp : Std_Op<"assert"> { 484 let summary = "Assert operation with message attribute"; 485 let description = [{ 486 Assert operation with single boolean operand and an error message attribute. 487 If the argument is `true` this operation has no effect. Otherwise, the 488 program execution will abort. The provided error message may be used by a 489 runtime to propagate the error to the user. 490 491 Example: 492 493 ```mlir 494 assert %b, "Expected ... to be true" 495 ``` 496 }]; 497 498 let arguments = (ins I1:$arg, StrAttr:$msg); 499 500 let assemblyFormat = "$arg `,` $msg attr-dict"; 501 502 // AssertOp is fully verified by its traits. 503 let verifier = ?; 504 505 let hasCanonicalizer = 1; 506} 507 508//===----------------------------------------------------------------------===// 509// AssumeAlignmentOp 510//===----------------------------------------------------------------------===// 511 512def AssumeAlignmentOp : Std_Op<"assume_alignment"> { 513 let summary = 514 "assertion that gives alignment information to the input memref"; 515 let description = [{ 516 The `assume_alignment` operation takes a memref and an integer of alignment 517 value, and internally annotates the buffer with the given alignment. If 518 the buffer isn't aligned to the given alignment, the behavior is undefined. 519 520 This operation doesn't affect the semantics of a correct program. It's for 521 optimization only, and the optimization is best-effort. 522 }]; 523 let arguments = (ins AnyMemRef:$memref, 524 Confined<I32Attr, [IntPositive]>:$alignment); 525 let results = (outs); 526 527 let assemblyFormat = "$memref `,` $alignment attr-dict `:` type($memref)"; 528} 529 530//===----------------------------------------------------------------------===// 531// AtanOp 532//===----------------------------------------------------------------------===// 533 534def AtanOp : FloatUnaryOp<"atan", []>{ 535 let summary = "arcus tangent of the given value"; 536 let description = [{ 537 Syntax: 538 539 ``` 540 operation ::= ssa-id `=` `std.atan` ssa-use `:` type 541 ``` 542 543 The `atan` operation computes the arcus tangent of a given value. It takes 544 one operand and returns one result of the same type. This type may be a 545 float scalar type, a vector whose element type is float, or a tensor of 546 floats. It has no standard attributes. 547 548 Example: 549 550 ```mlir 551 // Arcus tangent of scalar value. 552 %a = atan %b : f64 553 554 // SIMD vector element-wise arcus tangent. 555 %f = atan %g : vector<4xf32> 556 557 // Tensor element-wise arcus tangent. 558 %x = atan %y : tensor<4x?xf8> 559 ``` 560 }]; 561} 562 563//===----------------------------------------------------------------------===// 564// Atan2Op 565//===----------------------------------------------------------------------===// 566 567def Atan2Op : FloatArithmeticOp<"atan2">{ 568 let summary = "2-argument arcus tangent of the given values"; 569 let description = [{ 570 Syntax: 571 572 ``` 573 operation ::= ssa-id `=` `std.atan2` ssa-use `,` ssa-use `:` type 574 ``` 575 576 The `atan2` operation takes two operands and returns one result, all of 577 which must be of the same type. This type may be a floating point scalar 578 type, a vector whose element type is a floating point type, or a floating 579 point tensor. 580 581 The 2-argument arcus tangent `atan2(y, x)` returns the angle in the 582 Euclidian plane between the positive x-axis and the ray through the point 583 (x, y). It is a generalization of the 1-argument arcus tangent which 584 returns the angle on the basis of the ratio y/x. 585 586 See also https://en.wikipedia.org/wiki/Atan2 587 588 Example: 589 590 ```mlir 591 // Scalar variant. 592 %a = atan2 %b, %c : f32 593 594 // SIMD vector variant. 595 %f = atan2 %g, %h : vector<4xf32> 596 597 // Tensor variant. 598 %x = atan2 %y, %z : tensor<4x?xf32> 599 ``` 600 }]; 601} 602 603//===----------------------------------------------------------------------===// 604// AtomicRMWOp 605//===----------------------------------------------------------------------===// 606 607def AtomicRMWOp : Std_Op<"atomic_rmw", [ 608 AllTypesMatch<["value", "result"]>, 609 TypesMatchWith<"value type matches element type of memref", 610 "memref", "value", 611 "$_self.cast<MemRefType>().getElementType()"> 612 ]> { 613 let summary = "atomic read-modify-write operation"; 614 let description = [{ 615 The `atomic_rmw` operation provides a way to perform a read-modify-write 616 sequence that is free from data races. The kind enumeration specifies the 617 modification to perform. The value operand represents the new value to be 618 applied during the modification. The memref operand represents the buffer 619 that the read and write will be performed against, as accessed by the 620 specified indices. The arity of the indices is the rank of the memref. The 621 result represents the latest value that was stored. 622 623 Example: 624 625 ```mlir 626 %x = atomic_rmw "addf" %value, %I[%i] : (f32, memref<10xf32>) -> f32 627 ``` 628 }]; 629 630 let arguments = (ins 631 AtomicRMWKindAttr:$kind, 632 AnyTypeOf<[AnySignlessInteger, AnyFloat]>:$value, 633 MemRefOf<[AnySignlessInteger, AnyFloat]>:$memref, 634 Variadic<Index>:$indices); 635 let results = (outs AnyTypeOf<[AnySignlessInteger, AnyFloat]>:$result); 636 637 let assemblyFormat = [{ 638 $kind $value `,` $memref `[` $indices `]` attr-dict `:` `(` type($value) `,` 639 type($memref) `)` `->` type($result) 640 }]; 641 642 let extraClassDeclaration = [{ 643 MemRefType getMemRefType() { 644 return memref().getType().cast<MemRefType>(); 645 } 646 }]; 647} 648 649def GenericAtomicRMWOp : Std_Op<"generic_atomic_rmw", [ 650 SingleBlockImplicitTerminator<"AtomicYieldOp">, 651 TypesMatchWith<"result type matches element type of memref", 652 "memref", "result", 653 "$_self.cast<MemRefType>().getElementType()"> 654 ]> { 655 let summary = "atomic read-modify-write operation with a region"; 656 let description = [{ 657 The `generic_atomic_rmw` operation provides a way to perform a read-modify-write 658 sequence that is free from data races. The memref operand represents the 659 buffer that the read and write will be performed against, as accessed by 660 the specified indices. The arity of the indices is the rank of the memref. 661 The result represents the latest value that was stored. The region contains 662 the code for the modification itself. The entry block has a single argument 663 that represents the value stored in `memref[indices]` before the write is 664 performed. No side-effecting ops are allowed in the body of 665 `GenericAtomicRMWOp`. 666 667 Example: 668 669 ```mlir 670 %x = generic_atomic_rmw %I[%i] : memref<10xf32> { 671 ^bb0(%current_value : f32): 672 %c1 = constant 1.0 : f32 673 %inc = addf %c1, %current_value : f32 674 atomic_yield %inc : f32 675 } 676 ``` 677 }]; 678 679 let arguments = (ins 680 MemRefOf<[AnySignlessInteger, AnyFloat]>:$memref, 681 Variadic<Index>:$indices); 682 683 let results = (outs 684 AnyTypeOf<[AnySignlessInteger, AnyFloat]>:$result); 685 686 let regions = (region AnyRegion:$body); 687 688 let skipDefaultBuilders = 1; 689 let builders = [OpBuilderDAG<(ins "Value":$memref, "ValueRange":$ivs)>]; 690 691 let extraClassDeclaration = [{ 692 // The value stored in memref[ivs]. 693 Value getCurrentValue() { 694 return body().getArgument(0); 695 } 696 MemRefType getMemRefType() { 697 return memref().getType().cast<MemRefType>(); 698 } 699 }]; 700} 701 702def AtomicYieldOp : Std_Op<"atomic_yield", [ 703 HasParent<"GenericAtomicRMWOp">, 704 NoSideEffect, 705 Terminator 706 ]> { 707 let summary = "yield operation for GenericAtomicRMWOp"; 708 let description = [{ 709 "atomic_yield" yields an SSA value from a GenericAtomicRMWOp region. 710 }]; 711 712 let arguments = (ins AnyType:$result); 713 let assemblyFormat = "$result attr-dict `:` type($result)"; 714} 715 716//===----------------------------------------------------------------------===// 717// BranchOp 718//===----------------------------------------------------------------------===// 719 720def BranchOp : Std_Op<"br", 721 [DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>, 722 NoSideEffect, Terminator]> { 723 let summary = "branch operation"; 724 let description = [{ 725 The `br` operation represents a branch operation in a function. 726 The operation takes variable number of operands and produces no results. 727 The operand number and types for each successor must match the arguments of 728 the block successor. 729 730 Example: 731 732 ```mlir 733 ^bb2: 734 %2 = call @someFn() 735 br ^bb3(%2 : tensor<*xf32>) 736 ^bb3(%3: tensor<*xf32>): 737 ``` 738 }]; 739 740 let arguments = (ins Variadic<AnyType>:$destOperands); 741 let successors = (successor AnySuccessor:$dest); 742 743 let builders = [ 744 OpBuilderDAG<(ins "Block *":$dest, 745 CArg<"ValueRange", "{}">:$destOperands), [{ 746 $_state.addSuccessors(dest); 747 $_state.addOperands(destOperands); 748 }]>]; 749 750 // BranchOp is fully verified by traits. 751 let verifier = ?; 752 753 let extraClassDeclaration = [{ 754 Block *getDest(); 755 void setDest(Block *block); 756 757 /// Erase the operand at 'index' from the operand list. 758 void eraseOperand(unsigned index); 759 }]; 760 761 let hasCanonicalizer = 1; 762 let assemblyFormat = [{ 763 $dest (`(` $destOperands^ `:` type($destOperands) `)`)? attr-dict 764 }]; 765} 766 767//===----------------------------------------------------------------------===// 768// CallOp 769//===----------------------------------------------------------------------===// 770 771def CallOp : Std_Op<"call", 772 [CallOpInterface, MemRefsNormalizable, 773 DeclareOpInterfaceMethods<SymbolUserOpInterface>]> { 774 let summary = "call operation"; 775 let description = [{ 776 The `call` operation represents a direct call to a function that is within 777 the same symbol scope as the call. The operands and result types of the 778 call must match the specified function type. The callee is encoded as a 779 symbol reference attribute named "callee". 780 781 Example: 782 783 ```mlir 784 %2 = call @my_add(%0, %1) : (f32, f32) -> f32 785 ``` 786 }]; 787 788 let arguments = (ins FlatSymbolRefAttr:$callee, Variadic<AnyType>:$operands); 789 let results = (outs Variadic<AnyType>); 790 791 let builders = [ 792 OpBuilderDAG<(ins "FuncOp":$callee, CArg<"ValueRange", "{}">:$operands), [{ 793 $_state.addOperands(operands); 794 $_state.addAttribute("callee",$_builder.getSymbolRefAttr(callee)); 795 $_state.addTypes(callee.getType().getResults()); 796 }]>, 797 OpBuilderDAG<(ins "SymbolRefAttr":$callee, "TypeRange":$results, 798 CArg<"ValueRange", "{}">:$operands), [{ 799 $_state.addOperands(operands); 800 $_state.addAttribute("callee", callee); 801 $_state.addTypes(results); 802 }]>, 803 OpBuilderDAG<(ins "StringRef":$callee, "TypeRange":$results, 804 CArg<"ValueRange", "{}">:$operands), [{ 805 build($_builder, $_state, $_builder.getSymbolRefAttr(callee), results, 806 operands); 807 }]>]; 808 809 let extraClassDeclaration = [{ 810 StringRef getCallee() { return callee(); } 811 FunctionType getCalleeType(); 812 813 /// Get the argument operands to the called function. 814 operand_range getArgOperands() { 815 return {arg_operand_begin(), arg_operand_end()}; 816 } 817 818 operand_iterator arg_operand_begin() { return operand_begin(); } 819 operand_iterator arg_operand_end() { return operand_end(); } 820 821 /// Return the callee of this operation. 822 CallInterfaceCallable getCallableForCallee() { 823 return (*this)->getAttrOfType<SymbolRefAttr>("callee"); 824 } 825 }]; 826 827 let assemblyFormat = [{ 828 $callee `(` $operands `)` attr-dict `:` functional-type($operands, results) 829 }]; 830 let verifier = ?; 831} 832 833//===----------------------------------------------------------------------===// 834// CallIndirectOp 835//===----------------------------------------------------------------------===// 836 837def CallIndirectOp : Std_Op<"call_indirect", [ 838 CallOpInterface, 839 TypesMatchWith<"callee input types match argument types", 840 "callee", "operands", 841 "$_self.cast<FunctionType>().getInputs()">, 842 TypesMatchWith<"callee result types match result types", 843 "callee", "results", 844 "$_self.cast<FunctionType>().getResults()"> 845 ]> { 846 let summary = "indirect call operation"; 847 let description = [{ 848 The `call_indirect` operation represents an indirect call to a value of 849 function type. Functions are first class types in MLIR, and may be passed as 850 arguments and merged together with block arguments. The operands and result 851 types of the call must match the specified function type. 852 853 Function values can be created with the 854 [`constant` operation](#stdconstant-constantop). 855 856 Example: 857 858 ```mlir 859 %31 = call_indirect %15(%0, %1) 860 : (tensor<16xf32>, tensor<16xf32>) -> tensor<16xf32> 861 ``` 862 }]; 863 864 let arguments = (ins FunctionType:$callee, Variadic<AnyType>:$operands); 865 let results = (outs Variadic<AnyType>:$results); 866 867 let builders = [ 868 OpBuilderDAG<(ins "Value":$callee, CArg<"ValueRange", "{}">:$operands), [{ 869 $_state.operands.push_back(callee); 870 $_state.addOperands(operands); 871 $_state.addTypes(callee.getType().cast<FunctionType>().getResults()); 872 }]>]; 873 874 let extraClassDeclaration = [{ 875 Value getCallee() { return getOperand(0); } 876 877 /// Get the argument operands to the called function. 878 operand_range getArgOperands() { 879 return {arg_operand_begin(), arg_operand_end()}; 880 } 881 882 operand_iterator arg_operand_begin() { return ++operand_begin(); } 883 operand_iterator arg_operand_end() { return operand_end(); } 884 885 /// Return the callee of this operation. 886 CallInterfaceCallable getCallableForCallee() { return getCallee(); } 887 }]; 888 889 let verifier = ?; 890 let hasCanonicalizer = 1; 891 892 let assemblyFormat = "$callee `(` $operands `)` attr-dict `:` type($callee)"; 893} 894 895//===----------------------------------------------------------------------===// 896// CeilFOp 897//===----------------------------------------------------------------------===// 898 899def CeilFOp : FloatUnaryOp<"ceilf"> { 900 let summary = "ceiling of the specified value"; 901 let description = [{ 902 Syntax: 903 904 ``` 905 operation ::= ssa-id `=` `std.ceilf` ssa-use `:` type 906 ``` 907 908 The `ceilf` operation computes the ceiling of a given value. It takes one 909 operand and returns one result of the same type. This type may be a float 910 scalar type, a vector whose element type is float, or a tensor of floats. 911 It has no standard attributes. 912 913 Example: 914 915 ```mlir 916 // Scalar ceiling value. 917 %a = ceilf %b : f64 918 919 // SIMD vector element-wise ceiling value. 920 %f = ceilf %g : vector<4xf32> 921 922 // Tensor element-wise ceiling value. 923 %x = ceilf %y : tensor<4x?xf8> 924 ``` 925 }]; 926} 927 928//===----------------------------------------------------------------------===// 929// FloorFOp 930//===----------------------------------------------------------------------===// 931 932def FloorFOp : FloatUnaryOp<"floorf"> { 933 let summary = "floor of the specified value"; 934 let description = [{ 935 Syntax: 936 937 ``` 938 operation ::= ssa-id `=` `std.floorf` ssa-use `:` type 939 ``` 940 941 The `floorf` operation computes the floor of a given value. It takes one 942 operand and returns one result of the same type. This type may be a float 943 scalar type, a vector whose element type is float, or a tensor of floats. 944 It has no standard attributes. 945 946 Example: 947 948 ```mlir 949 // Scalar floor value. 950 %a = floorf %b : f64 951 952 // SIMD vector element-wise floor value. 953 %f = floorf %g : vector<4xf32> 954 955 // Tensor element-wise floor value. 956 %x = floorf %y : tensor<4x?xf8> 957 ``` 958 }]; 959} 960 961//===----------------------------------------------------------------------===// 962// CmpFOp 963//===----------------------------------------------------------------------===// 964 965// The predicate indicates the type of the comparison to perform: 966// (un)orderedness, (in)equality and less/greater than (or equal to) as 967// well as predicates that are always true or false. 968def CMPF_P_FALSE : I64EnumAttrCase<"AlwaysFalse", 0, "false">; 969def CMPF_P_OEQ : I64EnumAttrCase<"OEQ", 1, "oeq">; 970def CMPF_P_OGT : I64EnumAttrCase<"OGT", 2, "ogt">; 971def CMPF_P_OGE : I64EnumAttrCase<"OGE", 3, "oge">; 972def CMPF_P_OLT : I64EnumAttrCase<"OLT", 4, "olt">; 973def CMPF_P_OLE : I64EnumAttrCase<"OLE", 5, "ole">; 974def CMPF_P_ONE : I64EnumAttrCase<"ONE", 6, "one">; 975def CMPF_P_ORD : I64EnumAttrCase<"ORD", 7, "ord">; 976def CMPF_P_UEQ : I64EnumAttrCase<"UEQ", 8, "ueq">; 977def CMPF_P_UGT : I64EnumAttrCase<"UGT", 9, "ugt">; 978def CMPF_P_UGE : I64EnumAttrCase<"UGE", 10, "uge">; 979def CMPF_P_ULT : I64EnumAttrCase<"ULT", 11, "ult">; 980def CMPF_P_ULE : I64EnumAttrCase<"ULE", 12, "ule">; 981def CMPF_P_UNE : I64EnumAttrCase<"UNE", 13, "une">; 982def CMPF_P_UNO : I64EnumAttrCase<"UNO", 14, "uno">; 983def CMPF_P_TRUE : I64EnumAttrCase<"AlwaysTrue", 15, "true">; 984 985def CmpFPredicateAttr : I64EnumAttr< 986 "CmpFPredicate", "", 987 [CMPF_P_FALSE, CMPF_P_OEQ, CMPF_P_OGT, CMPF_P_OGE, CMPF_P_OLT, CMPF_P_OLE, 988 CMPF_P_ONE, CMPF_P_ORD, CMPF_P_UEQ, CMPF_P_UGT, CMPF_P_UGE, CMPF_P_ULT, 989 CMPF_P_ULE, CMPF_P_UNE, CMPF_P_UNO, CMPF_P_TRUE]> { 990 let cppNamespace = "::mlir"; 991} 992 993def CmpFOp : Std_Op<"cmpf", 994 [NoSideEffect, SameTypeOperands, ElementwiseMappable, 995 TypesMatchWith< 996 "result type has i1 element type and same shape as operands", 997 "lhs", "result", "getI1SameShape($_self)">]> { 998 let summary = "floating-point comparison operation"; 999 let description = [{ 1000 The `cmpf` operation compares its two operands according to the float 1001 comparison rules and the predicate specified by the respective attribute. 1002 The predicate defines the type of comparison: (un)orderedness, (in)equality 1003 and signed less/greater than (or equal to) as well as predicates that are 1004 always true or false. The operands must have the same type, and this type 1005 must be a float type, or a vector or tensor thereof. The result is an i1, 1006 or a vector/tensor thereof having the same shape as the inputs. Unlike cmpi, 1007 the operands are always treated as signed. The u prefix indicates 1008 *unordered* comparison, not unsigned comparison, so "une" means unordered or 1009 not equal. For the sake of readability by humans, custom assembly form for 1010 the operation uses a string-typed attribute for the predicate. The value of 1011 this attribute corresponds to lower-cased name of the predicate constant, 1012 e.g., "one" means "ordered not equal". The string representation of the 1013 attribute is merely a syntactic sugar and is converted to an integer 1014 attribute by the parser. 1015 1016 Example: 1017 1018 ```mlir 1019 %r1 = cmpf "oeq" %0, %1 : f32 1020 %r2 = cmpf "ult" %0, %1 : tensor<42x42xf64> 1021 %r3 = "std.cmpf"(%0, %1) {predicate: 0} : (f8, f8) -> i1 1022 ``` 1023 }]; 1024 1025 let arguments = (ins 1026 CmpFPredicateAttr:$predicate, 1027 FloatLike:$lhs, 1028 FloatLike:$rhs 1029 ); 1030 let results = (outs BoolLike:$result); 1031 1032 let builders = [ 1033 OpBuilderDAG<(ins "CmpFPredicate":$predicate, "Value":$lhs, 1034 "Value":$rhs), [{ 1035 ::buildCmpFOp($_builder, $_state, predicate, lhs, rhs); 1036 }]>]; 1037 1038 let extraClassDeclaration = [{ 1039 static StringRef getPredicateAttrName() { return "predicate"; } 1040 static CmpFPredicate getPredicateByName(StringRef name); 1041 1042 CmpFPredicate getPredicate() { 1043 return (CmpFPredicate)(*this)->getAttrOfType<IntegerAttr>( 1044 getPredicateAttrName()).getInt(); 1045 } 1046 }]; 1047 1048 let verifier = [{ return success(); }]; 1049 1050 let hasFolder = 1; 1051 1052 let assemblyFormat = "$predicate `,` $lhs `,` $rhs attr-dict `:` type($lhs)"; 1053} 1054 1055//===----------------------------------------------------------------------===// 1056// CmpIOp 1057//===----------------------------------------------------------------------===// 1058 1059def CMPI_P_EQ : I64EnumAttrCase<"eq", 0>; 1060def CMPI_P_NE : I64EnumAttrCase<"ne", 1>; 1061def CMPI_P_SLT : I64EnumAttrCase<"slt", 2>; 1062def CMPI_P_SLE : I64EnumAttrCase<"sle", 3>; 1063def CMPI_P_SGT : I64EnumAttrCase<"sgt", 4>; 1064def CMPI_P_SGE : I64EnumAttrCase<"sge", 5>; 1065def CMPI_P_ULT : I64EnumAttrCase<"ult", 6>; 1066def CMPI_P_ULE : I64EnumAttrCase<"ule", 7>; 1067def CMPI_P_UGT : I64EnumAttrCase<"ugt", 8>; 1068def CMPI_P_UGE : I64EnumAttrCase<"uge", 9>; 1069 1070def CmpIPredicateAttr : I64EnumAttr< 1071 "CmpIPredicate", "", 1072 [CMPI_P_EQ, CMPI_P_NE, CMPI_P_SLT, CMPI_P_SLE, CMPI_P_SGT, 1073 CMPI_P_SGE, CMPI_P_ULT, CMPI_P_ULE, CMPI_P_UGT, CMPI_P_UGE]> { 1074 let cppNamespace = "::mlir"; 1075} 1076 1077def CmpIOp : Std_Op<"cmpi", 1078 [NoSideEffect, SameTypeOperands, ElementwiseMappable, 1079 TypesMatchWith< 1080 "result type has i1 element type and same shape as operands", 1081 "lhs", "result", "getI1SameShape($_self)">]> { 1082 let summary = "integer comparison operation"; 1083 let description = [{ 1084 The `cmpi` operation is a generic comparison for integer-like types. Its two 1085 arguments can be integers, vectors or tensors thereof as long as their types 1086 match. The operation produces an i1 for the former case, a vector or a 1087 tensor of i1 with the same shape as inputs in the other cases. 1088 1089 Its first argument is an attribute that defines which type of comparison is 1090 performed. The following comparisons are supported: 1091 1092 - equal (mnemonic: `"eq"`; integer value: `0`) 1093 - not equal (mnemonic: `"ne"`; integer value: `1`) 1094 - signed less than (mnemonic: `"slt"`; integer value: `2`) 1095 - signed less than or equal (mnemonic: `"sle"`; integer value: `3`) 1096 - signed greater than (mnemonic: `"sgt"`; integer value: `4`) 1097 - signed greater than or equal (mnemonic: `"sge"`; integer value: `5`) 1098 - unsigned less than (mnemonic: `"ult"`; integer value: `6`) 1099 - unsigned less than or equal (mnemonic: `"ule"`; integer value: `7`) 1100 - unsigned greater than (mnemonic: `"ugt"`; integer value: `8`) 1101 - unsigned greater than or equal (mnemonic: `"uge"`; integer value: `9`) 1102 1103 The result is `1` if the comparison is true and `0` otherwise. For vector or 1104 tensor operands, the comparison is performed elementwise and the element of 1105 the result indicates whether the comparison is true for the operand elements 1106 with the same indices as those of the result. 1107 1108 Note: while the custom assembly form uses strings, the actual underlying 1109 attribute has integer type (or rather enum class in C++ code) as seen from 1110 the generic assembly form. String literals are used to improve readability 1111 of the IR by humans. 1112 1113 This operation only applies to integer-like operands, but not floats. The 1114 main reason being that comparison operations have diverging sets of 1115 attributes: integers require sign specification while floats require various 1116 floating point-related particularities, e.g., `-ffast-math` behavior, 1117 IEEE754 compliance, etc 1118 ([rationale](../Rationale/Rationale.md#splitting-floating-point-vs-integer-operations)). 1119 The type of comparison is specified as attribute to avoid introducing ten 1120 similar operations, taking into account that they are often implemented 1121 using the same operation downstream 1122 ([rationale](../Rationale/Rationale.md#specifying-comparison-kind-as-attribute)). The 1123 separation between signed and unsigned order comparisons is necessary 1124 because of integers being signless. The comparison operation must know how 1125 to interpret values with the foremost bit being set: negatives in two's 1126 complement or large positives 1127 ([rationale](../Rationale/Rationale.md#specifying-sign-in-integer-comparison-operations)). 1128 1129 Example: 1130 1131 ```mlir 1132 // Custom form of scalar "signed less than" comparison. 1133 %x = cmpi "slt", %lhs, %rhs : i32 1134 1135 // Generic form of the same operation. 1136 %x = "std.cmpi"(%lhs, %rhs) {predicate = 2 : i64} : (i32, i32) -> i1 1137 1138 // Custom form of vector equality comparison. 1139 %x = cmpi "eq", %lhs, %rhs : vector<4xi64> 1140 1141 // Generic form of the same operation. 1142 %x = "std.cmpi"(%lhs, %rhs) {predicate = 0 : i64} 1143 : (vector<4xi64>, vector<4xi64>) -> vector<4xi1> 1144 ``` 1145 }]; 1146 1147 let arguments = (ins 1148 CmpIPredicateAttr:$predicate, 1149 SignlessIntegerLike:$lhs, 1150 SignlessIntegerLike:$rhs 1151 ); 1152 let results = (outs BoolLike:$result); 1153 1154 let builders = [ 1155 OpBuilderDAG<(ins "CmpIPredicate":$predicate, "Value":$lhs, 1156 "Value":$rhs), [{ 1157 ::buildCmpIOp($_builder, $_state, predicate, lhs, rhs); 1158 }]>]; 1159 1160 let extraClassDeclaration = [{ 1161 static StringRef getPredicateAttrName() { return "predicate"; } 1162 static CmpIPredicate getPredicateByName(StringRef name); 1163 1164 CmpIPredicate getPredicate() { 1165 return (CmpIPredicate)(*this)->getAttrOfType<IntegerAttr>( 1166 getPredicateAttrName()).getInt(); 1167 } 1168 }]; 1169 1170 let verifier = [{ return success(); }]; 1171 1172 let hasFolder = 1; 1173 1174 let assemblyFormat = "$predicate `,` $lhs `,` $rhs attr-dict `:` type($lhs)"; 1175} 1176 1177//===----------------------------------------------------------------------===// 1178// CreateComplexOp 1179//===----------------------------------------------------------------------===// 1180 1181def CreateComplexOp : Std_Op<"create_complex", 1182 [NoSideEffect, 1183 AllTypesMatch<["real", "imaginary"]>, 1184 TypesMatchWith<"complex element type matches real operand type", 1185 "complex", "real", 1186 "$_self.cast<ComplexType>().getElementType()">, 1187 TypesMatchWith<"complex element type matches imaginary operand type", 1188 "complex", "imaginary", 1189 "$_self.cast<ComplexType>().getElementType()">]> { 1190 let summary = "creates a complex number"; 1191 let description = [{ 1192 The `create_complex` operation creates a complex number from two 1193 floating-point operands, the real and the imaginary part. 1194 1195 Example: 1196 1197 ```mlir 1198 %a = create_complex %b, %c : complex<f32> 1199 ``` 1200 }]; 1201 1202 let arguments = (ins AnyFloat:$real, AnyFloat:$imaginary); 1203 let results = (outs Complex<AnyFloat>:$complex); 1204 1205 let assemblyFormat = "$real `,` $imaginary attr-dict `:` type($complex)"; 1206 1207 // `CreateComplexOp` is fully verified by its traits. 1208 let verifier = ?; 1209} 1210 1211//===----------------------------------------------------------------------===// 1212// CondBranchOp 1213//===----------------------------------------------------------------------===// 1214 1215def CondBranchOp : Std_Op<"cond_br", 1216 [AttrSizedOperandSegments, 1217 DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>, 1218 NoSideEffect, Terminator]> { 1219 let summary = "conditional branch operation"; 1220 let description = [{ 1221 The `cond_br` terminator operation represents a conditional branch on a 1222 boolean (1-bit integer) value. If the bit is set, then the first destination 1223 is jumped to; if it is false, the second destination is chosen. The count 1224 and types of operands must align with the arguments in the corresponding 1225 target blocks. 1226 1227 The MLIR conditional branch operation is not allowed to target the entry 1228 block for a region. The two destinations of the conditional branch operation 1229 are allowed to be the same. 1230 1231 The following example illustrates a function with a conditional branch 1232 operation that targets the same block. 1233 1234 Example: 1235 1236 ```mlir 1237 func @select(%a: i32, %b: i32, %flag: i1) -> i32 { 1238 // Both targets are the same, operands differ 1239 cond_br %flag, ^bb1(%a : i32), ^bb1(%b : i32) 1240 1241 ^bb1(%x : i32) : 1242 return %x : i32 1243 } 1244 ``` 1245 }]; 1246 1247 let arguments = (ins I1:$condition, 1248 Variadic<AnyType>:$trueDestOperands, 1249 Variadic<AnyType>:$falseDestOperands); 1250 let successors = (successor AnySuccessor:$trueDest, AnySuccessor:$falseDest); 1251 1252 let builders = [ 1253 OpBuilderDAG<(ins "Value":$condition, "Block *":$trueDest, 1254 "ValueRange":$trueOperands, "Block *":$falseDest, 1255 "ValueRange":$falseOperands), [{ 1256 build($_builder, $_state, condition, trueOperands, falseOperands, trueDest, 1257 falseDest); 1258 }]>, 1259 OpBuilderDAG<(ins "Value":$condition, "Block *":$trueDest, 1260 "Block *":$falseDest, CArg<"ValueRange", "{}">:$falseOperands), [{ 1261 build($_builder, $_state, condition, trueDest, ValueRange(), falseDest, 1262 falseOperands); 1263 }]>]; 1264 1265 // CondBranchOp is fully verified by traits. 1266 let verifier = ?; 1267 1268 let extraClassDeclaration = [{ 1269 // These are the indices into the dests list. 1270 enum { trueIndex = 0, falseIndex = 1 }; 1271 1272 // The condition operand is the first operand in the list. 1273 Value getCondition() { return getOperand(0); } 1274 1275 /// Return the destination if the condition is true. 1276 Block *getTrueDest() { 1277 return getSuccessor(trueIndex); 1278 } 1279 1280 /// Return the destination if the condition is false. 1281 Block *getFalseDest() { 1282 return getSuccessor(falseIndex); 1283 } 1284 1285 // Accessors for operands to the 'true' destination. 1286 Value getTrueOperand(unsigned idx) { 1287 assert(idx < getNumTrueOperands()); 1288 return getOperand(getTrueDestOperandIndex() + idx); 1289 } 1290 1291 void setTrueOperand(unsigned idx, Value value) { 1292 assert(idx < getNumTrueOperands()); 1293 setOperand(getTrueDestOperandIndex() + idx, value); 1294 } 1295 1296 operand_range getTrueOperands() { return trueDestOperands(); } 1297 1298 unsigned getNumTrueOperands() { return getTrueOperands().size(); } 1299 1300 /// Erase the operand at 'index' from the true operand list. 1301 void eraseTrueOperand(unsigned index) { 1302 trueDestOperandsMutable().erase(index); 1303 } 1304 1305 // Accessors for operands to the 'false' destination. 1306 Value getFalseOperand(unsigned idx) { 1307 assert(idx < getNumFalseOperands()); 1308 return getOperand(getFalseDestOperandIndex() + idx); 1309 } 1310 void setFalseOperand(unsigned idx, Value value) { 1311 assert(idx < getNumFalseOperands()); 1312 setOperand(getFalseDestOperandIndex() + idx, value); 1313 } 1314 1315 operand_range getFalseOperands() { return falseDestOperands(); } 1316 1317 unsigned getNumFalseOperands() { return getFalseOperands().size(); } 1318 1319 /// Erase the operand at 'index' from the false operand list. 1320 void eraseFalseOperand(unsigned index) { 1321 falseDestOperandsMutable().erase(index); 1322 } 1323 1324 private: 1325 /// Get the index of the first true destination operand. 1326 unsigned getTrueDestOperandIndex() { return 1; } 1327 1328 /// Get the index of the first false destination operand. 1329 unsigned getFalseDestOperandIndex() { 1330 return getTrueDestOperandIndex() + getNumTrueOperands(); 1331 } 1332 }]; 1333 1334 let hasCanonicalizer = 1; 1335 let assemblyFormat = [{ 1336 $condition `,` 1337 $trueDest (`(` $trueDestOperands^ `:` type($trueDestOperands) `)`)? `,` 1338 $falseDest (`(` $falseDestOperands^ `:` type($falseDestOperands) `)`)? 1339 attr-dict 1340 }]; 1341} 1342 1343//===----------------------------------------------------------------------===// 1344// ConstantOp 1345//===----------------------------------------------------------------------===// 1346 1347def ConstantOp : Std_Op<"constant", 1348 [ConstantLike, NoSideEffect, DeclareOpInterfaceMethods<OpAsmOpInterface>]> { 1349 let summary = "constant"; 1350 let description = [{ 1351 Syntax: 1352 1353 ``` 1354 operation ::= ssa-id `=` `std.constant` attribute-value `:` type 1355 ``` 1356 1357 The `constant` operation produces an SSA value equal to some constant 1358 specified by an attribute. This is the way that MLIR uses to form simple 1359 integer and floating point constants, as well as more exotic things like 1360 references to functions and tensor/vector constants. 1361 1362 Example: 1363 1364 ```mlir 1365 // Integer constant 1366 %1 = constant 42 : i32 1367 1368 // Reference to function @myfn. 1369 %3 = constant @myfn : (tensor<16xf32>, f32) -> tensor<16xf32> 1370 1371 // Equivalent generic forms 1372 %1 = "std.constant"() {value = 42 : i32} : () -> i32 1373 %3 = "std.constant"() {value = @myfn} 1374 : () -> ((tensor<16xf32>, f32) -> tensor<16xf32>) 1375 ``` 1376 1377 MLIR does not allow direct references to functions in SSA operands because 1378 the compiler is multithreaded, and disallowing SSA values to directly 1379 reference a function simplifies this 1380 ([rationale](../Rationale/Rationale.md#multithreading-the-compiler)). 1381 }]; 1382 1383 let arguments = (ins AnyAttr:$value); 1384 let results = (outs AnyType); 1385 1386 let builders = [ 1387 OpBuilderDAG<(ins "Attribute":$value), 1388 [{ build($_builder, $_state, value.getType(), value); }]>]; 1389 1390 let extraClassDeclaration = [{ 1391 Attribute getValue() { return getAttr("value"); } 1392 1393 /// Returns true if a constant operation can be built with the given value 1394 /// and result type. 1395 static bool isBuildableWith(Attribute value, Type type); 1396 }]; 1397 1398 let hasFolder = 1; 1399} 1400 1401//===----------------------------------------------------------------------===// 1402// CopySignOp 1403//===----------------------------------------------------------------------===// 1404 1405def CopySignOp : FloatArithmeticOp<"copysign"> { 1406 let summary = "A copysign operation"; 1407 let description = [{ 1408 Syntax: 1409 1410 ``` 1411 operation ::= ssa-id `=` `std.copysign` ssa-use `:` type 1412 ``` 1413 1414 The `copysign` returns a value with the magnitude of the first operand and 1415 the sign of the second operand. It takes two operands and returns one 1416 result of the same type. This type may be a float scalar type, a vector 1417 whose element type is float, or a tensor of floats. It has no standard 1418 attributes. 1419 1420 Example: 1421 1422 ```mlir 1423 // Scalar copysign value. 1424 %a = copysign %b %c : f64 1425 1426 // SIMD vector element-wise copysign value. 1427 %f = copysign %g %h : vector<4xf32> 1428 1429 // Tensor element-wise copysign value. 1430 %x = copysign %y %z : tensor<4x?xf8> 1431 ``` 1432 }]; 1433} 1434 1435//===----------------------------------------------------------------------===// 1436// CosOp 1437//===----------------------------------------------------------------------===// 1438 1439def CosOp : FloatUnaryOp<"cos"> { 1440 let summary = "cosine of the specified value"; 1441 let description = [{ 1442 Syntax: 1443 1444 ``` 1445 operation ::= ssa-id `=` `std.cos` ssa-use `:` type 1446 ``` 1447 1448 The `cos` operation computes the cosine of a given value. It takes one 1449 operand and returns one result of the same type. This type may be a float 1450 scalar type, a vector whose element type is float, or a tensor of floats. 1451 It has no standard attributes. 1452 1453 Example: 1454 1455 ```mlir 1456 // Scalar cosine value. 1457 %a = cos %b : f64 1458 1459 // SIMD vector element-wise cosine value. 1460 %f = cos %g : vector<4xf32> 1461 1462 // Tensor element-wise cosine value. 1463 %x = cos %y : tensor<4x?xf8> 1464 ``` 1465 }]; 1466} 1467 1468//===----------------------------------------------------------------------===// 1469// SinOp 1470//===----------------------------------------------------------------------===// 1471 1472def SinOp : FloatUnaryOp<"sin"> { 1473 let summary = "sine of the specified value"; 1474 let description = [{ 1475 Syntax: 1476 1477 ``` 1478 operation ::= ssa-id `=` `std.sin` ssa-use `:` type 1479 ``` 1480 1481 The `sin` operation computes the sine of a given value. It takes one 1482 operand and returns one result of the same type. This type may be a float 1483 scalar type, a vector whose element type is float, or a tensor of floats. 1484 It has no standard attributes. 1485 1486 Example: 1487 1488 ```mlir 1489 // Scalar sine value. 1490 %a = sin %b : f64 1491 1492 // SIMD vector element-wise sine value. 1493 %f = sin %g : vector<4xf32> 1494 1495 // Tensor element-wise sine value. 1496 %x = sin %y : tensor<4x?xf8> 1497 ``` 1498 }]; 1499} 1500 1501//===----------------------------------------------------------------------===// 1502// DeallocOp 1503//===----------------------------------------------------------------------===// 1504 1505def DeallocOp : Std_Op<"dealloc", 1506 [MemoryEffects<[MemFree]>, MemRefsNormalizable]> { 1507 let summary = "memory deallocation operation"; 1508 let description = [{ 1509 The `dealloc` operation frees the region of memory referenced by a memref 1510 which was originally created by the `alloc` operation. 1511 The `dealloc` operation should not be called on memrefs which alias an 1512 alloc'd memref (e.g. memrefs returned by `view` operations). 1513 1514 Example: 1515 1516 ```mlir 1517 %0 = alloc() : memref<8x64xf32, (d0, d1) -> (d0, d1), 1> 1518 dealloc %0 : memref<8x64xf32, (d0, d1) -> (d0, d1), 1> 1519 ``` 1520 }]; 1521 1522 let arguments = (ins Arg<AnyMemRef, "", [MemFree]>:$memref); 1523 1524 let hasCanonicalizer = 1; 1525 let hasFolder = 1; 1526 let assemblyFormat = "$memref attr-dict `:` type($memref)"; 1527} 1528 1529//===----------------------------------------------------------------------===// 1530// DimOp 1531//===----------------------------------------------------------------------===// 1532 1533def DimOp : Std_Op<"dim", [NoSideEffect]> { 1534 let summary = "dimension index operation"; 1535 let description = [{ 1536 The `dim` operation takes a memref/tensor and a dimension operand of type 1537 `index`. 1538 It returns the size of the requested dimension of the given memref/tensor. 1539 If the dimension index is out of bounds the behavior is undefined. 1540 1541 The specified memref or tensor type is that of the first operand. 1542 1543 Example: 1544 1545 ```mlir 1546 // Always returns 4, can be constant folded: 1547 %c0 = constant 0 : index 1548 %x = = dim %A, %c0 : tensor<4 x ? x f32> 1549 1550 // Returns the dynamic dimension of %A. 1551 %c1 = constant 1 : index 1552 %y = dim %A, %c1 : tensor<4 x ? x f32> 1553 1554 // Equivalent generic form: 1555 %x = "std.dim"(%A, %c0) : (tensor<4 x ? x f32>, index) -> index 1556 %y = "std.dim"(%A, %c1) : (tensor<4 x ? x f32>, index) -> index 1557 ``` 1558 }]; 1559 1560 let arguments = (ins AnyTypeOf<[AnyRankedOrUnrankedMemRef, AnyTensor], 1561 "any tensor or memref type">:$memrefOrTensor, 1562 Index:$index); 1563 let results = (outs Index:$result); 1564 1565 let assemblyFormat = [{ 1566 attr-dict $memrefOrTensor `,` $index `:` type($memrefOrTensor) 1567 }]; 1568 1569 let builders = [ 1570 OpBuilderDAG<(ins "Value":$memrefOrTensor, "int64_t":$index)>, 1571 OpBuilderDAG<(ins "Value":$memrefOrTensor, "Value":$index)> 1572 ]; 1573 1574 let extraClassDeclaration = [{ 1575 /// Helper function to get the index as a simple integer if it is constant. 1576 Optional<int64_t> getConstantIndex(); 1577 }]; 1578 1579 let hasCanonicalizer = 1; 1580 let hasFolder = 1; 1581} 1582 1583//===----------------------------------------------------------------------===// 1584// DivFOp 1585//===----------------------------------------------------------------------===// 1586 1587def DivFOp : FloatArithmeticOp<"divf"> { 1588 let summary = "floating point division operation"; 1589} 1590 1591//===----------------------------------------------------------------------===// 1592// DynamicTensorFromElementsOp 1593//===----------------------------------------------------------------------===// 1594 1595def DynamicTensorFromElementsOp : Std_Op<"dynamic_tensor_from_elements", 1596 [RecursiveSideEffects, SingleBlockImplicitTerminator<"YieldOp">]> { 1597 string summary = "Creates a dynamically sized tensor from elements"; 1598 string description = [{ 1599 This operation creates a dynamically sized tensor with elements of any type. 1600 It expects one index operand per dynamic extent of the result tensor. 1601 1602 The body region defines the tensor's elements. It takes index operands as 1603 its region arguments that span the index space. The element at the given 1604 position is yielded with the `yield` operation (see `YieldOp`). There is 1605 no defined ordering to the invocations of the body. It is conceptually 1606 a "parallel map" operation. 1607 1608 Example: 1609 1610 ```mlir 1611 %tnsr = dynamic_tensor_from_elements %m, %n { 1612 ^bb0(%i : index, %j : index, %k : index): 1613 ... 1614 yield %elem : f32 1615 } : tensor<?x3x?f32> 1616 ``` 1617 }]; 1618 1619 let arguments = (ins Variadic<Index>:$dynamicExtents); 1620 let results = (outs AnyRankedTensor:$result); 1621 let regions = (region SizedRegion<1>:$body); 1622 1623 let builders = [ 1624 // Build op and populate its body per callback function. 1625 OpBuilderDAG<(ins "Type":$resultTy, "ValueRange":$dynamicExtents, 1626 "function_ref<void(OpBuilder &, Location, ValueRange)>")>, 1627 ]; 1628 1629 let hasCanonicalizer = 1; 1630} 1631 1632//===----------------------------------------------------------------------===// 1633// ExpOp 1634//===----------------------------------------------------------------------===// 1635 1636def ExpOp : FloatUnaryOp<"exp"> { 1637 let summary = "base-e exponential of the specified value"; 1638 let description = [{ 1639 Syntax: 1640 1641 ``` 1642 operation ::= ssa-id `=` `std.exp` ssa-use `:` type 1643 ``` 1644 1645 The `exp` operation takes one operand and returns one result of the same 1646 type. This type may be a float scalar type, a vector whose element type is 1647 float, or a tensor of floats. It has no standard attributes. 1648 1649 Example: 1650 1651 ```mlir 1652 // Scalar natural exponential. 1653 %a = exp %b : f64 1654 1655 // SIMD vector element-wise natural exponential. 1656 %f = exp %g : vector<4xf32> 1657 1658 // Tensor element-wise natural exponential. 1659 %x = exp %y : tensor<4x?xf8> 1660 ``` 1661 }]; 1662} 1663 1664//===----------------------------------------------------------------------===// 1665// ExpOp 1666//===----------------------------------------------------------------------===// 1667 1668def Exp2Op : FloatUnaryOp<"exp2"> { 1669 let summary = "base-2 exponential of the specified value"; 1670} 1671 1672//===----------------------------------------------------------------------===// 1673// ExtractElementOp 1674//===----------------------------------------------------------------------===// 1675 1676def ExtractElementOp : Std_Op<"extract_element", 1677 [NoSideEffect, 1678 TypesMatchWith<"result type matches element type of aggregate", 1679 "aggregate", "result", 1680 "$_self.cast<ShapedType>().getElementType()">]> { 1681 let summary = "element extract operation"; 1682 let description = [{ 1683 The `extract_element` op reads a tensor or vector and returns one element 1684 from it specified by an index list. The output of the 'extract_element' is a 1685 new value with the same type as the elements of the tensor or vector. The 1686 arity of indices matches the rank of the accessed value (i.e., if a tensor 1687 is of rank 3, then 3 indices are required for the extract. The indices 1688 should all be of `index` type. 1689 1690 Example: 1691 1692 ```mlir 1693 %3 = extract_element %v[%1, %2] : vector<4x4xi32> 1694 %4 = extract_element %t[%1, %2] : tensor<4x4xi32> 1695 %5 = extract_element %ut[%1, %2] : tensor<*xi32> 1696 ``` 1697 }]; 1698 1699 let arguments = (ins AnyTypeOf<[AnyVector, AnyTensor]>:$aggregate, 1700 Variadic<Index>:$indices); 1701 let results = (outs AnyType:$result); 1702 1703 let builders = [ 1704 OpBuilderDAG<(ins "Value":$aggregate, CArg<"ValueRange", "{}">:$indices), [{ 1705 auto resType = aggregate.getType().cast<ShapedType>() 1706 .getElementType(); 1707 build($_builder, $_state, resType, aggregate, indices); 1708 }]>]; 1709 1710 let extraClassDeclaration = [{ 1711 Value getAggregate() { return getOperand(0); } 1712 1713 operand_range getIndices() { 1714 return {operand_begin() + 1, operand_end()}; 1715 } 1716 }]; 1717 1718 let hasFolder = 1; 1719 1720 let assemblyFormat = [{ 1721 $aggregate `[` $indices `]` attr-dict `:` type($aggregate) 1722 }]; 1723} 1724 1725//===----------------------------------------------------------------------===// 1726// TensorFromElementsOp 1727//===----------------------------------------------------------------------===// 1728 1729def TensorFromElementsOp : Std_Op<"tensor_from_elements", [ 1730 NoSideEffect, 1731 TypesMatchWith<"operand types match result element type", 1732 "result", "elements", "SmallVector<Type, 2>(" 1733 "$_self.cast<ShapedType>().getDimSize(0), " 1734 "$_self.cast<ShapedType>().getElementType())"> 1735 ]> { 1736 string summary = "tensor from elements operation."; 1737 string description = [{ 1738 Create a 1D tensor from a range of same-type arguments. 1739 1740 Example: 1741 1742 ```mlir 1743 tensor_from_elements(i_1, ..., i_N) : tensor<Nxindex> 1744 ``` 1745 }]; 1746 1747 let arguments = (ins Variadic<AnyType>:$elements); 1748 let results = (outs 1DTensorOf<[AnyType]>:$result); 1749 1750 let assemblyFormat = "$elements attr-dict `:` type($result)"; 1751 1752 // This op is fully verified by its traits. 1753 let verifier = ?; 1754 1755 let skipDefaultBuilders = 1; 1756 let builders = [ 1757 OpBuilderDAG<(ins "Type":$elementType, "ValueRange":$elements)>, 1758 // Special case builder for when `elements` has size >=1. 1759 OpBuilderDAG<(ins "ValueRange":$elements)> 1760 ]; 1761 1762 let hasCanonicalizer = 1; 1763} 1764 1765//===----------------------------------------------------------------------===// 1766// FPExtOp 1767//===----------------------------------------------------------------------===// 1768 1769def FPExtOp : ArithmeticCastOp<"fpext">, Arguments<(ins AnyType:$in)> { 1770 let summary = "cast from floating-point to wider floating-point"; 1771 let description = [{ 1772 Cast a floating-point value to a larger floating-point-typed value. 1773 The destination type must to be strictly wider than the source type. 1774 Only scalars are currently supported. 1775 }]; 1776 1777 let extraClassDeclaration = [{ 1778 /// Return true if `a` and `b` are valid operand and result pairs for 1779 /// the operation. 1780 static bool areCastCompatible(Type a, Type b); 1781 }]; 1782 1783 let hasFolder = 0; 1784} 1785 1786//===----------------------------------------------------------------------===// 1787// FPToSIOp 1788//===----------------------------------------------------------------------===// 1789 1790def FPToSIOp : ArithmeticCastOp<"fptosi">, Arguments<(ins AnyType:$in)> { 1791 let summary = "cast from floating-point type to integer type"; 1792 let description = [{ 1793 Cast from a value interpreted as floating-point to the nearest (rounding 1794 towards zero) signed integer value. 1795 }]; 1796 1797 let extraClassDeclaration = [{ 1798 /// Return true if `a` and `b` are valid operand and result pairs for 1799 /// the operation. 1800 static bool areCastCompatible(Type a, Type b); 1801 }]; 1802 1803 let hasFolder = 0; 1804} 1805 1806//===----------------------------------------------------------------------===// 1807// FPToUIOp 1808//===----------------------------------------------------------------------===// 1809 1810def FPToUIOp : ArithmeticCastOp<"fptoui">, Arguments<(ins AnyType:$in)> { 1811 let summary = "cast from floating-point type to integer type"; 1812 let description = [{ 1813 Cast from a value interpreted as floating-point to the nearest (rounding 1814 towards zero) unsigned integer value. 1815 }]; 1816 1817 let extraClassDeclaration = [{ 1818 /// Return true if `a` and `b` are valid operand and result pairs for 1819 /// the operation. 1820 static bool areCastCompatible(Type a, Type b); 1821 }]; 1822 1823 let hasFolder = 0; 1824} 1825 1826//===----------------------------------------------------------------------===// 1827// FPTruncOp 1828//===----------------------------------------------------------------------===// 1829 1830def FPTruncOp : ArithmeticCastOp<"fptrunc">, Arguments<(ins AnyType:$in)> { 1831 let summary = "cast from floating-point to narrower floating-point"; 1832 let description = [{ 1833 Truncate a floating-point value to a smaller floating-point-typed value. 1834 The destination type must be strictly narrower than the source type. 1835 If the value cannot be exactly represented, it is rounded using the default 1836 rounding mode. Only scalars are currently supported. 1837 }]; 1838 1839 let extraClassDeclaration = [{ 1840 /// Return true if `a` and `b` are valid operand and result pairs for 1841 /// the operation. 1842 static bool areCastCompatible(Type a, Type b); 1843 }]; 1844 1845 let hasFolder = 0; 1846} 1847 1848//===----------------------------------------------------------------------===// 1849// GlobalMemrefOp 1850//===----------------------------------------------------------------------===// 1851 1852def GlobalMemrefOp : Std_Op<"global_memref", [Symbol]> { 1853 let summary = "declare or define a global memref variable"; 1854 let description = [{ 1855 The `global_memref` operation declares or defines a named global variable. 1856 The backing memory for the variable is allocated statically and is described 1857 by the type of the variable (which should be a statically shaped memref 1858 type). The operation is a declaration if no `inital_value` is specified, 1859 else it is a definition. The `initial_value` can either be a unit attribute 1860 to represent a definition of an uninitialized global variable, or an 1861 elements attribute to represent the definition of a global variable with an 1862 initial value. The global variable can also be marked constant using the 1863 `constant` unit attribute. Writing to such constant global variables is 1864 undefined. 1865 1866 The global variable can be accessed by using the `get_global_memref` to 1867 retrieve the memref for the global variable. Note that the memref 1868 for such global variable itself is immutable (i.e., get_global_memref for a 1869 given global variable will always return the same memref descriptor). 1870 1871 Example: 1872 1873 ```mlir 1874 // Private variable with an initial value. 1875 global_memref "private" @x : memref<2xf32> = dense<0.0,2.0> 1876 1877 // Declaration of an external variable. 1878 global_memref "private" @y : memref<4xi32> 1879 1880 // Uninitialized externally visible variable. 1881 global_memref @z : memref<3xf16> = uninitialized 1882 1883 // Externally visibile constant variable. 1884 global_memref constant @c : memref<2xi32> = dense<1, 4> 1885 ``` 1886 }]; 1887 1888 let arguments = (ins 1889 SymbolNameAttr:$sym_name, 1890 OptionalAttr<StrAttr>:$sym_visibility, 1891 TypeAttr:$type, 1892 OptionalAttr<AnyAttr>:$initial_value, 1893 UnitAttr:$constant 1894 ); 1895 1896 let assemblyFormat = [{ 1897 ($sym_visibility^)? 1898 (`constant` $constant^)? 1899 $sym_name `:` 1900 custom<GlobalMemrefOpTypeAndInitialValue>($type, $initial_value) 1901 attr-dict 1902 }]; 1903 1904 let extraClassDeclaration = [{ 1905 bool isExternal() { return !initial_value(); } 1906 bool isUninitialized() { 1907 return !isExternal() && initial_value().getValue().isa<UnitAttr>(); 1908 } 1909 }]; 1910} 1911 1912//===----------------------------------------------------------------------===// 1913// GetGlobalMemrefOp 1914//===----------------------------------------------------------------------===// 1915 1916def GetGlobalMemrefOp : Std_Op<"get_global_memref", 1917 [NoSideEffect, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> { 1918 let summary = "get the memref pointing to a global variable"; 1919 let description = [{ 1920 The `get_global_memref` operation retrieves the memref pointing to a 1921 named global variable. If the global variable is marked constant, writing 1922 to the result memref (such as through a `std.store` operation) is 1923 undefined. 1924 1925 Example: 1926 1927 ```mlir 1928 %x = get_global_memref @foo : memref<2xf32> 1929 ``` 1930 }]; 1931 1932 let arguments = (ins FlatSymbolRefAttr:$name); 1933 let results = (outs AnyStaticShapeMemRef:$result); 1934 let assemblyFormat = "$name `:` type($result) attr-dict"; 1935 1936 // `GetGlobalMemrefOp` is fully verified by its traits. 1937 let verifier = ?; 1938} 1939 1940//===----------------------------------------------------------------------===// 1941// ImOp 1942//===----------------------------------------------------------------------===// 1943 1944def ImOp : Std_Op<"im", 1945 [NoSideEffect, 1946 TypesMatchWith<"complex element type matches result type", 1947 "complex", "imaginary", 1948 "$_self.cast<ComplexType>().getElementType()">]> { 1949 let summary = "extracts the imaginary part of a complex number"; 1950 let description = [{ 1951 The `im` operation takes a single complex number as its operand and extracts 1952 the imaginary part as a floating-point value. 1953 1954 Example: 1955 1956 ```mlir 1957 %a = im %b : complex<f32> 1958 ``` 1959 }]; 1960 1961 let arguments = (ins Complex<AnyFloat>:$complex); 1962 let results = (outs AnyFloat:$imaginary); 1963 1964 let assemblyFormat = "$complex attr-dict `:` type($complex)"; 1965 1966 // `ImOp` is fully verified by its traits. 1967 let verifier = ?; 1968} 1969 1970//===----------------------------------------------------------------------===// 1971// IndexCastOp 1972//===----------------------------------------------------------------------===// 1973 1974def IndexCastOp : ArithmeticCastOp<"index_cast">, Arguments<(ins AnyType:$in)> { 1975 let summary = "cast between index and integer types"; 1976 let description = [{ 1977 Casts between integer scalars and 'index' scalars. Index is an integer of 1978 platform-specific bit width. If casting to a wider integer, the value is 1979 sign-extended. If casting to a narrower integer, the value is truncated. 1980 }]; 1981 1982 let extraClassDeclaration = [{ 1983 /// Return true if `a` and `b` are valid operand and result pairs for 1984 /// the operation. 1985 static bool areCastCompatible(Type a, Type b); 1986 }]; 1987 1988 let hasFolder = 1; 1989} 1990 1991//===----------------------------------------------------------------------===// 1992// LoadOp 1993//===----------------------------------------------------------------------===// 1994 1995def LoadOp : Std_Op<"load", 1996 [TypesMatchWith<"result type matches element type of 'memref'", 1997 "memref", "result", 1998 "$_self.cast<MemRefType>().getElementType()">, 1999 MemRefsNormalizable]> { 2000 let summary = "load operation"; 2001 let description = [{ 2002 The `load` op reads an element from a memref specified by an index list. The 2003 output of load is a new value with the same type as the elements of the 2004 memref. The arity of indices is the rank of the memref (i.e., if the memref 2005 loaded from is of rank 3, then 3 indices are required for the load following 2006 the memref identifier). 2007 2008 In an `affine.if` or `affine.for` body, the indices of a load are restricted 2009 to SSA values bound to surrounding loop induction variables, 2010 [symbols](Affine.md#dimensions-and-symbols), results of a 2011 [`constant` operation](#stdconstant-constantop), or the result of an 2012 `affine.apply` operation that can in turn take as arguments all of the 2013 aforementioned SSA values or the recursively result of such an 2014 `affine.apply` operation. 2015 2016 Example: 2017 2018 ```mlir 2019 %1 = affine.apply affine_map<(d0, d1) -> (3*d0)> (%i, %j) 2020 %2 = affine.apply affine_map<(d0, d1) -> (d1+1)> (%i, %j) 2021 %12 = load %A[%1, %2] : memref<8x?xi32, #layout, memspace0> 2022 2023 // Example of an indirect load (treated as non-affine) 2024 %3 = affine.apply affine_map<(d0) -> (2*d0 + 1)>(%12) 2025 %13 = load %A[%3, %2] : memref<4x?xi32, #layout, memspace0> 2026 ``` 2027 2028 **Context:** The `load` and `store` operations are specifically crafted to 2029 fully resolve a reference to an element of a memref, and (in affine 2030 `affine.if` and `affine.for` operations) the compiler can follow use-def 2031 chains (e.g. through [`affine.apply`](Affine.md#affineapply-affineapplyop) 2032 operations) to precisely analyze references at compile-time using polyhedral 2033 techniques. This is possible because of the 2034 [restrictions on dimensions and symbols](Affine.md#restrictions-on-dimensions-and-symbols) 2035 in these contexts. 2036 }]; 2037 2038 let arguments = (ins Arg<AnyMemRef, "the reference to load from", 2039 [MemRead]>:$memref, 2040 Variadic<Index>:$indices); 2041 let results = (outs AnyType:$result); 2042 2043 let builders = [ 2044 OpBuilderDAG<(ins "Value":$memref, CArg<"ValueRange", "{}">:$indices), [{ 2045 auto memrefType = memref.getType().cast<MemRefType>(); 2046 $_state.addOperands(memref); 2047 $_state.addOperands(indices); 2048 $_state.types.push_back(memrefType.getElementType()); 2049 }]>]; 2050 2051 let extraClassDeclaration = [{ 2052 Value getMemRef() { return getOperand(0); } 2053 void setMemRef(Value value) { setOperand(0, value); } 2054 MemRefType getMemRefType() { 2055 return getMemRef().getType().cast<MemRefType>(); 2056 } 2057 2058 operand_range getIndices() { return {operand_begin() + 1, operand_end()}; } 2059 }]; 2060 2061 let hasCanonicalizer = 1; 2062 let hasFolder = 1; 2063 2064 let assemblyFormat = "$memref `[` $indices `]` attr-dict `:` type($memref)"; 2065} 2066 2067//===----------------------------------------------------------------------===// 2068// LogOp 2069//===----------------------------------------------------------------------===// 2070 2071def LogOp : FloatUnaryOp<"log"> { 2072 let summary = "base-e logarithm of the specified value"; 2073} 2074 2075def Log10Op : FloatUnaryOp<"log10"> { 2076 let summary = "base-10 logarithm of the specified value"; 2077} 2078 2079def Log2Op : FloatUnaryOp<"log2"> { 2080 let summary = "base-2 logarithm of the specified value"; 2081} 2082 2083//===----------------------------------------------------------------------===// 2084// MemRefCastOp 2085//===----------------------------------------------------------------------===// 2086 2087def MemRefCastOp : CastOp<"memref_cast", [ 2088 DeclareOpInterfaceMethods<ViewLikeOpInterface> 2089 ]> { 2090 let summary = "memref cast operation"; 2091 let description = [{ 2092 Syntax: 2093 2094 ``` 2095 operation ::= ssa-id `=` `std.memref_cast` ssa-use `:` type `to` type 2096 ``` 2097 2098 The `memref_cast` operation converts a memref from one type to an equivalent 2099 type with a compatible shape. The source and destination types are 2100 compatible if: 2101 2102 a. Both are ranked memref types with the same element type, address space, 2103 and rank and: 2104 1. Both have the same layout or both have compatible strided layouts. 2105 2. The individual sizes (resp. offset and strides in the case of strided 2106 memrefs) may convert constant dimensions to dynamic dimensions and 2107 vice-versa. 2108 2109 If the cast converts any dimensions from an unknown to a known size, then it 2110 acts as an assertion that fails at runtime if the dynamic dimensions 2111 disagree with resultant destination size. 2112 2113 Example: 2114 2115 ```mlir 2116 // Assert that the input dynamic shape matches the destination static shape. 2117 %2 = memref_cast %1 : memref<?x?xf32> to memref<4x4xf32> 2118 // Erase static shape information, replacing it with dynamic information. 2119 %3 = memref_cast %1 : memref<4xf32> to memref<?xf32> 2120 2121 // The same holds true for offsets and strides. 2122 2123 // Assert that the input dynamic shape matches the destination static stride. 2124 %4 = memref_cast %1 : memref<12x4xf32, offset:?, strides: [?, ?]> to 2125 memref<12x4xf32, offset:5, strides: [4, 1]> 2126 // Erase static offset and stride information, replacing it with 2127 // dynamic information. 2128 %5 = memref_cast %1 : memref<12x4xf32, offset:5, strides: [4, 1]> to 2129 memref<12x4xf32, offset:?, strides: [?, ?]> 2130 ``` 2131 2132 b. Either or both memref types are unranked with the same element type, and 2133 address space. 2134 2135 Example: 2136 2137 ```mlir 2138 Cast to concrete shape. 2139 %4 = memref_cast %1 : memref<*xf32> to memref<4x?xf32> 2140 2141 Erase rank information. 2142 %5 = memref_cast %1 : memref<4x?xf32> to memref<*xf32> 2143 ``` 2144 }]; 2145 2146 let arguments = (ins AnyRankedOrUnrankedMemRef:$source); 2147 let results = (outs AnyRankedOrUnrankedMemRef); 2148 2149 let extraClassDeclaration = [{ 2150 /// Return true if `a` and `b` are valid operand and result pairs for 2151 /// the operation. 2152 static bool areCastCompatible(Type a, Type b); 2153 2154 /// The result of a memref_cast is always a memref. 2155 Type getType() { return getResult().getType(); } 2156 }]; 2157} 2158 2159 2160//===----------------------------------------------------------------------===// 2161// MemRefReinterpretCastOp 2162//===----------------------------------------------------------------------===// 2163 2164def MemRefReinterpretCastOp: 2165 BaseOpWithOffsetSizesAndStrides<"memref_reinterpret_cast", [ 2166 NoSideEffect, ViewLikeOpInterface, OffsetSizeAndStrideOpInterface 2167 ]> { 2168 let summary = "memref reinterpret cast operation"; 2169 let description = [{ 2170 Modify offset, sizes and strides of an unranked/ranked memref. 2171 2172 Example: 2173 ```mlir 2174 memref_reinterpret_cast %ranked to 2175 offset: [0], 2176 sizes: [%size0, 10], 2177 strides: [1, %stride1] 2178 : memref<?x?xf32> to memref<?x10xf32, offset: 0, strides: [1, ?]> 2179 2180 memref_reinterpret_cast %unranked to 2181 offset: [%offset], 2182 sizes: [%size0, %size1], 2183 strides: [%stride0, %stride1] 2184 : memref<*xf32> to memref<?x?xf32, offset: ?, strides: [?, ?]> 2185 ``` 2186 }]; 2187 2188 let arguments = (ins 2189 Arg<AnyRankedOrUnrankedMemRef, "", []>:$source, 2190 Variadic<Index>:$offsets, 2191 Variadic<Index>:$sizes, 2192 Variadic<Index>:$strides, 2193 I64ArrayAttr:$static_offsets, 2194 I64ArrayAttr:$static_sizes, 2195 I64ArrayAttr:$static_strides 2196 ); 2197 let results = (outs AnyMemRef:$result); 2198 2199 let builders = [ 2200 // Build a ReinterpretCastOp with mixed static and dynamic entries. 2201 OpBuilderDAG<(ins "MemRefType":$resultType, "Value":$source, 2202 "int64_t":$staticOffset, "ArrayRef<int64_t>":$staticSizes, 2203 "ArrayRef<int64_t>":$staticStrides, "ValueRange":$offset, 2204 "ValueRange":$sizes, "ValueRange":$strides, 2205 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 2206 // Build a ReinterpretCastOp with all dynamic entries. 2207 OpBuilderDAG<(ins "MemRefType":$resultType, "Value":$source, 2208 "Value":$offset, "ValueRange":$sizes, "ValueRange":$strides, 2209 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 2210 ]; 2211 2212 let extraClassDeclaration = extraBaseClassDeclaration # [{ 2213 // The result of the op is always a ranked memref. 2214 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 2215 Value getViewSource() { return source(); } 2216 2217 /// Return the rank of the source ShapedType. 2218 unsigned getResultRank() { 2219 return getResult().getType().cast<ShapedType>().getRank(); 2220 } 2221 2222 /// Return the expected rank of each of the`static_offsets`, `static_sizes` 2223 /// and `static_strides` attributes. 2224 std::array<unsigned, 3> getArrayAttrRanks() { 2225 unsigned resultRank = getResult().getType().cast<ShapedType>().getRank(); 2226 return {1, resultRank, resultRank}; 2227 } 2228 }]; 2229} 2230 2231//===----------------------------------------------------------------------===// 2232// MemRefReshapeOp 2233//===----------------------------------------------------------------------===// 2234 2235def MemRefReshapeOp: Std_Op<"memref_reshape", [ 2236 ViewLikeOpInterface, NoSideEffect]> { 2237 let summary = "memref reshape operation"; 2238 let description = [{ 2239 The `memref_reshape` operation converts a memref from one type to an 2240 equivalent type with a provided shape. The data is never copied or 2241 modified. The source and destination types are compatible if both have the 2242 same element type, same number of elements, address space and identity 2243 layout map. The following combinations are possible: 2244 2245 a. Source type is ranked or unranked. Shape argument has static size. 2246 Result type is ranked. 2247 2248 ```mlir 2249 // Reshape statically-shaped memref. 2250 %dst = memref_reshape %src(%shape) 2251 : (memref<4x1xf32>, memref<1xi32>) to memref<4xf32> 2252 %dst0 = memref_reshape %src(%shape0) 2253 : (memref<4x1xf32>, memref<2xi32>) to memref<2x2xf32> 2254 // Flatten unranked memref. 2255 %dst = memref_reshape %src(%shape) 2256 : (memref<*xf32>, memref<1xi32>) to memref<?xf32> 2257 ``` 2258 2259 a. Source type is ranked or unranked. Shape argument has dynamic size. 2260 Result type is unranked. 2261 2262 ```mlir 2263 // Reshape dynamically-shaped 1D memref. 2264 %dst = memref_reshape %src(%shape) 2265 : (memref<?xf32>, memref<?xi32>) to memref<*xf32> 2266 // Reshape unranked memref. 2267 %dst = memref_reshape %src(%shape) 2268 : (memref<*xf32>, memref<?xi32>) to memref<*xf32> 2269 ``` 2270 }]; 2271 2272 let arguments = (ins 2273 AnyRankedOrUnrankedMemRef:$source, 2274 MemRefRankOf<[AnySignlessInteger, Index], [1]>:$shape 2275 ); 2276 let results = (outs AnyRankedOrUnrankedMemRef:$result); 2277 2278 let builders = [OpBuilderDAG< 2279 (ins "MemRefType":$resultType, "Value":$operand, "Value":$shape), [{ 2280 $_state.addOperands(operand); 2281 $_state.addOperands(shape); 2282 $_state.addTypes(resultType); 2283 }]>]; 2284 2285 let extraClassDeclaration = [{ 2286 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 2287 Value getViewSource() { return source(); } 2288 }]; 2289 2290 let assemblyFormat = [{ 2291 $source `(` $shape `)` attr-dict `:` functional-type(operands, results) 2292 }]; 2293} 2294 2295//===----------------------------------------------------------------------===// 2296// MulFOp 2297//===----------------------------------------------------------------------===// 2298 2299def MulFOp : FloatArithmeticOp<"mulf"> { 2300 let summary = "floating point multiplication operation"; 2301 let description = [{ 2302 Syntax: 2303 2304 ``` 2305 operation ::= ssa-id `=` `std.mulf` ssa-use `,` ssa-use `:` type 2306 ``` 2307 2308 The `mulf` operation takes two operands and returns one result, each of 2309 these is required to be the same type. This type may be a floating point 2310 scalar type, a vector whose element type is a floating point type, or a 2311 floating point tensor. 2312 2313 Example: 2314 2315 ```mlir 2316 // Scalar multiplication. 2317 %a = mulf %b, %c : f64 2318 2319 // SIMD pointwise vector multiplication, e.g. for Intel SSE. 2320 %f = mulf %g, %h : vector<4xf32> 2321 2322 // Tensor pointwise multiplication. 2323 %x = mulf %y, %z : tensor<4x?xbf16> 2324 ``` 2325 2326 TODO: In the distant future, this will accept optional attributes for fast 2327 math, contraction, rounding mode, and other controls. 2328 }]; 2329 let hasFolder = 1; 2330} 2331 2332//===----------------------------------------------------------------------===// 2333// MulIOp 2334//===----------------------------------------------------------------------===// 2335 2336def MulIOp : IntArithmeticOp<"muli", [Commutative]> { 2337 let summary = "integer multiplication operation"; 2338 let hasFolder = 1; 2339} 2340 2341//===----------------------------------------------------------------------===// 2342// NegFOp 2343//===----------------------------------------------------------------------===// 2344 2345def NegFOp : FloatUnaryOp<"negf"> { 2346 let summary = "floating point negation"; 2347 let description = [{ 2348 Syntax: 2349 2350 ``` 2351 operation ::= ssa-id `=` `negf` ssa-use `:` type 2352 ``` 2353 2354 The `negf` operation computes the negation of a given value. It takes one 2355 operand and returns one result of the same type. This type may be a float 2356 scalar type, a vector whose element type is float, or a tensor of floats. 2357 It has no standard attributes. 2358 2359 Example: 2360 2361 ```mlir 2362 // Scalar negation value. 2363 %a = negf %b : f64 2364 2365 // SIMD vector element-wise negation value. 2366 %f = negf %g : vector<4xf32> 2367 2368 // Tensor element-wise negation value. 2369 %x = negf %y : tensor<4x?xf8> 2370 ``` 2371 }]; 2372} 2373 2374//===----------------------------------------------------------------------===// 2375// OrOp 2376//===----------------------------------------------------------------------===// 2377 2378def OrOp : IntArithmeticOp<"or", [Commutative]> { 2379 let summary = "integer binary or"; 2380 let description = [{ 2381 Syntax: 2382 2383 ``` 2384 operation ::= ssa-id `=` `or` ssa-use `,` ssa-use `:` type 2385 ``` 2386 2387 The `or` operation takes two operands and returns one result, each of these 2388 is required to be the same type. This type may be an integer scalar type, a 2389 vector whose element type is integer, or a tensor of integers. It has no 2390 standard attributes. 2391 2392 Example: 2393 2394 ```mlir 2395 // Scalar integer bitwise or. 2396 %a = or %b, %c : i64 2397 2398 // SIMD vector element-wise bitwise integer or. 2399 %f = or %g, %h : vector<4xi32> 2400 2401 // Tensor element-wise bitwise integer or. 2402 %x = or %y, %z : tensor<4x?xi8> 2403 ``` 2404 }]; 2405 let hasFolder = 1; 2406} 2407 2408//===----------------------------------------------------------------------===// 2409// PrefetchOp 2410//===----------------------------------------------------------------------===// 2411 2412def PrefetchOp : Std_Op<"prefetch"> { 2413 let summary = "prefetch operation"; 2414 let description = [{ 2415 The "prefetch" op prefetches data from a memref location described with 2416 subscript indices similar to std.load, and with three attributes: a 2417 read/write specifier, a locality hint, and a cache type specifier as shown 2418 below: 2419 2420 ```mlir 2421 prefetch %0[%i, %j], read, locality<3>, data : memref<400x400xi32> 2422 ``` 2423 2424 The read/write specifier is either 'read' or 'write', the locality hint 2425 ranges from locality<0> (no locality) to locality<3> (extremely local keep 2426 in cache). The cache type specifier is either 'data' or 'instr' 2427 and specifies whether the prefetch is performed on data cache or on 2428 instruction cache. 2429 }]; 2430 2431 let arguments = (ins AnyMemRef:$memref, Variadic<Index>:$indices, 2432 BoolAttr:$isWrite, 2433 Confined<I32Attr, [IntMinValue<0>, 2434 IntMaxValue<3>]>:$localityHint, 2435 BoolAttr:$isDataCache); 2436 2437 let extraClassDeclaration = [{ 2438 MemRefType getMemRefType() { 2439 return memref().getType().cast<MemRefType>(); 2440 } 2441 static StringRef getLocalityHintAttrName() { return "localityHint"; } 2442 static StringRef getIsWriteAttrName() { return "isWrite"; } 2443 static StringRef getIsDataCacheAttrName() { return "isDataCache"; } 2444 }]; 2445 2446 let hasFolder = 1; 2447} 2448 2449//===----------------------------------------------------------------------===// 2450// RankOp 2451//===----------------------------------------------------------------------===// 2452 2453def RankOp : Std_Op<"rank", [NoSideEffect]> { 2454 let summary = "rank operation"; 2455 let description = [{ 2456 The `rank` operation takes a memref/tensor operand and returns its rank. 2457 2458 Example: 2459 2460 ```mlir 2461 %1 = rank %arg0 : tensor<*xf32> 2462 %2 = rank %arg1 : memref<*xf32> 2463 ``` 2464 }]; 2465 2466 let arguments = (ins AnyTypeOf<[AnyRankedOrUnrankedMemRef, AnyTensor], 2467 "any tensor or memref type">:$memrefOrTensor); 2468 let results = (outs Index); 2469 let verifier = ?; 2470 2471 let builders = [ 2472 OpBuilderDAG<(ins "Value":$tensor), [{ 2473 auto indexType = $_builder.getIndexType(); 2474 build($_builder, $_state, indexType, tensor); 2475 }]>]; 2476 2477 let hasFolder = 1; 2478 let assemblyFormat = "$memrefOrTensor attr-dict `:` type($memrefOrTensor)"; 2479} 2480 2481//===----------------------------------------------------------------------===// 2482// ReOp 2483//===----------------------------------------------------------------------===// 2484 2485def ReOp : Std_Op<"re", 2486 [NoSideEffect, 2487 TypesMatchWith<"complex element type matches result type", 2488 "complex", "real", 2489 "$_self.cast<ComplexType>().getElementType()">]> { 2490 let summary = "extracts the real part of a complex number"; 2491 let description = [{ 2492 The `re` operation takes a single complex number as its operand and extracts 2493 the real part as a floating-point value. 2494 2495 Example: 2496 2497 ```mlir 2498 %a = re %b : complex<f32> 2499 ``` 2500 }]; 2501 2502 let arguments = (ins Complex<AnyFloat>:$complex); 2503 let results = (outs AnyFloat:$real); 2504 2505 let assemblyFormat = "$complex attr-dict `:` type($complex)"; 2506 2507 // `ReOp` is fully verified by its traits. 2508 let verifier = ?; 2509} 2510 2511//===----------------------------------------------------------------------===// 2512// RemFOp 2513//===----------------------------------------------------------------------===// 2514 2515def RemFOp : FloatArithmeticOp<"remf"> { 2516 let summary = "floating point division remainder operation"; 2517} 2518 2519//===----------------------------------------------------------------------===// 2520// ReturnOp 2521//===----------------------------------------------------------------------===// 2522 2523def ReturnOp : Std_Op<"return", [NoSideEffect, HasParent<"FuncOp">, 2524 MemRefsNormalizable, ReturnLike, Terminator]> { 2525 let summary = "return operation"; 2526 let description = [{ 2527 The `return` operation represents a return operation within a function. 2528 The operation takes variable number of operands and produces no results. 2529 The operand number and types must match the signature of the function 2530 that contains the operation. 2531 2532 Example: 2533 2534 ```mlir 2535 func @foo() : (i32, f8) { 2536 ... 2537 return %0, %1 : i32, f8 2538 } 2539 ``` 2540 }]; 2541 2542 let arguments = (ins Variadic<AnyType>:$operands); 2543 2544 let builders = [ 2545 OpBuilderDAG<(ins), 2546 [{ build($_builder, $_state, llvm::None); }]>]; 2547 2548 let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; 2549} 2550 2551//===----------------------------------------------------------------------===// 2552// RsqrtOp 2553//===----------------------------------------------------------------------===// 2554 2555def RsqrtOp : FloatUnaryOp<"rsqrt"> { 2556 let summary = "reciprocal of sqrt (1 / sqrt of the specified value)"; 2557 let description = [{ 2558 The `rsqrt` operation computes the reciprocal of the square root. It takes 2559 one operand and returns one result of the same type. This type may be a 2560 float scalar type, a vector whose element type is float, or a tensor of 2561 floats. It has no standard attributes. 2562 }]; 2563} 2564 2565//===----------------------------------------------------------------------===// 2566// SelectOp 2567//===----------------------------------------------------------------------===// 2568 2569def SelectOp : Std_Op<"select", [NoSideEffect, 2570 AllTypesMatch<["true_value", "false_value", "result"]>, 2571 ElementwiseMappable]> { 2572 let summary = "select operation"; 2573 let description = [{ 2574 The `select` operation chooses one value based on a binary condition 2575 supplied as its first operand. If the value of the first operand is `1`, 2576 the second operand is chosen, otherwise the third operand is chosen. 2577 The second and the third operand must have the same type. 2578 2579 The operation applies to vectors and tensors elementwise given the _shape_ 2580 of all operands is identical. The choice is made for each element 2581 individually based on the value at the same position as the element in the 2582 condition operand. If an i1 is provided as the condition, the entire vector 2583 or tensor is chosen. 2584 2585 The `select` operation combined with [`cmpi`](#stdcmpi-cmpiop) can be used 2586 to implement `min` and `max` with signed or unsigned comparison semantics. 2587 2588 Example: 2589 2590 ```mlir 2591 // Custom form of scalar selection. 2592 %x = select %cond, %true, %false : i32 2593 2594 // Generic form of the same operation. 2595 %x = "std.select"(%cond, %true, %false) : (i1, i32, i32) -> i32 2596 2597 // Element-wise vector selection. 2598 %vx = std.select %vcond, %vtrue, %vfalse : vector<42xi1>, vector<42xf32> 2599 2600 // Full vector selection. 2601 %vx = std.select %cond, %vtrue, %vfalse : vector<42xf32> 2602 ``` 2603 }]; 2604 2605 let arguments = (ins BoolLike:$condition, 2606 AnyType:$true_value, 2607 AnyType:$false_value); 2608 let results = (outs AnyType:$result); 2609 2610 let builders = [ 2611 OpBuilderDAG<(ins "Value":$condition, "Value":$trueValue, 2612 "Value":$falseValue), [{ 2613 $_state.addOperands({condition, trueValue, falseValue}); 2614 $_state.addTypes(trueValue.getType()); 2615 }]>]; 2616 2617 let extraClassDeclaration = [{ 2618 Value getCondition() { return condition(); } 2619 Value getTrueValue() { return true_value(); } 2620 Value getFalseValue() { return false_value(); } 2621 }]; 2622 2623 let hasFolder = 1; 2624} 2625 2626//===----------------------------------------------------------------------===// 2627// ShiftLeftOp 2628//===----------------------------------------------------------------------===// 2629 2630def ShiftLeftOp : IntArithmeticOp<"shift_left"> { 2631 let summary = "integer left-shift"; 2632 let description = [{ 2633 The shift_left operation shifts an integer value to the left by a variable 2634 amount. The low order bits are filled with zeros. 2635 2636 Example: 2637 2638 ```mlir 2639 %1 = constant 5 : i8 // %1 is 0b00000101 2640 %2 = constant 3 : i8 2641 %3 = shift_left %1, %2 : (i8, i8) -> i8 // %3 is 0b00101000 2642 ``` 2643 }]; 2644} 2645 2646//===----------------------------------------------------------------------===// 2647// SignedDivIOp 2648//===----------------------------------------------------------------------===// 2649 2650def SignedDivIOp : IntArithmeticOp<"divi_signed"> { 2651 let summary = "signed integer division operation"; 2652 let description = [{ 2653 Syntax: 2654 2655 ``` 2656 operation ::= ssa-id `=` `divi_signed` ssa-use `,` ssa-use `:` type 2657 ``` 2658 2659 Signed integer division. Rounds towards zero. Treats the leading bit as 2660 sign, i.e. `6 / -2 = -3`. 2661 2662 Note: the semantics of division by zero or signed division overflow (minimum 2663 value divided by -1) is TBD; do NOT assume any specific behavior. 2664 2665 Example: 2666 2667 ```mlir 2668 // Scalar signed integer division. 2669 %a = divis %b, %c : i64 2670 2671 // SIMD vector element-wise division. 2672 %f = divis %g, %h : vector<4xi32> 2673 2674 // Tensor element-wise integer division. 2675 %x = divis %y, %z : tensor<4x?xi8> 2676 ``` 2677 }]; 2678 let hasFolder = 1; 2679} 2680 2681//===----------------------------------------------------------------------===// 2682// SignedFloorDivIOp 2683//===----------------------------------------------------------------------===// 2684 2685def SignedFloorDivIOp : IntArithmeticOp<"floordivi_signed"> { 2686 let summary = "signed floor integer division operation"; 2687 let description = [{ 2688 Syntax: 2689 2690 ``` 2691 operation ::= ssa-id `=` `floordivi_signed` ssa-use `,` ssa-use `:` type 2692 ``` 2693 2694 Signed integer division. Rounds towards negative infinity, i.e. `5 / -2 = -3`. 2695 2696 Note: the semantics of division by zero or signed division overflow (minimum 2697 value divided by -1) is TBD; do NOT assume any specific behavior. 2698 2699 Example: 2700 2701 ```mlir 2702 // Scalar signed integer division. 2703 %a = floordivi_signed %b, %c : i64 2704 2705 ``` 2706 }]; 2707 let hasFolder = 1; 2708} 2709 2710//===----------------------------------------------------------------------===// 2711// SignedCeilDivIOp 2712//===----------------------------------------------------------------------===// 2713 2714def SignedCeilDivIOp : IntArithmeticOp<"ceildivi_signed"> { 2715 let summary = "signed ceil integer division operation"; 2716 let description = [{ 2717 Syntax: 2718 2719 ``` 2720 operation ::= ssa-id `=` `ceildivi_signed` ssa-use `,` ssa-use `:` type 2721 ``` 2722 2723 Signed integer division. Rounds towards positive infinity, i.e. `7 / -2 = -3`. 2724 2725 Note: the semantics of division by zero or signed division overflow (minimum 2726 value divided by -1) is TBD; do NOT assume any specific behavior. 2727 2728 Example: 2729 2730 ```mlir 2731 // Scalar signed integer division. 2732 %a = ceildivi_signed %b, %c : i64 2733 ``` 2734 }]; 2735 let hasFolder = 1; 2736} 2737 2738//===----------------------------------------------------------------------===// 2739// SignedRemIOp 2740//===----------------------------------------------------------------------===// 2741 2742def SignedRemIOp : IntArithmeticOp<"remi_signed"> { 2743 let summary = "signed integer division remainder operation"; 2744 let description = [{ 2745 Syntax: 2746 2747 ``` 2748 operation ::= ssa-id `=` `std.remi_signed` ssa-use `,` ssa-use `:` type 2749 ``` 2750 2751 Signed integer division remainder. Treats the leading bit as sign, i.e. `6 % 2752 -2 = 0`. 2753 2754 Note: the semantics of division by zero is TBD; do NOT assume any specific 2755 behavior. 2756 2757 Example: 2758 2759 ```mlir 2760 // Scalar signed integer division remainder. 2761 %a = remis %b, %c : i64 2762 2763 // SIMD vector element-wise division remainder. 2764 %f = remis %g, %h : vector<4xi32> 2765 2766 // Tensor element-wise integer division remainder. 2767 %x = remis %y, %z : tensor<4x?xi8> 2768 ``` 2769 }]; 2770 let hasFolder = 1; 2771} 2772 2773//===----------------------------------------------------------------------===// 2774// SignedShiftRightOp 2775//===----------------------------------------------------------------------===// 2776 2777def SignedShiftRightOp : IntArithmeticOp<"shift_right_signed"> { 2778 let summary = "signed integer right-shift"; 2779 let description = [{ 2780 The shift_right_signed operation shifts an integer value to the right by 2781 a variable amount. The integer is interpreted as signed. The high order 2782 bits in the output are filled with copies of the most-significant bit 2783 of the shifted value (which means that the sign of the value is preserved). 2784 2785 Example: 2786 2787 ```mlir 2788 %1 = constant 160 : i8 // %1 is 0b10100000 2789 %2 = constant 3 : i8 2790 %3 = shift_right_signed %1, %2 : (i8, i8) -> i8 // %3 is 0b11110100 2791 %4 = constant 96 : i8 // %4 is 0b01100000 2792 %5 = shift_right_signed %4, %2 : (i8, i8) -> i8 // %5 is 0b00001100 2793 ``` 2794 }]; 2795} 2796 2797//===----------------------------------------------------------------------===// 2798// SignExtendIOp 2799//===----------------------------------------------------------------------===// 2800 2801def SignExtendIOp : Std_Op<"sexti", 2802 [NoSideEffect, ElementwiseMappable]> { 2803 let summary = "integer sign extension operation"; 2804 let description = [{ 2805 The integer sign extension operation takes an integer input of 2806 width M and an integer destination type of width N. The destination 2807 bit-width must be larger than the input bit-width (N > M). 2808 The top-most (N - M) bits of the output are filled with copies 2809 of the most-significant bit of the input. 2810 2811 Example: 2812 2813 ```mlir 2814 %1 = constant 5 : i3 // %1 is 0b101 2815 %2 = sexti %1 : i3 to i6 // %2 is 0b111101 2816 %3 = constant 2 : i3 // %3 is 0b010 2817 %4 = sexti %3 : i3 to i6 // %4 is 0b000010 2818 2819 %5 = sexti %0 : vector<2 x i32> to vector<2 x i64> 2820 ``` 2821 }]; 2822 2823 let arguments = (ins SignlessIntegerLike:$value); 2824 let results = (outs SignlessIntegerLike); 2825 2826 let builders = [ 2827 OpBuilderDAG<(ins "Value":$value, "Type":$destType), [{ 2828 $_state.addOperands(value); 2829 $_state.addTypes(destType); 2830 }]>]; 2831 2832 let parser = [{ 2833 return impl::parseCastOp(parser, result); 2834 }]; 2835 let printer = [{ 2836 return printStandardCastOp(this->getOperation(), p); 2837 }]; 2838} 2839 2840//===----------------------------------------------------------------------===// 2841// SIToFPOp 2842//===----------------------------------------------------------------------===// 2843 2844def SIToFPOp : ArithmeticCastOp<"sitofp">, Arguments<(ins AnyType:$in)> { 2845 let summary = "cast from integer type to floating-point"; 2846 let description = [{ 2847 Cast from a value interpreted as signed or vector of signed integers to the 2848 corresponding floating-point scalar or vector value. If the value cannot be 2849 exactly represented, it is rounded using the default rounding mode. Scalars 2850 and vector types are currently supported. 2851 }]; 2852 2853 let extraClassDeclaration = [{ 2854 /// Return true if `a` and `b` are valid operand and result pairs for 2855 /// the operation. 2856 static bool areCastCompatible(Type a, Type b); 2857 }]; 2858 2859 let hasFolder = 0; 2860} 2861 2862//===----------------------------------------------------------------------===// 2863// SplatOp 2864//===----------------------------------------------------------------------===// 2865 2866def SplatOp : Std_Op<"splat", [NoSideEffect, 2867 TypesMatchWith<"operand type matches element type of result", 2868 "aggregate", "input", 2869 "$_self.cast<ShapedType>().getElementType()">]> { 2870 let summary = "splat or broadcast operation"; 2871 let description = [{ 2872 Broadcast the operand to all elements of the result vector or tensor. The 2873 operand has to be of either integer or float type. When the result is a 2874 tensor, it has to be statically shaped. 2875 2876 Example: 2877 2878 ```mlir 2879 %s = load %A[%i] : memref<128xf32> 2880 %v = splat %s : vector<4xf32> 2881 %t = splat %s : tensor<8x16xi32> 2882 ``` 2883 2884 TODO: This operation is easy to extend to broadcast to dynamically shaped 2885 tensors in the same way dynamically shaped memrefs are handled. 2886 2887 ```mlir 2888 // Broadcasts %s to a 2-d dynamically shaped tensor, with %m, %n binding 2889 // to the sizes of the two dynamic dimensions. 2890 %m = "foo"() : () -> (index) 2891 %n = "bar"() : () -> (index) 2892 %t = splat %s [%m, %n] : tensor<?x?xi32> 2893 ``` 2894 }]; 2895 2896 let arguments = (ins AnyTypeOf<[AnySignlessInteger, AnyFloat], 2897 "integer or float type">:$input); 2898 let results = (outs AnyTypeOf<[AnyVector, AnyStaticShapeTensor]>:$aggregate); 2899 2900 let builders = [ 2901 OpBuilderDAG<(ins "Value":$element, "Type":$aggregateType), 2902 [{ build($_builder, $_state, aggregateType, element); }]>]; 2903 2904 let hasFolder = 1; 2905 2906 let assemblyFormat = "$input attr-dict `:` type($aggregate)"; 2907} 2908 2909//===----------------------------------------------------------------------===// 2910// SqrtOp 2911//===----------------------------------------------------------------------===// 2912 2913def SqrtOp : FloatUnaryOp<"sqrt"> { 2914 let summary = "sqrt of the specified value"; 2915 let description = [{ 2916 The `sqrt` operation computes the square root. It takes one operand and 2917 returns one result of the same type. This type may be a float scalar type, a 2918 vector whose element type is float, or a tensor of floats. It has no standard 2919 attributes. 2920 2921 Example: 2922 2923 ```mlir 2924 // Scalar square root value. 2925 %a = sqrt %b : f64 2926 // SIMD vector element-wise square root value. 2927 %f = sqrt %g : vector<4xf32> 2928 // Tensor element-wise square root value. 2929 %x = sqrt %y : tensor<4x?xf32> 2930 ``` 2931 }]; 2932} 2933 2934//===----------------------------------------------------------------------===// 2935// StoreOp 2936//===----------------------------------------------------------------------===// 2937 2938def StoreOp : Std_Op<"store", 2939 [TypesMatchWith<"type of 'value' matches element type of 'memref'", 2940 "memref", "value", 2941 "$_self.cast<MemRefType>().getElementType()">, 2942 MemRefsNormalizable]> { 2943 let summary = "store operation"; 2944 let description = [{ 2945 Store a value to a memref location given by indices. The value stored should 2946 have the same type as the elemental type of the memref. The number of 2947 arguments provided within brackets need to match the rank of the memref. 2948 2949 In an affine context, the indices of a store are restricted to SSA values 2950 bound to surrounding loop induction variables, 2951 [symbols](Affine.md#restrictions-on-dimensions-and-symbols), results of a 2952 [`constant` operation](#stdconstant-constantop), or the result of an 2953 [`affine.apply`](Affine.md#affineapply-affineapplyop) operation that can in turn 2954 take as arguments all of the aforementioned SSA values or the recursively 2955 result of such an `affine.apply` operation. 2956 2957 Example: 2958 2959 ```mlir 2960 store %100, %A[%1, 1023] : memref<4x?xf32, #layout, memspace0> 2961 ``` 2962 2963 **Context:** The `load` and `store` operations are specifically crafted to 2964 fully resolve a reference to an element of a memref, and (in polyhedral 2965 `affine.if` and `affine.for` operations) the compiler can follow use-def 2966 chains (e.g. through [`affine.apply`](Affine.md#affineapply-affineapplyop) 2967 operations) to precisely analyze references at compile-time using polyhedral 2968 techniques. This is possible because of the 2969 [restrictions on dimensions and symbols](Affine.md#restrictions-on-dimensions-and-symbols) 2970 in these contexts. 2971 }]; 2972 2973 let arguments = (ins AnyType:$value, 2974 Arg<AnyMemRef, "the reference to store to", 2975 [MemWrite]>:$memref, 2976 Variadic<Index>:$indices); 2977 2978 let builders = [ 2979 OpBuilderDAG<(ins "Value":$valueToStore, "Value":$memref), [{ 2980 $_state.addOperands(valueToStore); 2981 $_state.addOperands(memref); 2982 }]>]; 2983 2984 let extraClassDeclaration = [{ 2985 Value getValueToStore() { return getOperand(0); } 2986 2987 Value getMemRef() { return getOperand(1); } 2988 void setMemRef(Value value) { setOperand(1, value); } 2989 MemRefType getMemRefType() { 2990 return getMemRef().getType().cast<MemRefType>(); 2991 } 2992 2993 operand_range getIndices() { 2994 return {operand_begin() + 2, operand_end()}; 2995 } 2996 }]; 2997 2998 let hasFolder = 1; 2999 3000 let assemblyFormat = [{ 3001 $value `,` $memref `[` $indices `]` attr-dict `:` type($memref) 3002 }]; 3003} 3004 3005//===----------------------------------------------------------------------===// 3006// SubCFOp 3007//===----------------------------------------------------------------------===// 3008 3009def SubCFOp : ComplexFloatArithmeticOp<"subcf"> { 3010 let summary = "complex number subtraction"; 3011 let description = [{ 3012 The `subcf` operation takes two complex number operands and returns their 3013 difference, a single complex number. 3014 All operands and result must be of the same type, a complex number with a 3015 floating-point element type. 3016 3017 Example: 3018 3019 ```mlir 3020 %a = subcf %b, %c : complex<f32> 3021 ``` 3022 }]; 3023} 3024 3025//===----------------------------------------------------------------------===// 3026// SubFOp 3027//===----------------------------------------------------------------------===// 3028 3029def SubFOp : FloatArithmeticOp<"subf"> { 3030 let summary = "floating point subtraction operation"; 3031 let hasFolder = 1; 3032} 3033 3034//===----------------------------------------------------------------------===// 3035// SubIOp 3036//===----------------------------------------------------------------------===// 3037 3038def SubIOp : IntArithmeticOp<"subi"> { 3039 let summary = "integer subtraction operation"; 3040 let hasFolder = 1; 3041} 3042 3043//===----------------------------------------------------------------------===// 3044// SubViewOp 3045//===----------------------------------------------------------------------===// 3046 3047def SubViewOp : BaseOpWithOffsetSizesAndStrides< 3048 "subview", [DeclareOpInterfaceMethods<ViewLikeOpInterface>, OffsetSizeAndStrideOpInterface] > { 3049 let summary = "memref subview operation"; 3050 let description = [{ 3051 The "subview" operation converts a memref type to another memref type 3052 which represents a reduced-size view of the original memref as specified by 3053 the operation's offsets, sizes and strides arguments. 3054 3055 The SubView operation supports the following arguments: 3056 3057 * semref: the "base" memref on which to create a "view" memref. 3058 * offsets: memref-rank number of offsets into the "base" memref at which to 3059 create the "view" memref. 3060 * sizes: memref-rank number of sizes which specify the sizes of the result 3061 "view" memref type. 3062 * strides: memref-rank number of strides that compose multiplicatively with 3063 the base memref strides in each dimension. 3064 3065 The representation based on offsets, sizes and strides support a 3066 partially-static specification via attributes specified through the 3067 `static_offsets`, `static_sizes` and `static_strides` arguments. A special 3068 sentinel value ShapedType::kDynamicSize and 3069 ShapedType::kDynamicStrideOrOffset encodes that the corresponding entry has 3070 a dynamic value. 3071 3072 A subview operation may additionally reduce the rank of the resulting view 3073 by removing dimensions that are statically known to be of size 1. 3074 3075 Example 1: 3076 3077 ```mlir 3078 %0 = alloc() : memref<64x4xf32, (d0, d1) -> (d0 * 4 + d1)> 3079 3080 // Create a sub-view of "base" memref '%0' with offset arguments '%c0', 3081 // dynamic sizes for each dimension, and stride arguments '%c1'. 3082 %1 = subview %0[%c0, %c0][%size0, %size1][%c1, %c1] 3083 : memref<64x4xf32, (d0, d1) -> (d0 * 4 + d1) > to 3084 memref<?x?xf32, (d0, d1)[s0, s1] -> (d0 * s1 + d1 + s0)> 3085 ``` 3086 3087 Example 2: 3088 3089 ```mlir 3090 %0 = alloc() : memref<8x16x4xf32, (d0, d1, d1) -> (d0 * 64 + d1 * 4 + d2)> 3091 3092 // Create a sub-view of "base" memref '%0' with dynamic offsets, sizes, 3093 // and strides. 3094 // Note that dynamic offsets are represented by the linearized dynamic 3095 // offset symbol 's0' in the subview memref layout map, and that the 3096 // dynamic strides operands, after being applied to the base memref 3097 // strides in each dimension, are represented in the view memref layout 3098 // map as symbols 's1', 's2' and 's3'. 3099 %1 = subview %0[%i, %j, %k][%size0, %size1, %size2][%x, %y, %z] 3100 : memref<8x16x4xf32, (d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)> to 3101 memref<?x?x?xf32, 3102 (d0, d1, d2)[s0, s1, s2, s3] -> (d0 * s1 + d1 * s2 + d2 * s3 + s0)> 3103 ``` 3104 3105 Example 3: 3106 3107 ```mlir 3108 %0 = alloc() : memref<8x16x4xf32, (d0, d1, d1) -> (d0 * 64 + d1 * 4 + d2)> 3109 3110 // Subview with constant offsets, sizes and strides. 3111 %1 = subview %0[0, 2, 0][4, 4, 4][64, 4, 1] 3112 : memref<8x16x4xf32, (d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)> to 3113 memref<4x4x4xf32, (d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2 + 8)> 3114 ``` 3115 3116 Example 4: 3117 3118 ```mlir 3119 %0 = alloc(%arg0, %arg1) : memref<?x?xf32> 3120 3121 // Subview with constant size, but dynamic offsets and 3122 // strides. The resulting memref has a static shape, but if the 3123 // base memref has an affine map to describe the layout, the result 3124 // memref also uses an affine map to describe the layout. The 3125 // strides of the result memref is computed as follows: 3126 // 3127 // Let #map1 represents the layout of the base memref, and #map2 3128 // represents the layout of the result memref. A #mapsubview can be 3129 // constructed to map an index from the result memref to the base 3130 // memref (note that the description below uses more convenient 3131 // naming for symbols, while in affine maps, symbols are 3132 // represented as unsigned numbers that identify that symbol in the 3133 // given affine map. 3134 // 3135 // #mapsubview = (d0, d1)[o0, o1, t0, t1] -> (d0 * t0 + o0, d1 * t1 + o1) 3136 // 3137 // where, o0, o1, ... are offsets, and t0, t1, ... are strides. Then, 3138 // 3139 // #map2 = #map1.compose(#mapsubview) 3140 // 3141 // If the layout map is represented as 3142 // 3143 // #map1 = (d0, d1)[s0, s1, s2] -> (d0 * s1 + d1 * s2 + s0) 3144 // 3145 // then, 3146 // 3147 // #map2 = (d0, d1)[s0, s1, s2, o0, o1, t0, t1] -> 3148 // (d0 * s1 * t0 + d1 * s2 * t1 + o0 * s1 + o1 * s2 + s0) 3149 // 3150 // Representing this canonically 3151 // 3152 // #map2 = (d0, d1)[r0, r1, r2] -> (d0 * r1 + d1 * r2 + r0) 3153 // 3154 // where, r0 = o0 * s1 + o1 * s2 + s0, r1 = s1 * t0, r2 = s2 * t1. 3155 %1 = subview %0[%i, %j][4, 4][%x, %y] : 3156 : memref<?x?xf32, (d0, d1)[s0, s1, s2] -> (d0 * s1 + d1 * s2 + s0)> to 3157 memref<4x4xf32, (d0, d1)[r0, r1, r2] -> (d0 * r1 + d1 * r2 + r0)> 3158 3159 // Note that the subview op does not guarantee that the result 3160 // memref is "inbounds" w.r.t to base memref. It is upto the client 3161 // to ensure that the subview is accessed in a manner that is 3162 // in-bounds. 3163 ``` 3164 3165 Example 5: 3166 3167 ```mlir 3168 // Rank-reducing subview. 3169 %1 = subview %0[0, 0, 0][1, 16, 4][1, 1, 1] : 3170 memref<8x16x4xf32> to memref<16x4xf32> 3171 %3 = subview %2[3, 4, 2][1, 6, 3][1, 1, 1] : 3172 memref<8x16x4xf32> to memref<6x3xf32, offset: 210, strides: [4, 1]> 3173 ``` 3174 } 3175 }]; 3176 3177 let arguments = (ins 3178 AnyMemRef:$source, 3179 Variadic<Index>:$offsets, 3180 Variadic<Index>:$sizes, 3181 Variadic<Index>:$strides, 3182 I64ArrayAttr:$static_offsets, 3183 I64ArrayAttr:$static_sizes, 3184 I64ArrayAttr:$static_strides 3185 ); 3186 let results = (outs AnyMemRef:$result); 3187 3188 let builders = [ 3189 // Build a SubViewOp with mixed static and dynamic entries. 3190 OpBuilderDAG<(ins "Value":$source, "ArrayRef<int64_t>":$staticOffsets, 3191 "ArrayRef<int64_t>":$staticSizes, "ArrayRef<int64_t>":$staticStrides, 3192 "ValueRange":$offsets, "ValueRange":$sizes, "ValueRange":$strides, 3193 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 3194 // Build a SubViewOp with all dynamic entries. 3195 OpBuilderDAG<(ins "Value":$source, "ValueRange":$offsets, 3196 "ValueRange":$sizes, "ValueRange":$strides, 3197 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 3198 // Build a SubViewOp with mixed static and dynamic entries 3199 // and custom result type. 3200 OpBuilderDAG<(ins "MemRefType":$resultType, "Value":$source, 3201 "ArrayRef<int64_t>":$staticOffsets, "ArrayRef<int64_t>":$staticSizes, 3202 "ArrayRef<int64_t>":$staticStrides, "ValueRange":$offsets, 3203 "ValueRange":$sizes, "ValueRange":$strides, 3204 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 3205 // Build a SubViewOp with all dynamic entries and custom result type. 3206 OpBuilderDAG<(ins "MemRefType":$resultType, "Value":$source, 3207 "ValueRange":$offsets, "ValueRange":$sizes, "ValueRange":$strides, 3208 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)> 3209 ]; 3210 3211 let extraClassDeclaration = extraBaseClassDeclaration # [{ 3212 /// Returns the type of the base memref operand. 3213 MemRefType getSourceType() { 3214 return source().getType().cast<MemRefType>(); 3215 } 3216 3217 /// The result of a subview is always a memref. 3218 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 3219 3220 /// A subview result type can be fully inferred from the source type and the 3221 /// static representation of offsets, sizes and strides. Special sentinels 3222 /// encode the dynamic case. 3223 static Type inferResultType(MemRefType sourceMemRefType, 3224 ArrayRef<int64_t> staticOffsets, 3225 ArrayRef<int64_t> staticSizes, 3226 ArrayRef<int64_t> staticStrides); 3227 3228 /// Return the expected rank of each of the`static_offsets`, `static_sizes` 3229 /// and `static_strides` attributes. 3230 std::array<unsigned, 3> getArrayAttrRanks() { 3231 unsigned rank = getSourceType().getRank(); 3232 return {rank, rank, rank}; 3233 } 3234 }]; 3235 3236 let hasCanonicalizer = 1; 3237 let hasFolder = 1; 3238} 3239 3240//===----------------------------------------------------------------------===// 3241// SubTensorOp 3242//===----------------------------------------------------------------------===// 3243 3244def SubTensorOp : BaseOpWithOffsetSizesAndStrides<"subtensor", [OffsetSizeAndStrideOpInterface]> { 3245 let summary = "subtensor operation"; 3246 let description = [{ 3247 The "subtensor" operation extract a tensor from another tensor as 3248 specified by the operation's offsets, sizes and strides arguments. 3249 3250 The subtensor operation supports the following arguments: 3251 3252 * tensor: the "base" tensor from which to extract a subtensor. 3253 * offsets: tensor-rank number of offsets into the "base" tensor from which 3254 to extract the subtensor. 3255 * sizes: tensor-rank number of sizes which specify the sizes of the result 3256 tensor type. 3257 * strides: tensor-rank number of strides specifying subsampling in each 3258 dimension. 3259 3260 The representation based on offsets, sizes and strides support a 3261 partially-static specification via attributes specified through the 3262 `static_offsets`, `static_sizes` and `static_strides` arguments. A special 3263 sentinel value ShapedType::kDynamicSize and 3264 ShapedType::kDynamicStrideOrOffset encodes that the corresponding entry has 3265 a dynamic value. 3266 3267 After buffer-allocation, the "subtensor" op is expected to lower into a 3268 "subview" op. 3269 3270 A subtensor operation may additionally reduce the rank of the resulting 3271 tensor by removing dimensions that are statically known to be of size 1. 3272 3273 Example: 3274 3275 ``` 3276 // Rank-reducing subtensor. 3277 %1 = subtensor %0[0, 0, 0][1, 16, 4][1, 1, 1] : 3278 tensor<8x16x4xf32> to tensor<16x4xf32> 3279 %3 = subtensor %2[3, 4, 2][1, 6, 3][1, 1, 1] : 3280 tensor<8x16x4xf32> to tensor<6x3xf32> 3281 ``` 3282 }]; 3283 3284 let arguments = (ins 3285 AnyRankedTensor:$source, 3286 Variadic<Index>:$offsets, 3287 Variadic<Index>:$sizes, 3288 Variadic<Index>:$strides, 3289 I64ArrayAttr:$static_offsets, 3290 I64ArrayAttr:$static_sizes, 3291 I64ArrayAttr:$static_strides 3292 ); 3293 let results = (outs AnyRankedTensor:$result); 3294 3295 let builders = [ 3296 // Build a SubViewOp with mixed static and dynamic entries. 3297 OpBuilderDAG<(ins "Value":$source, "ArrayRef<int64_t>":$staticOffsets, 3298 "ArrayRef<int64_t>":$staticSizes, "ArrayRef<int64_t>":$staticStrides, 3299 "ValueRange":$offsets, "ValueRange":$sizes, "ValueRange":$strides, 3300 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 3301 // Build a SubViewOp with all dynamic entries. 3302 OpBuilderDAG<(ins "Value":$source, "ValueRange":$offsets, 3303 "ValueRange":$sizes, "ValueRange":$strides, 3304 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)> 3305 ]; 3306 3307 let extraClassDeclaration = extraBaseClassDeclaration # [{ 3308 /// Returns the type of the base tensor operand. 3309 RankedTensorType getSourceType() { 3310 return source().getType().cast<RankedTensorType>(); 3311 } 3312 3313 /// The result of a subtensor is always a tensor. 3314 RankedTensorType getType() { 3315 return getResult().getType().cast<RankedTensorType>(); 3316 } 3317 3318 /// A subview result type can be fully inferred from the source type and the 3319 /// static representation of offsets, sizes and strides. Special sentinels 3320 /// encode the dynamic case. 3321 static Type inferResultType(RankedTensorType sourceRankedTensorType, 3322 ArrayRef<int64_t> staticOffsets, 3323 ArrayRef<int64_t> staticSizes, 3324 ArrayRef<int64_t> staticStrides); 3325 3326 /// Return the expected rank of each of the`static_offsets`, `static_sizes` 3327 /// and `static_strides` attributes. 3328 std::array<unsigned, 3> getArrayAttrRanks() { 3329 unsigned rank = getSourceType().getRank(); 3330 return {rank, rank, rank}; 3331 } 3332 }]; 3333 3334 let hasCanonicalizer = 1; 3335} 3336 3337//===----------------------------------------------------------------------===// 3338// SubTensorInsertOp 3339//===----------------------------------------------------------------------===// 3340 3341def SubTensorInsertOp : BaseOpWithOffsetSizesAndStrides<"subtensor_insert", [OffsetSizeAndStrideOpInterface]> { 3342 let summary = "subtensor_insert operation"; 3343 let description = [{ 3344 The "subtensor_insert" operation insert a tensor `source` into another 3345 tensor `dest` as specified by the operation's offsets, sizes and strides 3346 arguments. 3347 3348 It returns a copy of `dest` with the proper subtensor updated with the value 3349 of `source`. 3350 3351 The subtensor_insert operation has the encodes the following information: 3352 3353 * source: the tensor that is inserted. 3354 * dest: the tensor into which the source tensor is inserted. 3355 * offsets: tensor-rank number of offsets into the "base" tensor from which 3356 to extract the subtensor. 3357 * sizes: tensor-rank number of sizes which specify the sizes of the result 3358 tensor type. 3359 * strides: tensor-rank number of strides that specify subsampling in each 3360 dimension. 3361 3362 The representation based on offsets, sizes and strides support a 3363 partially-static specification via attributes specified through the 3364 `static_offsets`, `static_sizes` and `static_strides` arguments. A special 3365 sentinel value ShapedType::kDynamicSize and 3366 ShapedType::kDynamicStrideOrOffset encodes that the corresponding entry has 3367 a dynamic value. 3368 3369 After buffer-allocation, the "subtensor_insert" op is expected to become 3370 an in-place buffer update. 3371 }]; 3372 3373 let arguments = (ins 3374 AnyRankedTensor:$source, 3375 AnyRankedTensor:$dest, 3376 Variadic<Index>:$offsets, 3377 Variadic<Index>:$sizes, 3378 Variadic<Index>:$strides, 3379 I64ArrayAttr:$static_offsets, 3380 I64ArrayAttr:$static_sizes, 3381 I64ArrayAttr:$static_strides 3382 ); 3383 let results = (outs AnyRankedTensor:$result); 3384 3385 let builders = [ 3386 // Build a SubViewOp with mixed static and dynamic entries. 3387 OpBuilderDAG<(ins "Value":$source, "Value":$dest, 3388 "ArrayRef<int64_t>":$staticOffsets, "ArrayRef<int64_t>":$staticSizes, 3389 "ArrayRef<int64_t>":$staticStrides, "ValueRange":$offsets, 3390 "ValueRange":$sizes, "ValueRange":$strides, 3391 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 3392 // Build a SubViewOp with all dynamic entries. 3393 OpBuilderDAG<(ins "Value":$source, "Value":$dest, "ValueRange":$offsets, 3394 "ValueRange":$sizes, "ValueRange":$strides, 3395 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)> 3396 ]; 3397 3398 let extraClassDeclaration = extraBaseClassDeclaration # [{ 3399 /// Returns the type of the base tensor operand. 3400 RankedTensorType getSourceType() { 3401 return source().getType().cast<RankedTensorType>(); 3402 } 3403 3404 /// The result of a subtensor is always a tensor. 3405 RankedTensorType getType() { 3406 return getResult().getType().cast<RankedTensorType>(); 3407 } 3408 3409 /// Return the expected rank of each of the`static_offsets`, `static_sizes` 3410 /// and `static_strides` attributes. 3411 std::array<unsigned, 3> getArrayAttrRanks() { 3412 unsigned rank = getSourceType().getRank(); 3413 return {rank, rank, rank}; 3414 } 3415 }]; 3416} 3417 3418//===----------------------------------------------------------------------===// 3419// TanhOp 3420//===----------------------------------------------------------------------===// 3421 3422def TanhOp : FloatUnaryOp<"tanh"> { 3423 let summary = "hyperbolic tangent of the specified value"; 3424 let description = [{ 3425 Syntax: 3426 3427 ``` 3428 operation ::= ssa-id `=` `std.tanh` ssa-use `:` type 3429 ``` 3430 3431 The `tanh` operation computes the hyperbolic tangent. It takes one operand 3432 and returns one result of the same type. This type may be a float scalar 3433 type, a vector whose element type is float, or a tensor of floats. It has 3434 no standard attributes. 3435 3436 Example: 3437 3438 ```mlir 3439 // Scalar hyperbolic tangent value. 3440 %a = tanh %b : f64 3441 3442 // SIMD vector element-wise hyperbolic tangent value. 3443 %f = tanh %g : vector<4xf32> 3444 3445 // Tensor element-wise hyperbolic tangent value. 3446 %x = tanh %y : tensor<4x?xf8> 3447 ``` 3448 }]; 3449} 3450 3451//===----------------------------------------------------------------------===// 3452// TensorCastOp 3453//===----------------------------------------------------------------------===// 3454 3455def TensorCastOp : CastOp<"tensor_cast"> { 3456 let summary = "tensor cast operation"; 3457 let description = [{ 3458 Syntax: 3459 3460 ``` 3461 operation ::= ssa-id `=` `std.tensor_cast` ssa-use `:` type `to` type 3462 ``` 3463 3464 Convert a tensor from one type to an equivalent type without changing any 3465 data elements. The source and destination types must both be tensor types 3466 with the same element type. If both are ranked, then the rank should be the 3467 same and static dimensions should match. The operation is invalid if 3468 converting to a mismatching constant dimension. 3469 3470 Example: 3471 3472 ```mlir 3473 // Convert from unknown rank to rank 2 with unknown dimension sizes. 3474 %2 = "std.tensor_cast"(%1) : (tensor<*xf32>) -> tensor<?x?xf32> 3475 %2 = tensor_cast %1 : tensor<*xf32> to tensor<?x?xf32> 3476 3477 // Convert to a type with more known dimensions. 3478 %3 = "std.tensor_cast"(%2) : (tensor<?x?xf32>) -> tensor<4x?xf32> 3479 3480 // Discard static dimension and rank information. 3481 %4 = "std.tensor_cast"(%3) : (tensor<4x?xf32>) -> tensor<?x?xf32> 3482 %5 = "std.tensor_cast"(%4) : (tensor<?x?xf32>) -> tensor<*xf32> 3483 ``` 3484 }]; 3485 3486 let arguments = (ins AnyTensor:$source); 3487 let results = (outs AnyTensor); 3488 3489 let extraClassDeclaration = [{ 3490 /// Return true if `a` and `b` are valid operand and result pairs for 3491 /// the operation. 3492 static bool areCastCompatible(Type a, Type b); 3493 3494 /// The result of a tensor_cast is always a tensor. 3495 TensorType getType() { return getResult().getType().cast<TensorType>(); } 3496 }]; 3497 3498 let hasCanonicalizer = 1; 3499} 3500 3501//===----------------------------------------------------------------------===// 3502// TensorLoadOp 3503//===----------------------------------------------------------------------===// 3504 3505def TensorLoadOp : Std_Op<"tensor_load", 3506 [SameOperandsAndResultShape, SameOperandsAndResultElementType, 3507 TypesMatchWith<"result type matches tensor equivalent of 'memref'", 3508 "memref", "result", 3509 "getTensorTypeFromMemRefType($_self)">]> { 3510 let summary = "tensor load operation"; 3511 let description = [{ 3512 Create a tensor from a memref, making an independent copy of the element 3513 data. The result value is a tensor whose shape and element type match the 3514 memref operand. 3515 3516 The opposite of this op is tensor_to_memref. Together, these two ops are 3517 useful for source/target materializations when doing type conversions 3518 involving tensors and memrefs. 3519 3520 Example: 3521 3522 ```mlir 3523 // Produces a value of tensor<4x?xf32> type. 3524 %12 = tensor_load %10 : memref<4x?xf32, #layout, memspace0> 3525 ``` 3526 }]; 3527 3528 let arguments = (ins Arg<AnyRankedOrUnrankedMemRef, 3529 "the reference to load from", [MemRead]>:$memref); 3530 let results = (outs AnyTensor:$result); 3531 // TensorLoadOp is fully verified by traits. 3532 let verifier = ?; 3533 3534 let builders = [ 3535 OpBuilderDAG<(ins "Value":$memref), [{ 3536 $_state.addOperands(memref); 3537 $_state.addTypes(getTensorTypeFromMemRefType(memref.getType())); 3538 }]>]; 3539 3540 let extraClassDeclaration = [{ 3541 /// The result of a tensor_load is always a tensor. 3542 TensorType getType() { 3543 Type resultType = getResult().getType(); 3544 if (resultType.isa<TensorType>()) 3545 return resultType.cast<TensorType>(); 3546 return {}; 3547 } 3548 }]; 3549 3550 let assemblyFormat = "$memref attr-dict `:` type($memref)"; 3551 3552 let hasFolder = 1; 3553} 3554 3555//===----------------------------------------------------------------------===// 3556// TensorStoreOp 3557//===----------------------------------------------------------------------===// 3558 3559def TensorStoreOp : Std_Op<"tensor_store", 3560 [SameOperandsShape, SameOperandsElementType, 3561 TypesMatchWith<"type of 'value' matches tensor equivalent of 'memref'", 3562 "memref", "tensor", 3563 "getTensorTypeFromMemRefType($_self)">]> { 3564 let summary = "tensor store operation"; 3565 let description = [{ 3566 Stores the contents of a tensor into a memref. The first operand is a value 3567 of tensor type, the second operand is a value of memref type. The shapes and 3568 element types of these must match, and are specified by the memref type. 3569 3570 Example: 3571 3572 ```mlir 3573 %9 = dim %8, 1 : tensor<4x?xf32> 3574 %10 = alloc(%9) : memref<4x?xf32, #layout, memspace0> 3575 tensor_store %8, %10 : memref<4x?xf32, #layout, memspace0> 3576 ``` 3577 }]; 3578 3579 let arguments = (ins AnyTensor:$tensor, Arg<AnyRankedOrUnrankedMemRef, 3580 "the reference to store to", [MemWrite]>:$memref); 3581 // TensorStoreOp is fully verified by traits. 3582 let verifier = ?; 3583 3584 let assemblyFormat = "$tensor `,` $memref attr-dict `:` type($memref)"; 3585} 3586 3587//===----------------------------------------------------------------------===// 3588// TensorToMemrefOp 3589//===----------------------------------------------------------------------===// 3590 3591def TensorToMemrefOp : Std_Op<"tensor_to_memref", 3592 [SameOperandsAndResultShape, SameOperandsAndResultElementType, 3593 TypesMatchWith<"type of 'tensor' is the tensor equivalent of 'memref'", 3594 "memref", "tensor", 3595 "getTensorTypeFromMemRefType($_self)">]> { 3596 let summary = "tensor to memref operation"; 3597 let description = [{ 3598 Create a memref from a tensor. This is a transient op created as a 3599 materialization during type conversions between tensors and memrefs. 3600 3601 The opposite of this op is tensor_load. Together, these two ops are useful 3602 for source/target materializations when doing type conversions involving 3603 tensors and memrefs. 3604 3605 This op is defined by the fold 3606 `tensor_to_memref(tensor_load(%memref)) -> %memref`, which is the property 3607 that makes it a valid materialization in the type conversion framework. 3608 This implies that one cannot assume that this op allocates a new memref for 3609 its result. 3610 3611 Note: This op takes the memref type in its pretty form because the tensor 3612 type can always be inferred from the memref type, but the reverse is not 3613 true. For example, the memref might have a layout map or memory space which 3614 cannot be inferred from the tensor type. 3615 3616 ```mlir 3617 // Result type is tensor<4x?xf32> 3618 %12 = tensor_to_memref %10 : memref<4x?xf32, #map0, 42> 3619 ``` 3620 }]; 3621 3622 let arguments = (ins AnyTensor:$tensor); 3623 let results = (outs AnyRankedOrUnrankedMemRef:$memref); 3624 // This op is fully verified by traits. 3625 let verifier = ?; 3626 3627 let assemblyFormat = "$tensor attr-dict `:` type($memref)"; 3628 3629 let hasFolder = 1; 3630} 3631 3632//===----------------------------------------------------------------------===// 3633// TransposeOp 3634//===----------------------------------------------------------------------===// 3635 3636def TransposeOp : Std_Op<"transpose", [NoSideEffect]>, 3637 Arguments<(ins AnyStridedMemRef:$in, AffineMapAttr:$permutation)>, 3638 Results<(outs AnyStridedMemRef)> { 3639 let summary = "`transpose` produces a new strided memref (metadata-only)"; 3640 let description = [{ 3641 The `transpose` op produces a strided memref whose sizes and strides 3642 are a permutation of the original `in` memref. This is purely a metadata 3643 transformation. 3644 3645 Example: 3646 3647 ```mlir 3648 %1 = transpose %0 (i, j) -> (j, i) : memref<?x?xf32> to memref<?x?xf32, affine_map<(d0, d1)[s0] -> (d1 * s0 + d0)>> 3649 ``` 3650 }]; 3651 3652 let builders = [ 3653 OpBuilderDAG<(ins "Value":$in, "AffineMapAttr":$permutation, 3654 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>]; 3655 3656 let extraClassDeclaration = [{ 3657 static StringRef getPermutationAttrName() { return "permutation"; } 3658 ShapedType getShapedType() { return in().getType().cast<ShapedType>(); } 3659 }]; 3660 3661 let hasFolder = 1; 3662} 3663 3664//===----------------------------------------------------------------------===// 3665// TruncateIOp 3666//===----------------------------------------------------------------------===// 3667 3668def TruncateIOp : Std_Op<"trunci", [NoSideEffect, ElementwiseMappable]> { 3669 let summary = "integer truncation operation"; 3670 let description = [{ 3671 The integer truncation operation takes an integer input of 3672 width M and an integer destination type of width N. The destination 3673 bit-width must be smaller than the input bit-width (N < M). 3674 The top-most (N - M) bits of the input are discarded. 3675 3676 Example: 3677 3678 ```mlir 3679 %1 = constant 21 : i5 // %1 is 0b10101 3680 %2 = trunci %1 : i5 to i4 // %2 is 0b0101 3681 %3 = trunci %1 : i5 to i3 // %3 is 0b101 3682 3683 %5 = trunci %0 : vector<2 x i32> to vector<2 x i16> 3684 ``` 3685 }]; 3686 3687 let arguments = (ins SignlessIntegerLike:$value); 3688 let results = (outs SignlessIntegerLike); 3689 3690 let builders = [ 3691 OpBuilderDAG<(ins "Value":$value, "Type":$destType), [{ 3692 $_state.addOperands(value); 3693 $_state.addTypes(destType); 3694 }]>]; 3695 3696 let parser = [{ 3697 return impl::parseCastOp(parser, result); 3698 }]; 3699 let printer = [{ 3700 return printStandardCastOp(this->getOperation(), p); 3701 }]; 3702} 3703 3704//===----------------------------------------------------------------------===// 3705// UIToFPOp 3706//===----------------------------------------------------------------------===// 3707 3708def UIToFPOp : ArithmeticCastOp<"uitofp">, Arguments<(ins AnyType:$in)> { 3709 let summary = "cast from unsigned integer type to floating-point"; 3710 let description = [{ 3711 Cast from a value interpreted as unsigned integer or vector of unsigned 3712 integers to the corresponding scalar or vector floating-point value. If the 3713 value cannot be exactly represented, it is rounded using the default 3714 rounding mode. Scalars and vector types are currently supported. 3715 }]; 3716 3717 let extraClassDeclaration = [{ 3718 /// Return true if `a` and `b` are valid operand and result pairs for 3719 /// the operation. 3720 static bool areCastCompatible(Type a, Type b); 3721 }]; 3722 3723 let hasFolder = 0; 3724} 3725 3726//===----------------------------------------------------------------------===// 3727// UnsignedDivIOp 3728//===----------------------------------------------------------------------===// 3729 3730def UnsignedDivIOp : IntArithmeticOp<"divi_unsigned"> { 3731 let summary = "unsigned integer division operation"; 3732 let description = [{ 3733 Syntax: 3734 ``` 3735 operation ::= ssa-id `=` `std.divi_unsigned` ssa-use `,` ssa-use `:` type 3736 ``` 3737 3738 Unsigned integer division. Rounds towards zero. Treats the leading bit as 3739 the most significant, i.e. for `i16` given two's complement representation, 3740 `6 / -2 = 6 / (2^16 - 2) = 0`. 3741 3742 Note: the semantics of division by zero is TBD; do NOT assume any specific 3743 behavior. 3744 3745 Example: 3746 3747 ```mlir 3748 // Scalar unsigned integer division. 3749 %a = diviu %b, %c : i64 3750 3751 // SIMD vector element-wise division. 3752 %f = diviu %g, %h : vector<4xi32> 3753 3754 // Tensor element-wise integer division. 3755 %x = diviu %y, %z : tensor<4x?xi8> 3756 ``` 3757 }]; 3758 let hasFolder = 1; 3759} 3760 3761//===----------------------------------------------------------------------===// 3762// UnsignedRemIOp 3763//===----------------------------------------------------------------------===// 3764 3765def UnsignedRemIOp : IntArithmeticOp<"remi_unsigned"> { 3766 let summary = "unsigned integer division remainder operation"; 3767 let description = [{ 3768 Syntax: 3769 3770 ``` 3771 operation ::= ssa-id `=` `std.remi_unsigned` ssa-use `,` ssa-use `:` type 3772 ``` 3773 3774 Unsigned integer division remainder. Treats the leading bit as the most 3775 significant, i.e. for `i16`, `6 % -2 = 6 % (2^16 - 2) = 6`. 3776 3777 Note: the semantics of division by zero is TBD; do NOT assume any specific 3778 behavior. 3779 3780 Example: 3781 3782 ```mlir 3783 // Scalar unsigned integer division remainder. 3784 %a = remiu %b, %c : i64 3785 3786 // SIMD vector element-wise division remainder. 3787 %f = remiu %g, %h : vector<4xi32> 3788 3789 // Tensor element-wise integer division remainder. 3790 %x = remiu %y, %z : tensor<4x?xi8> 3791 ``` 3792 }]; 3793 let hasFolder = 1; 3794} 3795 3796//===----------------------------------------------------------------------===// 3797// UnsignedShiftRightOp 3798//===----------------------------------------------------------------------===// 3799 3800def UnsignedShiftRightOp : IntArithmeticOp<"shift_right_unsigned"> { 3801 let summary = "unsigned integer right-shift"; 3802 let description = [{ 3803 The shift_right_unsigned operation shifts an integer value to the right by 3804 a variable amount. The integer is interpreted as unsigned. The high order 3805 bits are always filled with zeros. 3806 3807 Example: 3808 3809 ```mlir 3810 %1 = constant 160 : i8 // %1 is 0b10100000 3811 %2 = constant 3 : i8 3812 %3 = shift_right_unsigned %1, %2 : (i8, i8) -> i8 // %3 is 0b00010100 3813 ``` 3814 }]; 3815} 3816 3817//===----------------------------------------------------------------------===// 3818// ViewOp 3819//===----------------------------------------------------------------------===// 3820 3821def ViewOp : Std_Op<"view", [ 3822 DeclareOpInterfaceMethods<ViewLikeOpInterface>, NoSideEffect]> { 3823 let summary = "memref view operation"; 3824 let description = [{ 3825 The "view" operation extracts an N-D contiguous memref with empty layout map 3826 with arbitrary element type from a 1-D contiguous memref with empty layout 3827 map of i8 element type. The ViewOp supports the following arguments: 3828 3829 * A single dynamic byte-shift operand must be specified which represents a 3830 a shift of the base 1-D memref pointer from which to create the resulting 3831 contiguous memref view with identity layout. 3832 * A dynamic size operand that must be specified for each dynamic dimension 3833 in the resulting view memref type. 3834 3835 The "view" operation gives a structured indexing form to a flat 1-D buffer. 3836 Unlike "subview" it can perform a type change. The type change behavior 3837 requires the op to have special semantics because, e.g. a byte shift of 3 3838 cannot be represented as an offset on f64. 3839 For now, a "view" op: 3840 3841 1. Only takes a contiguous source memref with 0 offset and empty layout. 3842 2. Must specify a byte_shift operand (in the future, a special integer 3843 attribute may be added to support the folded case). 3844 3. Returns a contiguous memref with 0 offset and empty layout. 3845 3846 Example: 3847 3848 ```mlir 3849 // Allocate a flat 1D/i8 memref. 3850 %0 = alloc() : memref<2048xi8> 3851 3852 // ViewOp with dynamic offset and static sizes. 3853 %1 = view %0[%offset_1024][] : memref<2048xi8> to memref<64x4xf32> 3854 3855 // ViewOp with dynamic offset and two dynamic size. 3856 %2 = view %0[%offset_1024][%size0, %size1] : 3857 memref<2048xi8> to memref<?x4x?xf32> 3858 ``` 3859 }]; 3860 3861 let arguments = (ins MemRefRankOf<[I8], [1]>:$source, 3862 Index:$byte_shift, 3863 Variadic<Index>:$sizes); 3864 let results = (outs AnyMemRef); 3865 3866 let extraClassDeclaration = [{ 3867 /// The result of a view is always a memref. 3868 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 3869 3870 /// Returns the dynamic sizes for this view operation. This is redundant 3871 /// with `sizes` but needed in template implementations. More specifically: 3872 /// ``` 3873 /// template <typename AnyMemRefDefOp> 3874 /// bool isMemRefSizeValidSymbol(AnyMemRefDefOp memrefDefOp, unsigned index, 3875 /// Region *region) 3876 /// ``` 3877 operand_range getDynamicSizes() { 3878 return {sizes().begin(), sizes().end()}; 3879 } 3880 }]; 3881 3882 let hasCanonicalizer = 1; 3883} 3884 3885//===----------------------------------------------------------------------===// 3886// YieldOp 3887//===----------------------------------------------------------------------===// 3888 3889def YieldOp : Std_Op<"yield", [NoSideEffect, ReturnLike, Terminator, 3890 HasParent<"DynamicTensorFromElementsOp">]> { 3891 let summary = "Yield a value from a region"; 3892 let description = [{ 3893 This operation is used to yield a single value from a within a region. It 3894 is used to create dynamically sized tensors 3895 (see `DynamicTensorFromElementsOp`). 3896 }]; 3897 3898 let arguments = (ins AnyType:$value); 3899 let assemblyFormat = "$value attr-dict `:` type($value)"; 3900 let verifier = ?; 3901} 3902 3903//===----------------------------------------------------------------------===// 3904// XOrOp 3905//===----------------------------------------------------------------------===// 3906 3907def XOrOp : IntArithmeticOp<"xor", [Commutative]> { 3908 let summary = "integer binary xor"; 3909 let description = [{ 3910 The `xor` operation takes two operands and returns one result, each of these 3911 is required to be the same type. This type may be an integer scalar type, a 3912 vector whose element type is integer, or a tensor of integers. It has no 3913 standard attributes. 3914 3915 Example: 3916 3917 ```mlir 3918 // Scalar integer bitwise xor. 3919 %a = xor %b, %c : i64 3920 3921 // SIMD vector element-wise bitwise integer xor. 3922 %f = xor %g, %h : vector<4xi32> 3923 3924 // Tensor element-wise bitwise integer xor. 3925 %x = xor %y, %z : tensor<4x?xi8> 3926 ``` 3927 }]; 3928 let hasFolder = 1; 3929} 3930 3931//===----------------------------------------------------------------------===// 3932// ZeroExtendIOp 3933//===----------------------------------------------------------------------===// 3934 3935def ZeroExtendIOp : Std_Op<"zexti", [NoSideEffect, ElementwiseMappable]> { 3936 let summary = "integer zero extension operation"; 3937 let description = [{ 3938 The integer zero extension operation takes an integer input of 3939 width M and an integer destination type of width N. The destination 3940 bit-width must be larger than the input bit-width (N > M). 3941 The top-most (N - M) bits of the output are filled with zeros. 3942 3943 Example: 3944 3945 ```mlir 3946 %1 = constant 5 : i3 // %1 is 0b101 3947 %2 = zexti %1 : i3 to i6 // %2 is 0b000101 3948 %3 = constant 2 : i3 // %3 is 0b010 3949 %4 = zexti %3 : i3 to i6 // %4 is 0b000010 3950 3951 %5 = zexti %0 : vector<2 x i32> to vector<2 x i64> 3952 ``` 3953 }]; 3954 3955 let arguments = (ins SignlessIntegerLike:$value); 3956 let results = (outs SignlessIntegerLike); 3957 3958 let builders = [ 3959 OpBuilderDAG<(ins "Value":$value, "Type":$destType), [{ 3960 $_state.addOperands(value); 3961 $_state.addTypes(destType); 3962 }]>]; 3963 3964 let parser = [{ 3965 return impl::parseCastOp(parser, result); 3966 }]; 3967 let printer = [{ 3968 return printStandardCastOp(this->getOperation(), p); 3969 }]; 3970} 3971 3972#endif // STANDARD_OPS 3973