diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp --- a/flang/lib/Lower/ConvertExprToHLFIR.cpp +++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp @@ -20,6 +20,7 @@ #include "flang/Lower/IntrinsicCall.h" #include "flang/Lower/StatementContext.h" #include "flang/Lower/SymbolMap.h" +#include "flang/Optimizer/Builder/Runtime/Character.h" #include "flang/Optimizer/Builder/Todo.h" #include "flang/Optimizer/HLFIR/HLFIROps.h" @@ -341,6 +342,121 @@ } }; +/// Convert parser's INTEGER relational operators to MLIR. +static mlir::arith::CmpIPredicate +translateRelational(Fortran::common::RelationalOperator rop) { + switch (rop) { + case Fortran::common::RelationalOperator::LT: + return mlir::arith::CmpIPredicate::slt; + case Fortran::common::RelationalOperator::LE: + return mlir::arith::CmpIPredicate::sle; + case Fortran::common::RelationalOperator::EQ: + return mlir::arith::CmpIPredicate::eq; + case Fortran::common::RelationalOperator::NE: + return mlir::arith::CmpIPredicate::ne; + case Fortran::common::RelationalOperator::GT: + return mlir::arith::CmpIPredicate::sgt; + case Fortran::common::RelationalOperator::GE: + return mlir::arith::CmpIPredicate::sge; + } + llvm_unreachable("unhandled INTEGER relational operator"); +} + +/// Convert parser's REAL relational operators to MLIR. +/// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018 +/// requirements in the IEEE context (table 17.1 of F2018). This choice is +/// also applied in other contexts because it is easier and in line with +/// other Fortran compilers. +/// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not +/// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee +/// whether the comparison will signal or not in case of quiet NaN argument. +static mlir::arith::CmpFPredicate +translateFloatRelational(Fortran::common::RelationalOperator rop) { + switch (rop) { + case Fortran::common::RelationalOperator::LT: + return mlir::arith::CmpFPredicate::OLT; + case Fortran::common::RelationalOperator::LE: + return mlir::arith::CmpFPredicate::OLE; + case Fortran::common::RelationalOperator::EQ: + return mlir::arith::CmpFPredicate::OEQ; + case Fortran::common::RelationalOperator::NE: + return mlir::arith::CmpFPredicate::UNE; + case Fortran::common::RelationalOperator::GT: + return mlir::arith::CmpFPredicate::OGT; + case Fortran::common::RelationalOperator::GE: + return mlir::arith::CmpFPredicate::OGE; + } + llvm_unreachable("unhandled REAL relational operator"); +} + +template +struct BinaryOp>> { + using Op = Fortran::evaluate::Relational< + Fortran::evaluate::Type>; + static hlfir::EntityWithAttributes gen(mlir::Location loc, + fir::FirOpBuilder &builder, + const Op &op, hlfir::Entity lhs, + hlfir::Entity rhs) { + auto cmp = builder.create( + loc, translateRelational(op.opr), lhs, rhs); + return hlfir::EntityWithAttributes{cmp}; + } +}; + +template +struct BinaryOp>> { + using Op = Fortran::evaluate::Relational< + Fortran::evaluate::Type>; + static hlfir::EntityWithAttributes gen(mlir::Location loc, + fir::FirOpBuilder &builder, + const Op &op, hlfir::Entity lhs, + hlfir::Entity rhs) { + auto cmp = builder.create( + loc, translateFloatRelational(op.opr), lhs, rhs); + return hlfir::EntityWithAttributes{cmp}; + } +}; + +template +struct BinaryOp>> { + using Op = Fortran::evaluate::Relational< + Fortran::evaluate::Type>; + static hlfir::EntityWithAttributes gen(mlir::Location loc, + fir::FirOpBuilder &builder, + const Op &op, hlfir::Entity lhs, + hlfir::Entity rhs) { + auto cmp = builder.create( + loc, translateFloatRelational(op.opr), lhs, rhs); + return hlfir::EntityWithAttributes{cmp}; + } +}; + +template +struct BinaryOp>> { + using Op = Fortran::evaluate::Relational< + Fortran::evaluate::Type>; + static hlfir::EntityWithAttributes gen(mlir::Location loc, + fir::FirOpBuilder &builder, + const Op &op, hlfir::Entity lhs, + hlfir::Entity rhs) { + auto [lhsExv, lhsCleanUp] = + hlfir::translateToExtendedValue(loc, builder, lhs); + auto [rhsExv, rhsCleanUp] = + hlfir::translateToExtendedValue(loc, builder, rhs); + auto cmp = fir::runtime::genCharCompare( + builder, loc, translateRelational(op.opr), lhsExv, rhsExv); + if (lhsCleanUp) + lhsCleanUp.value()(); + if (rhsCleanUp) + rhsCleanUp.value()(); + return hlfir::EntityWithAttributes{cmp}; + } +}; + /// Lower Expr to HLFIR. class HlfirBuilder { public: diff --git a/flang/test/Lower/HLFIR/binary-ops.f90 b/flang/test/Lower/HLFIR/binary-ops.f90 --- a/flang/test/Lower/HLFIR/binary-ops.f90 +++ b/flang/test/Lower/HLFIR/binary-ops.f90 @@ -207,3 +207,85 @@ ! CHECK: %[[VAL_12:.*]] = fir.load %{{.*}} : !fir.ref ! CHECK: %[[VAL_13:.*]] = arith.cmpi sgt, %[[VAL_11]], %[[VAL_12]] : i64 ! CHECK: arith.select %[[VAL_13]], %[[VAL_11]], %[[VAL_12]] : i64 + +subroutine cmp_int(l, x, y) + logical :: l + integer :: x, y + l = x .eq. y +end subroutine +! CHECK-LABEL: func.func @_QPcmp_int( +! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare {{.*}}x" +! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare {{.*}}y" +! CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref +! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref +! CHECK: %[[VAL_8:.*]] = arith.cmpi eq, %[[VAL_6]], %[[VAL_7]] : i32 + +subroutine cmp_int_2(l, x, y) + logical :: l + integer :: x, y + l = x .ne. y +! CHECK: arith.cmpi ne + l = x .gt. y +! CHECK: arith.cmpi sgt + l = x .ge. y +! CHECK: arith.cmpi sge + l = x .lt. y +! CHECK: arith.cmpi slt + l = x .le. y +! CHECK: arith.cmpi sle +end subroutine + +subroutine cmp_real(l, x, y) + logical :: l + real :: x, y + l = x .eq. y +end subroutine +! CHECK-LABEL: func.func @_QPcmp_real( +! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare {{.*}}x" +! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare {{.*}}y" +! CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref +! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref +! CHECK: %[[VAL_8:.*]] = arith.cmpf oeq, %[[VAL_6]], %[[VAL_7]] : f32 + +subroutine cmp_real_2(l, x, y) + logical :: l + real :: x, y + l = x .ne. y +! CHECK: arith.cmpf une + l = x .gt. y +! CHECK: arith.cmpf ogt + l = x .ge. y +! CHECK: arith.cmpf oge + l = x .lt. y +! CHECK: arith.cmpf olt + l = x .le. y +! CHECK: arith.cmpf ole +end subroutine + +subroutine cmp_cmplx(l, x, y) + logical :: l + complex :: x, y + l = x .eq. y +end subroutine +! CHECK-LABEL: func.func @_QPcmp_cmplx( +! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare {{.*}}x" +! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare {{.*}}y" +! CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref> +! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref> +! CHECK: %[[VAL_8:.*]] = fir.cmpc "oeq", %[[VAL_6]], %[[VAL_7]] : !fir.complex<4> + +subroutine cmp_char(l, x, y) + logical :: l + character(*) :: x, y + l = x .eq. y +end subroutine +! CHECK-LABEL: func.func @_QPcmp_char( +! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %{{.*}} typeparams %[[VAL_4:.*]]#1 {uniq_name = "_QFcmp_charEx"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +! CHECK: %[[VAL_7:.*]]:2 = hlfir.declare %{{.*}} typeparams %[[VAL_6:.*]]#1 {uniq_name = "_QFcmp_charEy"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +! CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_5]]#1 : (!fir.ref>) -> !fir.ref +! CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_7]]#1 : (!fir.ref>) -> !fir.ref +! CHECK: %[[VAL_10:.*]] = fir.convert %[[VAL_4]]#1 : (index) -> i64 +! CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_6]]#1 : (index) -> i64 +! CHECK: %[[VAL_12:.*]] = fir.call @_FortranACharacterCompareScalar1(%[[VAL_8]], %[[VAL_9]], %[[VAL_10]], %[[VAL_11]]) fastmath : (!fir.ref, !fir.ref, i64, i64) -> i32 +! CHECK: %[[VAL_13:.*]] = arith.constant 0 : i32 +! CHECK: %[[VAL_14:.*]] = arith.cmpi eq, %[[VAL_12]], %[[VAL_13]] : i32