Skip to content

Conversation

TIFitis
Copy link
Member

@TIFitis TIFitis commented Sep 15, 2025

This PR introduces a new ConvertComplexPow pass for Flang that handles complex power operations. The change forces lowering to complex.pow operations when --math-runtime=precise is not used, then uses the ConvertComplexPow pass to convert these operations back to library calls.

  • Adds a new ConvertComplexPow pass that converts complex.pow ops to appropriate runtime library calls
  • Updates complex power lowering to use complex.pow operations by default instead of direct library calls

#158722 Adds a new complex.powi op enabling algebraic optimisations.

@llvmbot llvmbot added flang:driver flang Flang issues not falling into any other category flang:fir-hlfir labels Sep 15, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 15, 2025

@llvm/pr-subscribers-mlir
@llvm/pr-subscribers-flang-fir-hlfir

@llvm/pr-subscribers-flang-driver

Author: Akash Banerjee (TIFitis)

Changes

This PR forces lowering to complex.pow ops for flang when --math-runtime=precise is not used.
Also, adds a ConvertComplexPow pass to convert the complex.pow ops back to the original lib calls.

The primary motivation for this is to benefit from math optimisations such as x**3 -> xxx. I'll be adding the optimisation shortly in a subsequent PR.


Patch is 23.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/158642.diff

15 Files Affected:

  • (modified) flang/include/flang/Optimizer/Transforms/Passes.td (+11)
  • (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+15-15)
  • (modified) flang/lib/Optimizer/Passes/Pipelines.cpp (+1)
  • (modified) flang/lib/Optimizer/Transforms/CMakeLists.txt (+1)
  • (added) flang/lib/Optimizer/Transforms/ConvertComplexPow.cpp (+125)
  • (modified) flang/test/Driver/bbc-mlir-pass-pipeline.f90 (+2)
  • (modified) flang/test/Driver/mlir-debug-pass-pipeline.f90 (+2)
  • (modified) flang/test/Driver/mlir-pass-pipeline.f90 (+2)
  • (modified) flang/test/Fir/basic-program.fir (+2)
  • (modified) flang/test/Lower/HLFIR/binary-ops.f90 (+2-2)
  • (modified) flang/test/Lower/Intrinsics/pow_complex16.f90 (+3-2)
  • (modified) flang/test/Lower/Intrinsics/pow_complex16i.f90 (+3-2)
  • (modified) flang/test/Lower/Intrinsics/pow_complex16k.f90 (+3-2)
  • (modified) flang/test/Lower/power-operator.f90 (+19-15)
  • (added) flang/test/Transforms/convert-complex-pow.fir (+102)
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index e3001454cdf19..0ed4bb66aff0d 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -551,6 +551,17 @@ def SimplifyFIROperations : Pass<"simplify-fir-operations", "mlir::ModuleOp"> {
       "Prefer expanding without using Fortran runtime calls.">];
 }
 
+def ConvertComplexPow : Pass<"convert-complex-pow", "mlir::func::FuncOp"> {
+  let summary = "Convert complex.pow operations to library calls";
+  let description = [{
+    Replace `complex.pow` operations with calls to the appropriate
+    Fortran runtime or libm functions.
+  }];
+  let dependentDialects = ["fir::FIROpsDialect", "mlir::func::FuncDialect",
+                           "mlir::complex::ComplexDialect",
+                           "mlir::arith::ArithDialect"];
+}
+
 def OptimizeArrayRepacking
     : Pass<"optimize-array-repacking", "mlir::func::FuncOp"> {
   let summary = "Optimizes redundant array repacking operations";
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index ce1376fd209cc..466458c05dba7 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -1327,18 +1327,18 @@ mlir::Value genComplexPow(fir::FirOpBuilder &builder, mlir::Location loc,
                           const MathOperation &mathOp,
                           mlir::FunctionType mathLibFuncType,
                           llvm::ArrayRef<mlir::Value> args) {
-  bool isAMDGPU = fir::getTargetTriple(builder.getModule()).isAMDGCN();
-  if (!isAMDGPU)
+  if (mathRuntimeVersion == preciseVersion)
     return genLibCall(builder, loc, mathOp, mathLibFuncType, args);
-
   auto complexTy = mlir::cast<mlir::ComplexType>(mathLibFuncType.getInput(0));
-  auto realTy = complexTy.getElementType();
-  mlir::Value realExp = builder.createConvert(loc, realTy, args[1]);
-  mlir::Value zero = builder.createRealConstant(loc, realTy, 0);
-  mlir::Value complexExp =
-      builder.create<mlir::complex::CreateOp>(loc, complexTy, realExp, zero);
-  mlir::Value result =
-      builder.create<mlir::complex::PowOp>(loc, args[0], complexExp);
+  mlir::Value exp = args[1];
+  if (!mlir::isa<mlir::ComplexType>(exp.getType())) {
+    auto realTy = complexTy.getElementType();
+    mlir::Value realExp = builder.createConvert(loc, realTy, exp);
+    mlir::Value zero = builder.createRealConstant(loc, realTy, 0);
+    exp =
+        builder.create<mlir::complex::CreateOp>(loc, complexTy, realExp, zero);
+  }
+  mlir::Value result = builder.create<mlir::complex::PowOp>(loc, args[0], exp);
   result = builder.createConvert(loc, mathLibFuncType.getResult(0), result);
   return result;
 }
@@ -1668,11 +1668,11 @@ static constexpr MathOperation mathOperations[] = {
     {"pow", RTNAME_STRING(PowF128), FuncTypeReal16Real16Real16, genLibF128Call},
     {"pow", "cpowf",
      genFuncType<Ty::Complex<4>, Ty::Complex<4>, Ty::Complex<4>>,
-     genComplexMathOp<mlir::complex::PowOp>},
+     genComplexPow},
     {"pow", "cpow", genFuncType<Ty::Complex<8>, Ty::Complex<8>, Ty::Complex<8>>,
-     genComplexMathOp<mlir::complex::PowOp>},
+     genComplexPow},
     {"pow", RTNAME_STRING(CPowF128), FuncTypeComplex16Complex16Complex16,
-     genLibF128Call},
+     genComplexPow},
     {"pow", RTNAME_STRING(FPow4i),
      genFuncType<Ty::Real<4>, Ty::Real<4>, Ty::Integer<4>>,
      genMathOp<mlir::math::FPowIOp>},
@@ -1698,7 +1698,7 @@ static constexpr MathOperation mathOperations[] = {
      genFuncType<Ty::Complex<8>, Ty::Complex<8>, Ty::Integer<4>>,
      genComplexPow},
     {"pow", RTNAME_STRING(cqpowi), FuncTypeComplex16Complex16Integer4,
-     genLibF128Call},
+     genComplexPow},
     {"pow", RTNAME_STRING(cpowk),
      genFuncType<Ty::Complex<4>, Ty::Complex<4>, Ty::Integer<8>>,
      genComplexPow},
@@ -1706,7 +1706,7 @@ static constexpr MathOperation mathOperations[] = {
      genFuncType<Ty::Complex<8>, Ty::Complex<8>, Ty::Integer<8>>,
      genComplexPow},
     {"pow", RTNAME_STRING(cqpowk), FuncTypeComplex16Complex16Integer8,
-     genLibF128Call},
+     genComplexPow},
     {"pow-unsigned", RTNAME_STRING(UPow1),
      genFuncType<Ty::Integer<1>, Ty::Integer<1>, Ty::Integer<1>>, genLibCall},
     {"pow-unsigned", RTNAME_STRING(UPow2),
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index 7c2777baebef1..ddcfffc9f158f 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -225,6 +225,7 @@ void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm,
 
   pm.addPass(mlir::createCanonicalizerPass(config));
   pm.addPass(fir::createSimplifyRegionLite());
+  pm.addPass(fir::createConvertComplexPow());
   pm.addPass(mlir::createCSEPass());
 
   if (pc.OptLevel.isOptimizingForSpeed())
diff --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt
index a8812e08c1ccd..4ec16274830fe 100644
--- a/flang/lib/Optimizer/Transforms/CMakeLists.txt
+++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt
@@ -35,6 +35,7 @@ add_flang_library(FIRTransforms
   GenRuntimeCallsForTest.cpp
   SimplifyFIROperations.cpp
   OptimizeArrayRepacking.cpp
+  ConvertComplexPow.cpp
 
   DEPENDS
   CUFAttrs
diff --git a/flang/lib/Optimizer/Transforms/ConvertComplexPow.cpp b/flang/lib/Optimizer/Transforms/ConvertComplexPow.cpp
new file mode 100644
index 0000000000000..8b62237cf539d
--- /dev/null
+++ b/flang/lib/Optimizer/Transforms/ConvertComplexPow.cpp
@@ -0,0 +1,125 @@
+//===- ConvertComplexPow.cpp - Convert complex.pow to library calls -------===//
+//
+// 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 "flang/Common/static-multimap-view.h"
+#include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Dialect/FIRDialect.h"
+#include "flang/Optimizer/Transforms/Passes.h"
+#include "flang/Runtime/entry-names.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/Complex/IR/Complex.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Pass/Pass.h"
+
+namespace fir {
+#define GEN_PASS_DEF_CONVERTCOMPLEXPOW
+#include "flang/Optimizer/Transforms/Passes.h.inc"
+} // namespace fir
+
+using namespace mlir;
+
+namespace {
+class ConvertComplexPowPass
+    : public fir::impl::ConvertComplexPowBase<ConvertComplexPowPass> {
+public:
+  void getDependentDialects(DialectRegistry &registry) const override {
+    registry.insert<fir::FIROpsDialect, complex::ComplexDialect,
+                    arith::ArithDialect, func::FuncDialect>();
+  }
+  void runOnOperation() override;
+};
+} // namespace
+
+// Helper to declare or get a math library function.
+static func::FuncOp getOrDeclare(fir::FirOpBuilder &builder, Location loc,
+                                 StringRef name, FunctionType type) {
+  if (auto func = builder.getNamedFunction(name))
+    return func;
+  auto func = builder.createFunction(loc, name, type);
+  func->setAttr(fir::getSymbolAttrName(), builder.getStringAttr(name));
+  func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(),
+                builder.getUnitAttr());
+  return func;
+}
+
+static bool isZero(Value v) {
+  if (auto cst = v.getDefiningOp<arith::ConstantOp>())
+    if (auto attr = dyn_cast<FloatAttr>(cst.getValue()))
+      return attr.getValue().isZero();
+  return false;
+}
+
+void ConvertComplexPowPass::runOnOperation() {
+  auto func = getOperation();
+  auto mod = func->getParentOfType<ModuleOp>();
+  if (fir::getTargetTriple(mod).isAMDGCN())
+    return;
+
+  fir::FirOpBuilder builder(func, fir::getKindMapping(mod));
+
+  func.walk([&](complex::PowOp op) {
+    builder.setInsertionPoint(op);
+    Location loc = op.getLoc();
+    auto complexTy = cast<ComplexType>(op.getType());
+    auto elemTy = complexTy.getElementType();
+
+    Value base = op.getLhs();
+    Value rhs = op.getRhs();
+
+    Value intExp;
+    if (auto create = rhs.getDefiningOp<complex::CreateOp>()) {
+      if (isZero(create.getImaginary())) {
+        if (auto conv = create.getReal().getDefiningOp<fir::ConvertOp>()) {
+          if (auto intTy = dyn_cast<IntegerType>(conv.getValue().getType()))
+            intExp = conv.getValue();
+        }
+      }
+    }
+
+    func::FuncOp callee;
+    SmallVector<Value> args;
+    if (intExp) {
+      unsigned realBits = cast<FloatType>(elemTy).getWidth();
+      unsigned intBits = cast<IntegerType>(intExp.getType()).getWidth();
+      auto funcTy = builder.getFunctionType(
+          {complexTy, builder.getIntegerType(intBits)}, {complexTy});
+      if (realBits == 32 && intBits == 32)
+        callee = getOrDeclare(builder, loc, RTNAME_STRING(cpowi), funcTy);
+      else if (realBits == 32 && intBits == 64)
+        callee = getOrDeclare(builder, loc, RTNAME_STRING(cpowk), funcTy);
+      else if (realBits == 64 && intBits == 32)
+        callee = getOrDeclare(builder, loc, RTNAME_STRING(zpowi), funcTy);
+      else if (realBits == 64 && intBits == 64)
+        callee = getOrDeclare(builder, loc, RTNAME_STRING(zpowk), funcTy);
+      else if (realBits == 128 && intBits == 32)
+        callee = getOrDeclare(builder, loc, RTNAME_STRING(cqpowi), funcTy);
+      else if (realBits == 128 && intBits == 64)
+        callee = getOrDeclare(builder, loc, RTNAME_STRING(cqpowk), funcTy);
+      else
+        return;
+      args = {base, intExp};
+    } else {
+      unsigned realBits = cast<FloatType>(elemTy).getWidth();
+      auto funcTy =
+          builder.getFunctionType({complexTy, complexTy}, {complexTy});
+      if (realBits == 32)
+        callee = getOrDeclare(builder, loc, "cpowf", funcTy);
+      else if (realBits == 64)
+        callee = getOrDeclare(builder, loc, "cpow", funcTy);
+      else if (realBits == 128)
+        callee = getOrDeclare(builder, loc, RTNAME_STRING(CPowF128), funcTy);
+      else
+        return;
+      args = {base, rhs};
+    }
+
+    auto call = fir::CallOp::create(builder, loc, callee, args);
+    op.replaceAllUsesWith(call.getResult(0));
+    op.erase();
+  });
+}
diff --git a/flang/test/Driver/bbc-mlir-pass-pipeline.f90 b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
index f3791fe9f8dc3..30cb97e4455ee 100644
--- a/flang/test/Driver/bbc-mlir-pass-pipeline.f90
+++ b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
@@ -69,6 +69,8 @@
 ! CHECK-NEXT: SCFToControlFlow
 ! CHECK-NEXT: Canonicalizer
 ! CHECK-NEXT: SimplifyRegionLite
+! CHECK-NEXT: 'func.func' Pipeline
+! CHECK-NEXT:   ConvertComplexPow
 ! CHECK-NEXT: CSE
 ! CHECK-NEXT:   (S) 0 num-cse'd - Number of operations CSE'd
 ! CHECK-NEXT:   (S) 0 num-dce'd - Number of operations DCE'd
diff --git a/flang/test/Driver/mlir-debug-pass-pipeline.f90 b/flang/test/Driver/mlir-debug-pass-pipeline.f90
index 42a71b2d6adc3..bb6d5509c3269 100644
--- a/flang/test/Driver/mlir-debug-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-debug-pass-pipeline.f90
@@ -96,6 +96,8 @@
 ! ALL-NEXT: SCFToControlFlow
 ! ALL-NEXT: Canonicalizer
 ! ALL-NEXT: SimplifyRegionLite
+! ALL-NEXT: 'func.func' Pipeline
+! ALL-NEXT:   ConvertComplexPow
 ! ALL-NEXT: CSE
 ! ALL-NEXT:   (S) 0 num-cse'd - Number of operations CSE'd
 ! ALL-NEXT:   (S) 0 num-dce'd - Number of operations DCE'd
diff --git a/flang/test/Driver/mlir-pass-pipeline.f90 b/flang/test/Driver/mlir-pass-pipeline.f90
index e85a7728fc9af..6006f6672ee72 100644
--- a/flang/test/Driver/mlir-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-pass-pipeline.f90
@@ -127,6 +127,8 @@
 ! ALL-NEXT: SCFToControlFlow
 ! ALL-NEXT: Canonicalizer
 ! ALL-NEXT: SimplifyRegionLite
+! ALL-NEXT: 'func.func' Pipeline
+! ALL-NEXT:   ConvertComplexPow
 ! ALL-NEXT: CSE
 ! ALL-NEXT:   (S) 0 num-cse'd - Number of operations CSE'd
 ! ALL-NEXT:   (S) 0 num-dce'd - Number of operations DCE'd
diff --git a/flang/test/Fir/basic-program.fir b/flang/test/Fir/basic-program.fir
index 0a31397efb332..a2e3cda8f2325 100644
--- a/flang/test/Fir/basic-program.fir
+++ b/flang/test/Fir/basic-program.fir
@@ -125,6 +125,8 @@ func.func @_QQmain() {
 // PASSES-NEXT: SCFToControlFlow
 // PASSES-NEXT: Canonicalizer
 // PASSES-NEXT: SimplifyRegionLite
+// PASSES-NEXT: 'func.func' Pipeline
+// PASSES-NEXT:   ConvertComplexPow
 // PASSES-NEXT: CSE
 // PASSES-NEXT:   (S) 0 num-cse'd - Number of operations CSE'd
 // PASSES-NEXT:   (S) 0 num-dce'd - Number of operations DCE'd
diff --git a/flang/test/Lower/HLFIR/binary-ops.f90 b/flang/test/Lower/HLFIR/binary-ops.f90
index 72cd048ea3615..1fbd333db37c3 100644
--- a/flang/test/Lower/HLFIR/binary-ops.f90
+++ b/flang/test/Lower/HLFIR/binary-ops.f90
@@ -168,7 +168,7 @@ subroutine complex_power(x, y, z)
 ! CHECK:  %[[VAL_5:.*]]:2 = hlfir.declare %{{.*}}z"} : (!fir.ref<complex<f32>>, !fir.dscope) -> (!fir.ref<complex<f32>>, !fir.ref<complex<f32>>)
 ! CHECK:  %[[VAL_6:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref<complex<f32>>
 ! CHECK:  %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<complex<f32>>
-! CHECK:  %[[VAL_8:.*]] = fir.call @cpowf(%[[VAL_6]], %[[VAL_7]]) fastmath<contract> : (complex<f32>, complex<f32>) -> complex<f32>
+! CHECK:  %[[VAL_8:.*]] = complex.pow %[[VAL_6]], %[[VAL_7]] fastmath<contract> : complex<f32>
 
 
 subroutine real_to_int_power(x, y, z)
@@ -193,7 +193,7 @@ subroutine complex_to_int_power(x, y, z)
 ! CHECK:  %[[VAL_5:.*]]:2 = hlfir.declare %{{.*}}z"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
 ! CHECK:  %[[VAL_6:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref<complex<f32>>
 ! CHECK:  %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<i32>
-! CHECK:  %[[VAL_8:.*]] = fir.call @_FortranAcpowi(%[[VAL_6]], %[[VAL_7]]) fastmath<contract> : (complex<f32>, i32) -> complex<f32>
+! CHECK:  %[[VAL_8:.*]] = complex.pow
 
 subroutine extremum(c, n, l)
   integer(8), intent(in) :: l
diff --git a/flang/test/Lower/Intrinsics/pow_complex16.f90 b/flang/test/Lower/Intrinsics/pow_complex16.f90
index 7467986832479..c026dd242e964 100644
--- a/flang/test/Lower/Intrinsics/pow_complex16.f90
+++ b/flang/test/Lower/Intrinsics/pow_complex16.f90
@@ -1,9 +1,10 @@
 ! REQUIRES: flang-supports-f128-math
 ! RUN: bbc -emit-fir %s -o - | FileCheck %s
-! RUN: bbc --math-runtime=precise -emit-fir %s -o - | FileCheck %s
+! RUN: bbc --math-runtime=precise -emit-fir %s -o - | FileCheck %s --check-prefixes="PRECISE"
 ! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
 
-! CHECK: fir.call @_FortranACPowF128({{.*}}){{.*}}: (complex<f128>, complex<f128>) -> complex<f128>
+! PRECISE: fir.call @_FortranACPowF128({{.*}}){{.*}}: (complex<f128>, complex<f128>) -> complex<f128>
+! CHECK: complex.pow %{{.*}}, %{{.*}} fastmath<contract> : complex<f128>
   complex(16) :: a, b
   b = a ** b
 end
diff --git a/flang/test/Lower/Intrinsics/pow_complex16i.f90 b/flang/test/Lower/Intrinsics/pow_complex16i.f90
index 6f8684d9a663a..1827863a57f43 100644
--- a/flang/test/Lower/Intrinsics/pow_complex16i.f90
+++ b/flang/test/Lower/Intrinsics/pow_complex16i.f90
@@ -1,9 +1,10 @@
 ! REQUIRES: flang-supports-f128-math
 ! RUN: bbc -emit-fir %s -o - | FileCheck %s
-! RUN: bbc --math-runtime=precise -emit-fir %s -o - | FileCheck %s
+! RUN: bbc --math-runtime=precise -emit-fir %s -o - | FileCheck %s --check-prefixes="PRECISE"
 ! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
 
-! CHECK: fir.call @_FortranAcqpowi({{.*}}){{.*}}: (complex<f128>, i32) -> complex<f128>
+! PRECISE: fir.call @_FortranAcqpowi({{.*}}){{.*}}: (complex<f128>, i32) -> complex<f128>
+! CHECK: complex.pow %{{.*}}, %{{.*}} fastmath<contract> : complex<f128>
   complex(16) :: a
   integer(4) :: b
   b = a ** b
diff --git a/flang/test/Lower/Intrinsics/pow_complex16k.f90 b/flang/test/Lower/Intrinsics/pow_complex16k.f90
index d3765050640ae..039dfd5152a06 100644
--- a/flang/test/Lower/Intrinsics/pow_complex16k.f90
+++ b/flang/test/Lower/Intrinsics/pow_complex16k.f90
@@ -1,9 +1,10 @@
 ! REQUIRES: flang-supports-f128-math
 ! RUN: bbc -emit-fir %s -o - | FileCheck %s
-! RUN: bbc --math-runtime=precise -emit-fir %s -o - | FileCheck %s
+! RUN: bbc --math-runtime=precise -emit-fir %s -o - | FileCheck %s --check-prefixes="PRECISE"
 ! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
 
-! CHECK: fir.call @_FortranAcqpowk({{.*}}){{.*}}: (complex<f128>, i64) -> complex<f128>
+! PRECISE: fir.call @_FortranAcqpowk({{.*}}){{.*}}: (complex<f128>, i64) -> complex<f128>
+! CHECK: complex.pow %{{.*}}, %{{.*}} fastmath<contract> : complex<f128>
   complex(16) :: a
   integer(8) :: b
   b = a ** b
diff --git a/flang/test/Lower/power-operator.f90 b/flang/test/Lower/power-operator.f90
index 7436e031d20cb..3058927144248 100644
--- a/flang/test/Lower/power-operator.f90
+++ b/flang/test/Lower/power-operator.f90
@@ -1,10 +1,10 @@
-! RUN: bbc -emit-fir %s -o - | FileCheck %s --check-prefixes="CHECK,PRECISE"
-! RUN: bbc --math-runtime=precise -emit-fir %s -o - | FileCheck %s --check-prefixes="PRECISE"
-! RUN: bbc --force-mlir-complex -emit-fir %s -o - | FileCheck %s --check-prefixes="FAST"
-! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s --check-prefixes="CHECK,PRECISE"
-! RUN: %flang_fc1 -fapprox-func -emit-fir %s -o - | FileCheck %s --check-prefixes="CHECK,FAST"
-! RUN: %flang_fc1 -emit-fir -mllvm --math-runtime=precise %s -o - | FileCheck %s --check-prefixes="PRECISE"
-! RUN: %flang_fc1 -emit-fir -mllvm --force-mlir-complex %s -o - | FileCheck %s --check-prefixes="FAST"
+! RUN: bbc -emit-fir %s -o - | FileCheck %s
+! RUN: bbc --math-runtime=precise -emit-fir %s -o - | FileCheck %s --check-prefix=PRECISE
+! RUN: bbc --force-mlir-complex -emit-fir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -fapprox-func -emit-fir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-fir -mllvm --math-runtime=precise %s -o - | FileCheck %s --check-prefix=PRECISE
+! RUN: %flang_fc1 -emit-fir -mllvm --force-mlir-complex %s -o - | FileCheck %s
 
 ! Test power operation lowering
 
@@ -96,7 +96,8 @@ subroutine pow_c4_i4(x, y, z)
   complex :: x, z
   integer :: y
   z = x ** y
-  ! CHECK: call @_FortranAcpowi
+  ! CHECK: complex.pow
+  ! PRECISE: fir.call @_FortranAcpowi
 end subroutine
 
 ! CHECK-LABEL: pow_c4_i8
@@ -104,7 +105,8 @@ subroutine pow_c4_i8(x, y, z)
   complex :: x, z
   integer(8) :: y
   z = x ** y
-  ! CHECK: call @_FortranAcpowk
+  ! CHECK: complex.pow
+  ! PRECISE: fir.call @_FortranAcpowk
 end subroutine
 
 ! CHECK-LABEL: pow_c8_i4
@@ -112,7 +114,8 @@ subroutine pow_c8_i4(x, y, z)
   complex(8) :: x, z
   integer :: y
   z = x ** y
-  ! CHECK: call @_FortranAzpowi
+  ! CHECK: complex.pow
+  ! PRECISE: fir.call @_FortranAzpowi
 end subroutine
 
 ! CHECK-LABEL: pow_c8_i8
@@ -120,22 +123,23 @@ subroutine pow_c8_i8(x, y, z)
   complex(8) :: x, z
   integer(8) :: y
   z = x ** y
-  ! CHECK: call @_FortranAzpowk
+  ! CHECK: complex.pow
+  ! PRECISE: fir.call @_FortranAzpowk
 end subroutine
 
 ! CHECK-LABEL: pow_c4_c4
 subroutine pow_c4_c4(x, y, z)
   complex :: x, y, z
   z = x ** y
-  ! FAST: complex.pow %{{.*}}, %{{.*}} : complex<f32>
-  ! PRECISE: call @cpowf
+  ! CHECK: complex.pow %{{.*}}, %{{.*}} : complex<f32>
+  ! PRECISE: fir.call @cpowf
 end subroutine
 
 ! CHECK-LABEL: pow_c8_c8
 subroutine pow_c8_c8(x, y, z)
   complex(8) :: x, y, z
   z = x ** y
-  ! FAST: complex.pow %{{.*}}, %{{.*}} : complex<f64>
-  ! PRECISE: call @cpow
+  ! CHECK: complex.pow %{{.*}}, %{{.*}} : complex<f64>
+  ! PRECISE: fir.call @cpow
 end subroutine
 
diff --git a/flang/test/Transforms/convert-complex-pow.fir b/flang/test/Transforms/convert-complex-pow.fir
new file mode 100644
index 0000000000000..d980817aba9b9
--- /dev/null
+++ b/flang/test/Transforms/convert-complex-pow.fir
@@ -0,0 +1,102 @@
+// RUN: fir-opt --convert-complex-pow %s | FileCheck %s
+
+module {
+  func.func @pow_c4_i4(%arg0: complex<f32>, %arg1: i32) -> complex<f32> {
+    %c0 = arith.constant 0.000000e+00 : f32
+    %c1 = fir.convert %arg1 : (i32) -> f32
+    %c2 = complex.create %c1, %c0 : complex<f32>
+    %0 = complex.pow %arg0, %c2 : complex<f32>
+    return %0 : complex<f32>
+  }
+
+  func.func @pow_c4_i8(%arg0: complex<f32>, %arg1: i64) -> complex<f32> {
+    %c0 = arith.constant 0.000000e+00 : f32
+    %c1 = fir.convert %arg1 : (i64) -> f32
+    %c2 = complex.create %c1, %c0 : complex<f32>
+    %0...
[truncated]

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a new ConvertComplexPow pass for Flang that handles complex power operations. The change forces lowering to complex.pow operations when --math-runtime=precise is not used, then provides a new pass to convert these operations back to library calls.

  • Adds a new ConvertComplexPow pass that converts complex.pow ops to appropriate runtime library calls
  • Updates complex power lowering to use complex.pow operations by default instead of direct library calls
  • Updates test expectations to reflect the new lowering behavior

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
flang/lib/Optimizer/Transforms/ConvertComplexPow.cpp New pass implementation that converts complex.pow ops to library calls
flang/lib/Optimizer/Builder/IntrinsicCall.cpp Updated genComplexPow to generate complex.pow ops instead of direct calls
flang/lib/Optimizer/Passes/Pipelines.cpp Integrated the new pass into the compilation pipeline
flang/include/flang/Optimizer/Transforms/Passes.td Added pass definition and documentation
flang/lib/Optimizer/Transforms/CMakeLists.txt Added new source file to build
flang/test/Transforms/convert-complex-pow.fir New test file for the ConvertComplexPow pass
Multiple test files Updated to reflect new lowering behavior and test expectations

Comment on lines +103 to +104
else
return;
Copy link
Preview

Copilot AI Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Early returns without error handling or logging make debugging difficult. Consider adding a diagnostic message or comment explaining why these combinations are unsupported.

Copilot uses AI. Check for mistakes.

Comment on lines +116 to +117
else
return;
Copy link
Preview

Copilot AI Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Early return without error handling or logging makes debugging difficult. Consider adding a diagnostic message or comment explaining why this bit width is unsupported.

Suggested change
else
return;
else {
emitWarning(loc, "Unsupported complex.pow bit width: realBits=" +
std::to_string(realBits));
return;
}

Copilot uses AI. Check for mistakes.

Copy link
Contributor

@vzakhari vzakhari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the changes! Please see my comments inlined.

// CHECK-NOT: complex.pow

// CHECK-LABEL: func.func @pow_c4_c4(
// CHECK: fir.call @cpowf(%{{.*}}, %{{.*}}) : (complex<f32>, complex<f32>) -> complex<f32>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please verify that the fast-math flags are properly propagated from complex.pow to the call?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


void ConvertComplexPowPass::runOnOperation() {
ModuleOp mod = getOperation();
if (fir::getTargetTriple(mod).isAMDGCN())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please make the pass generic, and instead not schedule it for AMDGCN in the Pipelines.cpp?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return;
args = {base, intExp};
} else {
unsigned realBits = cast<FloatType>(elemTy).getWidth();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am really worried about dropping the imaginary part for these cases. Imagine, somewhere in Flang we start generating complex.pow with a true complex exponent. This pass will just silently drop it and produce incorrect code. Please add a LIT test for this case.

I think we need to keep complex.pow if we cannot prove that the imaginary part is zero.

Ideally, we should have powi and powf operations in the complex dialect, so that we do not have to rely on the particular fir.convert/complex.create pattern generated by the lowering. Moreover, SSA values may become block arguments making it harder to recognize the specific pattern even more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we should have powi and powf operations in the complex dialect, so that we do not have to rely on the particular fir.convert/complex.create pattern generated by the lowering. Moreover, SSA values may become block arguments making it harder to recognize the specific pattern even more.

I have added powi in #158722. I'll address the rest of this comment along with other comments tomorrow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding powi! I think adding powf is not required. I just misread the code.

genComplexPow},
{"pow", RTNAME_STRING(cqpowi), FuncTypeComplex16Complex16Integer4,
genLibF128Call},
genComplexPow},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you have complex.powi, I think we can just use genComplexMathOp<mlir::complex::powi> or genMathOp<mlir::complex::powi> here.

We can probably get rid of genComplexPow and use genMathOp instead.

Copy link
Member Author

@TIFitis TIFitis Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

genComplexMathOp would lower to libCall if (!forceMlirComplex && !canUseApprox && !isAMDGPU)
which means we would restrict lowering to complex.pow for some cases where we are currently forcing it. Is that okay?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if (!forceMlirComplex && !canUseApprox && !isAMDGPU) check is yet another workaround that has to be removed eventually (not in this PR).

I think genMathOp should work here just fine or am I missing something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I glossed over genMathOp, I've added this change in #158722 to removed genComplexPow.

Propagate fast-math attributes, update tests.
Fix Targetmachine build error.
Copy link
Contributor

@vzakhari vzakhari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM assuming that the final code will look like in #158722.

@TIFitis TIFitis merged commit 54677d6 into main Sep 19, 2025
10 checks passed
@TIFitis TIFitis deleted the users/Akash/complex_lowering branch September 19, 2025 00:51
TIFitis added a commit that referenced this pull request Sep 19, 2025
This PR adds a new complex.powi operation to MLIR's complex dialect for
computing complex numbers raised to integer powers.

Key changes include:

- Addition of the new `PowiOp` operation definition in the Complex
dialect
- Integration with algebraic simplification passes for optimization
- Support for conversion to ROCDL library calls
- Updates to Flang frontend to generate the new operation

This depends on #158642.
@luporl
Copy link
Contributor

luporl commented Sep 22, 2025

flang/test/Transforms/convert-complex-pow.fir is causing a crash on macOS:

FAIL: Flang :: Transforms/convert-complex-pow.fir (1 of 1)
******************** TEST 'Flang :: Transforms/convert-complex-pow.fir' FAILED ********************
Exit Code: 2

Command Output (stdout):
--
# RUN: at line 1
fir-opt --convert-complex-pow /Users/leandro.lupori/home/git/flang/llvm-project/flang/test/Transforms/convert-complex-pow.fir | /Users/leandro.lupori/home/git/flang/build/bin/FileCheck /Users/leandro.lupori/home/git/flang/llvm-project/flang/test/Transforms/convert-complex-pow.fir
# executed command: fir-opt --convert-complex-pow /Users/leandro.lupori/home/git/flang/llvm-project/flang/test/Transforms/convert-complex-pow.fir
# .---command stderr------------
# | PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
# | Stack dump:
# | 0.	Program arguments: fir-opt --convert-complex-pow /Users/leandro.lupori/home/git/flang/llvm-project/flang/test/Transforms/convert-complex-pow.fir
# |  #0 0x0000000106274464 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x101808464)
# |  #1 0x000000010627226c llvm::sys::RunSignalHandlers() (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x10180626c)
# |  #2 0x0000000106274f5c SignalHandler(int, __siginfo*, void*) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x101808f5c)
# |  #3 0x000000018db5d6a4 (/usr/lib/system/libsystem_platform.dylib+0x1804ad6a4)
# |  #4 0x0000000104c79a7c void llvm::function_ref<void (mlir::Operation*)>::callback_fn<(anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0>(long, mlir::Operation*) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x10020da7c)
# |  #5 0x0000000104c79a7c void llvm::function_ref<void (mlir::Operation*)>::callback_fn<(anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0>(long, mlir::Operation*) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x10020da7c)
# |  #6 0x0000000104bf5784 void mlir::detail::walk<mlir::ForwardIterator>(mlir::Operation*, llvm::function_ref<void (mlir::Operation*)>, mlir::WalkOrder) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x100189784)
# |  #7 0x0000000104bf5784 void mlir::detail::walk<mlir::ForwardIterator>(mlir::Operation*, llvm::function_ref<void (mlir::Operation*)>, mlir::WalkOrder) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x100189784)
# |  #8 0x0000000104c794c0 (anonymous namespace)::ConvertComplexPowPass::runOnOperation() (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x10020d4c0)
# |  #9 0x0000000105da747c mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x10133b47c)
# | #10 0x0000000105da7c9c mlir::detail::OpToOpPassAdaptor::runPipeline(mlir::OpPassManager&, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int, mlir::PassInstrumentor*, mlir::PassInstrumentation::PipelineParentInfo const*) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x10133bc9c)
# | #11 0x0000000105dade60 mlir::PassManager::runPasses(mlir::Operation*, mlir::AnalysisManager) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x101341e60)
# | #12 0x0000000105dad5dc mlir::PassManager::run(mlir::Operation*) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x1013415dc)
# | #13 0x0000000105e25378 performActions(llvm::raw_ostream&, std::__1::shared_ptr<llvm::SourceMgr> const&, mlir::MLIRContext*, mlir::MlirOptMainConfig const&) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x1013b9378)
# | #14 0x0000000105e24cf0 llvm::LogicalResult llvm::function_ref<llvm::LogicalResult (std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, llvm::MemoryBufferRef const&, llvm::raw_ostream&)>::callback_fn<mlir::MlirOptMain(llvm::raw_ostream&, std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&)::$_0>(long, std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, llvm::MemoryBufferRef const&, llvm::raw_ostream&) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x1013b8cf0)
# | #15 0x0000000105e08b28 mlir::splitAndProcessBuffer(std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, llvm::MemoryBufferRef const&, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x10139cb28)
# | #16 0x0000000105e1d96c mlir::MlirOptMain(llvm::raw_ostream&, std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x1013b196c)
# | #17 0x0000000105e1dce0 mlir::MlirOptMain(int, char**, llvm::StringRef, llvm::StringRef, mlir::DialectRegistry&) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x1013b1ce0)
# | #18 0x0000000105e1df0c mlir::MlirOptMain(int, char**, llvm::StringRef, mlir::DialectRegistry&) (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x1013b1f0c)
# | #19 0x0000000104a6dabc main (/Users/leandro.lupori/home/git/flang/build/bin/fir-opt+0x100001abc)
# | #20 0x000000018d782b98
# `-----------------------------
# error: command failed with exit status: -11
# executed command: /Users/leandro.lupori/home/git/flang/build/bin/FileCheck /Users/leandro.lupori/home/git/flang/llvm-project/flang/test/Transforms/convert-complex-pow.fir
# .---command stderr------------
# | FileCheck error: '<stdin>' is empty.
# | FileCheck command line:  /Users/leandro.lupori/home/git/flang/build/bin/FileCheck /Users/leandro.lupori/home/git/flang/llvm-project/flang/test/Transforms/convert-complex-pow.fir
# `-----------------------------
# error: command failed with exit status: 2

--

@TIFitis
Copy link
Member Author

TIFitis commented Sep 22, 2025

@luporl Can you point me to the build bot failure for this so I can investigate further? Thanks.

Also any chance that fdb1f48 fixes it? All the build bots look green for it, except one broken build.

@luporl
Copy link
Contributor

luporl commented Sep 22, 2025

@luporl Can you point me to the build bot failure for this so I can investigate further? Thanks.

I noticed this when updating flang on my laptop. I'll try to get a stacktrace of flang with debug info enabled.

Also any chance that fdb1f48 fixes it? All the build bots look green for it, except one broken build.

Unfortunately it doesn't fix it.

EDIT: I confused this crash with another issue related to lld. Removed the non-related lld comments.

@luporl
Copy link
Contributor

luporl commented Sep 22, 2025

PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
Stack dump:
0.	Program arguments: ../build_flang/bin/fir-opt --convert-complex-pow /Users/leandro.lupori/home/git/flang/llvm-project/flang/test/Transforms/convert-complex-pow.fir
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  fir-opt                  0x000000010348bc78 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  fir-opt                  0x0000000103489a04 llvm::sys::RunSignalHandlers() + 172
2  fir-opt                  0x000000010348c770 SignalHandler(int, __siginfo*, void*) + 360
3  libsystem_platform.dylib 0x000000018db5d6a4 _sigtramp + 56
4  fir-opt                  0x00000001006d0040 mlir::TypeID::operator!=(mlir::TypeID const&) const + 32
5  fir-opt                  0x00000001006d0014 mlir::OperationName::Impl::isRegistered() const + 48
6  fir-opt                  0x00000001006cff2c mlir::OperationName::isRegistered() const + 28
7  fir-opt                  0x00000001006cfec4 mlir::OperationName::getRegisteredInfo() const + 28
8  fir-opt                  0x00000001006cfd38 mlir::Operation::getRegisteredInfo() + 56
9  fir-opt                  0x0000000100ca6364 mlir::Op<mlir::complex::PowOp, mlir::OpTrait::ZeroRegions, mlir::OpTrait::OneResult, mlir::OpTrait::OneTypedResult<mlir::ComplexType>::Impl, mlir::OpTrait::ZeroSuccessors, mlir::OpTrait::NOperands<2u>::Impl, mlir::OpTrait::OpInvariants, mlir::BytecodeOpInterface::Trait, mlir::ConditionallySpeculatable::Trait, mlir::OpTrait::AlwaysSpeculatableImplTrait, mlir::MemoryEffectOpInterface::Trait, mlir::OpTrait::SameOperandsAndResultType, mlir::OpTrait::Elementwise, mlir::arith::ArithFastMathInterface::Trait, mlir::InferTypeOpInterface::Trait>::classof(mlir::Operation*) + 52
10 fir-opt                  0x0000000100ca62d0 llvm::CastInfo<mlir::complex::PowOp, mlir::Operation*, void>::isPossible(mlir::Operation*) + 24
11 fir-opt                  0x0000000100ca6284 llvm::DefaultDoCastIfPossible<mlir::complex::PowOp, mlir::Operation*, llvm::CastInfo<mlir::complex::PowOp, mlir::Operation*, void>>::doCastIfPossible(mlir::Operation*) + 24
12 fir-opt                  0x0000000100ca5668 decltype(auto) llvm::dyn_cast<mlir::complex::PowOp, mlir::Operation>(mlir::Operation*) + 112
13 fir-opt                  0x0000000100ca4dac (anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0::operator()(mlir::Operation*) const + 1424
14 fir-opt                  0x0000000100ca4810 void llvm::function_ref<void (mlir::Operation*)>::callback_fn<(anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0>(long, mlir::Operation*) + 32
15 fir-opt                  0x0000000100b201b8 llvm::function_ref<void (mlir::Operation*)>::operator()(mlir::Operation*) const + 40
16 fir-opt                  0x0000000100b20118 void mlir::detail::walk<mlir::ForwardIterator>(mlir::Operation*, llvm::function_ref<void (mlir::Operation*)>, mlir::WalkOrder) + 464
17 fir-opt                  0x0000000100b200c8 void mlir::detail::walk<mlir::ForwardIterator>(mlir::Operation*, llvm::function_ref<void (mlir::Operation*)>, mlir::WalkOrder) + 384
18 fir-opt                  0x0000000100b200c8 void mlir::detail::walk<mlir::ForwardIterator>(mlir::Operation*, llvm::function_ref<void (mlir::Operation*)>, mlir::WalkOrder) + 384
19 fir-opt                  0x0000000100ca476c std::__1::enable_if<llvm::is_one_of<mlir::Operation*, mlir::Operation*, mlir::Region*, mlir::Block*>::value, void>::type mlir::detail::walk<(mlir::WalkOrder)1, mlir::ForwardIterator, (anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0, mlir::Operation*, void>(mlir::Operation*, (anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0&&) + 68
20 fir-opt                  0x0000000100ca471c std::__1::enable_if<llvm::function_traits<__decay((anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0)>::num_args == 1, void>::type mlir::Operation::walk<(mlir::WalkOrder)1, mlir::ForwardIterator, (anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0, void>((anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0&&) + 32
21 fir-opt                  0x0000000100ca46f0 std::__1::enable_if<llvm::function_traits<__decay((anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0)>::num_args == 1, void>::type mlir::OpState::walk<(mlir::WalkOrder)1, mlir::ForwardIterator, (anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0, void>((anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0&&) + 36
22 fir-opt                  0x0000000100ca448c (anonymous namespace)::ConvertComplexPowPass::runOnOperation() + 128
23 fir-opt                  0x00000001026fc67c mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int) + 1336
24 fir-opt                  0x00000001026fcff8 mlir::detail::OpToOpPassAdaptor::runPipeline(mlir::OpPassManager&, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int, mlir::PassInstrumentor*, mlir::PassInstrumentation::PipelineParentInfo const*) + 608
25 fir-opt                  0x00000001027033c4 mlir::PassManager::runPasses(mlir::Operation*, mlir::AnalysisManager) + 460
26 fir-opt                  0x0000000102702b40 mlir::PassManager::run(mlir::Operation*) + 5724
27 fir-opt                  0x00000001027989c0 performActions(llvm::raw_ostream&, std::__1::shared_ptr<llvm::SourceMgr> const&, mlir::MLIRContext*, mlir::MlirOptMainConfig const&) + 1524
28 fir-opt                  0x0000000102798338 llvm::LogicalResult llvm::function_ref<llvm::LogicalResult (std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, llvm::MemoryBufferRef const&, llvm::raw_ostream&)>::callback_fn<mlir::MlirOptMain(llvm::raw_ostream&, std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&)::$_0>(long, std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, llvm::MemoryBufferRef const&, llvm::raw_ostream&) + 804
29 fir-opt                  0x0000000102777a24 mlir::splitAndProcessBuffer(std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, llvm::MemoryBufferRef const&, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef) + 596
30 fir-opt                  0x000000010278eb9c mlir::MlirOptMain(llvm::raw_ostream&, std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&) + 432
31 fir-opt                  0x000000010278ef10 mlir::MlirOptMain(int, char**, llvm::StringRef, llvm::StringRef, mlir::DialectRegistry&) + 384
32 fir-opt                  0x000000010278f13c mlir::MlirOptMain(int, char**, llvm::StringRef, mlir::DialectRegistry&) + 144
33 fir-opt                  0x00000001005fddbc main + 148
34 dyld                     0x000000018d782b98 start + 6076
Segmentation fault: 11     ../build_flang/bin/fir-opt --convert-complex-pow /Users/leandro.lupori/home/git/flang/llvm-project/flang/test/Transforms/convert-complex-pow.fir

The op argument In ConvertComplexPowPass::runOnOperation() doesn't look right and ends up causing a null pointer dereference:

(lldb) f
frame #10: 0x00000001006a8dac fir-opt`(anonymous namespace)::ConvertComplexPowPass::runOnOperation()::$_0::operator()(this=0x000000016fdfd6b0, op=0x0000600003e2c010) const at ConvertComplexPow.cpp:88:22
   85  	      powIop.erase();
   86  	    }
   87
-> 88  	    if (auto powOp = dyn_cast<complex::PowOp>(op)) {
   89  	      builder.setInsertionPoint(powOp);
   90  	      Location loc = powOp.getLoc();
   91  	      auto complexTy = cast<ComplexType>(powOp.getType());
(lldb) p *op
(mlir::Operation) {
  llvm::ilist_node_with_parent<mlir::Operation, mlir::Block> = {
    llvm::ilist_node<mlir::Operation> = {
      llvm::ilist_node_impl<llvm::ilist_detail::node_options<mlir::Operation, true, false, void, false, void> > = {
        llvm::ilist_detail::node_options<mlir::Operation, true, false, void, false, void>::node_base_type = {
          llvm::ilist_detail::node_base_prevnext<llvm::ilist_node_base<true, void>, true> = {
            PrevAndSentinel ={...}
            Next = nullptr{...}
          }
        }
      }
    }
  }
  block = nullptr
  location = {
    impl = {
      mlir::Attribute = {
        impl = nullptr
      }
    }
  }
  orderIndex = 0
  numResults = 0
  numSuccs = 0
  numRegions = 0
  hasOperandStorage = false
  propertiesStorageSize = '\0'
  name = {
    impl = nullptr
  }
  attrs = {
    mlir::Attribute::AttrBase<DictionaryAttr, ::mlir::Attribute, detail::DictionaryAttrStorage> = {
      mlir::Attribute = {
        impl = nullptr
      }
    }
  }
}

@luporl
Copy link
Contributor

luporl commented Sep 22, 2025

This change fixes the issue for me:

--- a/flang/lib/Optimizer/Transforms/ConvertComplexPow.cpp
+++ b/flang/lib/Optimizer/Transforms/ConvertComplexPow.cpp
@@ -83,9 +83,7 @@ void ConvertComplexPowPass::runOnOperation() {
         call.setFastmathAttr(fmf);
       powIop.replaceAllUsesWith(call.getResult(0));
       powIop.erase();
-    }
-
-    if (auto powOp = dyn_cast<complex::PowOp>(op)) {
+    } else if (auto powOp = dyn_cast<complex::PowOp>(op)) {
       builder.setInsertionPoint(powOp);
       Location loc = powOp.getLoc();
       auto complexTy = cast<ComplexType>(powOp.getType());

@TIFitis
Copy link
Member Author

TIFitis commented Sep 22, 2025

@luporl Thanks for the stack trace. I'll report back on this tomorrow.

@TIFitis
Copy link
Member Author

TIFitis commented Sep 23, 2025

@luporl Fix pushed in 79a0bf0.

@luporl
Copy link
Contributor

luporl commented Sep 23, 2025

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:driver flang:fir-hlfir flang Flang issues not falling into any other category mlir
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants