[llvm-branch-commits] [flang] 85ec3af - Revert "[flang][mem2reg] promote memory slots through declares (#196975)"
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jun 17 03:12:14 PDT 2026
Author: jeanPerier
Date: 2026-06-17T12:12:10+02:00
New Revision: 85ec3af65318d5efe5922932ac5fc80ab518e602
URL: https://github.com/llvm/llvm-project/commit/85ec3af65318d5efe5922932ac5fc80ab518e602
DIFF: https://github.com/llvm/llvm-project/commit/85ec3af65318d5efe5922932ac5fc80ab518e602.diff
LOG: Revert "[flang][mem2reg] promote memory slots through declares (#196975)"
This reverts commit c1ec4b3c79967ae5ef824f7194540f6529405a03.
Added:
Modified:
flang/include/flang/Optimizer/Dialect/FIROps.h
flang/include/flang/Optimizer/Dialect/FIROps.td
flang/lib/Optimizer/Dialect/FIROps.cpp
flang/test/Fir/mem2reg.mlir
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.h b/flang/include/flang/Optimizer/Dialect/FIROps.h
index 54aa3d58abe8c..9d771b1e60604 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.h
@@ -19,7 +19,6 @@
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
#include "mlir/Interfaces/LoopLikeInterface.h"
-#include "mlir/Interfaces/MemorySlotInterfaces.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "mlir/Interfaces/ViewLikeInterface.h"
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 5a17db1c0ef1e..35a87c29d0cb6 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -3070,15 +3070,9 @@ def fir_VolatileCastOp
}
def fir_ConvertOp
- : fir_SimpleOneResultOp<"convert",
- [NoMemoryEffect, ViewLikeOpInterface,
- ConditionallySpeculatable,
- DeclareOpInterfaceMethods<PromotableOpInterface>,
- DeclareOpInterfaceMethods<
- PromotableAliaserInterface,
- ["projectSlotValueToAliasValue",
- "projectAliasValueToSlotValue"]>,
- fir_FortranObjectViewOpInterface]> {
+ : fir_SimpleOneResultOp<"convert", [NoMemoryEffect, ViewLikeOpInterface,
+ ConditionallySpeculatable,
+ fir_FortranObjectViewOpInterface]> {
let summary = "encapsulates all Fortran entity type conversions";
let description = [{
@@ -3607,7 +3601,6 @@ def fir_DeclareOp
DeclareOpInterfaceMethods<PromotableOpInterface,
["requiresReplacedValues",
"visitReplacedValues"]>,
- DeclareOpInterfaceMethods<PromotableAliaserInterface>,
fir_FortranObjectViewOpInterface]> {
let summary = "declare a variable";
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 6a64b76736cf5..1d61989eba6cf 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -2012,89 +2012,6 @@ void fir::ConvertOp::getCanonicalizationPatterns(
context);
}
-// Returns the pointee element type of a FIR reference-like type or a memref;
-// returns null if `ty` is not pointer-like or has no extractable element
-// type (e.g. unranked memref, box).
-static mlir::Type slotPointeeElemType(mlir::Type ty) {
- if (mlir::Type fEle = fir::dyn_cast_ptrEleTy(ty))
- return fEle;
- if (auto memrefTy = mlir::dyn_cast<mlir::MemRefType>(ty))
- return memrefTy.getElementType();
- return {};
-}
-
-// Returns true if a `fir.convert` between pointers with element types
-// `srcElem` and `dstElem` is a no-op at the value level or can be replaced by
-// a bicast.
-static bool isAcceptableSlotElemTypePair(mlir::Type srcElem,
- mlir::Type dstElem) {
- if (srcElem == dstElem)
- return true;
- if (!isBitcastCompatibleType(srcElem) || !isBitcastCompatibleType(dstElem))
- return false;
- auto srcBits = getBitcastBitSize(srcElem);
- auto dstBits = getBitcastBitSize(dstElem);
- return srcBits && dstBits && *srcBits == *dstBits;
-}
-
-static bool isPromotableSlotAliasConvert(fir::ConvertOp op) {
- mlir::Type srcElem = slotPointeeElemType(op.getValue().getType());
- mlir::Type dstElem = slotPointeeElemType(op.getResult().getType());
- return srcElem && dstElem && isAcceptableSlotElemTypePair(srcElem, dstElem);
-}
-
-void fir::ConvertOp::getPromotableSlotAliases(
- mlir::OpOperand &aliasedSlotPointerOperand,
- const mlir::MemorySlot & /*parentSlot*/,
- llvm::SmallVectorImpl<mlir::MemorySlot> &newMemorySlots) {
- if (aliasedSlotPointerOperand.get() != getValue())
- return;
- if (!isPromotableSlotAliasConvert(*this))
- return;
- mlir::Type dstElem = slotPointeeElemType(getResult().getType());
- newMemorySlots.push_back(mlir::MemorySlot{getResult(), dstElem});
-}
-
-mlir::Value fir::ConvertOp::projectSlotValueToAliasValue(
- mlir::OpOperand & /*aliasedSlotPointerOperand*/,
- const mlir::MemorySlot & /*parentSlot*/, const mlir::MemorySlot &aliasSlot,
- mlir::Value slotValue, mlir::OpBuilder &builder) {
- if (slotValue.getType() == aliasSlot.elemType)
- return slotValue;
- return fir::BitcastOp::create(builder, getLoc(), aliasSlot.elemType,
- slotValue);
-}
-
-mlir::Value fir::ConvertOp::projectAliasValueToSlotValue(
- mlir::OpOperand & /*aliasedSlotPointerOperand*/,
- const mlir::MemorySlot &parentSlot, const mlir::MemorySlot & /*aliasSlot*/,
- mlir::Value aliasValue, mlir::Value /*reachingDef*/,
- mlir::OpBuilder &builder) {
- if (aliasValue.getType() == parentSlot.elemType)
- return aliasValue;
- return fir::BitcastOp::create(builder, getLoc(), parentSlot.elemType,
- aliasValue);
-}
-
-bool fir::ConvertOp::canUsesBeRemoved(
- const mlir::SmallPtrSetImpl<mlir::OpOperand *> &blockingUses,
- mlir::SmallVectorImpl<mlir::OpOperand *> &newBlockingUses,
- const mlir::DataLayout &dataLayout) {
- // Only participate in promotion when this convert is a slot alias (if the
- // address cast is not used, DCE should have removed it).
- if (!isPromotableSlotAliasConvert(*this))
- return false;
- for (mlir::OpOperand &use : getResult().getUses())
- newBlockingUses.push_back(&use);
- return true;
-}
-
-mlir::DeletionKind fir::ConvertOp::removeBlockingUses(
- const mlir::SmallPtrSetImpl<mlir::OpOperand *> &blockingUses,
- mlir::OpBuilder &builder) {
- return mlir::DeletionKind::Delete;
-}
-
static bool isI1(mlir::Type ty) {
if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(ty))
return intTy.getWidth() == 1;
@@ -3671,8 +3588,17 @@ llvm::SmallVector<mlir::Attribute> fir::LenParamIndexOp::getAttributes() {
// LoadOp
//===----------------------------------------------------------------------===//
+static bool isSlotOrDeclaredSlot(mlir::Value val,
+ const mlir::MemorySlot &slot) {
+ if (val == slot.ptr)
+ return true;
+ if (auto declareOp = val.getDefiningOp<fir::DeclareOp>())
+ return declareOp.getMemref() == slot.ptr;
+ return false;
+}
+
bool fir::LoadOp::loadsFrom(const mlir::MemorySlot &slot) {
- return getMemref() == slot.ptr;
+ return isSlotOrDeclaredSlot(getMemref(), slot);
}
bool fir::LoadOp::storesTo(const mlir::MemorySlot &slot) { return false; }
@@ -3692,8 +3618,7 @@ bool fir::LoadOp::canUsesBeRemoved(
if (blockingUses.size() != 1)
return false;
mlir::Value blockingUse = (*blockingUses.begin())->get();
- return blockingUse == slot.ptr && getMemref() == slot.ptr &&
- getResult().getType() == slot.elemType;
+ return isSlotOrDeclaredSlot(blockingUse, slot) && getMemref() == blockingUse;
}
mlir::DeletionKind fir::LoadOp::removeBlockingUses(
@@ -5243,7 +5168,7 @@ llvm::LogicalResult fir::SliceOp::verify() {
bool fir::StoreOp::loadsFrom(const mlir::MemorySlot &slot) { return false; }
bool fir::StoreOp::storesTo(const mlir::MemorySlot &slot) {
- return getMemref() == slot.ptr;
+ return isSlotOrDeclaredSlot(getMemref(), slot);
}
mlir::Value fir::StoreOp::getStored(const mlir::MemorySlot &slot,
@@ -5261,8 +5186,8 @@ bool fir::StoreOp::canUsesBeRemoved(
if (blockingUses.size() != 1)
return false;
mlir::Value blockingUse = (*blockingUses.begin())->get();
- return blockingUse == slot.ptr && getMemref() == slot.ptr &&
- getValue() != slot.ptr && getValue().getType() == slot.elemType;
+ return isSlotOrDeclaredSlot(blockingUse, slot) &&
+ getMemref() == blockingUse && getValue() != blockingUse;
}
mlir::DeletionKind fir::StoreOp::removeBlockingUses(
@@ -6125,8 +6050,17 @@ bool fir::DeclareOp::canUsesBeRemoved(
const mlir::DataLayout &dataLayout) {
if (!isLegalTypeForValueDeclare(fir::unwrapRefType(getType())))
return false;
- for (mlir::OpOperand &use : getResult().getUses())
+ // MLIR's mem2reg computes defining blocks only from direct users of
+ // the slot pointer. Stores through fir.declare are not direct users,
+ // so they are not registered as defining blocks. This causes missing
+ // phi nodes at join points (e.g., loop headers). Restrict promotion
+ // to the single-block case where no phi nodes are needed.
+ mlir::Block *declBlock = getOperation()->getBlock();
+ for (mlir::OpOperand &use : getResult().getUses()) {
+ if (use.getOwner()->getBlock() != declBlock)
+ return false;
newBlockingUses.push_back(&use);
+ }
return true;
}
@@ -6136,38 +6070,17 @@ mlir::DeletionKind fir::DeclareOp::removeBlockingUses(
return mlir::DeletionKind::Delete;
}
-void fir::DeclareOp::getPromotableSlotAliases(
- mlir::OpOperand &aliasedSlotPointerOperand,
- const mlir::MemorySlot & /*parentSlot*/,
- llvm::SmallVectorImpl<mlir::MemorySlot> &newMemorySlots) {
- if (aliasedSlotPointerOperand.get() != getMemref())
- return;
- // fir.declare is a transparent alias of its memref operand at the same
- // element type.
- mlir::Type aliasElemType = fir::dyn_cast_ptrEleTy(getResult().getType());
- if (!aliasElemType)
- return;
- newMemorySlots.push_back(mlir::MemorySlot{getResult(), aliasElemType});
-}
-
bool fir::DeclareOp::requiresReplacedValues() { return true; }
void fir::DeclareOp::visitReplacedValues(
llvm::ArrayRef<std::pair<mlir::Operation *, mlir::Value>> definitions,
mlir::OpBuilder &builder) {
- // TODO: extend `fir.declare_value` to carry the variable's declared type
- // independently of the value's type. Once that is in place the type
- // mismatch check below can be dropped (a bit-cast equivalent will be
- // recorded on the op instead of skipping emission).
- mlir::Type declaredElemType = fir::dyn_cast_ptrEleTy(getResult().getType());
for (auto [op, value] : definitions) {
// Do not emit DeclareValue when we have a dummy scope as this can
// potentially result in us generating it where the DummyScope does not
// dominate it. This can happen after inlining.
if (getDummyScope())
continue;
- if (value.getType() != declaredElemType)
- continue;
builder.setInsertionPointAfter(op);
fir::DeclareValueOp::create(builder, getLoc(), value, nullptr,
getUniqNameAttr(), getFortranAttrsAttr(),
diff --git a/flang/test/Fir/mem2reg.mlir b/flang/test/Fir/mem2reg.mlir
index 2aaf856c620d8..154580c626e7c 100644
--- a/flang/test/Fir/mem2reg.mlir
+++ b/flang/test/Fir/mem2reg.mlir
@@ -158,24 +158,17 @@ func.func @box_not_mem2reg(%arg0: !fir.ref<!fir.box<f32>> {fir.bindc_name = "i"}
// -----
-// Conditional store in a
diff erent block through fir.declare is promoted:
-// the through-declare store is discovered as a defining block via the
-// `getPromotableSlotView` interface and a block argument is added at the
-// merge point.
+// Conditional store in a
diff erent block through fir.declare is not promoted
+// because MLIR mem2reg would not place the needed phi nodes correctly.
// CHECK-LABEL: func.func @block_argument_value(
// CHECK-SAME: %[[ARG0:.*]]: i32,
// CHECK-SAME: %[[ARG1:.*]]: i1) -> i32 {
-// CHECK-NOT: fir.alloca
-// CHECK-NOT: fir.declare {{.*}} : (!fir.ref<i32>)
-// CHECK: %[[C42:.*]] = arith.constant 42 : i32
-// CHECK: fir.declare_value %[[C42]] {{.*}}
-// CHECK: llvm.cond_br %[[ARG1]], ^bb1, ^bb2(%[[C42]] : i32)
-// CHECK: ^bb1:
-// CHECK: fir.declare_value %[[ARG0]] {{.*}}
-// CHECK: llvm.br ^bb2(%[[ARG0]] : i32)
-// CHECK: ^bb2(%[[MERGE:.*]]: i32):
-// CHECK: return %[[MERGE]] : i32
+// CHECK: fir.alloca i32
+// CHECK: fir.declare
+// CHECK: fir.store
+// CHECK: fir.store
+// CHECK: fir.load
func.func @block_argument_value(%arg0: i32, %cdt: i1) -> i32 {
%c42_i32 = arith.constant 42 : i32
%3 = fir.alloca i32 {bindc_name = "jlocal", uniq_name = "_QFfooEjlocal"}
@@ -192,26 +185,19 @@ func.func @block_argument_value(%arg0: i32, %cdt: i1) -> i32 {
// -----
-// Conditional store inside a loop through fir.declare is promoted: the
-// loop header gets a block argument carrying the conditional update from
-// the loop body. This is the case that motivated the previous same-block
-// restriction in fir::DeclareOp::canUsesBeRemoved.
+// Conditional store inside a loop through fir.declare must not be promoted.
+// MLIR's mem2reg does not register stores through declares as defining blocks,
+// so phi nodes at the loop header would be missing, losing the update.
// CHECK-LABEL: func.func @loop_conditional_update(
// CHECK-SAME: %[[ARG0:.*]]: i32,
// CHECK-SAME: %[[ARG1:.*]]: i1) -> i32 {
-// CHECK-NOT: fir.alloca
-// CHECK-NOT: fir.declare {{.*}} : (!fir.ref<i32>)
-// CHECK: fir.declare_value %[[ARG0]] {{.*}}
-// CHECK: llvm.br ^bb1(%[[ARG0]] : i32)
-// CHECK: ^bb1(%[[LOOP_ARG:.*]]: i32):
-// CHECK: llvm.cond_br %[[ARG1]], ^bb2, ^bb3
-// CHECK: ^bb2:
-// CHECK: %[[NEW:.*]] = arith.subi %[[LOOP_ARG]], {{.*}} : i32
-// CHECK: fir.declare_value %[[NEW]] {{.*}}
-// CHECK: llvm.br ^bb1(%[[NEW]] : i32)
-// CHECK: ^bb3:
-// CHECK: return %[[LOOP_ARG]] : i32
+// CHECK: fir.alloca i32
+// CHECK: fir.declare
+// CHECK: fir.store
+// CHECK: fir.load
+// CHECK: fir.store
+// CHECK: fir.load
func.func @loop_conditional_update(%arg0: i32, %cdt: i1) -> i32 {
%c1 = arith.constant 1 : i32
%alloca = fir.alloca i32 {bindc_name = "mywatch", uniq_name = "_QFkernelEmywatch"}
@@ -232,171 +218,6 @@ func.func @loop_conditional_update(%arg0: i32, %cdt: i1) -> i32 {
// -----
-// fir.convert at the same element type is a transparent view; the slot is
-// fully promoted with no value conversion needed.
-
-// CHECK-LABEL: func.func @convert_same_type(
-// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
-// CHECK-NOT: fir.alloca
-// CHECK-NOT: fir.convert
-// CHECK: return %[[ARG0]] : i32
-func.func @convert_same_type(%arg0: i32) -> i32 {
- %alloca = fir.alloca i32
- %ptr = fir.convert %alloca : (!fir.ref<i32>) -> !fir.ref<i32>
- fir.store %arg0 to %ptr : !fir.ref<i32>
- %v = fir.load %ptr : !fir.ref<i32>
- return %v : i32
-}
-
-// -----
-
-// A type-changing fir.convert exposes the slot at a
diff erent element type;
-// mem2reg materialises fir.bitcast value conversions at the store and load.
-
-// CHECK-LABEL: func.func @convert_type_changing(
-// CHECK-SAME: %[[ARG0:.*]]: f32) -> f32
-// CHECK-NOT: fir.alloca
-// CHECK: %[[I32:.*]] = fir.bitcast %[[ARG0]] : (f32) -> i32
-// CHECK: fir.bitcast %[[I32]] : (i32) -> f32
-// CHECK: return %{{.*}} : f32
-func.func @convert_type_changing(%arg0: f32) -> f32 {
- %alloca = fir.alloca i32
- %ptr = fir.convert %alloca : (!fir.ref<i32>) -> !fir.ref<f32>
- fir.store %arg0 to %ptr : !fir.ref<f32>
- %v = fir.load %ptr : !fir.ref<f32>
- return %v : f32
-}
-
-// -----
-
-// Chained view: alloca -> fir.declare -> fir.convert -> load/store. The
-// declare's element type (i32)
diff ers from the value type the store sees
-// at the leaf view (f32), so `fir.declare_value` is skipped until
-// `fir.declare_value` can carry the declared type independently of the
-// value type (see the TODO in fir::DeclareOp::visitReplacedValues).
-
-// CHECK-LABEL: func.func @declare_then_convert(
-// CHECK-SAME: %[[ARG0:.*]]: f32) -> f32
-// CHECK-NOT: fir.alloca
-// CHECK-NOT: fir.declare {{.*}} : (!fir.ref<i32>)
-// CHECK-NOT: fir.declare_value
-// CHECK: %[[I32:.*]] = fir.bitcast %[[ARG0]] : (f32) -> i32
-// CHECK: %[[F32:.*]] = fir.bitcast %[[I32]] : (i32) -> f32
-// CHECK: return %[[F32]] : f32
-func.func @declare_then_convert(%arg0: f32) -> f32 {
- %alloca = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"}
- %declare = fir.declare %alloca {uniq_name = "_QFEx"} : (!fir.ref<i32>) -> !fir.ref<i32>
- %ptr = fir.convert %declare : (!fir.ref<i32>) -> !fir.ref<f32>
- fir.store %arg0 to %ptr : !fir.ref<f32>
- %v = fir.load %ptr : !fir.ref<f32>
- return %v : f32
-}
-
-// -----
-
-// Inverse chain: alloca -> fir.convert -> fir.declare -> load/store. The
-// declare's memref is the result of a type-changing convert, so the
-// declare sees the slot at f32 — the same type as the store value, so
-// `fir.declare_value` is emitted.
-
-// CHECK-LABEL: func.func @convert_then_declare(
-// CHECK-SAME: %[[ARG0:.*]]: f32) -> f32
-// CHECK-NOT: fir.alloca
-// CHECK-NOT: fir.declare {{.*}} : (!fir.ref<f32>)
-// CHECK: %[[I32:.*]] = fir.bitcast %[[ARG0]] : (f32) -> i32
-// CHECK: fir.declare_value %[[ARG0]] {{.*}} : f32
-// CHECK: %[[F32:.*]] = fir.bitcast %[[I32]] : (i32) -> f32
-// CHECK: return %[[F32]] : f32
-func.func @convert_then_declare(%arg0: f32) -> f32 {
- %alloca = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"}
- %ptr = fir.convert %alloca : (!fir.ref<i32>) -> !fir.ref<f32>
- %declare = fir.declare %ptr {uniq_name = "_QFEx"} : (!fir.ref<f32>) -> !fir.ref<f32>
- fir.store %arg0 to %declare : !fir.ref<f32>
- %v = fir.load %declare : !fir.ref<f32>
- return %v : f32
-}
-
-// -----
-
-// A memref.alloca slot with a mixed memref/FIR view chain
-// (memref -> fir.convert -> fir.declare -> fir.convert -> memref). All
-// element types are the same, so the slot promotes with no value
-// conversions; only `fir.declare_value` is emitted from the declare.
-
-// CHECK-LABEL: func.func @memref_with_declare_chain(
-// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32
-// CHECK-NOT: memref.alloca
-// CHECK-NOT: fir.declare {{.*}} : (!fir.ref<i32>)
-// CHECK-NOT: fir.convert
-// CHECK-NOT: memref.store
-// CHECK-NOT: memref.load
-// CHECK: fir.declare_value %[[ARG0]] {{.*}} : i32
-// CHECK: return %[[ARG0]] : i32
-func.func @memref_with_declare_chain(%arg0: i32) -> i32 {
- %alloca = memref.alloca() : memref<i32>
- %fref = fir.convert %alloca : (memref<i32>) -> !fir.ref<i32>
- %decl = fir.declare %fref {uniq_name = "_QFEx"} : (!fir.ref<i32>) -> !fir.ref<i32>
- %mref = fir.convert %decl : (!fir.ref<i32>) -> memref<i32>
- memref.store %arg0, %mref[] : memref<i32>
- %v = memref.load %mref[] : memref<i32>
- return %v : i32
-}
-
-// -----
-
-// Same mixed memref/FIR chain with a conditional store across blocks. The
-// merge-point block argument is at the root slot's element type (i32), and
-// `fir.declare_value` is emitted at each store site.
-
-// CHECK-LABEL: func.func @memref_with_declare_chain_blocks(
-// CHECK-SAME: %[[ARG0:.*]]: i32,
-// CHECK-SAME: %[[COND:.*]]: i1) -> i32
-// CHECK-NOT: memref.alloca
-// CHECK-NOT: fir.declare {{.*}} : (!fir.ref<i32>)
-// CHECK: %[[C42:.*]] = arith.constant 42 : i32
-// CHECK: fir.declare_value %[[C42]] {{.*}} : i32
-// CHECK: cf.cond_br %[[COND]], ^[[BB1:.*]], ^[[BB2:.*]](%[[C42]] : i32)
-// CHECK: ^[[BB1]]:
-// CHECK: fir.declare_value %[[ARG0]] {{.*}} : i32
-// CHECK: cf.br ^[[BB2]](%[[ARG0]] : i32)
-// CHECK: ^[[BB2]](%[[MERGE:.*]]: i32):
-// CHECK: return %[[MERGE]] : i32
-func.func @memref_with_declare_chain_blocks(%arg0: i32, %cond: i1) -> i32 {
- %c42 = arith.constant 42 : i32
- %alloca = memref.alloca() : memref<i32>
- %fref = fir.convert %alloca : (memref<i32>) -> !fir.ref<i32>
- %decl = fir.declare %fref {uniq_name = "_QFEx"} : (!fir.ref<i32>) -> !fir.ref<i32>
- %mref = fir.convert %decl : (!fir.ref<i32>) -> memref<i32>
- memref.store %c42, %mref[] : memref<i32>
- cf.cond_br %cond, ^bb1, ^bb2
-^bb1:
- memref.store %arg0, %mref[] : memref<i32>
- cf.br ^bb2
-^bb2:
- %v = memref.load %mref[] : memref<i32>
- return %v : i32
-}
-
-// -----
-
-// A ref->integer fir.convert is not a memory view; it must block promotion.
-
-// CHECK-LABEL: func.func @convert_to_integer_blocks_promotion(
-// CHECK: %[[ALLOCA:.*]] = fir.alloca i32
-// CHECK: fir.store
-// CHECK: %[[INT:.*]] = fir.convert %[[ALLOCA]] : (!fir.ref<i32>) -> i64
-// CHECK: fir.call @use_addr(%[[INT]])
-func.func private @use_addr(%a: i64)
-func.func @convert_to_integer_blocks_promotion(%arg0: i32) {
- %alloca = fir.alloca i32
- fir.store %arg0 to %alloca : !fir.ref<i32>
- %addr = fir.convert %alloca : (!fir.ref<i32>) -> i64
- fir.call @use_addr(%addr) : (i64) -> ()
- return
-}
-
-// -----
-
// Make sure we do not generate fir.declare_value for a replaced value
// fir.declare with dummy_scope. This can result in the declare_value being
// inserted before the dummy_scope it uses as would be the case here.
More information about the llvm-branch-commits
mailing list