| //===- FenvOpTest.cpp - Unit tests for CIR fenv operations ----------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "mlir/IR/BuiltinOps.h" |
| #include "mlir/IR/MLIRContext.h" |
| #include "mlir/IR/OwningOpRef.h" |
| #include "mlir/Interfaces/SideEffectInterfaces.h" |
| #include "mlir/Parser/Parser.h" |
| #include "clang/CIR/Dialect/IR/CIRDialect.h" |
| |
| #include <gtest/gtest.h> |
| |
| using namespace mlir; |
| |
| namespace { |
| |
| class CIRFenvOpTest : public ::testing::Test { |
| protected: |
| CIRFenvOpTest() { context.loadDialect<cir::CIRDialect>(); } |
| |
| OwningOpRef<ModuleOp> parse(StringRef ir) { |
| OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &context); |
| EXPECT_TRUE(module) << "failed to parse IR"; |
| return module; |
| } |
| |
| template <typename OpTy> SmallVector<OpTy> findOps(ModuleOp module) { |
| SmallVector<OpTy> ops; |
| module.walk([&](OpTy op) { ops.push_back(op); }); |
| return ops; |
| } |
| |
| static SmallVector<MemoryEffects::EffectInstance> getEffects(Operation *op) { |
| MemoryEffectOpInterface effectsOp = cast<MemoryEffectOpInterface>(op); |
| SmallVector<MemoryEffects::EffectInstance> effects; |
| effectsOp.getEffects(effects); |
| return effects; |
| } |
| |
| static void expectFenvReadAndWrite(Operation *op) { |
| SmallVector<MemoryEffects::EffectInstance> effects = getEffects(op); |
| ASSERT_EQ(effects.size(), 2u); |
| |
| unsigned reads = 0; |
| unsigned writes = 0; |
| for (const MemoryEffects::EffectInstance &effect : effects) { |
| EXPECT_EQ(effect.getResource(), |
| cir::FloatingPointEnvironmentResource::get()); |
| reads += isa<MemoryEffects::Read>(effect.getEffect()); |
| writes += isa<MemoryEffects::Write>(effect.getEffect()); |
| } |
| EXPECT_EQ(reads, 1u); |
| EXPECT_EQ(writes, 1u); |
| } |
| |
| MLIRContext context; |
| }; |
| |
| TEST_F(CIRFenvOpTest, MemoryEffects) { |
| OwningOpRef<ModuleOp> module = parse(R"CIR( |
| !s32i = !cir.int<s, 32> |
| cir.func @f(%a: !cir.float, %b: !cir.float, %c: !cir.float, |
| %va: !cir.vector<4 x !cir.float>, |
| %vb: !cir.vector<4 x !cir.float>) { |
| %0 = cir.fadd %a, %b : !cir.float |
| %1 = cir.fadd %a, %b : !cir.float fenv<> |
| %2 = cir.sqrt %a : !cir.float fenv<> |
| %3 = cir.pow %a, %b : !cir.float fenv<> |
| %4 = cir.fma %a, %b, %c : !cir.float |
| %5 = cir.fma %a, %b, %c : !cir.float fenv<> |
| %6 = cir.lround %a : !cir.float -> !s32i |
| %7 = cir.lround %a : !cir.float -> !s32i fenv<> |
| %8 = cir.cast floating %a : !cir.float -> !cir.double |
| %9 = cir.cast floating %a : !cir.float -> !cir.double fenv<> |
| %10 = cir.cmp lt %a, %b : !cir.float |
| %11 = cir.cmp lt %a, %b : !cir.float fenv<> |
| %12 = cir.vec.cmp(eq, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> |
| %13 = cir.vec.cmp(eq, %va, %vb) : !cir.vector<4 x !cir.float>, !cir.vector<4 x !s32i> fenv<> |
| cir.return |
| } |
| )CIR"); |
| ASSERT_TRUE(module); |
| |
| SmallVector<cir::FAddOp> faddOps = findOps<cir::FAddOp>(*module); |
| ASSERT_EQ(faddOps.size(), 2u); |
| EXPECT_TRUE(getEffects(faddOps[0]).empty()); |
| EXPECT_TRUE(isMemoryEffectFree(faddOps[0])); |
| expectFenvReadAndWrite(faddOps[1]); |
| EXPECT_FALSE(isMemoryEffectFree(faddOps[1])); |
| |
| SmallVector<cir::SqrtOp> sqrtOps = findOps<cir::SqrtOp>(*module); |
| ASSERT_EQ(sqrtOps.size(), 1u); |
| expectFenvReadAndWrite(sqrtOps[0]); |
| |
| SmallVector<cir::PowOp> powOps = findOps<cir::PowOp>(*module); |
| ASSERT_EQ(powOps.size(), 1u); |
| expectFenvReadAndWrite(powOps[0]); |
| |
| SmallVector<cir::FMAOp> fmaOps = findOps<cir::FMAOp>(*module); |
| ASSERT_EQ(fmaOps.size(), 2u); |
| EXPECT_TRUE(getEffects(fmaOps[0]).empty()); |
| EXPECT_TRUE(isMemoryEffectFree(fmaOps[0])); |
| expectFenvReadAndWrite(fmaOps[1]); |
| EXPECT_FALSE(isMemoryEffectFree(fmaOps[1])); |
| |
| SmallVector<cir::LroundOp> lroundOps = findOps<cir::LroundOp>(*module); |
| ASSERT_EQ(lroundOps.size(), 2u); |
| EXPECT_TRUE(getEffects(lroundOps[0]).empty()); |
| EXPECT_TRUE(isMemoryEffectFree(lroundOps[0])); |
| expectFenvReadAndWrite(lroundOps[1]); |
| EXPECT_FALSE(isMemoryEffectFree(lroundOps[1])); |
| |
| SmallVector<cir::CastOp> castOps = findOps<cir::CastOp>(*module); |
| ASSERT_EQ(castOps.size(), 2u); |
| EXPECT_TRUE(getEffects(castOps[0]).empty()); |
| EXPECT_TRUE(isMemoryEffectFree(castOps[0])); |
| expectFenvReadAndWrite(castOps[1]); |
| EXPECT_FALSE(isMemoryEffectFree(castOps[1])); |
| |
| SmallVector<cir::CmpOp> cmpOps = findOps<cir::CmpOp>(*module); |
| ASSERT_EQ(cmpOps.size(), 2u); |
| EXPECT_TRUE(getEffects(cmpOps[0]).empty()); |
| EXPECT_TRUE(isMemoryEffectFree(cmpOps[0])); |
| expectFenvReadAndWrite(cmpOps[1]); |
| EXPECT_FALSE(isMemoryEffectFree(cmpOps[1])); |
| |
| SmallVector<cir::VecCmpOp> vecCmpOps = findOps<cir::VecCmpOp>(*module); |
| ASSERT_EQ(vecCmpOps.size(), 2u); |
| EXPECT_TRUE(getEffects(vecCmpOps[0]).empty()); |
| EXPECT_TRUE(isMemoryEffectFree(vecCmpOps[0])); |
| expectFenvReadAndWrite(vecCmpOps[1]); |
| EXPECT_FALSE(isMemoryEffectFree(vecCmpOps[1])); |
| } |
| |
| TEST_F(CIRFenvOpTest, Speculatability) { |
| OwningOpRef<ModuleOp> module = parse(R"CIR( |
| cir.func @f(%a: !cir.float, %b: !cir.float) { |
| %0 = cir.fadd %a, %b : !cir.float |
| %1 = cir.fadd %a, %b : !cir.float fenv<> |
| %2 = cir.fadd %a, %b : !cir.float fenv<except_mode = masked> |
| %3 = cir.fadd %a, %b : !cir.float fenv<strict_except = false> |
| %4 = cir.fadd %a, %b : !cir.float fenv<except_mode = masked, strict_except = true> |
| %5 = cir.fadd %a, %b : !cir.float fenv<except_mode = unmasked, strict_except = false> |
| %6 = cir.fadd %a, %b : !cir.float fenv<except_mode = unknown, strict_except = false> |
| cir.return |
| } |
| )CIR"); |
| ASSERT_TRUE(module); |
| |
| SmallVector<cir::FAddOp> ops = findOps<cir::FAddOp>(*module); |
| ASSERT_EQ(ops.size(), 7u); |
| |
| // Missing fenv fields use the defaults: masked exceptions and non-strict |
| // exception behavior. |
| for (unsigned i = 0; i != 4; ++i) |
| EXPECT_TRUE(isSpeculatable(ops[i])) << "operation " << i; |
| |
| for (unsigned i = 4; i != ops.size(); ++i) |
| EXPECT_FALSE(isSpeculatable(ops[i])) << "operation " << i; |
| |
| cir::FPEnvConstrainedOpInterface fenvOp = |
| cast<cir::FPEnvConstrainedOpInterface>(ops[1].getOperation()); |
| EXPECT_EQ(fenvOp.getFenvDynamicRoundingMode(), |
| cir::FPDynamicRoundingMode::Unknown); |
| EXPECT_EQ(fenvOp.getFenvExceptionMode(), cir::FPExceptionMode::Masked); |
| EXPECT_FALSE(fenvOp.getFenvStrictExcept()); |
| } |
| |
| } // namespace |