Add support for floating-point comparison 'fcmp' to the LLVM dialect.
authorNagy Mostafa <nagy.mostafa@gmail.com>
Fri, 9 Aug 2019 01:29:23 +0000 (18:29 -0700)
committerA. Unique TensorFlower <gardener@tensorflow.org>
Fri, 9 Aug 2019 01:29:48 +0000 (18:29 -0700)
This adds support for fcmp to the LLVM dialect and adds any necessary lowerings, as well as support for EDSCs.

Closes tensorflow/mlir#69

PiperOrigin-RevId: 262475255

mlir/include/mlir/LLVMIR/LLVMOps.td
mlir/include/mlir/StandardOps/Ops.h
mlir/lib/Conversion/StandardToLLVM/ConvertStandardToLLVM.cpp
mlir/lib/EDSC/Builders.cpp
mlir/lib/LLVMIR/IR/LLVMDialect.cpp
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
mlir/test/EDSC/builder-api-test.cpp
mlir/test/LLVMIR/convert-to-llvmir.mlir
mlir/test/Target/llvmir.mlir

index 7c23330..129c329 100644 (file)
@@ -164,10 +164,54 @@ def LLVM_ICmpOp : LLVM_OneResultOp<"icmp", [NoSideEffect]>,
   let llvmBuilder = [{
     $res = builder.CreateICmp(getLLVMCmpPredicate($predicate), $lhs, $rhs);
   }];
-  let parser = [{ return parseICmpOp(parser, result); }];
+  let parser = [{ return parseCmpOp<ICmpPredicate>(parser, result); }];
   let printer = [{ printICmpOp(p, *this); }];
 }
 
+// Predicate for float comparisons
+def FCmpPredicateFALSE  : I64EnumAttrCase<"_false", 0>;
+def FCmpPredicateOEQ    : I64EnumAttrCase<"oeq", 1>;
+def FCmpPredicateOGT    : I64EnumAttrCase<"ogt", 2>;
+def FCmpPredicateOGE    : I64EnumAttrCase<"oge", 3>;
+def FCmpPredicateOLT    : I64EnumAttrCase<"olt", 4>;
+def FCmpPredicateOLE    : I64EnumAttrCase<"ole", 5>;
+def FCmpPredicateONE    : I64EnumAttrCase<"one", 6>;
+def FCmpPredicateORD    : I64EnumAttrCase<"ord", 7>;
+def FCmpPredicateUEQ    : I64EnumAttrCase<"ueq", 8>;
+def FCmpPredicateUGT    : I64EnumAttrCase<"ugt", 9>;
+def FCmpPredicateUGE    : I64EnumAttrCase<"uge", 10>;
+def FCmpPredicateULT    : I64EnumAttrCase<"ult", 11>;
+def FCmpPredicateULE    : I64EnumAttrCase<"ule", 12>;
+def FCmpPredicateUNE    : I64EnumAttrCase<"une", 13>;
+def FCmpPredicateUNO    : I64EnumAttrCase<"uno", 14>;
+def FCmpPredicateTRUE   : I64EnumAttrCase<"_true", 15>;
+
+def FCmpPredicate : I64EnumAttr<
+    "FCmpPredicate",
+    "llvm.fcmp comparison predicate",
+    [FCmpPredicateFALSE, FCmpPredicateOEQ, FCmpPredicateOGT, FCmpPredicateOGE,
+     FCmpPredicateOLT, FCmpPredicateOLE, FCmpPredicateONE, FCmpPredicateORD,
+     FCmpPredicateUEQ, FCmpPredicateUGT, FCmpPredicateUGE, FCmpPredicateULT,
+     FCmpPredicateULE, FCmpPredicateUNE, FCmpPredicateUNO, FCmpPredicateTRUE 
+    ]> {
+  let cppNamespace = "mlir::LLVM";
+
+  let returnType = "FCmpPredicate";
+  let convertFromStorage =
+      "static_cast<" # returnType # ">($_self.getValue().getZExtValue())";
+}
+
+// Other integer operations.
+def LLVM_FCmpOp : LLVM_OneResultOp<"fcmp", [NoSideEffect]>,
+                  Arguments<(ins FCmpPredicate:$predicate, LLVM_Type:$lhs,
+                             LLVM_Type:$rhs)> {
+  let llvmBuilder = [{
+    $res = builder.CreateFCmp(getLLVMCmpPredicate($predicate), $lhs, $rhs);
+  }];
+  let parser = [{ return parseCmpOp<FCmpPredicate>(parser, result); }];
+  let printer = [{ printFCmpOp(p, *this); }];
+}
+
 // Floating point binary operations.
 def LLVM_FAddOp : LLVM_ArithmeticOp<"fadd", "CreateFAdd">;
 def LLVM_FSubOp : LLVM_ArithmeticOp<"fsub", "CreateFSub">;
index cd1e72d..fbd6462 100644 (file)
@@ -63,7 +63,7 @@ enum class CmpIPredicate {
 };
 
 /// The predicate indicates the type of the comparison to perform:
-/// (un)orderedness, (in)equality and signed less/greater than (or equal to) as
+/// (un)orderedness, (in)equality and less/greater than (or equal to) as
 /// well as predicates that are always true or false.
 enum class CmpFPredicate {
   FirstValidValue,
index 09ddcd1..23acaed 100644 (file)
@@ -865,10 +865,11 @@ struct IndexCastOpLowering : public LLVMLegalizationPattern<IndexCastOp> {
   }
 };
 
-// Convert std.cmpi predicate into the LLVM dialect ICmpPredicate.  The two
+// Convert std.cmp predicate into the LLVM dialect CmpPredicate.  The two
 // enums share the numerical values so just cast.
-static LLVM::ICmpPredicate convertCmpIPredicate(CmpIPredicate pred) {
-  return static_cast<LLVM::ICmpPredicate>(pred);
+template <typename LLVMPredType, typename StdPredType>
+static LLVMPredType convertCmpPredicate(StdPredType pred) {
+  return static_cast<LLVMPredType>(pred);
 }
 
 struct CmpIOpLowering : public LLVMLegalizationPattern<CmpIOp> {
@@ -882,8 +883,27 @@ struct CmpIOpLowering : public LLVMLegalizationPattern<CmpIOp> {
 
     rewriter.replaceOpWithNewOp<LLVM::ICmpOp>(
         op, lowering.convertType(cmpiOp.getResult()->getType()),
-        rewriter.getI64IntegerAttr(
-            static_cast<int64_t>(convertCmpIPredicate(cmpiOp.getPredicate()))),
+        rewriter.getI64IntegerAttr(static_cast<int64_t>(
+            convertCmpPredicate<LLVM::ICmpPredicate>(cmpiOp.getPredicate()))),
+        transformed.lhs(), transformed.rhs());
+
+    return matchSuccess();
+  }
+};
+
+struct CmpFOpLowering : public LLVMLegalizationPattern<CmpFOp> {
+  using LLVMLegalizationPattern<CmpFOp>::LLVMLegalizationPattern;
+
+  PatternMatchResult
+  matchAndRewrite(Operation *op, ArrayRef<Value *> operands,
+                  ConversionPatternRewriter &rewriter) const override {
+    auto cmpfOp = cast<CmpFOp>(op);
+    CmpFOpOperandAdaptor transformed(operands);
+
+    rewriter.replaceOpWithNewOp<LLVM::FCmpOp>(
+        op, lowering.convertType(cmpfOp.getResult()->getType()),
+        rewriter.getI64IntegerAttr(static_cast<int64_t>(
+            convertCmpPredicate<LLVM::FCmpPredicate>(cmpfOp.getPredicate()))),
         transformed.lhs(), transformed.rhs());
 
     return matchSuccess();
@@ -1026,9 +1046,9 @@ void mlir::populateStdToLLVMConversionPatterns(
   patterns.insert<
       AddFOpLowering, AddIOpLowering, AndOpLowering, AllocOpLowering,
       BranchOpLowering, CallIndirectOpLowering, CallOpLowering, CmpIOpLowering,
-      CondBranchOpLowering, ConstLLVMOpLowering, DeallocOpLowering,
-      DimOpLowering, DivISOpLowering, DivIUOpLowering, DivFOpLowering,
-      FuncOpConversion, IndexCastOpLowering, LoadOpLowering,
+      CmpFOpLowering, CondBranchOpLowering, ConstLLVMOpLowering,
+      DeallocOpLowering, DimOpLowering, DivISOpLowering, DivIUOpLowering,
+      DivFOpLowering, FuncOpConversion, IndexCastOpLowering, LoadOpLowering,
       MemRefCastOpLowering, MulFOpLowering, MulIOpLowering, OrOpLowering,
       RemISOpLowering, RemIUOpLowering, RemFOpLowering, ReturnOpLowering,
       SelectOpLowering, SIToFPLowering, StoreOpLowering, SubFOpLowering,
index 134a43a..d524900 100644 (file)
@@ -389,8 +389,8 @@ ValueHandle mlir::edsc::op::operator||(ValueHandle lhs, ValueHandle rhs) {
   return !(!lhs && !rhs);
 }
 
-static ValueHandle createComparisonExpr(CmpIPredicate predicate,
-                                        ValueHandle lhs, ValueHandle rhs) {
+static ValueHandle createIComparisonExpr(CmpIPredicate predicate,
+                                         ValueHandle lhs, ValueHandle rhs) {
   auto lhsType = lhs.getType();
   auto rhsType = rhs.getType();
   (void)lhsType;
@@ -404,22 +404,56 @@ static ValueHandle createComparisonExpr(CmpIPredicate predicate,
   return ValueHandle(op.getResult());
 }
 
+static ValueHandle createFComparisonExpr(CmpFPredicate predicate,
+                                         ValueHandle lhs, ValueHandle rhs) {
+  auto lhsType = lhs.getType();
+  auto rhsType = rhs.getType();
+  (void)lhsType;
+  (void)rhsType;
+  assert(lhsType == rhsType && "cannot mix types in operators");
+  assert(lhsType.isa<FloatType>() && "only float comparisons are supported");
+
+  auto op = ScopedContext::getBuilder().create<CmpFOp>(
+      ScopedContext::getLocation(), predicate, lhs.getValue(), rhs.getValue());
+  return ValueHandle(op.getResult());
+}
+
+// All floating point comparison are ordered through EDSL
 ValueHandle mlir::edsc::op::operator==(ValueHandle lhs, ValueHandle rhs) {
-  return createComparisonExpr(CmpIPredicate::EQ, lhs, rhs);
+  auto type = lhs.getType();
+  return type.isa<FloatType>()
+             ? createFComparisonExpr(CmpFPredicate::OEQ, lhs, rhs)
+             : createIComparisonExpr(CmpIPredicate::EQ, lhs, rhs);
 }
 ValueHandle mlir::edsc::op::operator!=(ValueHandle lhs, ValueHandle rhs) {
-  return createComparisonExpr(CmpIPredicate::NE, lhs, rhs);
+  auto type = lhs.getType();
+  return type.isa<FloatType>()
+             ? createFComparisonExpr(CmpFPredicate::ONE, lhs, rhs)
+             : createIComparisonExpr(CmpIPredicate::NE, lhs, rhs);
 }
 ValueHandle mlir::edsc::op::operator<(ValueHandle lhs, ValueHandle rhs) {
-  // TODO(ntv,zinenko): signed by default, how about unsigned?
-  return createComparisonExpr(CmpIPredicate::SLT, lhs, rhs);
+  auto type = lhs.getType();
+  return type.isa<FloatType>()
+             ? createFComparisonExpr(CmpFPredicate::OLT, lhs, rhs)
+             :
+             // TODO(ntv,zinenko): signed by default, how about unsigned?
+             createIComparisonExpr(CmpIPredicate::SLT, lhs, rhs);
 }
 ValueHandle mlir::edsc::op::operator<=(ValueHandle lhs, ValueHandle rhs) {
-  return createComparisonExpr(CmpIPredicate::SLE, lhs, rhs);
+  auto type = lhs.getType();
+  return type.isa<FloatType>()
+             ? createFComparisonExpr(CmpFPredicate::OLE, lhs, rhs)
+             : createIComparisonExpr(CmpIPredicate::SLE, lhs, rhs);
 }
 ValueHandle mlir::edsc::op::operator>(ValueHandle lhs, ValueHandle rhs) {
-  return createComparisonExpr(CmpIPredicate::SGT, lhs, rhs);
+  auto type = lhs.getType();
+  return type.isa<FloatType>()
+             ? createFComparisonExpr(CmpFPredicate::OGT, lhs, rhs)
+             : createIComparisonExpr(CmpIPredicate::SGT, lhs, rhs);
 }
 ValueHandle mlir::edsc::op::operator>=(ValueHandle lhs, ValueHandle rhs) {
-  return createComparisonExpr(CmpIPredicate::SGE, lhs, rhs);
+  auto type = lhs.getType();
+  return type.isa<FloatType>()
+             ? createFComparisonExpr(CmpFPredicate::OGE, lhs, rhs)
+             : createIComparisonExpr(CmpIPredicate::SGE, lhs, rhs);
 }
index 8db9abe..2469f9c 100644 (file)
@@ -38,9 +38,8 @@ using namespace mlir::LLVM;
 #include "mlir/LLVMIR/LLVMOpsEnums.cpp.inc"
 
 //===----------------------------------------------------------------------===//
-// Printing/parsing for LLVM::ICmpOp.
+// Printing/parsing for LLVM::CmpOp.
 //===----------------------------------------------------------------------===//
-
 static void printICmpOp(OpAsmPrinter *p, ICmpOp &op) {
   *p << op.getOperationName() << " \"" << stringifyICmpPredicate(op.predicate())
      << "\" " << *op.getOperand(0) << ", " << *op.getOperand(1);
@@ -48,9 +47,19 @@ static void printICmpOp(OpAsmPrinter *p, ICmpOp &op) {
   *p << " : " << op.lhs()->getType();
 }
 
+static void printFCmpOp(OpAsmPrinter *p, FCmpOp &op) {
+  *p << op.getOperationName() << " \"" << stringifyFCmpPredicate(op.predicate())
+     << "\" " << *op.getOperand(0) << ", " << *op.getOperand(1);
+  p->printOptionalAttrDict(op.getAttrs(), {"predicate"});
+  *p << " : " << op.lhs()->getType();
+}
+
 // <operation> ::= `llvm.icmp` string-literal ssa-use `,` ssa-use
 //                 attribute-dict? `:` type
-static ParseResult parseICmpOp(OpAsmParser *parser, OperationState *result) {
+// <operation> ::= `llvm.fcmp` string-literal ssa-use `,` ssa-use
+//                 attribute-dict? `:` type
+template <typename CmpPredicateType>
+static ParseResult parseCmpOp(OpAsmParser *parser, OperationState *result) {
   Builder &builder = parser->getBuilder();
 
   Attribute predicate;
@@ -73,15 +82,27 @@ static ParseResult parseICmpOp(OpAsmParser *parser, OperationState *result) {
   if (!predicateStr)
     return parser->emitError(predicateLoc,
                              "expected 'predicate' attribute of string type");
-  Optional<ICmpPredicate> predicateValue =
-      symbolizeICmpPredicate(predicateStr.getValue());
-  if (!predicateValue)
-    return parser->emitError(predicateLoc)
-           << "'" << predicateStr.getValue()
-           << "' is an incorrect value of the 'predicate' attribute";
-
-  attrs[0].second = parser->getBuilder().getI64IntegerAttr(
-      static_cast<int64_t>(predicateValue.getValue()));
+
+  int64_t predicateValue = 0;
+  if (std::is_same<CmpPredicateType, ICmpPredicate>()) {
+    Optional<ICmpPredicate> predicate =
+        symbolizeICmpPredicate(predicateStr.getValue());
+    if (!predicate)
+      return parser->emitError(predicateLoc)
+             << "'" << predicateStr.getValue()
+             << "' is an incorrect value of the 'predicate' attribute";
+    predicateValue = static_cast<int64_t>(predicate.getValue());
+  } else {
+    Optional<FCmpPredicate> predicate =
+        symbolizeFCmpPredicate(predicateStr.getValue());
+    if (!predicate)
+      return parser->emitError(predicateLoc)
+             << "'" << predicateStr.getValue()
+             << "' is an incorrect value of the 'predicate' attribute";
+    predicateValue = static_cast<int64_t>(predicate.getValue());
+  }
+
+  attrs[0].second = parser->getBuilder().getI64IntegerAttr(predicateValue);
 
   // The result type is either i1 or a vector type <? x i1> if the inputs are
   // vectors.
index 8b18c56..08255b4 100644 (file)
@@ -140,6 +140,45 @@ static llvm::CmpInst::Predicate getLLVMCmpPredicate(ICmpPredicate p) {
   }
 }
 
+static llvm::CmpInst::Predicate getLLVMCmpPredicate(FCmpPredicate p) {
+  switch (p) {
+  case LLVM::FCmpPredicate::_false:
+    return llvm::CmpInst::Predicate::FCMP_FALSE;
+  case LLVM::FCmpPredicate::oeq:
+    return llvm::CmpInst::Predicate::FCMP_OEQ;
+  case LLVM::FCmpPredicate::ogt:
+    return llvm::CmpInst::Predicate::FCMP_OGT;
+  case LLVM::FCmpPredicate::oge:
+    return llvm::CmpInst::Predicate::FCMP_OGE;
+  case LLVM::FCmpPredicate::olt:
+    return llvm::CmpInst::Predicate::FCMP_OLT;
+  case LLVM::FCmpPredicate::ole:
+    return llvm::CmpInst::Predicate::FCMP_OLE;
+  case LLVM::FCmpPredicate::one:
+    return llvm::CmpInst::Predicate::FCMP_ONE;
+  case LLVM::FCmpPredicate::ord:
+    return llvm::CmpInst::Predicate::FCMP_ORD;
+  case LLVM::FCmpPredicate::ueq:
+    return llvm::CmpInst::Predicate::FCMP_UEQ;
+  case LLVM::FCmpPredicate::ugt:
+    return llvm::CmpInst::Predicate::FCMP_UGT;
+  case LLVM::FCmpPredicate::uge:
+    return llvm::CmpInst::Predicate::FCMP_UGE;
+  case LLVM::FCmpPredicate::ult:
+    return llvm::CmpInst::Predicate::FCMP_ULT;
+  case LLVM::FCmpPredicate::ule:
+    return llvm::CmpInst::Predicate::FCMP_ULE;
+  case LLVM::FCmpPredicate::une:
+    return llvm::CmpInst::Predicate::FCMP_UNE;
+  case LLVM::FCmpPredicate::uno:
+    return llvm::CmpInst::Predicate::FCMP_UNO;
+  case LLVM::FCmpPredicate::_true:
+    return llvm::CmpInst::Predicate::FCMP_TRUE;
+  default:
+    llvm_unreachable("incorrect comparison predicate");
+  }
+}
+
 // A helper to look up remapped operands in the value remapping table.
 template <typename Range>
 SmallVector<llvm::Value *, 8> ModuleTranslation::lookupValues(Range &&values) {
index ef87e3e..a39388e 100644 (file)
@@ -439,7 +439,7 @@ TEST_FUNC(insertion_in_block) {
   f.erase();
 }
 
-TEST_FUNC(select_op) {
+TEST_FUNC(select_op_i32) {
   using namespace edsc;
   using namespace edsc::intrinsics;
   using namespace edsc::op;
@@ -474,6 +474,81 @@ TEST_FUNC(select_op) {
   f.erase();
 }
 
+TEST_FUNC(select_op_f32) {
+  using namespace edsc;
+  using namespace edsc::intrinsics;
+  using namespace edsc::op;
+  auto f32Type = FloatType::getF32(&globalContext());
+  auto memrefType = MemRefType::get({-1, -1}, f32Type, {}, 0);
+  auto f = makeFunction("select_op", {}, {memrefType, memrefType});
+
+  OpBuilder builder(f.getBody());
+  ScopedContext scope(builder, f.getLoc());
+  // clang-format off
+  ValueHandle zero = constant_index(0), one = constant_index(1);
+  MemRefView vA(f.getArgument(0)), vB(f.getArgument(1));
+  IndexedValue A(f.getArgument(0)), B(f.getArgument(1));
+  IndexHandle i, j;
+  LoopNestBuilder({&i, &j}, {zero, zero}, {one, one}, {1, 1})([&]{
+
+    edsc::intrinsics::select(B(i, j) == B(i+one, j), *A(zero, zero), *A(i, j));
+    edsc::intrinsics::select(B(i, j) != B(i+one, j), *A(zero, zero), *A(i, j));
+    edsc::intrinsics::select(B(i, j) >= B(i+one, j), *A(zero, zero), *A(i, j));
+    edsc::intrinsics::select(B(i, j) <= B(i+one, j), *A(zero, zero), *A(i, j));
+    edsc::intrinsics::select(B(i, j) < B(i+one, j), *A(zero, zero), *A(i, j));
+    edsc::intrinsics::select(B(i, j) > B(i+one, j), *A(zero, zero), *A(i, j));
+  });
+
+  // CHECK-LABEL: @select_op
+  //      CHECK: affine.for %{{.*}} = 0 to 1 {
+  // CHECK-NEXT:   affine.for %{{.*}} = 0 to 1 {
+  //  CHECK-DAG:     cmpf "oeq"
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.apply
+  // CHECK-NEXT:     select
+  //  CHECK-DAG:     cmpf "one"
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.apply
+  // CHECK-NEXT:     select
+  //  CHECK-DAG:     cmpf "oge"
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.apply
+  // CHECK-NEXT:     select
+  //  CHECK-DAG:     cmpf "ole"
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.apply
+  // CHECK-NEXT:     select
+  //  CHECK-DAG:     cmpf "olt"
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.apply
+  // CHECK-NEXT:     select
+  //  CHECK-DAG:     cmpf "ogt"
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.load
+  //  CHECK-DAG:     affine.apply
+  // CHECK-NEXT:     select
+  // clang-format on
+  f.print(llvm::outs());
+  f.erase();
+}
+
 // Inject an EDSC-constructed computation to exercise imperfectly nested 2-d
 // tiling.
 TEST_FUNC(tile_2d) {
index 340e53c..65818b0 100644 (file)
@@ -477,3 +477,39 @@ func @cond_br_same_target(%arg0: i1, %arg1: i32, %arg2 : i32) -> (i32) {
 // CHECK:      ^[[dummyBlock]]:
 // CHECK-NEXT:  llvm.br ^[[origBlock]](%arg2 : !llvm.i32)
 }
+
+// CHECK-LABEL: func @fcmp(%arg0: !llvm.float, %arg1: !llvm.float) {
+func @fcmp(f32, f32) -> () {
+^bb0(%arg0: f32, %arg1: f32):
+  // CHECK:      llvm.fcmp "oeq" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "ogt" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "oge" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "olt" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "ole" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "one" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "ord" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "ueq" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "ugt" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "uge" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "ult" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "ule" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "une" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.fcmp "uno" %arg0, %arg1 : !llvm.float
+  // CHECK-NEXT: llvm.return
+  %1 = cmpf "oeq", %arg0, %arg1 : f32
+  %2 = cmpf "ogt", %arg0, %arg1 : f32
+  %3 = cmpf "oge", %arg0, %arg1 : f32
+  %4 = cmpf "olt", %arg0, %arg1 : f32
+  %5 = cmpf "ole", %arg0, %arg1 : f32
+  %6 = cmpf "one", %arg0, %arg1 : f32
+  %7 = cmpf "ord", %arg0, %arg1 : f32
+  %8 = cmpf "ueq", %arg0, %arg1 : f32
+  %9 = cmpf "ugt", %arg0, %arg1 : f32
+  %10 = cmpf "uge", %arg0, %arg1 : f32
+  %11 = cmpf "ult", %arg0, %arg1 : f32
+  %12 = cmpf "ule", %arg0, %arg1 : f32
+  %13 = cmpf "une", %arg0, %arg1 : f32
+  %14 = cmpf "uno", %arg0, %arg1 : f32
+  
+  return 
+}
index 8a77b09..9b6e0a8 100644 (file)
@@ -829,3 +829,36 @@ func @noreach() {
 // CHECK:    unreachable
   llvm.unreachable
 }
+
+// CHECK-LABEL: define void @fcmp
+func @fcmp(%arg0: !llvm.float, %arg1: !llvm.float) {
+  // CHECK: fcmp oeq float %0, %1
+  // CHECK-NEXT: fcmp ogt float %0, %1
+  // CHECK-NEXT: fcmp oge float %0, %1
+  // CHECK-NEXT: fcmp olt float %0, %1
+  // CHECK-NEXT: fcmp ole float %0, %1
+  // CHECK-NEXT: fcmp one float %0, %1
+  // CHECK-NEXT: fcmp ord float %0, %1
+  // CHECK-NEXT: fcmp ueq float %0, %1
+  // CHECK-NEXT: fcmp ugt float %0, %1
+  // CHECK-NEXT: fcmp uge float %0, %1
+  // CHECK-NEXT: fcmp ult float %0, %1
+  // CHECK-NEXT: fcmp ule float %0, %1
+  // CHECK-NEXT: fcmp une float %0, %1
+  // CHECK-NEXT: fcmp uno float %0, %1
+  %0 = llvm.fcmp "oeq" %arg0, %arg1 : !llvm.float
+  %1 = llvm.fcmp "ogt" %arg0, %arg1 : !llvm.float
+  %2 = llvm.fcmp "oge" %arg0, %arg1 : !llvm.float
+  %3 = llvm.fcmp "olt" %arg0, %arg1 : !llvm.float
+  %4 = llvm.fcmp "ole" %arg0, %arg1 : !llvm.float
+  %5 = llvm.fcmp "one" %arg0, %arg1 : !llvm.float
+  %6 = llvm.fcmp "ord" %arg0, %arg1 : !llvm.float
+  %7 = llvm.fcmp "ueq" %arg0, %arg1 : !llvm.float
+  %8 = llvm.fcmp "ugt" %arg0, %arg1 : !llvm.float
+  %9 = llvm.fcmp "uge" %arg0, %arg1 : !llvm.float
+  %10 = llvm.fcmp "ult" %arg0, %arg1 : !llvm.float
+  %11 = llvm.fcmp "ule" %arg0, %arg1 : !llvm.float
+  %12 = llvm.fcmp "une" %arg0, %arg1 : !llvm.float
+  %13 = llvm.fcmp "uno" %arg0, %arg1 : !llvm.float
+  llvm.return
+}