[clang] [CIR]Upstream generic intrinsic emission path (PR #179098)
Priyanshu Kumar via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 4 21:56:37 PST 2026
https://github.com/Priyanshu3820 updated https://github.com/llvm/llvm-project/pull/179098
>From 920033dae1b1ab9ecb420b3d869a7eade300f87f Mon Sep 17 00:00:00 2001
From: Priyanshu <10b.priyanshu at gmail.com>
Date: Sun, 1 Feb 2026 15:38:02 +0530
Subject: [PATCH 1/6] Upstream generic intrinsic emission path
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 200 ++++++++++++++++++
.../CIR/CodeGenBuiltins/X86/rd-builtins.c | 25 +++
2 files changed, 225 insertions(+)
create mode 100644 clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 0e5a5b531df78..3f5cb6e440591 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -26,6 +26,7 @@
#include "clang/Basic/OperatorKinds.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/MissingFeatures.h"
+#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/ErrorHandling.h"
using namespace clang;
@@ -726,6 +727,108 @@ static RValue tryEmitFPMathIntrinsic(CIRGenFunction &cgf, const CallExpr *e,
return RValue::getIgnored();
}
+static mlir::Type
+decodeFixedType(ArrayRef<llvm::Intrinsic::IITDescriptor> &infos,
+ mlir::MLIRContext *context) {
+ using namespace llvm::Intrinsic;
+
+ IITDescriptor descriptor = infos.front();
+ infos = infos.slice(1);
+
+ switch (descriptor.Kind) {
+ case IITDescriptor::Void:
+ return cir::VoidType::get(context);
+ case IITDescriptor::Integer:
+ return cir::IntType::get(context, descriptor.Integer_Width,
+ /*isSigned=*/true);
+ case IITDescriptor::Float:
+ return cir::SingleType::get(context);
+ case IITDescriptor::Double:
+ return cir::DoubleType::get(context);
+ default:
+ llvm_unreachable("NYI");
+ }
+}
+
+/// Helper function to correct integer signedness for intrinsic arguments.
+/// IIT always returns signed integers, but the actual intrinsic may expect
+/// unsigned integers based on the AST FunctionDecl parameter types.
+static mlir::Type getIntrinsicArgumentTypeFromAST(mlir::Type iitType,
+ const CallExpr *E,
+ unsigned argIndex,
+ mlir::MLIRContext *context) {
+ // If it's not an integer type, return as-is
+ auto intTy = dyn_cast<cir::IntType>(iitType);
+ if (!intTy)
+ return iitType;
+
+ // Get the FunctionDecl from the CallExpr
+ const FunctionDecl *FD = nullptr;
+ if (const auto *DRE =
+ dyn_cast<DeclRefExpr>(E->getCallee()->IgnoreImpCasts())) {
+ FD = dyn_cast<FunctionDecl>(DRE->getDecl());
+ }
+
+ // If we have FunctionDecl and this argument exists, check its signedness
+ if (FD && argIndex < FD->getNumParams()) {
+ QualType paramType = FD->getParamDecl(argIndex)->getType();
+ if (paramType->isUnsignedIntegerType()) {
+ // Create unsigned version of the type
+ return cir::IntType::get(context, intTy.getWidth(), /*isSigned=*/false);
+ }
+ }
+
+ // Default: keep IIT type (signed)
+ return iitType;
+}
+
+static mlir::Value getCorrectedPtr(mlir::Value argValue, mlir::Type expectedTy,
+ CIRGenBuilderTy &builder) {
+ auto ptrType = mlir::dyn_cast<cir::PointerType>(argValue.getType());
+ assert(ptrType && "expected pointer type");
+
+ auto expectedPtrType = mlir::cast<cir::PointerType>(expectedTy);
+ assert(ptrType.getPointee() != expectedPtrType.getPointee() &&
+ "types should not match");
+
+ if (ptrType.getAddrSpace() != expectedPtrType.getAddrSpace()) {
+ auto newPtrType = cir::PointerType::get(ptrType.getPointee(),
+ expectedPtrType.getAddrSpace());
+ return builder.createAddrSpaceCast(argValue, newPtrType);
+ }
+
+ return argValue;
+}
+
+static cir::FuncType getIntrinsicType(mlir::MLIRContext *context,
+ llvm::Intrinsic::ID id) {
+ using namespace llvm::Intrinsic;
+
+ SmallVector<IITDescriptor, 8> table;
+ getIntrinsicInfoTableEntries(id, table);
+
+ ArrayRef<IITDescriptor> tableRef = table;
+ mlir::Type resultTy = decodeFixedType(tableRef, context);
+
+ SmallVector<mlir::Type, 8> argTypes;
+ bool isVarArg = false;
+ while (!tableRef.empty()) {
+ auto kind = tableRef.front().Kind;
+ if (kind == IITDescriptor::VarArg) {
+ isVarArg = true;
+ break; // VarArg is last
+ }
+ argTypes.push_back(decodeFixedType(tableRef, context));
+ }
+
+ // CIR convention: no explicit void return type
+ if (isa<cir::VoidType>(resultTy))
+ return cir::FuncType::get(context, argTypes, /*optionalReturnType=*/nullptr,
+ isVarArg);
+
+ return cir::FuncType::get(context, argTypes, resultTy, isVarArg);
+}
+
RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
const CallExpr *e,
ReturnValueSlot returnValue) {
@@ -1798,6 +1901,103 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return emitLibraryCall(*this, fd, e,
cgm.getBuiltinLibFunction(fd, builtinID));
+ // See if we have a target specific intrinsic.
+ std::string name = getContext().BuiltinInfo.getName(builtinID);
+ Intrinsic::ID intrinsicID = Intrinsic::not_intrinsic;
+ StringRef prefix =
+ llvm::Triple::getArchTypePrefix(getTarget().getTriple().getArch());
+ if (!prefix.empty()) {
+ intrinsicID = Intrinsic::getIntrinsicForClangBuiltin(prefix.data(), name);
+ // NOTE we don't need to perform a compatibility flag check here since the
+ // intrinsics are declared in Builtins*.def via LANGBUILTIN which filter the
+ // MS builtins via ALL_MS_LANGUAGES and are filtered earlier.
+ if (intrinsicID == Intrinsic::not_intrinsic)
+ intrinsicID = Intrinsic::getIntrinsicForMSBuiltin(prefix.data(), name);
+ }
+
+ if (intrinsicID != Intrinsic::not_intrinsic) {
+ unsigned iceArguments = 0;
+ ASTContext::GetBuiltinTypeError error;
+ getContext().GetBuiltinType(builtinID, error, &iceArguments);
+ assert(error == ASTContext::GE_None && "Should not codegen an error");
+
+ llvm::StringRef name = llvm::Intrinsic::getName(intrinsicID);
+ // cir::LLVMIntrinsicCallOp expects intrinsic name to not have prefix
+ // "llvm." For example, `llvm.nvvm.barrier0` should be passed as
+ // `nvvm.barrier0`.
+ if (!name.consume_front("llvm."))
+ assert(false && "bad intrinsic name!");
+
+ cir::FuncType intrinsicType =
+ getIntrinsicType(&getMLIRContext(), intrinsicID);
+
+ SmallVector<mlir::Value> args;
+ for (unsigned i = 0; i < e->getNumArgs(); i++) {
+ mlir::Value argValue =
+ emitScalarOrConstFoldImmArg(iceArguments, i, e->getArg(i));
+ // If the intrinsic arg type is different from the builtin arg type
+ // we need to do a bit cast.
+ mlir::Type argType = argValue.getType();
+ mlir::Type expectedTy = intrinsicType.getInput(i);
+
+ // Use helper to get the correct integer type based on AST signedness
+ mlir::Type correctedExpectedTy =
+ getIntrinsicArgumentTypeFromAST(expectedTy, e, i, &getMLIRContext());
+
+ if (argType != correctedExpectedTy)
+ argValue = getCorrectedPtr(argValue, expectedTy, builder);
+
+ args.push_back(argValue);
+ }
+
+ cir::LLVMIntrinsicCallOp intrinsicCall = cir::LLVMIntrinsicCallOp::create(
+ builder, getLoc(e->getExprLoc()), builder.getStringAttr(name),
+ intrinsicType.getReturnType(), args);
+
+ // Convert the intrinsic result to the CallExpr/AST expected return type if
+ // they differ. This can happen when an intrinsic's IIT uses a signed
+ // integer type while the AST declares an unsigned type, or when an
+ // intrinsic returns an integer but the AST expects a pointer (or vice
+ // versa). Coerce conservatively so subsequent stores/verifications succeed.
+ mlir::Value intrinsicRes = intrinsicCall.getResult();
+ mlir::Type builtinReturnType = intrinsicRes.getType();
+ mlir::Type expectedRetTy = convertType(e->getType());
+
+ if (builtinReturnType != expectedRetTy) {
+ // Integer -> Integer or width/signage differences.
+ if (cir::IntType fromInt =
+ mlir::dyn_cast<cir::IntType>(builtinReturnType)) {
+ if (cir::IntType toInt = mlir::dyn_cast<cir::IntType>(expectedRetTy))
+ intrinsicRes = builder.createIntCast(intrinsicRes, expectedRetTy);
+ else if (mlir::dyn_cast<cir::PointerType>(expectedRetTy))
+ intrinsicRes = builder.createIntToPtr(intrinsicRes, expectedRetTy);
+ else
+ intrinsicRes = builder.createBitcast(intrinsicRes, expectedRetTy);
+
+ } else if (cir::PointerType fromPtr =
+ mlir::dyn_cast<cir::PointerType>(builtinReturnType)) {
+ if (mlir::dyn_cast<cir::IntType>(expectedRetTy))
+ intrinsicRes = builder.createPtrToInt(intrinsicRes, expectedRetTy);
+ else if (cir::PointerType toPtr =
+ mlir::dyn_cast<cir::PointerType>(expectedRetTy)) {
+ if (fromPtr.getAddrSpace() != toPtr.getAddrSpace())
+ intrinsicRes =
+ builder.createAddrSpaceCast(intrinsicRes, expectedRetTy);
+ else if (fromPtr.getPointee() != toPtr.getPointee())
+ intrinsicRes = builder.createBitcast(intrinsicRes, expectedRetTy);
+ } else
+ intrinsicRes = builder.createBitcast(intrinsicRes, expectedRetTy);
+
+ } else
+ intrinsicRes = builder.createBitcast(intrinsicRes, expectedRetTy);
+ }
+
+ if (isa<cir::VoidType>(expectedRetTy))
+ return RValue::get(nullptr);
+
+ return RValue::get(intrinsicRes);
+ }
+
// Some target-specific builtins can have aggregate return values, e.g.
// __builtin_arm_mve_vld2q_u32. So if the result is an aggregate, force
// returnValue to be non-null, so that the target-specific emission code can
diff --git a/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c
new file mode 100644
index 0000000000000..a1ac394110e39
--- /dev/null
+++ b/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c
@@ -0,0 +1,25 @@
+// RUN: %clang -target x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang -target x86_64-unknown-linux-gnu -fclangir -S -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang -target x86_64-unknown-linux-gnu -S -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+#include <x86intrin.h>
+
+unsigned long long test_rdpmc(int a) {
+ // CIR-LABEL: test_rdpmc
+ // CIR: cir.call @__rdpmc
+ // CIR: cir.store %{{.*}}, %{{.*}} : !u64i, !cir.ptr<!u64i>
+ // CIR: cir.return %{{.*}} : !u64i
+
+ // LLVM-LABEL: @test_rdpmc
+ // LLVM: call i64 @llvm.x86.rdpmc
+ // LLVM: store i64 %{{.*}}, ptr %{{.*}}, align 8
+ // LLVM: ret i64 %{{.*}}
+
+ // OGCG-LABEL: @test_rdpmc
+ // OGCG: call i64 @llvm.x86.rdpmc
+ // OGCG: ret i64 %{{.*}}
+ return _rdpmc(a);
+}
>From 2364a138f0d81d2b77e634ab3377ccbe7391b3fe Mon Sep 17 00:00:00 2001
From: Priyanshu <10b.priyanshu at gmail.com>
Date: Sun, 1 Feb 2026 22:33:03 +0530
Subject: [PATCH 2/6] Update test
---
clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c
index a1ac394110e39..a66302c50cec5 100644
--- a/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c
+++ b/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c
@@ -1,12 +1,16 @@
-// RUN: %clang -target x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: %clang_cc1 -ffreestanding -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
-// RUN: %clang -target x86_64-unknown-linux-gnu -fclangir -S -emit-llvm %s -o %t-cir.ll
+// RUN: %clang_cc1 -ffreestanding -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
-// RUN: %clang -target x86_64-unknown-linux-gnu -S -emit-llvm %s -o %t.ll
+// RUN: %clang_cc1 -ffreestanding -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
#include <x86intrin.h>
+// CIR-LABEL: @__rdpmc
+// CIR: cir.call_llvm_intrinsic "x86.rdpmc"
+// CIR: cir.cast integral %{{.*}} : !s64i -> !u64i
+
unsigned long long test_rdpmc(int a) {
// CIR-LABEL: test_rdpmc
// CIR: cir.call @__rdpmc
>From 1f519a7f68965474688a2d7c5fc920448caff7a8 Mon Sep 17 00:00:00 2001
From: Priyanshu <10b.priyanshu at gmail.com>
Date: Tue, 3 Feb 2026 19:26:22 +0530
Subject: [PATCH 3/6] Adress reviews and update test
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 122 +++++++-----------
.../CIR/CodeGenBuiltins/X86/rd-builtins.c | 3 +-
2 files changed, 47 insertions(+), 78 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 3f5cb6e440591..ebd3cee020bf8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -727,8 +727,10 @@ static RValue tryEmitFPMathIntrinsic(CIRGenFunction &cgf, const CallExpr *e,
return RValue::getIgnored();
}
+// FIXME: Remove cgf parameter when all descriptor kinds are implemented
static mlir::Type
-decodeFixedType(ArrayRef<llvm::Intrinsic::IITDescriptor> &infos,
+decodeFixedType(CIRGenFunction &cgf,
+ ArrayRef<llvm::Intrinsic::IITDescriptor> &infos,
mlir::MLIRContext *context) {
using namespace llvm::Intrinsic;
@@ -738,6 +740,8 @@ decodeFixedType(ArrayRef<llvm::Intrinsic::IITDescriptor> &infos,
switch (descriptor.Kind) {
case IITDescriptor::Void:
return cir::VoidType::get(context);
+ // If the intrinsic expects unsigned integers, the signedness is corrected in
+ // correctIntegerSignedness()
case IITDescriptor::Integer:
return cir::IntType::get(context, descriptor.Integer_Width,
/*isSigned=*/true);
@@ -746,39 +750,23 @@ decodeFixedType(ArrayRef<llvm::Intrinsic::IITDescriptor> &infos,
case IITDescriptor::Double:
return cir::DoubleType::get(context);
default:
- llvm_unreachable("NYI");
+ cgf.cgm.errorNYI("Unimplemented intrinsic type descriptor");
+ return cir::VoidType::get(context);
}
}
-/// Helper function to correct integer signedness for intrinsic arguments.
-/// IIT always returns signed integers, but the actual intrinsic may expect
-/// unsigned integers based on the AST FunctionDecl parameter types.
-static mlir::Type getIntrinsicArgumentTypeFromAST(mlir::Type iitType,
- const CallExpr *E,
- unsigned argIndex,
- mlir::MLIRContext *context) {
- // If it's not an integer type, return as-is
+/// Helper function to correct integer signedness for intrinsic arguments and
+/// return type. IIT always returns signed integers, but the actual intrinsic
+/// may expect unsigned integers based on the AST FunctionDecl parameter types.
+static mlir::Type correctIntegerSignedness(mlir::Type iitType, QualType astType,
+ mlir::MLIRContext *context) {
auto intTy = dyn_cast<cir::IntType>(iitType);
if (!intTy)
return iitType;
- // Get the FunctionDecl from the CallExpr
- const FunctionDecl *FD = nullptr;
- if (const auto *DRE =
- dyn_cast<DeclRefExpr>(E->getCallee()->IgnoreImpCasts())) {
- FD = dyn_cast<FunctionDecl>(DRE->getDecl());
- }
-
- // If we have FunctionDecl and this argument exists, check its signedness
- if (FD && argIndex < FD->getNumParams()) {
- QualType paramType = FD->getParamDecl(argIndex)->getType();
- if (paramType->isUnsignedIntegerType()) {
- // Create unsigned version of the type
- return cir::IntType::get(context, intTy.getWidth(), /*isSigned=*/false);
- }
+ if (astType->isUnsignedIntegerType()) {
+ return cir::IntType::get(context, intTy.getWidth(), /*isSigned=*/false);
}
-
- // Default: keep IIT type (signed)
return iitType;
}
@@ -788,8 +776,7 @@ static mlir::Value getCorrectedPtr(mlir::Value argValue, mlir::Type expectedTy,
assert(ptrType && "expected pointer type");
auto expectedPtrType = mlir::cast<cir::PointerType>(expectedTy);
- assert(ptrType.getPointee() != expectedPtrType.getPointee() &&
- "types should not match");
+ assert(ptrType != expectedPtrType && "types should not match");
if (ptrType.getAddrSpace() != expectedPtrType.getAddrSpace()) {
auto newPtrType = cir::PointerType::get(ptrType.getPointee(),
@@ -797,10 +784,11 @@ static mlir::Value getCorrectedPtr(mlir::Value argValue, mlir::Type expectedTy,
return builder.createAddrSpaceCast(argValue, newPtrType);
}
- return argValue;
+ return builder.createBitcast(argValue, expectedTy);
}
-static cir::FuncType getIntrinsicType(mlir::MLIRContext *context,
+static cir::FuncType getIntrinsicType(CIRGenFunction &cgf,
+ mlir::MLIRContext *context,
llvm::Intrinsic::ID id) {
using namespace llvm::Intrinsic;
@@ -808,17 +796,18 @@ static cir::FuncType getIntrinsicType(mlir::MLIRContext *context,
getIntrinsicInfoTableEntries(id, table);
ArrayRef<IITDescriptor> tableRef = table;
- mlir::Type resultTy = decodeFixedType(tableRef, context);
+ mlir::Type resultTy = decodeFixedType(cgf, tableRef, context);
SmallVector<mlir::Type, 8> argTypes;
bool isVarArg = false;
while (!tableRef.empty()) {
- auto kind = tableRef.front().Kind;
+ llvm::Intrinsic::IITDescriptor::IITDescriptorKind kind =
+ tableRef.front().Kind;
if (kind == IITDescriptor::VarArg) {
isVarArg = true;
break; // VarArg is last
}
- argTypes.push_back(decodeFixedType(tableRef, context));
+ argTypes.push_back(decodeFixedType(cgf, tableRef, context));
}
// CIR convention: no explicit void return type
@@ -836,8 +825,12 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
// See if we can constant fold this builtin. If so, don't emit it at all.
// TODO: Extend this handling to all builtin calls that we can constant-fold.
+ // Do not constant-fold immediate (target-specific) builtins; their ASTs can
+ // trigger the constant evaluator in cases it cannot safely handle.
+ // Skip EvaluateAsRValue for those.
Expr::EvalResult result;
- if (e->isPRValue() && e->EvaluateAsRValue(result, cgm.getASTContext()) &&
+ if (e->isPRValue() && !getContext().BuiltinInfo.isImmediate(builtinID) &&
+ e->EvaluateAsRValue(result, cgm.getASTContext()) &&
!result.hasSideEffects()) {
if (result.Val.isInt())
return RValue::get(builder.getConstInt(loc, result.Val.getInt()));
@@ -1929,9 +1922,10 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
assert(false && "bad intrinsic name!");
cir::FuncType intrinsicType =
- getIntrinsicType(&getMLIRContext(), intrinsicID);
+ getIntrinsicType(*this, &getMLIRContext(), intrinsicID);
SmallVector<mlir::Value> args;
+ const FunctionDecl *fd = e->getDirectCallee();
for (unsigned i = 0; i < e->getNumArgs(); i++) {
mlir::Value argValue =
emitScalarOrConstFoldImmArg(iceArguments, i, e->getArg(i));
@@ -1940,9 +1934,12 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
mlir::Type argType = argValue.getType();
mlir::Type expectedTy = intrinsicType.getInput(i);
- // Use helper to get the correct integer type based on AST signedness
- mlir::Type correctedExpectedTy =
- getIntrinsicArgumentTypeFromAST(expectedTy, e, i, &getMLIRContext());
+ // Correct integer signedness based on AST parameter type
+ mlir::Type correctedExpectedTy = expectedTy;
+ if (fd && i < fd->getNumParams()) {
+ correctedExpectedTy = correctIntegerSignedness(
+ expectedTy, fd->getParamDecl(i)->getType(), &getMLIRContext());
+ }
if (argType != correctedExpectedTy)
argValue = getCorrectedPtr(argValue, expectedTy, builder);
@@ -1950,49 +1947,22 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
args.push_back(argValue);
}
+ // Correct return type signedness based on AST return type before creating
+ // the call, avoiding unnecessary casts in the IR.
+ mlir::Type correctedReturnType = intrinsicType.getReturnType();
+ if (fd) {
+ correctedReturnType =
+ correctIntegerSignedness(intrinsicType.getReturnType(),
+ fd->getReturnType(), &getMLIRContext());
+ }
+
cir::LLVMIntrinsicCallOp intrinsicCall = cir::LLVMIntrinsicCallOp::create(
builder, getLoc(e->getExprLoc()), builder.getStringAttr(name),
- intrinsicType.getReturnType(), args);
+ correctedReturnType, args);
- // Convert the intrinsic result to the CallExpr/AST expected return type if
- // they differ. This can happen when an intrinsic's IIT uses a signed
- // integer type while the AST declares an unsigned type, or when an
- // intrinsic returns an integer but the AST expects a pointer (or vice
- // versa). Coerce conservatively so subsequent stores/verifications succeed.
mlir::Value intrinsicRes = intrinsicCall.getResult();
- mlir::Type builtinReturnType = intrinsicRes.getType();
- mlir::Type expectedRetTy = convertType(e->getType());
-
- if (builtinReturnType != expectedRetTy) {
- // Integer -> Integer or width/signage differences.
- if (cir::IntType fromInt =
- mlir::dyn_cast<cir::IntType>(builtinReturnType)) {
- if (cir::IntType toInt = mlir::dyn_cast<cir::IntType>(expectedRetTy))
- intrinsicRes = builder.createIntCast(intrinsicRes, expectedRetTy);
- else if (mlir::dyn_cast<cir::PointerType>(expectedRetTy))
- intrinsicRes = builder.createIntToPtr(intrinsicRes, expectedRetTy);
- else
- intrinsicRes = builder.createBitcast(intrinsicRes, expectedRetTy);
-
- } else if (cir::PointerType fromPtr =
- mlir::dyn_cast<cir::PointerType>(builtinReturnType)) {
- if (mlir::dyn_cast<cir::IntType>(expectedRetTy))
- intrinsicRes = builder.createPtrToInt(intrinsicRes, expectedRetTy);
- else if (cir::PointerType toPtr =
- mlir::dyn_cast<cir::PointerType>(expectedRetTy)) {
- if (fromPtr.getAddrSpace() != toPtr.getAddrSpace())
- intrinsicRes =
- builder.createAddrSpaceCast(intrinsicRes, expectedRetTy);
- else if (fromPtr.getPointee() != toPtr.getPointee())
- intrinsicRes = builder.createBitcast(intrinsicRes, expectedRetTy);
- } else
- intrinsicRes = builder.createBitcast(intrinsicRes, expectedRetTy);
-
- } else
- intrinsicRes = builder.createBitcast(intrinsicRes, expectedRetTy);
- }
- if (isa<cir::VoidType>(expectedRetTy))
+ if (isa<cir::VoidType>(correctedReturnType))
return RValue::get(nullptr);
return RValue::get(intrinsicRes);
diff --git a/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c
index a66302c50cec5..28d4d6f06ddd1 100644
--- a/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c
+++ b/clang/test/CIR/CodeGenBuiltins/X86/rd-builtins.c
@@ -8,8 +8,7 @@
#include <x86intrin.h>
// CIR-LABEL: @__rdpmc
-// CIR: cir.call_llvm_intrinsic "x86.rdpmc"
-// CIR: cir.cast integral %{{.*}} : !s64i -> !u64i
+// CIR: cir.call_llvm_intrinsic "x86.rdpmc" %{{.*}} : (!s32i) -> !u64i
unsigned long long test_rdpmc(int a) {
// CIR-LABEL: test_rdpmc
>From 121a5cb8de443c3909bcb35cf68b7150dde167aa Mon Sep 17 00:00:00 2001
From: Priyanshu Kumar <10b.priyanshu at gmail.com>
Date: Thu, 5 Feb 2026 00:19:57 +0530
Subject: [PATCH 4/6] Apply suggestion from @andykaylor
Co-authored-by: Andy Kaylor <akaylor at nvidia.com>
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index ebd3cee020bf8..ecc2b3eed6807 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1918,8 +1918,8 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
// cir::LLVMIntrinsicCallOp expects intrinsic name to not have prefix
// "llvm." For example, `llvm.nvvm.barrier0` should be passed as
// `nvvm.barrier0`.
- if (!name.consume_front("llvm."))
- assert(false && "bad intrinsic name!");
+ assert(name.starts_with("llvm.");
+ name = name.drop_front(/*strlen("llvm.")=*/5);
cir::FuncType intrinsicType =
getIntrinsicType(*this, &getMLIRContext(), intrinsicID);
>From e08fd3b96879b8fbd80eaa73635a7e85b8b0b02a Mon Sep 17 00:00:00 2001
From: Priyanshu <10b.priyanshu at gmail.com>
Date: Thu, 5 Feb 2026 01:07:49 +0530
Subject: [PATCH 5/6] Fix syntax error
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index ecc2b3eed6807..ed4150acdebfd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1918,7 +1918,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
// cir::LLVMIntrinsicCallOp expects intrinsic name to not have prefix
// "llvm." For example, `llvm.nvvm.barrier0` should be passed as
// `nvvm.barrier0`.
- assert(name.starts_with("llvm.");
+ assert(name.starts_with("llvm.") && "expected llvm. prefix");
name = name.drop_front(/*strlen("llvm.")=*/5);
cir::FuncType intrinsicType =
>From 13ef7eccd6db4136c312bbb40ba8b850bf09287d Mon Sep 17 00:00:00 2001
From: Priyanshu <10b.priyanshu at gmail.com>
Date: Thu, 5 Feb 2026 11:26:14 +0530
Subject: [PATCH 6/6] Add test
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 2 ++
clang/test/CIR/CodeGen/builtins-x86.c | 36 +++++++++++++++++++++++++
2 files changed, 38 insertions(+)
create mode 100644 clang/test/CIR/CodeGen/builtins-x86.c
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index ed4150acdebfd..d59dc35eba71f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -779,6 +779,8 @@ static mlir::Value getCorrectedPtr(mlir::Value argValue, mlir::Type expectedTy,
assert(ptrType != expectedPtrType && "types should not match");
if (ptrType.getAddrSpace() != expectedPtrType.getAddrSpace()) {
+ assert(!cir::MissingFeatures::addressSpace() &&
+ "address space handling not yet implemented");
auto newPtrType = cir::PointerType::get(ptrType.getPointee(),
expectedPtrType.getAddrSpace());
return builder.createAddrSpaceCast(argValue, newPtrType);
diff --git a/clang/test/CIR/CodeGen/builtins-x86.c b/clang/test/CIR/CodeGen/builtins-x86.c
new file mode 100644
index 0000000000000..0748147bd5b57
--- /dev/null
+++ b/clang/test/CIR/CodeGen/builtins-x86.c
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t-ogcg.ll
+// RUN: FileCheck --input-file=%t-ogcg.ll %s -check-prefix=OGCG
+
+void test_sfence(void) {
+ // CIR-LABEL: @test_sfence
+ // CIR: cir.call_llvm_intrinsic "x86.sse.sfence" : () -> !void
+ // LLVM-LABEL: @test_sfence
+ // LLVM: call void @llvm.x86.sse.sfence
+ // OGCG-LABEL: @test_sfence
+ // OGCG: call void @llvm.x86.sse.sfence
+ __builtin_ia32_sfence();
+}
+
+// CIR-LABEL: @test_lfence
+void test_lfence(void) {
+ // CIR: cir.call_llvm_intrinsic "x86.sse2.lfence" : () -> !void
+ // LLVM-LABEL: @test_lfence
+ // LLVM: call void @llvm.x86.sse2.lfence()
+ // OGCG-LABEL: @test_lfence
+ // OGCG: call void @llvm.x86.sse2.lfence()
+ __builtin_ia32_lfence();
+}
+
+void test_pause(void) {
+ // CIR-LABEL: @test_pause
+ // CIR: cir.call_llvm_intrinsic "x86.sse2.pause" : () -> !void
+ // LLVM-LABEL: @test_pause
+ // LLVM: call void @llvm.x86.sse2.pause()
+ // OGCG-LABEL: @test_pause
+ // OGCG: call void @llvm.x86.sse2.pause()
+ __builtin_ia32_pause();
+}
More information about the cfe-commits
mailing list