Skip to content

Conversation

@medhatiwari
Copy link
Contributor

Handle xsave/xrstor family of X86 builtins in ClangIR

Part of #167752

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Dec 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 5, 2025

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Medha Tiwari (medhatiwari)

Changes

Handle xsave/xrstor family of X86 builtins in ClangIR

Part of #167752


Full diff: https://github.com/llvm/llvm-project/pull/170877.diff

2 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp (+72-1)
  • (added) clang/test/CIR/CodeGenBuiltins/X86/xsave-builtins.c (+175)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
index 1c1ef4da20b0d..26630522de1b0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
@@ -533,9 +533,80 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID,
   case X86::BI__builtin_ia32_xsaves:
   case X86::BI__builtin_ia32_xsaves64:
   case X86::BI__builtin_ia32_xsetbv:
-  case X86::BI_xsetbv:
+  case X86::BI_xsetbv: {
+    mlir::Location loc = getLoc(expr->getExprLoc());
+    StringRef intrinsicName;
+    switch (builtinID) {
+    default:
+      llvm_unreachable("Unexpected builtin");
+    case X86::BI__builtin_ia32_xsave:
+      intrinsicName = "x86.xsave";
+      break;
+    case X86::BI__builtin_ia32_xsave64:
+      intrinsicName = "x86.xsave64";
+      break;
+    case X86::BI__builtin_ia32_xrstor:
+      intrinsicName = "x86.xrstor";
+      break;
+    case X86::BI__builtin_ia32_xrstor64:
+      intrinsicName = "x86.xrstor64";
+      break;
+    case X86::BI__builtin_ia32_xsaveopt:
+      intrinsicName = "x86.xsaveopt";
+      break;
+    case X86::BI__builtin_ia32_xsaveopt64:
+      intrinsicName = "x86.xsaveopt64";
+      break;
+    case X86::BI__builtin_ia32_xrstors:
+      intrinsicName = "x86.xrstors";
+      break;
+    case X86::BI__builtin_ia32_xrstors64:
+      intrinsicName = "x86.xrstors64";
+      break;
+    case X86::BI__builtin_ia32_xsavec:
+      intrinsicName = "x86.xsavec";
+      break;
+    case X86::BI__builtin_ia32_xsavec64:
+      intrinsicName = "x86.xsavec64";
+      break;
+    case X86::BI__builtin_ia32_xsaves:
+      intrinsicName = "x86.xsaves";
+      break;
+    case X86::BI__builtin_ia32_xsaves64:
+      intrinsicName = "x86.xsaves64";
+      break;
+    case X86::BI__builtin_ia32_xsetbv:
+    case X86::BI_xsetbv:
+      intrinsicName = "x86.xsetbv";
+      break;
+    }
+
+    // The xsave family of instructions take a 64-bit mask that specifies
+    // which processor state components to save/restore. The hardware expects
+    // this mask split into two 32-bit registers: EDX (high 32 bits) and
+    // EAX (low 32 bits).
+    mlir::Type i32Ty = builder.getSInt32Ty();
+    mlir::Type i64Ty = builder.getSInt64Ty();
+
+    // Mhi = (uint32_t)(ops[1] >> 32) - extract high 32 bits via right shift
+    cir::ConstantOp shift32 =
+        builder.getConstant(loc, cir::IntAttr::get(i64Ty, 32));
+    mlir::Value mhi =
+        builder.createShift(loc, ops[1], shift32.getResult(), /*isRight=*/true);
+    mhi = builder.createIntCast(mhi, i32Ty);
+
+    // Mlo = (uint32_t)ops[1] - extract low 32 bits by truncation
+    mlir::Value mlo = builder.createIntCast(ops[1], i32Ty);
+
+    return emitIntrinsicCallOp(builder, loc, intrinsicName, voidTy,
+                               mlir::ValueRange{ops[0], mhi, mlo});
+  }
   case X86::BI__builtin_ia32_xgetbv:
   case X86::BI_xgetbv:
+    // xgetbv reads the extended control register specified by ops[0] (ECX)
+    // and returns the 64-bit value
+    return emitIntrinsicCallOp(builder, getLoc(expr->getExprLoc()),
+                               "x86.xgetbv", builder.getUInt64Ty(), ops[0]);
   case X86::BI__builtin_ia32_storedqudi128_mask:
   case X86::BI__builtin_ia32_storedqusi128_mask:
   case X86::BI__builtin_ia32_storedquhi128_mask:
diff --git a/clang/test/CIR/CodeGenBuiltins/X86/xsave-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/xsave-builtins.c
new file mode 100644
index 0000000000000..484f6c402979d
--- /dev/null
+++ b/clang/test/CIR/CodeGenBuiltins/X86/xsave-builtins.c
@@ -0,0 +1,175 @@
+// RUN: %clang_cc1 -x c -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +xsave -target-feature +xsaveopt -target-feature +xsavec -target-feature +xsaves -fclangir -emit-cir -o %t.cir -Wall -Werror
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -x c -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +xsave -target-feature +xsaveopt -target-feature +xsavec -target-feature +xsaves -fclangir -emit-llvm -o %t.ll -Wall -Werror
+// RUN: FileCheck --check-prefixes=LLVM --input-file=%t.ll %s
+
+// RUN: %clang_cc1 -x c -ffreestanding %s -triple=x86_64-unknown-linux -target-feature +xsave -target-feature +xsaveopt -target-feature +xsavec -target-feature +xsaves -emit-llvm -o - -Wall -Werror | FileCheck %s -check-prefix=OGCG
+
+void test_xsave(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xsave
+  // CIR: cir.call_llvm_intrinsic "x86.xsave"
+
+  // LLVM-LABEL: test_xsave
+  // LLVM: call void @llvm.x86.xsave
+
+  // OGCG-LABEL: test_xsave
+  // OGCG: call void @llvm.x86.xsave
+  __builtin_ia32_xsave(p, m);
+}
+
+void test_xsave64(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xsave64
+  // CIR: cir.call_llvm_intrinsic "x86.xsave64"
+
+  // LLVM-LABEL: test_xsave64
+  // LLVM: call void @llvm.x86.xsave64
+
+  // OGCG-LABEL: test_xsave64
+  // OGCG: call void @llvm.x86.xsave64
+  __builtin_ia32_xsave64(p, m);
+}
+
+void test_xrstor(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xrstor
+  // CIR: cir.call_llvm_intrinsic "x86.xrstor"
+
+  // LLVM-LABEL: test_xrstor
+  // LLVM: call void @llvm.x86.xrstor
+
+  // OGCG-LABEL: test_xrstor
+  // OGCG: call void @llvm.x86.xrstor
+  __builtin_ia32_xrstor(p, m);
+}
+
+void test_xrstor64(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xrstor64
+  // CIR: cir.call_llvm_intrinsic "x86.xrstor64"
+
+  // LLVM-LABEL: test_xrstor64
+  // LLVM: call void @llvm.x86.xrstor64
+
+  // OGCG-LABEL: test_xrstor64
+  // OGCG: call void @llvm.x86.xrstor64
+  __builtin_ia32_xrstor64(p, m);
+}
+
+void test_xsaveopt(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xsaveopt
+  // CIR: cir.call_llvm_intrinsic "x86.xsaveopt"
+
+  // LLVM-LABEL: test_xsaveopt
+  // LLVM: call void @llvm.x86.xsaveopt
+
+  // OGCG-LABEL: test_xsaveopt
+  // OGCG: call void @llvm.x86.xsaveopt
+  __builtin_ia32_xsaveopt(p, m);
+}
+
+void test_xsaveopt64(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xsaveopt64
+  // CIR: cir.call_llvm_intrinsic "x86.xsaveopt64"
+
+  // LLVM-LABEL: test_xsaveopt64
+  // LLVM: call void @llvm.x86.xsaveopt64
+
+  // OGCG-LABEL: test_xsaveopt64
+  // OGCG: call void @llvm.x86.xsaveopt64
+  __builtin_ia32_xsaveopt64(p, m);
+}
+
+void test_xsavec(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xsavec
+  // CIR: cir.call_llvm_intrinsic "x86.xsavec"
+
+  // LLVM-LABEL: test_xsavec
+  // LLVM: call void @llvm.x86.xsavec
+
+  // OGCG-LABEL: test_xsavec
+  // OGCG: call void @llvm.x86.xsavec
+  __builtin_ia32_xsavec(p, m);
+}
+
+void test_xsavec64(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xsavec64
+  // CIR: cir.call_llvm_intrinsic "x86.xsavec64"
+
+  // LLVM-LABEL: test_xsavec64
+  // LLVM: call void @llvm.x86.xsavec64
+
+  // OGCG-LABEL: test_xsavec64
+  // OGCG: call void @llvm.x86.xsavec64
+  __builtin_ia32_xsavec64(p, m);
+}
+
+void test_xsaves(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xsaves
+  // CIR: cir.call_llvm_intrinsic "x86.xsaves"
+
+  // LLVM-LABEL: test_xsaves
+  // LLVM: call void @llvm.x86.xsaves
+
+  // OGCG-LABEL: test_xsaves
+  // OGCG: call void @llvm.x86.xsaves
+  __builtin_ia32_xsaves(p, m);
+}
+
+void test_xsaves64(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xsaves64
+  // CIR: cir.call_llvm_intrinsic "x86.xsaves64"
+
+  // LLVM-LABEL: test_xsaves64
+  // LLVM: call void @llvm.x86.xsaves64
+
+  // OGCG-LABEL: test_xsaves64
+  // OGCG: call void @llvm.x86.xsaves64
+  __builtin_ia32_xsaves64(p, m);
+}
+
+void test_xrstors(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xrstors
+  // CIR: cir.call_llvm_intrinsic "x86.xrstors"
+
+  // LLVM-LABEL: test_xrstors
+  // LLVM: call void @llvm.x86.xrstors
+
+  // OGCG-LABEL: test_xrstors
+  // OGCG: call void @llvm.x86.xrstors
+  __builtin_ia32_xrstors(p, m);
+}
+
+void test_xrstors64(void *p, unsigned long long m) {
+  // CIR-LABEL: test_xrstors64
+  // CIR: cir.call_llvm_intrinsic "x86.xrstors64"
+
+  // LLVM-LABEL: test_xrstors64
+  // LLVM: call void @llvm.x86.xrstors64
+
+  // OGCG-LABEL: test_xrstors64
+  // OGCG: call void @llvm.x86.xrstors64
+  __builtin_ia32_xrstors64(p, m);
+}
+
+unsigned long long test_xgetbv(unsigned int a) {
+  // CIR-LABEL: test_xgetbv
+  // CIR: cir.call_llvm_intrinsic "x86.xgetbv"
+
+  // LLVM-LABEL: test_xgetbv
+  // LLVM: call i64 @llvm.x86.xgetbv
+
+  // OGCG-LABEL: test_xgetbv
+  // OGCG: call i64 @llvm.x86.xgetbv
+  return __builtin_ia32_xgetbv(a);
+}
+
+void test_xsetbv(unsigned int a, unsigned long long m) {
+  // CIR-LABEL: test_xsetbv
+  // CIR: cir.call_llvm_intrinsic "x86.xsetbv"
+
+  // LLVM-LABEL: test_xsetbv
+  // LLVM: call void @llvm.x86.xsetbv
+
+  // OGCG-LABEL: test_xsetbv
+  // OGCG: call void @llvm.x86.xsetbv
+  __builtin_ia32_xsetbv(a, m);
+}
+

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! This looks pretty good. I just have one minor suggestion and a request for improving the test.

Comment on lines 592 to 593
cir::ConstantOp shift32 =
builder.getConstant(loc, cir::IntAttr::get(i64Ty, 32));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cir::ConstantOp shift32 =
builder.getConstant(loc, cir::IntAttr::get(i64Ty, 32));
cir::ConstantOp shift32 = builder.getSInt64(32, loc);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, changed to builder.getSInt64(32, loc). Also fixed the parameter name to /isShiftLeft=/false for clarity.


void test_xsave(void *p, unsigned long long m) {
// CIR-LABEL: test_xsave
// CIR: cir.call_llvm_intrinsic "x86.xsave"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add checks that show all the casts, shifts, and parameters?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added detailed CIR and LLVM checks for test_xsave showing the shift, casts, and intrinsic call with parameters. The pattern is the same for all other xsave variants, so I kept just the intrinsic name check for the rest to avoid redundancy.

@github-actions
Copy link

github-actions bot commented Dec 6, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Signed-off-by: Medha Tiwari <medhatiwari@ibm.com>
@medhatiwari medhatiwari force-pushed the clangir-xsave-builtins branch from 63fa958 to f53280c Compare December 6, 2025 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants