From e76ff90cd71aa4c60d54e9752bd53b82071ce0b6 Mon Sep 17 00:00:00 2001 From: Thibault-Monnier Date: Thu, 13 Nov 2025 21:28:20 +0100 Subject: [PATCH 1/3] Upstream CIR codegen for mxcsr x86 builtins --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 34 +++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 16 ++++++++ clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp | 37 +++++++++++++++++-- clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 23 ++++++++++++ clang/test/CIR/CodeGen/X86/sse-builtins.c | 36 ++++++++++++++++++ 6 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 clang/test/CIR/CodeGen/X86/sse-builtins.c diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 16258513239d9..0d639cb7b0b53 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2579,6 +2579,40 @@ def CIR_FuncOp : CIR_Op<"func", [ }]; } +//===----------------------------------------------------------------------===// +// LLVMIntrinsicCallOp +//===----------------------------------------------------------------------===// + +def CIR_LLVMIntrinsicCallOp : CIR_Op<"call_llvm_intrinsic"> { + let summary = "Call to llvm intrinsic functions that is not defined in CIR"; + let description = [{ + `cir.llvm.intrinsic` operation represents a call-like expression which has + return type and arguments that maps directly to a llvm intrinsic. + It only records intrinsic `intrinsic_name`. + }]; + + let results = (outs Optional:$result); + let arguments = (ins + StrAttr:$intrinsic_name, Variadic:$arg_ops); + + let skipDefaultBuilders = 1; + + let assemblyFormat = [{ + $intrinsic_name $arg_ops `:` functional-type($arg_ops, $result) attr-dict + }]; + + let builders = [ + OpBuilder<(ins "mlir::StringAttr":$intrinsic_name, "mlir::Type":$resType, + CArg<"mlir::ValueRange", "{}">:$operands), [{ + $_state.addAttribute("intrinsic_name", intrinsic_name); + $_state.addOperands(operands); + if (resType) + $_state.addTypes(resType); + }]>, + ]; + +} + //===----------------------------------------------------------------------===// // CallOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 4e6a5ee7ee210..b54256715be96 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -625,6 +625,22 @@ CIRGenFunction::emitTargetBuiltinExpr(unsigned builtinID, const CallExpr *e, getTarget().getTriple().getArch()); } +mlir::Value CIRGenFunction::emitScalarOrConstFoldImmArg( + const unsigned iceArguments, const unsigned idx, const Expr *argExpr) { + mlir::Value arg = {}; + if ((iceArguments & (1 << idx)) == 0) { + arg = emitScalarExpr(argExpr); + } else { + // If this is required to be a constant, constant fold it so that we + // know that the generated intrinsic gets a ConstantInt. + const std::optional result = + argExpr->getIntegerConstantExpr(getContext()); + assert(result && "Expected argument to be a constant"); + arg = builder.getConstInt(getLoc(argExpr->getSourceRange()), *result); + } + return arg; +} + /// Given a builtin id for a function like "__builtin_fabsf", return a Function* /// for "fabsf". cir::FuncOp CIRGenModule::getBuiltinLibFunction(const FunctionDecl *fd, diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp index 0198a9d4eb192..9c5ae38d8e0fa 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp @@ -16,7 +16,6 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/CIR/MissingFeatures.h" -#include "llvm/IR/IntrinsicsX86.h" using namespace clang; using namespace clang::CIRGen; @@ -43,6 +42,18 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID, // Find out if any arguments are required to be integer constant expressions. assert(!cir::MissingFeatures::handleBuiltinICEArguments()); + llvm::SmallVector ops; + + // Find out if any arguments are required to be integer constant expressions. + unsigned iceArguments = 0; + ASTContext::GetBuiltinTypeError error; + getContext().GetBuiltinType(builtinID, error, &iceArguments); + assert(error == ASTContext::GE_None && "Should not codegen an error"); + + for (auto [idx, arg] : llvm::enumerate(e->arguments())) { + ops.push_back(emitScalarOrConstFoldImmArg(iceArguments, idx, arg)); + } + switch (builtinID) { default: return {}; @@ -82,10 +93,30 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID, case X86::BI__builtin_ia32_vec_set_v16hi: case X86::BI__builtin_ia32_vec_set_v8si: case X86::BI__builtin_ia32_vec_set_v4di: + cgm.errorNYI(e->getSourceRange(), + std::string("unimplemented X86 builtin call: ") + + getContext().BuiltinInfo.getName(builtinID)); + return {}; case X86::BI_mm_setcsr: - case X86::BI__builtin_ia32_ldmxcsr: + case X86::BI__builtin_ia32_ldmxcsr: { + Address tmp = + createMemTemp(e->getArg(0)->getType(), getLoc(e->getExprLoc())); + builder.createStore(getLoc(e->getExprLoc()), ops[0], tmp); + return cir::LLVMIntrinsicCallOp::create( + builder, getLoc(e->getExprLoc()), + builder.getStringAttr("x86.sse.ldmxcsr"), builder.getVoidTy(), + tmp.getPointer()) + .getResult(); + } case X86::BI_mm_getcsr: - case X86::BI__builtin_ia32_stmxcsr: + case X86::BI__builtin_ia32_stmxcsr: { + Address tmp = createMemTemp(e->getType(), getLoc(e->getExprLoc())); + cir::LLVMIntrinsicCallOp::create(builder, getLoc(e->getExprLoc()), + builder.getStringAttr("x86.sse.stmxcsr"), + builder.getVoidTy(), tmp.getPointer()) + .getResult(); + return builder.createLoad(getLoc(e->getExprLoc()), tmp); + } case X86::BI__builtin_ia32_xsave: case X86::BI__builtin_ia32_xsave64: case X86::BI__builtin_ia32_xrstor: diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index f879e580989f7..c2ef98d2b25d6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1699,6 +1699,9 @@ class CIRGenFunction : public CIRGenTypeCache { void emitScalarInit(const clang::Expr *init, mlir::Location loc, LValue lvalue, bool capturedByInit = false); + mlir::Value emitScalarOrConstFoldImmArg(unsigned iceArguments, unsigned idx, + const Expr *argExpr); + void emitStaticVarDecl(const VarDecl &d, cir::GlobalLinkageKind linkage); void emitStoreOfComplex(mlir::Location loc, mlir::Value v, LValue dest, diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index b4afed7019417..6415160efa901 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -646,6 +646,29 @@ mlir::LogicalResult CIRToLLVMASinOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMLLVMIntrinsicCallOpLowering::matchAndRewrite( + cir::LLVMIntrinsicCallOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type llvmResTy = + getTypeConverter()->convertType(op->getResultTypes()[0]); + if (!llvmResTy) + return op.emitError("expected LLVM result type"); + StringRef name = op.getIntrinsicName(); + // Some llvm intrinsics require ElementType attribute to be attached to + // the argument of pointer type. That prevents us from generating LLVM IR + // because from LLVM dialect, we have LLVM IR like the below which fails + // LLVM IR verification. + // %3 = call i64 @llvm.aarch64.ldxr.p0(ptr %2) + // The expected LLVM IR should be like + // %3 = call i64 @llvm.aarch64.ldxr.p0(ptr elementtype(i32) %2) + // TODO(cir): MLIR LLVM dialect should handle this part as CIR has no way + // to set LLVM IR attribute. + assert(!cir::MissingFeatures::llvmIntrinsicElementTypeSupport()); + replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm." + name, llvmResTy, + adaptor.getOperands()); + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite( cir::AssumeOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { diff --git a/clang/test/CIR/CodeGen/X86/sse-builtins.c b/clang/test/CIR/CodeGen/X86/sse-builtins.c new file mode 100644 index 0000000000000..45d522b60a811 --- /dev/null +++ b/clang/test/CIR/CodeGen/X86/sse-builtins.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fclangir -emit-cir -o %t.cir -Wall -Werror +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -x c -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fclangir -emit-llvm -o %t.ll -Wall -Werror +// RUN: FileCheck --check-prefixes=LLVM --input-file=%t.ll %s + +// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fno-signed-char -fclangir -emit-cir -o %t.cir -Wall -Werror +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -x c++ -flax-vector-conversions=none -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +sse -fclangir -emit-llvm -o %t.ll -Wall -Werror +// RUN: FileCheck --check-prefixes=LLVM --input-file=%t.ll %s + +// This test mimics clang/test/CodeGen/X86/sse-builtins.c, which eventually +// CIR shall be able to support fully. + +#include + +void test_mm_setcsr(unsigned int A) { + // CIR-LABEL: test_mm_setcsr + // CIR: cir.store {{.*}}, {{.*}} : !u32i + // CIR: cir.call_llvm_intrinsic "x86.sse.ldmxcsr" {{.*}} : (!cir.ptr) -> !void + + // LLVM-LABEL: test_mm_setcsr + // LLVM: store i32 + // LLVM: call void @llvm.x86.sse.ldmxcsr(ptr {{.*}}) + _mm_setcsr(A); +} + +unsigned int test_mm_getcsr(void) { + // CIR-LABEL: test_mm_getcsr + // CIR: cir.call_llvm_intrinsic "x86.sse.stmxcsr" %{{.*}} : (!cir.ptr) -> !void + // CIR: cir.load {{.*}} : !cir.ptr, !u32i + + // LLVM-LABEL: test_mm_getcsr + // LLVM: call void @llvm.x86.sse.stmxcsr(ptr %{{.*}}) + // LLVM: load i32 + return _mm_getcsr(); +} From 65ec4ba4b0b7c226b29d3977a9e0922841a175fd Mon Sep 17 00:00:00 2001 From: Thibault-Monnier Date: Sat, 15 Nov 2025 15:04:05 +0100 Subject: [PATCH 2/3] Remove duplicate definition --- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 307db9da644f5..d88a4ad76f27b 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -670,29 +670,6 @@ mlir::LogicalResult CIRToLLVMASinOpLowering::matchAndRewrite( return mlir::success(); } -mlir::LogicalResult CIRToLLVMLLVMIntrinsicCallOpLowering::matchAndRewrite( - cir::LLVMIntrinsicCallOp op, OpAdaptor adaptor, - mlir::ConversionPatternRewriter &rewriter) const { - mlir::Type llvmResTy = - getTypeConverter()->convertType(op->getResultTypes()[0]); - if (!llvmResTy) - return op.emitError("expected LLVM result type"); - StringRef name = op.getIntrinsicName(); - // Some llvm intrinsics require ElementType attribute to be attached to - // the argument of pointer type. That prevents us from generating LLVM IR - // because from LLVM dialect, we have LLVM IR like the below which fails - // LLVM IR verification. - // %3 = call i64 @llvm.aarch64.ldxr.p0(ptr %2) - // The expected LLVM IR should be like - // %3 = call i64 @llvm.aarch64.ldxr.p0(ptr elementtype(i32) %2) - // TODO(cir): MLIR LLVM dialect should handle this part as CIR has no way - // to set LLVM IR attribute. - assert(!cir::MissingFeatures::llvmIntrinsicElementTypeSupport()); - replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm." + name, llvmResTy, - adaptor.getOperands()); - return mlir::success(); -} - mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite( cir::AssumeOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { From 712fdfd3e0e772a034c322d6d54316d804b65baa Mon Sep 17 00:00:00 2001 From: Thibault-Monnier Date: Sat, 15 Nov 2025 15:25:34 +0100 Subject: [PATCH 3/3] Apply suggested changes --- clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp | 24 +++++++++------------- clang/test/CIR/CodeGen/X86/sse-builtins.c | 8 ++++++++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp index b64bccf531ba6..8ab76688f65fa 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp @@ -140,23 +140,19 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID, return {}; case X86::BI_mm_setcsr: case X86::BI__builtin_ia32_ldmxcsr: { - Address tmp = - createMemTemp(e->getArg(0)->getType(), getLoc(e->getExprLoc())); - builder.createStore(getLoc(e->getExprLoc()), ops[0], tmp); - return cir::LLVMIntrinsicCallOp::create( - builder, getLoc(e->getExprLoc()), - builder.getStringAttr("x86.sse.ldmxcsr"), builder.getVoidTy(), - tmp.getPointer()) - .getResult(); + mlir::Location loc = getLoc(e->getExprLoc()); + Address tmp = createMemTemp(e->getArg(0)->getType(), loc); + builder.createStore(loc, ops[0], tmp); + return emitIntrinsicCallOp(*this, e, "x86.sse.ldmxcsr", builder.getVoidTy(), + tmp.getPointer()); } case X86::BI_mm_getcsr: case X86::BI__builtin_ia32_stmxcsr: { - Address tmp = createMemTemp(e->getType(), getLoc(e->getExprLoc())); - cir::LLVMIntrinsicCallOp::create(builder, getLoc(e->getExprLoc()), - builder.getStringAttr("x86.sse.stmxcsr"), - builder.getVoidTy(), tmp.getPointer()) - .getResult(); - return builder.createLoad(getLoc(e->getExprLoc()), tmp); + mlir::Location loc = getLoc(e->getExprLoc()); + Address tmp = createMemTemp(e->getType(), loc); + emitIntrinsicCallOp(*this, e, "x86.sse.stmxcsr", builder.getVoidTy(), + tmp.getPointer()); + return builder.createLoad(loc, tmp); } case X86::BI__builtin_ia32_xsave: case X86::BI__builtin_ia32_xsave64: diff --git a/clang/test/CIR/CodeGen/X86/sse-builtins.c b/clang/test/CIR/CodeGen/X86/sse-builtins.c index 59a743fe93f46..04e69a9990159 100644 --- a/clang/test/CIR/CodeGen/X86/sse-builtins.c +++ b/clang/test/CIR/CodeGen/X86/sse-builtins.c @@ -24,6 +24,10 @@ void test_mm_setcsr(unsigned int A) { // LLVM-LABEL: test_mm_setcsr // LLVM: store i32 // LLVM: call void @llvm.x86.sse.ldmxcsr(ptr {{.*}}) + + // OGCG-LABEL: test_mm_setcsr + // OGCG: store i32 + // OGCG: call void @llvm.x86.sse.ldmxcsr(ptr {{.*}}) _mm_setcsr(A); } @@ -35,6 +39,10 @@ unsigned int test_mm_getcsr(void) { // LLVM-LABEL: test_mm_getcsr // LLVM: call void @llvm.x86.sse.stmxcsr(ptr %{{.*}}) // LLVM: load i32 + + // OGCG-LABEL: test_mm_getcsr + // OGCG: call void @llvm.x86.sse.stmxcsr(ptr %{{.*}}) + // OGCG: load i32 return _mm_getcsr(); }