[Mlir-commits] [mlir] [mlir][emitc] Fix creating pointer from constant array (PR #162083)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sun Dec 7 04:56:14 PST 2025
https://github.com/Jimmy2027 updated https://github.com/llvm/llvm-project/pull/162083
>From a1a16c9b4f9e195a927b2fe82231b86635dbad58 Mon Sep 17 00:00:00 2001
From: Hendrik Klug <hendrik.klug at gmail.com>
Date: Sun, 7 Dec 2025 12:48:52 +0000
Subject: [PATCH] [mlir][emitc] Emit casting away constness when taking address
of const global
- Modify the C++ emitter to detect when an AddressOf op traces back to a const global. If it does, emit a
C-style cast (T*)(&...) to strip the const qualification
- Adapt mlir/test/Target/Cpp/global.mlir to check for correct syntax of
the generated code
---
mlir/lib/Target/Cpp/TranslateToCpp.cpp | 66 +++++++++++++-
mlir/test/Target/Cpp/global.mlir | 114 ++++++++++++++++++++++++-
2 files changed, 174 insertions(+), 6 deletions(-)
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index a37ea5e87a316..6f3a4b14ae038 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -397,6 +397,51 @@ static bool shouldBeInlined(ExpressionOp expressionOp) {
return false;
}
+/// Helper function to check if a value traces back to a const global.
+/// Handles direct GetGlobalOp and GetGlobalOp through one or more SubscriptOps.
+/// Returns the GlobalOp if found and it has const_specifier, nullptr otherwise.
+static emitc::GlobalOp getConstGlobal(Value value, Operation *fromOp) {
+ while (auto subscriptOp = value.getDefiningOp<emitc::SubscriptOp>()) {
+ value = subscriptOp.getValue();
+ }
+
+ auto getGlobalOp = value.getDefiningOp<emitc::GetGlobalOp>();
+ if (!getGlobalOp)
+ return nullptr;
+
+ // Find the nearest symbol table to check whether the global is const.
+ Operation *symbolTableOp = fromOp;
+ while (symbolTableOp && !symbolTableOp->hasTrait<OpTrait::SymbolTable>()) {
+ symbolTableOp = symbolTableOp->getParentOp();
+ }
+
+ if (!symbolTableOp)
+ return nullptr;
+
+ SymbolTable symbolTable(symbolTableOp);
+ auto globalOp = symbolTable.lookup<emitc::GlobalOp>(getGlobalOp.getName());
+
+ if (globalOp && globalOp.getConstSpecifier())
+ return globalOp;
+
+ return nullptr;
+}
+
+/// Emit address-of with a cast to strip const qualification.
+/// Produces: (ResultType)(&operand)
+static LogicalResult emitAddressOfWithConstCast(CppEmitter &emitter,
+ Operation &op, Value operand) {
+ raw_ostream &os = emitter.ostream();
+ os << "(";
+ if (failed(emitter.emitType(op.getLoc(), op.getResult(0).getType())))
+ return failure();
+ os << ")(&";
+ if (failed(emitter.emitOperand(operand)))
+ return failure();
+ os << ")";
+ return success();
+}
+
static LogicalResult printOperation(CppEmitter &emitter,
emitc::DereferenceOp dereferenceOp) {
std::string out;
@@ -496,8 +541,15 @@ static LogicalResult printOperation(CppEmitter &emitter,
if (failed(emitter.emitAssignPrefix(op)))
return failure();
+
+ Value operand = addressOfOp.getReference();
+
+ // Check if we're taking address of a const global.
+ if (getConstGlobal(operand, &op))
+ return emitAddressOfWithConstCast(emitter, op, operand);
+
os << "&";
- return emitter.emitOperand(addressOfOp.getReference());
+ return emitter.emitOperand(operand);
}
static LogicalResult printOperation(CppEmitter &emitter,
@@ -903,8 +955,16 @@ static LogicalResult printOperation(CppEmitter &emitter,
if (failed(emitter.emitAssignPrefix(op)))
return failure();
- os << applyOp.getApplicableOperator();
- return emitter.emitOperand(applyOp.getOperand());
+
+ StringRef applicableOperator = applyOp.getApplicableOperator();
+ Value operand = applyOp.getOperand();
+
+ // Check if we're taking address of a const global.
+ if (applicableOperator == "&" && getConstGlobal(operand, &op))
+ return emitAddressOfWithConstCast(emitter, op, operand);
+
+ os << applicableOperator;
+ return emitter.emitOperand(operand);
}
static LogicalResult printOperation(CppEmitter &emitter,
diff --git a/mlir/test/Target/Cpp/global.mlir b/mlir/test/Target/Cpp/global.mlir
index c54338bc2faff..2981c1f2bceaf 100644
--- a/mlir/test/Target/Cpp/global.mlir
+++ b/mlir/test/Target/Cpp/global.mlir
@@ -1,5 +1,11 @@
// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s -check-prefix=CPP-DEFAULT
// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s -check-prefix=CPP-DECLTOP
+// check that the generated code is syntactically valid
+// RUN: mlir-translate -mlir-to-cpp %s | %host_cxx -fsyntax-only -x c++ -
+
+emitc.include "stdint.h"
+emitc.include "limits.h"
+emitc.include "stddef.h"
emitc.global extern @decl : i8
// CPP-DEFAULT: extern int8_t decl;
@@ -21,6 +27,10 @@ emitc.global const @myconstant : !emitc.array<2xi16> = dense<2>
// CPP-DEFAULT: const int16_t myconstant[2] = {2, 2};
// CPP-DECLTOP: const int16_t myconstant[2] = {2, 2};
+emitc.global const @myconstant_2d : !emitc.array<2x3xi16> = dense<1>
+// CPP-DEFAULT: const int16_t myconstant_2d[2][3] = {1, 1, 1, 1, 1, 1};
+// CPP-DECLTOP: const int16_t myconstant_2d[2][3] = {1, 1, 1, 1, 1, 1};
+
emitc.global extern const @extern_constant : !emitc.array<2xi16>
// CPP-DEFAULT: extern const int16_t extern_constant[2];
// CPP-DECLTOP: extern const int16_t extern_constant[2];
@@ -29,9 +39,9 @@ emitc.global static @static_var : f32
// CPP-DEFAULT: static float static_var;
// CPP-DECLTOP: static float static_var;
-emitc.global static @static_const : f32 = 3.0
-// CPP-DEFAULT: static float static_const = 3.000000000e+00f;
-// CPP-DECLTOP: static float static_const = 3.000000000e+00f;
+emitc.global static const @static_const : f32 = 3.0
+// CPP-DEFAULT: static const float static_const = 3.000000000e+00f;
+// CPP-DECLTOP: static const float static_const = 3.000000000e+00f;
emitc.global @opaque_init : !emitc.opaque<"char"> = #emitc.opaque<"CHAR_MIN">
// CPP-DEFAULT: char opaque_init = CHAR_MIN;
@@ -98,3 +108,101 @@ func.func @use_global_array_write(%i: index, %val : f32) {
// CPP-DECLTOP-SAME: (size_t [[V1:.*]], float [[V2:.*]])
// CPP-DECLTOP-NEXT: myglobal[[[V1]]] = [[V2]];
// CPP-DECLTOP-NEXT: return;
+
+func.func @use_const_global_array_pointer(%i: index) -> !emitc.ptr<i16> {
+ %0 = emitc.get_global @myconstant : !emitc.array<2xi16>
+ %1 = emitc.subscript %0[%i] : (!emitc.array<2xi16>, index) -> !emitc.lvalue<i16>
+ %2 = emitc.apply "&"(%1) : (!emitc.lvalue<i16>) -> !emitc.ptr<i16>
+ return %2 : !emitc.ptr<i16>
+}
+// CPP-DEFAULT-LABEL: int16_t* use_const_global_array_pointer
+// CPP-DEFAULT-SAME: (size_t [[V1:.*]])
+// CPP-DEFAULT-NEXT: int16_t* [[V2:.*]] = (int16_t*)(&myconstant[[[V1]]]);
+// CPP-DEFAULT-NEXT: return [[V2]];
+
+// CPP-DECLTOP-LABEL: int16_t* use_const_global_array_pointer
+// CPP-DECLTOP-SAME: (size_t [[V1:.*]])
+// CPP-DECLTOP-NEXT: int16_t* [[V2:.*]];
+// CPP-DECLTOP-NEXT: [[V2]] = (int16_t*)(&myconstant[[[V1]]]);
+// CPP-DECLTOP-NEXT: return [[V2]];
+
+func.func @use_const_global_scalar_pointer() -> !emitc.ptr<f32> {
+ %0 = emitc.get_global @static_const : !emitc.lvalue<f32>
+ %1 = emitc.apply "&"(%0) : (!emitc.lvalue<f32>) -> !emitc.ptr<f32>
+ return %1 : !emitc.ptr<f32>
+}
+// CPP-DEFAULT-LABEL: float* use_const_global_scalar_pointer()
+// CPP-DEFAULT-NEXT: float* [[V1:.*]] = (float*)(&static_const);
+// CPP-DEFAULT-NEXT: return [[V1]];
+
+// CPP-DECLTOP-LABEL: float* use_const_global_scalar_pointer()
+// CPP-DECLTOP-NEXT: float* [[V1:.*]];
+// CPP-DECLTOP-NEXT: [[V1]] = (float*)(&static_const);
+// CPP-DECLTOP-NEXT: return [[V1]];
+
+func.func @use_const_global_2d_array_pointer(%i: index, %j: index) -> !emitc.ptr<i16> {
+ %0 = emitc.get_global @myconstant_2d : !emitc.array<2x3xi16>
+ %1 = emitc.subscript %0[%i, %j] : (!emitc.array<2x3xi16>, index, index) -> !emitc.lvalue<i16>
+ %2 = emitc.apply "&"(%1) : (!emitc.lvalue<i16>) -> !emitc.ptr<i16>
+ return %2 : !emitc.ptr<i16>
+}
+// CPP-DEFAULT-LABEL: int16_t* use_const_global_2d_array_pointer
+// CPP-DEFAULT-SAME: (size_t [[I:.*]], size_t [[J:.*]])
+// CPP-DEFAULT-NEXT: int16_t* [[V:.*]] = (int16_t*)(&myconstant_2d[[[I]]][[[J]]]);
+// CPP-DEFAULT-NEXT: return [[V]];
+
+// CPP-DECLTOP-LABEL: int16_t* use_const_global_2d_array_pointer
+// CPP-DECLTOP-SAME: (size_t [[I:.*]], size_t [[J:.*]])
+// CPP-DECLTOP-NEXT: int16_t* [[V:.*]];
+// CPP-DECLTOP-NEXT: [[V]] = (int16_t*)(&myconstant_2d[[[I]]][[[J]]]);
+// CPP-DECLTOP-NEXT: return [[V]];
+
+// Test emitc.address_of with const globals (same as emitc.apply "&" tests above)
+
+func.func @use_const_global_array_pointer_address_of(%i: index) -> !emitc.ptr<i16> {
+ %0 = emitc.get_global @myconstant : !emitc.array<2xi16>
+ %1 = emitc.subscript %0[%i] : (!emitc.array<2xi16>, index) -> !emitc.lvalue<i16>
+ %2 = emitc.address_of %1 : !emitc.lvalue<i16>
+ return %2 : !emitc.ptr<i16>
+}
+// CPP-DEFAULT-LABEL: int16_t* use_const_global_array_pointer_address_of
+// CPP-DEFAULT-SAME: (size_t [[V1:.*]])
+// CPP-DEFAULT-NEXT: int16_t* [[V2:.*]] = (int16_t*)(&myconstant[[[V1]]]);
+// CPP-DEFAULT-NEXT: return [[V2]];
+
+// CPP-DECLTOP-LABEL: int16_t* use_const_global_array_pointer_address_of
+// CPP-DECLTOP-SAME: (size_t [[V1:.*]])
+// CPP-DECLTOP-NEXT: int16_t* [[V2:.*]];
+// CPP-DECLTOP-NEXT: [[V2]] = (int16_t*)(&myconstant[[[V1]]]);
+// CPP-DECLTOP-NEXT: return [[V2]];
+
+func.func @use_const_global_scalar_pointer_address_of() -> !emitc.ptr<f32> {
+ %0 = emitc.get_global @static_const : !emitc.lvalue<f32>
+ %1 = emitc.address_of %0 : !emitc.lvalue<f32>
+ return %1 : !emitc.ptr<f32>
+}
+// CPP-DEFAULT-LABEL: float* use_const_global_scalar_pointer_address_of()
+// CPP-DEFAULT-NEXT: float* [[V1:.*]] = (float*)(&static_const);
+// CPP-DEFAULT-NEXT: return [[V1]];
+
+// CPP-DECLTOP-LABEL: float* use_const_global_scalar_pointer_address_of()
+// CPP-DECLTOP-NEXT: float* [[V1:.*]];
+// CPP-DECLTOP-NEXT: [[V1]] = (float*)(&static_const);
+// CPP-DECLTOP-NEXT: return [[V1]];
+
+func.func @use_const_global_2d_array_pointer_address_of(%i: index, %j: index) -> !emitc.ptr<i16> {
+ %0 = emitc.get_global @myconstant_2d : !emitc.array<2x3xi16>
+ %1 = emitc.subscript %0[%i, %j] : (!emitc.array<2x3xi16>, index, index) -> !emitc.lvalue<i16>
+ %2 = emitc.address_of %1 : !emitc.lvalue<i16>
+ return %2 : !emitc.ptr<i16>
+}
+// CPP-DEFAULT-LABEL: int16_t* use_const_global_2d_array_pointer_address_of
+// CPP-DEFAULT-SAME: (size_t [[I:.*]], size_t [[J:.*]])
+// CPP-DEFAULT-NEXT: int16_t* [[V:.*]] = (int16_t*)(&myconstant_2d[[[I]]][[[J]]]);
+// CPP-DEFAULT-NEXT: return [[V]];
+
+// CPP-DECLTOP-LABEL: int16_t* use_const_global_2d_array_pointer_address_of
+// CPP-DECLTOP-SAME: (size_t [[I:.*]], size_t [[J:.*]])
+// CPP-DECLTOP-NEXT: int16_t* [[V:.*]];
+// CPP-DECLTOP-NEXT: [[V]] = (int16_t*)(&myconstant_2d[[[I]]][[[J]]]);
+// CPP-DECLTOP-NEXT: return [[V]];
More information about the Mlir-commits
mailing list