[clang] feat: included support for is_constant builtin (PR #166832)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 7 05:53:37 PST 2025
https://github.com/spamprx updated https://github.com/llvm/llvm-project/pull/166832
>From 227f9ad624e570353abff8de7a09b276e9c6b4e2 Mon Sep 17 00:00:00 2001
From: spamprx <cs23btech11012 at iith.ac.in>
Date: Fri, 7 Nov 2025 00:55:26 +0530
Subject: [PATCH 1/3] feat: included support for is_constant builtin
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 38 ++++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 25 +++++++++++++
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 19 ++++++++++
3 files changed, 82 insertions(+)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 2b361ed0982c6..9765684a1cd72 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4052,6 +4052,44 @@ def CIR_ExpectOp : CIR_Op<"expect", [
}];
}
+//===----------------------------------------------------------------------===//
+// IsConstantOp
+//===----------------------------------------------------------------------===//
+
+def CIR_IsConstantOp : CIR_Op<"is_constant", [Pure]> {
+ let summary = "Check if a value is a compile-time constant";
+ let description = [{
+ The `cir.is_constant` operation checks whether its input value is a
+ compile-time constant. This operation models the `__builtin_constant_p`
+ builtin function.
+
+ The operation takes a single operand of any CIR type and returns a signed
+ 32-bit integer. The result is 1 if the operand is a compile-time constant,
+ and 0 otherwise.
+
+ If the value can be determined to be constant at compile time, this
+ operation may be folded to a constant value. Otherwise, it will be lowered
+ to the `llvm.is.constant` intrinsic.
+
+ Example:
+
+ ```mlir
+ %0 = cir.is_constant %expr : i32 -> !s32i
+ %1 = cir.is_constant %ptr : !cir.ptr<i32> -> !s32i
+ ```
+ }];
+
+ let arguments = (ins CIR_AnyType:$value);
+ let results = (outs CIR_SInt32:$result);
+
+ let assemblyFormat = [{
+ $value `:` type($value) `->` type($result) attr-dict
+ }];
+
+ let hasFolder = 1;
+ let hasLLVMLowering = false;
+}
+
//===----------------------------------------------------------------------===//
// PrefetchOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index e35100ffe4b6b..8777ff16f068f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -199,6 +199,31 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return RValue::get(
builder.createBitcast(allocaAddr, builder.getVoidPtrTy()));
}
+
+ case Builtin::BI__builtin_constant_p: {
+ auto loc = getLoc(e->getSourceRange());
+
+ Expr::EvalResult evalResult;
+
+ // Try to evaluate at compile time first
+ if (e->getArg(0)->EvaluateAsRValue(evalResult, getContext()) &&
+ !evalResult.hasSideEffects()) {
+ // Expression is a compile-time constant, return 1
+ llvm::APInt apInt(32, 1);
+ llvm::APSInt apSInt(apInt, /*isUnsigned=*/false);
+ return RValue::get(builder.getConstInt(loc, apSInt));
+ }
+
+ // Expression cannot be evaluated at compile time, emit runtime check
+ mlir::Value argValue = emitScalarExpr(e->getArg(0));
+ mlir::Type resultType = builder.getSInt32Ty();
+ auto isConstantOp = cir::IsConstantOp::create(builder, loc, resultType, argValue);
+ mlir::Value resultValue = isConstantOp.getResult();
+ mlir::Type exprTy = convertType(e->getType());
+ if (exprTy != resultValue.getType())
+ resultValue = builder.createIntCast(resultValue, exprTy);
+ return RValue::get(resultValue);
+ }
case Builtin::BIcos:
case Builtin::BIcosf:
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 7ba03ce40140c..de23514cf5a79 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2117,6 +2117,25 @@ OpFoldResult cir::UnaryOp::fold(FoldAdaptor adaptor) {
return {};
}
+//===----------------------------------------------------------------------===//
+// IsConstantOp Definitions
+//===----------------------------------------------------------------------===//
+
+OpFoldResult cir::IsConstantOp::fold(FoldAdaptor adaptor) {
+ // If the input value is a constant attribute, return 1 (true)
+ mlir::Attribute value = adaptor.getValue();
+ if (value) {
+ // The value is a compile-time constant, so return 1
+ mlir::Type resultType = getResult().getType();
+ llvm::APInt apInt(32, 1);
+ llvm::APSInt apSInt(apInt, /*isUnsigned=*/false);
+ return cir::IntAttr::get(resultType, apSInt);
+ }
+
+ // If the input is not a constant, we cannot fold
+ return {};
+}
+
//===----------------------------------------------------------------------===//
// CopyOp Definitions
//===----------------------------------------------------------------------===//
>From f2d9b64ec396de9a57366fa826284bd652128222 Mon Sep 17 00:00:00 2001
From: siddu0660 <cs23btech11025 at iith.ac.in>
Date: Fri, 7 Nov 2025 01:18:51 +0530
Subject: [PATCH 2/3] feat: updated TableGen code for BinOpOverFlow
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 41 ++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 9765684a1cd72..44b5fb484510a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1702,6 +1702,47 @@ def CIR_BinOp : CIR_Op<"binop", [
}];
}
+//===----------------------------------------------------------------------===//
+// BinOpOverflow
+//===----------------------------------------------------------------------===//
+
+def CIR_BinOpOverflow : CIR_Op<"binop.overflow", [Pure]> {
+ let summary = "Binary operations with overflow detection";
+ let description = [{
+ cir.binop.overflow performs the binary operation according to
+ the specified opcode kind (add, sub, or mul) and returns both
+ the result and an overflow flag.
+
+ It requires two input operands and returns two results:
+ - The result of the operation (same type as operands)
+ - An overflow flag (boolean type)
+
+ The operation supports both signed and unsigned integer types.
+ For signed types, overflow is detected when the result cannot
+ be represented in the result type. For unsigned types, overflow
+ is detected when the result wraps around.
+
+ ```mlir
+ %result, %overflow = cir.binop.overflow(add, %1, %2) : !s32i -> (!s32i, !bool)
+ %result, %overflow = cir.binop.overflow(sub, %3, %4) : !u32i -> (!u32i, !bool)
+ %result, %overflow = cir.binop.overflow(mul, %5, %6) : !s64i -> (!s64i, !bool)
+ ```
+ }];
+
+ let arguments = (ins
+ CIR_BinOpKind:$kind,
+ CIR_AnyType:$lhs, CIR_AnyType:$rhs
+ );
+
+ let results = (outs CIR_AnyType:$result, CIR_BoolType:$overflow);
+
+ let assemblyFormat = [{
+ `(` $kind `,` $lhs `,` $rhs `)` `:` type($lhs) `->` `(` type($result) `,` type($overflow) `)` attr-dict
+ }];
+
+ let hasVerifier = 1;
+}
+
//===----------------------------------------------------------------------===//
// ShiftOp
//===----------------------------------------------------------------------===//
>From 267c6baecc025b7020469101665997019cf80606 Mon Sep 17 00:00:00 2001
From: spamprx <cs23btech11012 at iith.ac.in>
Date: Fri, 7 Nov 2025 17:25:48 +0530
Subject: [PATCH 3/3] Remove redundant CIR_BinOpOverflow and implement
CIR_IsConstantOp in CIRGenBuiltin
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 117 ++++++-------------
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 56 +++++----
2 files changed, 72 insertions(+), 101 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 63d31d36fe933..5d0913dae3308 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1702,47 +1702,6 @@ def CIR_BinOp : CIR_Op<"binop", [
}];
}
-//===----------------------------------------------------------------------===//
-// BinOpOverflow
-//===----------------------------------------------------------------------===//
-
-def CIR_BinOpOverflow : CIR_Op<"binop.overflow", [Pure]> {
- let summary = "Binary operations with overflow detection";
- let description = [{
- cir.binop.overflow performs the binary operation according to
- the specified opcode kind (add, sub, or mul) and returns both
- the result and an overflow flag.
-
- It requires two input operands and returns two results:
- - The result of the operation (same type as operands)
- - An overflow flag (boolean type)
-
- The operation supports both signed and unsigned integer types.
- For signed types, overflow is detected when the result cannot
- be represented in the result type. For unsigned types, overflow
- is detected when the result wraps around.
-
- ```mlir
- %result, %overflow = cir.binop.overflow(add, %1, %2) : !s32i -> (!s32i, !bool)
- %result, %overflow = cir.binop.overflow(sub, %3, %4) : !u32i -> (!u32i, !bool)
- %result, %overflow = cir.binop.overflow(mul, %5, %6) : !s64i -> (!s64i, !bool)
- ```
- }];
-
- let arguments = (ins
- CIR_BinOpKind:$kind,
- CIR_AnyType:$lhs, CIR_AnyType:$rhs
- );
-
- let results = (outs CIR_AnyType:$result, CIR_BoolType:$overflow);
-
- let assemblyFormat = [{
- `(` $kind `,` $lhs `,` $rhs `)` `:` type($lhs) `->` `(` type($result) `,` type($overflow) `)` attr-dict
- }];
-
- let hasVerifier = 1;
-}
-
//===----------------------------------------------------------------------===//
// ShiftOp
//===----------------------------------------------------------------------===//
@@ -3084,6 +3043,44 @@ def CIR_ArrayDtor : CIR_ArrayInitDestroy<"array.dtor"> {
}];
}
+//===----------------------------------------------------------------------===//
+// IsConstantOp
+//===----------------------------------------------------------------------===//
+
+def CIR_IsConstantOp : CIR_Op<"is_constant", [Pure]> {
+ let summary = "Check if a value is a compile-time constant";
+ let description = [{
+ The `cir.is_constant` operation checks whether its input value is a
+ compile-time constant. This operation models the `__builtin_constant_p`
+ builtin function.
+
+ The operation takes a single operand of any CIR type and returns a signed
+ 32-bit integer. The result is 1 if the operand is a compile-time constant,
+ and 0 otherwise.
+
+ If the value can be determined to be constant at compile time, this
+ operation may be folded to a constant value. Otherwise, it will be lowered
+ to the `llvm.is.constant` intrinsic.
+
+ Example:
+
+ ```mlir
+ %0 = cir.is_constant %expr : i32 -> !s32i
+ %1 = cir.is_constant %ptr : !cir.ptr<i32> -> !s32i
+ ```
+ }];
+
+ let arguments = (ins CIR_AnyType:$value);
+ let results = (outs CIR_BoolType:$result);
+
+ let assemblyFormat = [{
+ `(` $value `:` type($value) `)` `:` type($result) attr-dict
+ }];
+
+ // let hasFolder = 1;
+ // let hasLLVMLowering = false;
+}
+
//===----------------------------------------------------------------------===//
// VecCreate
//===----------------------------------------------------------------------===//
@@ -4093,44 +4090,6 @@ def CIR_ExpectOp : CIR_Op<"expect", [
}];
}
-//===----------------------------------------------------------------------===//
-// IsConstantOp
-//===----------------------------------------------------------------------===//
-
-def CIR_IsConstantOp : CIR_Op<"is_constant", [Pure]> {
- let summary = "Check if a value is a compile-time constant";
- let description = [{
- The `cir.is_constant` operation checks whether its input value is a
- compile-time constant. This operation models the `__builtin_constant_p`
- builtin function.
-
- The operation takes a single operand of any CIR type and returns a signed
- 32-bit integer. The result is 1 if the operand is a compile-time constant,
- and 0 otherwise.
-
- If the value can be determined to be constant at compile time, this
- operation may be folded to a constant value. Otherwise, it will be lowered
- to the `llvm.is.constant` intrinsic.
-
- Example:
-
- ```mlir
- %0 = cir.is_constant %expr : i32 -> !s32i
- %1 = cir.is_constant %ptr : !cir.ptr<i32> -> !s32i
- ```
- }];
-
- let arguments = (ins CIR_AnyType:$value);
- let results = (outs CIR_SInt32:$result);
-
- let assemblyFormat = [{
- $value `:` type($value) `->` type($result) attr-dict
- }];
-
- let hasFolder = 1;
- let hasLLVMLowering = false;
-}
-
//===----------------------------------------------------------------------===//
// PrefetchOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 6ef4ee3dfe612..b7219063c9d43 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -201,28 +201,40 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
}
case Builtin::BI__builtin_constant_p: {
- auto loc = getLoc(e->getSourceRange());
-
- Expr::EvalResult evalResult;
-
- // Try to evaluate at compile time first
- if (e->getArg(0)->EvaluateAsRValue(evalResult, getContext()) &&
- !evalResult.hasSideEffects()) {
- // Expression is a compile-time constant, return 1
- llvm::APInt apInt(32, 1);
- llvm::APSInt apSInt(apInt, /*isUnsigned=*/false);
- return RValue::get(builder.getConstInt(loc, apSInt));
- }
-
- // Expression cannot be evaluated at compile time, emit runtime check
- mlir::Value argValue = emitScalarExpr(e->getArg(0));
- mlir::Type resultType = builder.getSInt32Ty();
- auto isConstantOp = cir::IsConstantOp::create(builder, loc, resultType, argValue);
- mlir::Value resultValue = isConstantOp.getResult();
- mlir::Type exprTy = convertType(e->getType());
- if (exprTy != resultValue.getType())
- resultValue = builder.createIntCast(resultValue, exprTy);
- return RValue::get(resultValue);
+ mlir::Location loc = getLoc(e->getSourceRange());
+ mlir::Type ResultType = convertType(e->getType());
+
+ const Expr *Arg = e->getArg(0);
+ QualType ArgType = Arg->getType();
+ // FIXME: The allowance for Obj-C pointers and block pointers is historical
+ // and likely a mistake.
+ if (!ArgType->isIntegralOrEnumerationType() && !ArgType->isFloatingType() &&
+ !ArgType->isObjCObjectPointerType() && !ArgType->isBlockPointerType())
+ // Per the GCC documentation, only numeric constants are recognized after
+ // inlining.
+ return RValue::get(
+ builder.getConstInt(getLoc(e->getSourceRange()),
+ mlir::cast<cir::IntType>(ResultType), 0));
+
+ if (Arg->HasSideEffects(getContext()))
+ // The argument is unevaluated, so be conservative if it might have
+ // side-effects.
+ return RValue::get(
+ builder.getConstInt(getLoc(e->getSourceRange()),
+ mlir::cast<cir::IntType>(ResultType), 0));
+
+ mlir::Value ArgValue = emitScalarExpr(Arg);
+ if (ArgType->isObjCObjectPointerType())
+ // Convert Objective-C objects to id because we cannot distinguish between
+ // LLVM types for Obj-C classes as they are opaque.
+ ArgType = cgm.getASTContext().getObjCIdType();
+ ArgValue = builder.createBitcast(ArgValue, convertType(ArgType));
+
+ mlir::Value Result = cir::IsConstantOp::create(
+ builder, getLoc(e->getSourceRange()), ArgValue);
+ if (Result.getType() != ResultType)
+ Result = builder.createBoolToInt(Result, ResultType);
+ return RValue::get(Result);
}
case Builtin::BIcos:
More information about the cfe-commits
mailing list