llvm/mlir/test/Dialect/Func/duplicate-function-elimination.mlir

// RUN: mlir-opt %s --split-input-file --duplicate-function-elimination | \
// RUN: FileCheck %s

func.func @identity(%arg0: tensor<f32>) -> tensor<f32> {
  return %arg0 : tensor<f32>
}

func.func @also_identity(%arg0: tensor<f32>) -> tensor<f32> {
  return %arg0 : tensor<f32>
}

func.func @yet_another_identity(%arg0: tensor<f32>) -> tensor<f32> {
  return %arg0 : tensor<f32>
}

func.func @user(%arg0: tensor<f32>) -> tensor<f32> {
  %0 = call @identity(%arg0) : (tensor<f32>) -> tensor<f32>
  %1 = call @also_identity(%0) : (tensor<f32>) -> tensor<f32>
  %2 = call @yet_another_identity(%1) : (tensor<f32>) -> tensor<f32>
  return %2 : tensor<f32>
}

// CHECK:     @identity
// CHECK-NOT: @also_identity
// CHECK-NOT: @yet_another_identity
// CHECK:     @user
// CHECK-3:     call @identity

// -----

func.func @add_lr(%arg0: f32, %arg1: f32) -> f32 {
  %0 = arith.addf %arg0, %arg1 : f32
  return %0 : f32
}

func.func @also_add_lr(%arg0: f32, %arg1: f32) -> f32 {
  %0 = arith.addf %arg0, %arg1 : f32
  return %0 : f32
}

func.func @add_rl(%arg0: f32, %arg1: f32) -> f32 {
  %0 = arith.addf %arg1, %arg0 : f32
  return %0 : f32
}

func.func @also_add_rl(%arg0: f32, %arg1: f32) -> f32 {
  %0 = arith.addf %arg1, %arg0 : f32
  return %0 : f32
}

func.func @user(%arg0: f32, %arg1: f32) -> f32 {
  %0 = call @add_lr(%arg0, %arg1) : (f32, f32) -> f32
  %1 = call @also_add_lr(%arg0, %arg1) : (f32, f32) -> f32
  %2 = call @add_rl(%0, %1) : (f32, f32) -> f32
  %3 = call @also_add_rl(%arg0, %2) : (f32, f32) -> f32
 return %3 : f32
}

// CHECK:     @add_lr
// CHECK-NOT: @also_add_lr
// CHECK:     @add_rl
// CHECK-NOT: @also_add_rl
// CHECK:     @user
// CHECK-2:     call @add_lr
// CHECK-2:     call @add_rl

// -----

func.func @ite(%pred: i1, %then: f32, %else: f32) -> f32 {
  %0 = scf.if %pred -> f32 {
    scf.yield %then : f32
  } else {
    scf.yield %else : f32
  }
  return %0 : f32
}

func.func @also_ite(%pred: i1, %then: f32, %else: f32) -> f32 {
  %0 = scf.if %pred -> f32 {
    scf.yield %then : f32
  } else {
    scf.yield %else : f32
  }
  return %0 : f32
}

func.func @reverse_ite(%pred: i1, %then: f32, %else: f32) -> f32 {
  %0 = scf.if %pred -> f32 {
    scf.yield %else : f32
  } else {
    scf.yield %then : f32
  }
  return %0 : f32
}

func.func @user(%pred : i1, %arg0: f32, %arg1: f32) -> f32 {
  %0 = call @also_ite(%pred, %arg0, %arg1) : (i1, f32, f32) -> f32
  %1 = call @ite(%pred, %arg0, %arg1) : (i1, f32, f32) -> f32
  %2 = call @reverse_ite(%pred, %0, %1) : (i1, f32, f32) -> f32
 return %2 : f32
}

// CHECK:     @ite
// CHECK-NOT: @also_ite
// CHECK:     @reverse_ite
// CHECK:     @user
// CHECK-2:     call @ite
// CHECK:       call @reverse_ite

// -----

func.func @deep_tree(%p0: i1, %p1: i1, %p2: i1, %p3: i1, %even: f32, %odd: f32)
    -> f32 {
  %0 = scf.if %p0 -> f32 {
    %1 = scf.if %p1 -> f32 {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    } else {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    }
    scf.yield %1 : f32
  } else {
    %1 = scf.if %p1 -> f32 {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    } else {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    }
    scf.yield %1 : f32
  }
  return %0 : f32
}

func.func @also_deep_tree(%p0: i1, %p1: i1, %p2: i1, %p3: i1, %even: f32,
    %odd: f32) -> f32 {
  %0 = scf.if %p0 -> f32 {
    %1 = scf.if %p1 -> f32 {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    } else {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    }
    scf.yield %1 : f32
  } else {
    %1 = scf.if %p1 -> f32 {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    } else {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    }
    scf.yield %1 : f32
  }
  return %0 : f32
}

func.func @reverse_deep_tree(%p0: i1, %p1: i1, %p2: i1, %p3: i1, %even: f32,
    %odd: f32) -> f32 {
  %0 = scf.if %p0 -> f32 {
    %1 = scf.if %p1 -> f32 {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    } else {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    }
    scf.yield %1 : f32
  } else {
    %1 = scf.if %p1 -> f32 {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    } else {
      %2 = scf.if %p2 -> f32 {
        %3 = scf.if %p3 -> f32 {
          scf.yield %odd : f32
        } else {
          scf.yield %even : f32
        }
        scf.yield %3 : f32
      } else {
        %3 = scf.if %p3 -> f32 {
          scf.yield %even : f32
        } else {
          scf.yield %odd : f32
        }
        scf.yield %3 : f32
      }
      scf.yield %2 : f32
    }
    scf.yield %1 : f32
  }
  return %0 : f32
}

func.func @user(%p0: i1, %p1: i1, %p2: i1, %p3: i1, %odd: f32, %even: f32)
    -> (f32, f32, f32) {
  %0 = call @deep_tree(%p0, %p1, %p2, %p3, %odd, %even)
      : (i1, i1, i1, i1, f32, f32) -> f32
  %1 = call @also_deep_tree(%p0, %p1, %p2, %p3, %odd, %even)
      : (i1, i1, i1, i1, f32, f32) -> f32
  %2 = call @reverse_deep_tree(%p0, %p1, %p2, %p3, %odd, %even)
      : (i1, i1, i1, i1, f32, f32) -> f32
  return %0, %1, %2 : f32, f32, f32
}

// CHECK:     @deep_tree
// CHECK-NOT: @also_deep_tree
// CHECK:     @reverse_deep_tree
// CHECK:     @user
// CHECK-2:     call @deep_tree
// CHECK:       call @reverse_deep_tree