[llvm-branch-commits] [clang] [CIR] Implement function personality attribute and its lowering (PR #171001)
https://github.com/xlauko ready_for_review https://github.com/llvm/llvm-project/pull/171001 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [CIR] Implement function personality attribute and its lowering (PR #171001)
xlauko wrote: > [!WARNING] > This pull request is not mergeable via GitHub because a downstack PR is > open. Once all requirements are satisfied, merge this PR as a stack href="https://app.graphite.com/github/pr/llvm/llvm-project/171001?utm_source=stack-comment-downstack-mergeability-warning"; > >on Graphite. > https://graphite.dev/docs/merge-pull-requests";>Learn more * **#171001** https://app.graphite.com/github/pr/llvm/llvm-project/171001?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> 👈 https://app.graphite.com/github/pr/llvm/llvm-project/171001?utm_source=stack-comment-view-in-graphite"; target="_blank">(View in Graphite) * **#171000** https://app.graphite.com/github/pr/llvm/llvm-project/171000?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> * `main` This stack of pull requests is managed by https://graphite.dev?utm-source=stack-comment";>Graphite. Learn more about https://stacking.dev/?utm_source=stack-comment";>stacking. https://github.com/llvm/llvm-project/pull/171001 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[clang] release/21.x: [clang-format] Fix a regression in annotating star befo… (PR #170997)
https://github.com/HazardyKnusperkeks approved this pull request. https://github.com/llvm/llvm-project/pull/170997 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [CIR] Implement function personality attribute and its lowering (PR #171001)
https://github.com/xlauko created
https://github.com/llvm/llvm-project/pull/171001
None
>From 36b200464ffb87f36ef88c3751c4f1047a658b81 Mon Sep 17 00:00:00 2001
From: xlauko
Date: Sat, 6 Dec 2025 22:25:53 +0100
Subject: [PATCH] [CIR] Implement function personality attribute and its
lowering
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 5 +
clang/include/clang/CIR/MissingFeatures.h | 2 +-
clang/lib/CIR/CodeGen/CIRGenException.cpp | 20 --
clang/lib/CIR/CodeGen/CIRGenModule.cpp| 21 +++
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 18
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 20 --
clang/test/CIR/IR/func.cir| 8 +++
clang/test/CIR/Lowering/eh-inflight.cir | 9 +---
8 files changed, 77 insertions(+), 26 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index caa047a51b689..3d6de2a97d650 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2676,6 +2676,10 @@ def CIR_FuncOp : CIR_Op<"func", [
The `always_inline` attribute marks a function that should always be
inlined.
The `inline_hint` attribute suggests that the function should be inlined.
+The `personality` attribute specifies the personality function to use for
+exception handling. This is a symbol reference to the personality function
+(e.g., `@__gxx_personality_v0` for C++ exceptions).
+
Example:
```mlir
@@ -2722,6 +2726,7 @@ def CIR_FuncOp : CIR_Op<"func", [
OptionalAttr:$arg_attrs,
OptionalAttr:$res_attrs,
OptionalAttr:$aliasee,
+OptionalAttr:$personality,
CIR_OptionalPriorityAttr:$global_ctor_priority,
CIR_OptionalPriorityAttr:$global_dtor_priority,
OptionalAttr:$cxx_special_member
diff --git a/clang/include/clang/CIR/MissingFeatures.h
b/clang/include/clang/CIR/MissingFeatures.h
index 826a4b13f5c0c..95b3bfa58956e 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -93,7 +93,6 @@ struct MissingFeatures {
static bool opFuncNoReturn() { return false; }
static bool setFunctionAttributes() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }
- static bool setFunctionPersonality() { return false; }
// CallOp handling
static bool opCallAggregateArgs() { return false; }
@@ -274,6 +273,7 @@ struct MissingFeatures {
static bool fpConstraints() { return false; }
static bool generateDebugInfo() { return false; }
+ static bool getRuntimeFunctionDecl() { return false; }
static bool globalViewIndices() { return false; }
static bool globalViewIntLowering() { return false; }
static bool handleBuiltinICEArguments() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp
b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 375828421eb1b..53165bf3d1b83 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -185,6 +185,18 @@ const EHPersonality &EHPersonality::get(CIRGenFunction
&cgf) {
return get(cgf.cgm, dyn_cast_or_null(fg));
}
+static llvm::StringRef
+getPersonalityFn(CIRGenModule &cgm, const EHPersonality &personality) {
+ // Create the personality function type: i32 (...)
+ mlir::Type i32Ty = cgm.getBuilder().getI32Type();
+ auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true);
+
+ cir::FuncOp personalityFn = cgm.createRuntimeFunction(
+ funcTy, personality.personalityFn, mlir::ArrayAttr(), /*isLocal=*/true);
+
+ return personalityFn.getSymName();
+}
+
void CIRGenFunction::emitCXXThrowExpr(const CXXThrowExpr *e) {
const llvm::Triple &triple = getTarget().getTriple();
if (cgm.getLangOpts().OpenMPIsTargetDevice &&
@@ -640,10 +652,14 @@ void
CIRGenFunction::populateCatchHandlersIfRequired(cir::TryOp tryOp) {
assert(ehStack.requiresCatchOrCleanup());
assert(!ehStack.empty());
- assert(!cir::MissingFeatures::setFunctionPersonality());
+ const EHPersonality &personality = EHPersonality::get(*this);
+
+ // Set personality function if not already set
+ auto funcOp = mlir::cast(curFn);
+ if (!funcOp.getPersonality())
+funcOp.setPersonality(getPersonalityFn(cgm, personality));
// CIR does not cache landing pads.
- const EHPersonality &personality = EHPersonality::get(*this);
if (personality.usesFuncletPads()) {
cgm.errorNYI("getInvokeDestImpl: usesFuncletPads");
} else {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index e1894c040dd53..f784eb929248e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2301,14 +2301,27 @@ void CIRGenModule::setCXXSpecialMemberAttr(
}
}
+static void setWindowsItaniumDLLImport(CIRGenModule &cgm, bool isLocal,
+
[llvm-branch-commits] [clang] [CIR] Implement function personality attribute and its lowering (PR #171001)
llvmbot wrote:
@llvm/pr-subscribers-clang
Author: Henrich Lauko (xlauko)
Changes
---
Full diff: https://github.com/llvm/llvm-project/pull/171001.diff
8 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+5)
- (modified) clang/include/clang/CIR/MissingFeatures.h (+1-1)
- (modified) clang/lib/CIR/CodeGen/CIRGenException.cpp (+18-2)
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+17-4)
- (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+18)
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+4-16)
- (modified) clang/test/CIR/IR/func.cir (+8)
- (modified) clang/test/CIR/Lowering/eh-inflight.cir (+6-3)
``diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index caa047a51b689..3d6de2a97d650 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2676,6 +2676,10 @@ def CIR_FuncOp : CIR_Op<"func", [
The `always_inline` attribute marks a function that should always be
inlined.
The `inline_hint` attribute suggests that the function should be inlined.
+The `personality` attribute specifies the personality function to use for
+exception handling. This is a symbol reference to the personality function
+(e.g., `@__gxx_personality_v0` for C++ exceptions).
+
Example:
```mlir
@@ -2722,6 +2726,7 @@ def CIR_FuncOp : CIR_Op<"func", [
OptionalAttr:$arg_attrs,
OptionalAttr:$res_attrs,
OptionalAttr:$aliasee,
+OptionalAttr:$personality,
CIR_OptionalPriorityAttr:$global_ctor_priority,
CIR_OptionalPriorityAttr:$global_dtor_priority,
OptionalAttr:$cxx_special_member
diff --git a/clang/include/clang/CIR/MissingFeatures.h
b/clang/include/clang/CIR/MissingFeatures.h
index 826a4b13f5c0c..95b3bfa58956e 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -93,7 +93,6 @@ struct MissingFeatures {
static bool opFuncNoReturn() { return false; }
static bool setFunctionAttributes() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }
- static bool setFunctionPersonality() { return false; }
// CallOp handling
static bool opCallAggregateArgs() { return false; }
@@ -274,6 +273,7 @@ struct MissingFeatures {
static bool fpConstraints() { return false; }
static bool generateDebugInfo() { return false; }
+ static bool getRuntimeFunctionDecl() { return false; }
static bool globalViewIndices() { return false; }
static bool globalViewIntLowering() { return false; }
static bool handleBuiltinICEArguments() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp
b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 375828421eb1b..53165bf3d1b83 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -185,6 +185,18 @@ const EHPersonality &EHPersonality::get(CIRGenFunction
&cgf) {
return get(cgf.cgm, dyn_cast_or_null(fg));
}
+static llvm::StringRef
+getPersonalityFn(CIRGenModule &cgm, const EHPersonality &personality) {
+ // Create the personality function type: i32 (...)
+ mlir::Type i32Ty = cgm.getBuilder().getI32Type();
+ auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true);
+
+ cir::FuncOp personalityFn = cgm.createRuntimeFunction(
+ funcTy, personality.personalityFn, mlir::ArrayAttr(), /*isLocal=*/true);
+
+ return personalityFn.getSymName();
+}
+
void CIRGenFunction::emitCXXThrowExpr(const CXXThrowExpr *e) {
const llvm::Triple &triple = getTarget().getTriple();
if (cgm.getLangOpts().OpenMPIsTargetDevice &&
@@ -640,10 +652,14 @@ void
CIRGenFunction::populateCatchHandlersIfRequired(cir::TryOp tryOp) {
assert(ehStack.requiresCatchOrCleanup());
assert(!ehStack.empty());
- assert(!cir::MissingFeatures::setFunctionPersonality());
+ const EHPersonality &personality = EHPersonality::get(*this);
+
+ // Set personality function if not already set
+ auto funcOp = mlir::cast(curFn);
+ if (!funcOp.getPersonality())
+funcOp.setPersonality(getPersonalityFn(cgm, personality));
// CIR does not cache landing pads.
- const EHPersonality &personality = EHPersonality::get(*this);
if (personality.usesFuncletPads()) {
cgm.errorNYI("getInvokeDestImpl: usesFuncletPads");
} else {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index e1894c040dd53..f784eb929248e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2301,14 +2301,27 @@ void CIRGenModule::setCXXSpecialMemberAttr(
}
}
+static void setWindowsItaniumDLLImport(CIRGenModule &cgm, bool isLocal,
+ cir::FuncOp funcOp, StringRef name) {
+ // In Windows Itanium environments, try to mark runtime functions
+ // dllimport. For Mingw and MSVC, don't. We don't really know if the
[llvm-branch-commits] [clang] [CIR] Implement function personality attribute and its lowering (PR #171001)
github-actions[bot] wrote:
:warning: C/C++ code formatter, clang-format found issues in your code.
:warning:
You can test this locally with the following command:
``bash
git-clang-format --diff origin/main HEAD --extensions cpp,h --
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/CIRGenException.cpp
clang/lib/CIR/CodeGen/CIRGenModule.cpp clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp --diff_from_common_commit
``
:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:
View the diff from clang-format here.
``diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp
b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 53165bf3d..3a229d4b5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -185,15 +185,15 @@ const EHPersonality &EHPersonality::get(CIRGenFunction
&cgf) {
return get(cgf.cgm, dyn_cast_or_null(fg));
}
-static llvm::StringRef
-getPersonalityFn(CIRGenModule &cgm, const EHPersonality &personality) {
+static llvm::StringRef getPersonalityFn(CIRGenModule &cgm,
+const EHPersonality &personality) {
// Create the personality function type: i32 (...)
mlir::Type i32Ty = cgm.getBuilder().getI32Type();
auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true);
-
+
cir::FuncOp personalityFn = cgm.createRuntimeFunction(
funcTy, personality.personalityFn, mlir::ArrayAttr(), /*isLocal=*/true);
-
+
return personalityFn.getSymName();
}
@@ -653,7 +653,7 @@ void
CIRGenFunction::populateCatchHandlersIfRequired(cir::TryOp tryOp) {
assert(!ehStack.empty());
const EHPersonality &personality = EHPersonality::get(*this);
-
+
// Set personality function if not already set
auto funcOp = mlir::cast(curFn);
if (!funcOp.getPersonality())
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index c5f19ab58..61f385028 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1946,7 +1946,8 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser,
OperationState &state) {
mlir::StringAttr personalityAttr;
if (parser.parseOptionalSymbolName(personalityAttr).failed())
return failure();
-state.addAttribute(personalityNameAttr,
FlatSymbolRefAttr::get(personalityAttr));
+state.addAttribute(personalityNameAttr,
+ FlatSymbolRefAttr::get(personalityAttr));
if (parser.parseRParen().failed())
return failure();
}
``
https://github.com/llvm/llvm-project/pull/171001
___
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [CIR] Implement function personality attribute and its lowering (PR #171001)
https://github.com/xlauko updated
https://github.com/llvm/llvm-project/pull/171001
>From 68ca1b86b97366f0c1768cc60c02cd947710e20a Mon Sep 17 00:00:00 2001
From: xlauko
Date: Sat, 6 Dec 2025 22:25:53 +0100
Subject: [PATCH] [CIR] Implement function personality attribute and its
lowering
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 5 +
clang/include/clang/CIR/MissingFeatures.h | 2 +-
clang/lib/CIR/CodeGen/CIRGenException.cpp | 20 --
clang/lib/CIR/CodeGen/CIRGenModule.cpp| 21 +++
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 19 +
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 19 +++--
clang/test/CIR/IR/func.cir| 8 +++
clang/test/CIR/Lowering/eh-inflight.cir | 9 +---
8 files changed, 77 insertions(+), 26 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index caa047a51b689..3d6de2a97d650 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2676,6 +2676,10 @@ def CIR_FuncOp : CIR_Op<"func", [
The `always_inline` attribute marks a function that should always be
inlined.
The `inline_hint` attribute suggests that the function should be inlined.
+The `personality` attribute specifies the personality function to use for
+exception handling. This is a symbol reference to the personality function
+(e.g., `@__gxx_personality_v0` for C++ exceptions).
+
Example:
```mlir
@@ -2722,6 +2726,7 @@ def CIR_FuncOp : CIR_Op<"func", [
OptionalAttr:$arg_attrs,
OptionalAttr:$res_attrs,
OptionalAttr:$aliasee,
+OptionalAttr:$personality,
CIR_OptionalPriorityAttr:$global_ctor_priority,
CIR_OptionalPriorityAttr:$global_dtor_priority,
OptionalAttr:$cxx_special_member
diff --git a/clang/include/clang/CIR/MissingFeatures.h
b/clang/include/clang/CIR/MissingFeatures.h
index 826a4b13f5c0c..95b3bfa58956e 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -93,7 +93,6 @@ struct MissingFeatures {
static bool opFuncNoReturn() { return false; }
static bool setFunctionAttributes() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }
- static bool setFunctionPersonality() { return false; }
// CallOp handling
static bool opCallAggregateArgs() { return false; }
@@ -274,6 +273,7 @@ struct MissingFeatures {
static bool fpConstraints() { return false; }
static bool generateDebugInfo() { return false; }
+ static bool getRuntimeFunctionDecl() { return false; }
static bool globalViewIndices() { return false; }
static bool globalViewIntLowering() { return false; }
static bool handleBuiltinICEArguments() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp
b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 375828421eb1b..3a229d4b51160 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -185,6 +185,18 @@ const EHPersonality &EHPersonality::get(CIRGenFunction
&cgf) {
return get(cgf.cgm, dyn_cast_or_null(fg));
}
+static llvm::StringRef getPersonalityFn(CIRGenModule &cgm,
+const EHPersonality &personality) {
+ // Create the personality function type: i32 (...)
+ mlir::Type i32Ty = cgm.getBuilder().getI32Type();
+ auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true);
+
+ cir::FuncOp personalityFn = cgm.createRuntimeFunction(
+ funcTy, personality.personalityFn, mlir::ArrayAttr(), /*isLocal=*/true);
+
+ return personalityFn.getSymName();
+}
+
void CIRGenFunction::emitCXXThrowExpr(const CXXThrowExpr *e) {
const llvm::Triple &triple = getTarget().getTriple();
if (cgm.getLangOpts().OpenMPIsTargetDevice &&
@@ -640,10 +652,14 @@ void
CIRGenFunction::populateCatchHandlersIfRequired(cir::TryOp tryOp) {
assert(ehStack.requiresCatchOrCleanup());
assert(!ehStack.empty());
- assert(!cir::MissingFeatures::setFunctionPersonality());
+ const EHPersonality &personality = EHPersonality::get(*this);
+
+ // Set personality function if not already set
+ auto funcOp = mlir::cast(curFn);
+ if (!funcOp.getPersonality())
+funcOp.setPersonality(getPersonalityFn(cgm, personality));
// CIR does not cache landing pads.
- const EHPersonality &personality = EHPersonality::get(*this);
if (personality.usesFuncletPads()) {
cgm.errorNYI("getInvokeDestImpl: usesFuncletPads");
} else {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index e1894c040dd53..f784eb929248e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2301,14 +2301,27 @@ void CIRGenModule::setCXXSpecialMemberAttr(
}
}
+static void setWindowsItaniumDLLImport(CIRGenModule &cgm, bool isLocal,
+
[llvm-branch-commits] [clang] [CIR] Implement function personality attribute and its lowering (PR #171001)
https://github.com/xlauko updated
https://github.com/llvm/llvm-project/pull/171001
>From 68ca1b86b97366f0c1768cc60c02cd947710e20a Mon Sep 17 00:00:00 2001
From: xlauko
Date: Sat, 6 Dec 2025 22:25:53 +0100
Subject: [PATCH] [CIR] Implement function personality attribute and its
lowering
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 5 +
clang/include/clang/CIR/MissingFeatures.h | 2 +-
clang/lib/CIR/CodeGen/CIRGenException.cpp | 20 --
clang/lib/CIR/CodeGen/CIRGenModule.cpp| 21 +++
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 19 +
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 19 +++--
clang/test/CIR/IR/func.cir| 8 +++
clang/test/CIR/Lowering/eh-inflight.cir | 9 +---
8 files changed, 77 insertions(+), 26 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index caa047a51b689..3d6de2a97d650 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2676,6 +2676,10 @@ def CIR_FuncOp : CIR_Op<"func", [
The `always_inline` attribute marks a function that should always be
inlined.
The `inline_hint` attribute suggests that the function should be inlined.
+The `personality` attribute specifies the personality function to use for
+exception handling. This is a symbol reference to the personality function
+(e.g., `@__gxx_personality_v0` for C++ exceptions).
+
Example:
```mlir
@@ -2722,6 +2726,7 @@ def CIR_FuncOp : CIR_Op<"func", [
OptionalAttr:$arg_attrs,
OptionalAttr:$res_attrs,
OptionalAttr:$aliasee,
+OptionalAttr:$personality,
CIR_OptionalPriorityAttr:$global_ctor_priority,
CIR_OptionalPriorityAttr:$global_dtor_priority,
OptionalAttr:$cxx_special_member
diff --git a/clang/include/clang/CIR/MissingFeatures.h
b/clang/include/clang/CIR/MissingFeatures.h
index 826a4b13f5c0c..95b3bfa58956e 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -93,7 +93,6 @@ struct MissingFeatures {
static bool opFuncNoReturn() { return false; }
static bool setFunctionAttributes() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }
- static bool setFunctionPersonality() { return false; }
// CallOp handling
static bool opCallAggregateArgs() { return false; }
@@ -274,6 +273,7 @@ struct MissingFeatures {
static bool fpConstraints() { return false; }
static bool generateDebugInfo() { return false; }
+ static bool getRuntimeFunctionDecl() { return false; }
static bool globalViewIndices() { return false; }
static bool globalViewIntLowering() { return false; }
static bool handleBuiltinICEArguments() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp
b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 375828421eb1b..3a229d4b51160 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -185,6 +185,18 @@ const EHPersonality &EHPersonality::get(CIRGenFunction
&cgf) {
return get(cgf.cgm, dyn_cast_or_null(fg));
}
+static llvm::StringRef getPersonalityFn(CIRGenModule &cgm,
+const EHPersonality &personality) {
+ // Create the personality function type: i32 (...)
+ mlir::Type i32Ty = cgm.getBuilder().getI32Type();
+ auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true);
+
+ cir::FuncOp personalityFn = cgm.createRuntimeFunction(
+ funcTy, personality.personalityFn, mlir::ArrayAttr(), /*isLocal=*/true);
+
+ return personalityFn.getSymName();
+}
+
void CIRGenFunction::emitCXXThrowExpr(const CXXThrowExpr *e) {
const llvm::Triple &triple = getTarget().getTriple();
if (cgm.getLangOpts().OpenMPIsTargetDevice &&
@@ -640,10 +652,14 @@ void
CIRGenFunction::populateCatchHandlersIfRequired(cir::TryOp tryOp) {
assert(ehStack.requiresCatchOrCleanup());
assert(!ehStack.empty());
- assert(!cir::MissingFeatures::setFunctionPersonality());
+ const EHPersonality &personality = EHPersonality::get(*this);
+
+ // Set personality function if not already set
+ auto funcOp = mlir::cast(curFn);
+ if (!funcOp.getPersonality())
+funcOp.setPersonality(getPersonalityFn(cgm, personality));
// CIR does not cache landing pads.
- const EHPersonality &personality = EHPersonality::get(*this);
if (personality.usesFuncletPads()) {
cgm.errorNYI("getInvokeDestImpl: usesFuncletPads");
} else {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index e1894c040dd53..f784eb929248e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2301,14 +2301,27 @@ void CIRGenModule::setCXXSpecialMemberAttr(
}
}
+static void setWindowsItaniumDLLImport(CIRGenModule &cgm, bool isLocal,
+
[llvm-branch-commits] [clang] release/21.x: [clang-format] Don't swap `(const override)` with QAS_Right (#167191) (PR #170966)
https://github.com/owenca approved this pull request. https://github.com/llvm/llvm-project/pull/170966 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] release/21.x: [clang-format] Fix a regression in annotating star befo… (PR #170997)
https://github.com/owenca milestoned https://github.com/llvm/llvm-project/pull/170997 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] release/21.x: [clang-format] Fix a regression in annotating star befo… (PR #170997)
https://github.com/owenca created
https://github.com/llvm/llvm-project/pull/170997
…re lambda (#170969)
Backport 4930e94011f6c62231de880273821d453dae0f14
>From 0dc7b74ee5026614cb85b90789e45fa4d2f25279 Mon Sep 17 00:00:00 2001
From: owenca
Date: Sat, 6 Dec 2025 13:32:11 -0800
Subject: [PATCH] release/21.x: [clang-format] Fix a regression in annotating
star before lambda (#170969)
Backport 4930e94011f6c62231de880273821d453dae0f14
---
clang/lib/Format/UnwrappedLineParser.cpp | 8
clang/unittests/Format/TokenAnnotatorTest.cpp | 6 ++
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp
b/clang/lib/Format/UnwrappedLineParser.cpp
index 934605733542f..3df071a197c67 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -2385,17 +2385,17 @@ bool UnwrappedLineParser::tryToParseLambdaIntroducer() {
const FormatToken *LeftSquare = FormatTok;
nextToken();
if (Previous) {
+const auto *PrevPrev = Previous->getPreviousNonComment();
+if (Previous->is(tok::star) && PrevPrev && PrevPrev->isTypeName(LangOpts))
+ return false;
if (Previous->closesScope()) {
// Not a potential C-style cast.
if (Previous->isNot(tok::r_paren))
return false;
- const auto *BeforeRParen = Previous->getPreviousNonComment();
// Lambdas can be cast to function types only, e.g.
`std::function`
// and `int (*)()`.
- if (!BeforeRParen || !BeforeRParen->isOneOf(tok::greater, tok::r_paren))
+ if (!PrevPrev || !PrevPrev->isOneOf(tok::greater, tok::r_paren))
return false;
-} else if (Previous->is(tok::star)) {
- Previous = Previous->getPreviousNonComment();
}
if (Previous && Previous->Tok.getIdentifierInfo() &&
!Previous->isOneOf(tok::kw_return, tok::kw_co_await, tok::kw_co_yield,
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp
b/clang/unittests/Format/TokenAnnotatorTest.cpp
index 04dc69180960c..810c716b7a411 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -2248,6 +2248,12 @@ TEST_F(TokenAnnotatorTest, UnderstandsLambdas) {
EXPECT_TOKEN(Tokens[3], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[5], tok::l_paren, TT_LambdaDefinitionLParen);
EXPECT_TOKEN(Tokens[10], tok::l_square, TT_ArraySubscriptLSquare);
+
+ Tokens = annotate("foo = bar * [] { return 2; }();");
+ ASSERT_EQ(Tokens.size(), 15u) << Tokens;
+ EXPECT_TOKEN(Tokens[3], tok::star, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[4], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_LambdaLBrace);
}
TEST_F(TokenAnnotatorTest, UnderstandsFunctionAnnotations) {
___
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] release/21.x: [clang-format] Fix a regression in annotating star befo… (PR #170997)
llvmbot wrote:
@llvm/pr-subscribers-clang-format
Author: owenca (owenca)
Changes
…re lambda (#170969)
Backport 4930e94011f6c62231de880273821d453dae0f14
---
Full diff: https://github.com/llvm/llvm-project/pull/170997.diff
2 Files Affected:
- (modified) clang/lib/Format/UnwrappedLineParser.cpp (+4-4)
- (modified) clang/unittests/Format/TokenAnnotatorTest.cpp (+6)
``diff
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp
b/clang/lib/Format/UnwrappedLineParser.cpp
index 934605733542f..3df071a197c67 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -2385,17 +2385,17 @@ bool UnwrappedLineParser::tryToParseLambdaIntroducer() {
const FormatToken *LeftSquare = FormatTok;
nextToken();
if (Previous) {
+const auto *PrevPrev = Previous->getPreviousNonComment();
+if (Previous->is(tok::star) && PrevPrev && PrevPrev->isTypeName(LangOpts))
+ return false;
if (Previous->closesScope()) {
// Not a potential C-style cast.
if (Previous->isNot(tok::r_paren))
return false;
- const auto *BeforeRParen = Previous->getPreviousNonComment();
// Lambdas can be cast to function types only, e.g.
`std::function`
// and `int (*)()`.
- if (!BeforeRParen || !BeforeRParen->isOneOf(tok::greater, tok::r_paren))
+ if (!PrevPrev || !PrevPrev->isOneOf(tok::greater, tok::r_paren))
return false;
-} else if (Previous->is(tok::star)) {
- Previous = Previous->getPreviousNonComment();
}
if (Previous && Previous->Tok.getIdentifierInfo() &&
!Previous->isOneOf(tok::kw_return, tok::kw_co_await, tok::kw_co_yield,
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp
b/clang/unittests/Format/TokenAnnotatorTest.cpp
index 04dc69180960c..810c716b7a411 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -2248,6 +2248,12 @@ TEST_F(TokenAnnotatorTest, UnderstandsLambdas) {
EXPECT_TOKEN(Tokens[3], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[5], tok::l_paren, TT_LambdaDefinitionLParen);
EXPECT_TOKEN(Tokens[10], tok::l_square, TT_ArraySubscriptLSquare);
+
+ Tokens = annotate("foo = bar * [] { return 2; }();");
+ ASSERT_EQ(Tokens.size(), 15u) << Tokens;
+ EXPECT_TOKEN(Tokens[3], tok::star, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[4], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_LambdaLBrace);
}
TEST_F(TokenAnnotatorTest, UnderstandsFunctionAnnotations) {
``
https://github.com/llvm/llvm-project/pull/170997
___
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [llvm] [RFC][LLVM][Clang] Add LLVM plugin hook for back-ends (PR #170846)
https://github.com/aengelke edited https://github.com/llvm/llvm-project/pull/170846 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [llvm] [RFC][LLVM][Clang] Add LLVM plugin hook for back-ends (PR #170846)
https://github.com/aengelke updated
https://github.com/llvm/llvm-project/pull/170846
>From 89e9b4a5863e957971a3febc95862c1d5fe43f28 Mon Sep 17 00:00:00 2001
From: Alexis Engelke
Date: Fri, 5 Dec 2025 12:33:55 +
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
=?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.5-bogner
---
clang/lib/CodeGen/BackendUtil.cpp | 33 +++
llvm/include/llvm/Passes/PassPlugin.h | 31 +
llvm/lib/Passes/PassPlugin.cpp| 5
3 files changed, 49 insertions(+), 20 deletions(-)
diff --git a/clang/lib/CodeGen/BackendUtil.cpp
b/clang/lib/CodeGen/BackendUtil.cpp
index 97bc063ad34e5..188ea36d44523 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -144,6 +144,7 @@ class EmitAssemblyHelper {
const LangOptions &LangOpts;
llvm::Module *TheModule;
IntrusiveRefCntPtr VFS;
+ llvm::SmallVector Plugins;
std::unique_ptr OS;
@@ -973,16 +974,9 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
}
#endif
}
- // Attempt to load pass plugins and register their callbacks with PB.
- for (auto &PluginFN : CodeGenOpts.PassPlugins) {
-auto PassPlugin = PassPlugin::Load(PluginFN);
-if (PassPlugin) {
- PassPlugin->registerPassBuilderCallbacks(PB);
-} else {
- Diags.Report(diag::err_fe_unable_to_load_plugin)
- << PluginFN << toString(PassPlugin.takeError());
-}
- }
+ // Register plugin callbacks with PB.
+ for (auto &Plugin : Plugins)
+Plugin.registerPassBuilderCallbacks(PB);
for (const auto &PassCallback : CodeGenOpts.PassBuilderCallbacks)
PassCallback(PB);
#define HANDLE_EXTENSION(Ext)
\
@@ -1211,6 +1205,14 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
void EmitAssemblyHelper::RunCodegenPipeline(
BackendAction Action, std::unique_ptr &OS,
std::unique_ptr &DwoOS) {
+ // Invoke pre-codegen callback from plugin, which might want to take over the
+ // entire code generation itself.
+ for (auto &Plugin : Plugins) {
+CodeGenFileType CGFT = getCodeGenFileType(Action);
+if (Plugin.invokePreCodeGenCallback(*TheModule, *TM, CGFT, *OS))
+ return;
+ }
+
// We still use the legacy PM to run the codegen pipeline since the new PM
// does not work with the codegen pipeline.
// FIXME: make the new PM work with the codegen pipeline.
@@ -1274,6 +1276,17 @@ void EmitAssemblyHelper::emitAssembly(BackendAction
Action,
// Before executing passes, print the final values of the LLVM options.
cl::PrintOptionValues();
+ // Attempt to load pass plugins.
+ for (auto &PluginFN : CodeGenOpts.PassPlugins) {
+auto PassPlugin = PassPlugin::Load(PluginFN);
+if (PassPlugin) {
+ Plugins.push_back(std::move(*PassPlugin));
+} else {
+ Diags.Report(diag::err_fe_unable_to_load_plugin)
+ << PluginFN << toString(PassPlugin.takeError());
+}
+ }
+
std::unique_ptr ThinLinkOS, DwoOS;
RunOptimizationPipeline(Action, OS, ThinLinkOS, BC);
RunCodegenPipeline(Action, OS, DwoOS);
diff --git a/llvm/include/llvm/Passes/PassPlugin.h
b/llvm/include/llvm/Passes/PassPlugin.h
index 947504bc207a7..9ca0b4c29ed96 100644
--- a/llvm/include/llvm/Passes/PassPlugin.h
+++ b/llvm/include/llvm/Passes/PassPlugin.h
@@ -14,6 +14,7 @@
#define LLVM_PASSES_PASSPLUGIN_H
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
@@ -21,7 +22,9 @@
#include
namespace llvm {
+class Module;
class PassBuilder;
+class TargetMachine;
/// \macro LLVM_PLUGIN_API_VERSION
/// Identifies the API version understood by this plugin.
@@ -30,14 +33,15 @@ class PassBuilder;
/// against that of the plugin. A mismatch is an error. The supported version
/// will be incremented for ABI-breaking changes to the \c
PassPluginLibraryInfo
/// struct, i.e. when callbacks are added, removed, or reordered.
-#define LLVM_PLUGIN_API_VERSION 1
+#define LLVM_PLUGIN_API_VERSION 2
extern "C" {
/// Information about the plugin required to load its passes
///
/// This struct defines the core interface for pass plugins and is supposed to
-/// be filled out by plugin implementors. LLVM-side users of a plugin are
-/// expected to use the \c PassPlugin class below to interface with it.
+/// be filled out by plugin implementors. Unused function pointers can be set
to
+/// nullptr. LLVM-side users of a plugin are expected to use the \c PassPlugin
+/// class below to interface with it.
struct PassPluginLibraryInfo {
/// The API version understood by this plugin, usually \c
/// LLVM_PLUGIN_API_VERSION
@@ -49,7 +53,14 @@ struct PassPluginLibraryInfo {
/// The callback for registering plugin passes with a \c PassBuilder
/// ins
[llvm-branch-commits] [clang] [llvm] [RFC][LLVM][Clang] Add LLVM plugin hook for back-ends (PR #170846)
https://github.com/aengelke updated
https://github.com/llvm/llvm-project/pull/170846
>From 89e9b4a5863e957971a3febc95862c1d5fe43f28 Mon Sep 17 00:00:00 2001
From: Alexis Engelke
Date: Fri, 5 Dec 2025 12:33:55 +
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
=?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.5-bogner
---
clang/lib/CodeGen/BackendUtil.cpp | 33 +++
llvm/include/llvm/Passes/PassPlugin.h | 31 +
llvm/lib/Passes/PassPlugin.cpp| 5
3 files changed, 49 insertions(+), 20 deletions(-)
diff --git a/clang/lib/CodeGen/BackendUtil.cpp
b/clang/lib/CodeGen/BackendUtil.cpp
index 97bc063ad34e5..188ea36d44523 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -144,6 +144,7 @@ class EmitAssemblyHelper {
const LangOptions &LangOpts;
llvm::Module *TheModule;
IntrusiveRefCntPtr VFS;
+ llvm::SmallVector Plugins;
std::unique_ptr OS;
@@ -973,16 +974,9 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
}
#endif
}
- // Attempt to load pass plugins and register their callbacks with PB.
- for (auto &PluginFN : CodeGenOpts.PassPlugins) {
-auto PassPlugin = PassPlugin::Load(PluginFN);
-if (PassPlugin) {
- PassPlugin->registerPassBuilderCallbacks(PB);
-} else {
- Diags.Report(diag::err_fe_unable_to_load_plugin)
- << PluginFN << toString(PassPlugin.takeError());
-}
- }
+ // Register plugin callbacks with PB.
+ for (auto &Plugin : Plugins)
+Plugin.registerPassBuilderCallbacks(PB);
for (const auto &PassCallback : CodeGenOpts.PassBuilderCallbacks)
PassCallback(PB);
#define HANDLE_EXTENSION(Ext)
\
@@ -1211,6 +1205,14 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
void EmitAssemblyHelper::RunCodegenPipeline(
BackendAction Action, std::unique_ptr &OS,
std::unique_ptr &DwoOS) {
+ // Invoke pre-codegen callback from plugin, which might want to take over the
+ // entire code generation itself.
+ for (auto &Plugin : Plugins) {
+CodeGenFileType CGFT = getCodeGenFileType(Action);
+if (Plugin.invokePreCodeGenCallback(*TheModule, *TM, CGFT, *OS))
+ return;
+ }
+
// We still use the legacy PM to run the codegen pipeline since the new PM
// does not work with the codegen pipeline.
// FIXME: make the new PM work with the codegen pipeline.
@@ -1274,6 +1276,17 @@ void EmitAssemblyHelper::emitAssembly(BackendAction
Action,
// Before executing passes, print the final values of the LLVM options.
cl::PrintOptionValues();
+ // Attempt to load pass plugins.
+ for (auto &PluginFN : CodeGenOpts.PassPlugins) {
+auto PassPlugin = PassPlugin::Load(PluginFN);
+if (PassPlugin) {
+ Plugins.push_back(std::move(*PassPlugin));
+} else {
+ Diags.Report(diag::err_fe_unable_to_load_plugin)
+ << PluginFN << toString(PassPlugin.takeError());
+}
+ }
+
std::unique_ptr ThinLinkOS, DwoOS;
RunOptimizationPipeline(Action, OS, ThinLinkOS, BC);
RunCodegenPipeline(Action, OS, DwoOS);
diff --git a/llvm/include/llvm/Passes/PassPlugin.h
b/llvm/include/llvm/Passes/PassPlugin.h
index 947504bc207a7..9ca0b4c29ed96 100644
--- a/llvm/include/llvm/Passes/PassPlugin.h
+++ b/llvm/include/llvm/Passes/PassPlugin.h
@@ -14,6 +14,7 @@
#define LLVM_PASSES_PASSPLUGIN_H
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
@@ -21,7 +22,9 @@
#include
namespace llvm {
+class Module;
class PassBuilder;
+class TargetMachine;
/// \macro LLVM_PLUGIN_API_VERSION
/// Identifies the API version understood by this plugin.
@@ -30,14 +33,15 @@ class PassBuilder;
/// against that of the plugin. A mismatch is an error. The supported version
/// will be incremented for ABI-breaking changes to the \c
PassPluginLibraryInfo
/// struct, i.e. when callbacks are added, removed, or reordered.
-#define LLVM_PLUGIN_API_VERSION 1
+#define LLVM_PLUGIN_API_VERSION 2
extern "C" {
/// Information about the plugin required to load its passes
///
/// This struct defines the core interface for pass plugins and is supposed to
-/// be filled out by plugin implementors. LLVM-side users of a plugin are
-/// expected to use the \c PassPlugin class below to interface with it.
+/// be filled out by plugin implementors. Unused function pointers can be set
to
+/// nullptr. LLVM-side users of a plugin are expected to use the \c PassPlugin
+/// class below to interface with it.
struct PassPluginLibraryInfo {
/// The API version understood by this plugin, usually \c
/// LLVM_PLUGIN_API_VERSION
@@ -49,7 +53,14 @@ struct PassPluginLibraryInfo {
/// The callback for registering plugin passes with a \c PassBuilder
/// ins
[llvm-branch-commits] [clang] [llvm] [RFC][LLVM][Clang] Add LLVM plugin hook for back-ends (PR #170846)
llvmbot wrote:
@llvm/pr-subscribers-clang-codegen
Author: Alexis Engelke (aengelke)
Changes
Add a mechanism to permit plugins running code between optimizations and
the back-end passes. Implement this through the LLVM plug-in mechanism
to make permit plugins to be written independently of the front-end.
The primary motivation for this point is TPDE-LLVM, which substitutes
the LLVM back-end (optionally falling back to it for unsupported IR). We
have been distributing a Clang patch; but requiring a custom-build
toolchain is impracticable for many users.
---
Full diff: https://github.com/llvm/llvm-project/pull/170846.diff
6 Files Affected:
- (modified) clang/lib/CodeGen/BackendUtil.cpp (+23-10)
- (modified) llvm/examples/Bye/Bye.cpp (+35-15)
- (modified) llvm/include/llvm/Passes/PassPlugin.h (+34-5)
- (modified) llvm/lib/Passes/PassPlugin.cpp (-5)
- (added) llvm/test/Feature/codegen-plugin.ll (+18)
- (modified) llvm/tools/llc/llc.cpp (+28-5)
``diff
diff --git a/clang/lib/CodeGen/BackendUtil.cpp
b/clang/lib/CodeGen/BackendUtil.cpp
index af3480d5755f1..451c6c990bf40 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -148,6 +148,7 @@ class EmitAssemblyHelper {
const LangOptions &LangOpts;
llvm::Module *TheModule;
IntrusiveRefCntPtr VFS;
+ llvm::SmallVector Plugins;
std::unique_ptr OS;
@@ -1017,16 +1018,9 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
}
#endif
}
- // Attempt to load pass plugins and register their callbacks with PB.
- for (auto &PluginFN : CodeGenOpts.PassPlugins) {
-auto PassPlugin = PassPlugin::Load(PluginFN);
-if (PassPlugin) {
- PassPlugin->registerPassBuilderCallbacks(PB);
-} else {
- Diags.Report(diag::err_fe_unable_to_load_plugin)
- << PluginFN << toString(PassPlugin.takeError());
-}
- }
+ // Register plugin callbacks with PB.
+ for (auto &Plugin : Plugins)
+Plugin.registerPassBuilderCallbacks(PB);
for (const auto &PassCallback : CodeGenOpts.PassBuilderCallbacks)
PassCallback(PB);
#define HANDLE_EXTENSION(Ext)
\
@@ -1265,6 +1259,14 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
void EmitAssemblyHelper::RunCodegenPipeline(
BackendAction Action, std::unique_ptr &OS,
std::unique_ptr &DwoOS) {
+ // Invoke pre-codegen callback from plugin, which might want to take over the
+ // entire code generation itself.
+ for (auto &Plugin : Plugins) {
+CodeGenFileType CGFT = getCodeGenFileType(Action);
+if (Plugin.invokePreCodeGenCallback(*TheModule, *TM, CGFT, *OS))
+ return;
+ }
+
// We still use the legacy PM to run the codegen pipeline since the new PM
// does not work with the codegen pipeline.
// FIXME: make the new PM work with the codegen pipeline.
@@ -1328,6 +1330,17 @@ void EmitAssemblyHelper::emitAssembly(BackendAction
Action,
// Before executing passes, print the final values of the LLVM options.
cl::PrintOptionValues();
+ // Attempt to load pass plugins.
+ for (auto &PluginFN : CodeGenOpts.PassPlugins) {
+auto PassPlugin = PassPlugin::Load(PluginFN);
+if (PassPlugin) {
+ Plugins.push_back(std::move(*PassPlugin));
+} else {
+ Diags.Report(diag::err_fe_unable_to_load_plugin)
+ << PluginFN << toString(PassPlugin.takeError());
+}
+ }
+
std::unique_ptr ThinLinkOS, DwoOS;
RunOptimizationPipeline(Action, OS, ThinLinkOS, BC);
RunCodegenPipeline(Action, OS, DwoOS);
diff --git a/llvm/examples/Bye/Bye.cpp b/llvm/examples/Bye/Bye.cpp
index d88bf9e490e9c..4d612e2350a01 100644
--- a/llvm/examples/Bye/Bye.cpp
+++ b/llvm/examples/Bye/Bye.cpp
@@ -11,6 +11,9 @@ using namespace llvm;
static cl::opt Wave("wave-goodbye", cl::init(false),
cl::desc("wave good bye"));
+static cl::opt LastWords("last-words", cl::init(false),
+ cl::desc("say last words (suppress codegen)"));
+
namespace {
bool runBye(Function &F) {
@@ -35,6 +38,37 @@ struct Bye : PassInfoMixin {
}
};
+void registerPassBuilderCallbacks(PassBuilder &PB) {
+ PB.registerVectorizerStartEPCallback(
+ [](llvm::FunctionPassManager &PM, OptimizationLevel Level) {
+PM.addPass(Bye());
+ });
+ PB.registerPipelineParsingCallback(
+ [](StringRef Name, llvm::FunctionPassManager &PM,
+ ArrayRef) {
+if (Name == "goodbye") {
+ PM.addPass(Bye());
+ return true;
+}
+return false;
+ });
+}
+
+bool preCodeGenCallback(Module &M, TargetMachine &, CodeGenFileType CGFT,
+raw_pwrite_stream &OS) {
+ if (LastWords) {
+if (CGFT != CodeGenFileType::AssemblyFile) {
+ // Test error emission.
+ M.getContext().emitError("last words unsupported for binary output");
+ return false;
+}
+OS << "CodeGen Bye\n";
+return true; // Suppress remaining compilation pipeline.
+ }
+ // Do nothing
[llvm-branch-commits] [clang] [llvm] [RFC][LLVM][Clang] Add LLVM plugin hook for back-ends (PR #170846)
https://github.com/aengelke ready_for_review https://github.com/llvm/llvm-project/pull/170846 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] release/21.x: [HEXAGON] [MachinePipeliner] Fix the DAG in case of dependent phis. (#135925) (PR #170749)
androm3da wrote: @iajbar @quic-asaravan could there be some other dependencies that need to be cherry-picked for this fix? ``` 2025-12-04T21:46:58.3210476Z TEST 'LLVM :: CodeGen/Hexagon/phi-elim.ll' FAILED 2025-12-04T21:46:58.3213147Z Exit Code: 134 2025-12-04T21:46:58.3213736Z 2025-12-04T21:46:58.3214072Z Command Output (stderr): 2025-12-04T21:46:58.3215568Z -- 2025-12-04T21:46:58.3217037Z /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/llc -mtriple=hexagon < /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/CodeGen/Hexagon/phi-elim.ll # RUN: at line 1 2025-12-04T21:46:58.3219195Z + /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/llc -mtriple=hexagon 2025-12-04T21:46:58.3221933Z llc: /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/CodeGen/ScheduleDAG.cpp:522: void llvm::ScheduleDAGTopologicalSort::InitDAGTopologicalSorting(): Assertion `Node2Index[SU.NodeNum] > Node2Index[PD.getSUnit()->NodeNum] && "Wrong topological sorting"' failed. 2025-12-04T21:46:58.3224463Z PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace. 2025-12-04T21:46:58.3225351Z Stack dump: 2025-12-04T21:46:58.3226149Z 0. Program arguments: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/llc -mtriple=hexagon 2025-12-04T21:46:58.3227183Z 1. Running pass 'Function Pass Manager' on module ''. 2025-12-04T21:46:58.3227888Z 2. Running pass 'Modulo Software Pipelining' on function '@f0' 2025-12-04T21:46:58.3229721Z #0 0x578003f045c8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Support/Unix/Signals.inc:834:13 2025-12-04T21:46:58.3231779Z #1 0x578003f01cd5 llvm::sys::RunSignalHandlers() /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Support/Signals.cpp:105:18 2025-12-04T21:46:58.3233408Z #2 0x578003f05391 SignalHandler(int, siginfo_t*, void*) /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Support/Unix/Signals.inc:426:38 2025-12-04T21:46:58.3234537Z #3 0x7b8e5cce1330 (/lib/x86_64-linux-gnu/libc.so.6+0x45330) 2025-12-04T21:46:58.3235243Z #4 0x7b8e5cd3ab2c pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x9eb2c) 2025-12-04T21:46:58.3235996Z #5 0x7b8e5cce127e raise (/lib/x86_64-linux-gnu/libc.so.6+0x4527e) 2025-12-04T21:46:58.3236627Z #6 0x7b8e5ccc48ff abort (/lib/x86_64-linux-gnu/libc.so.6+0x288ff) 2025-12-04T21:46:58.3237500Z #7 0x7b8e5ccc481b (/lib/x86_64-linux-gnu/libc.so.6+0x2881b) 2025-12-04T21:46:58.3238107Z #8 0x7b8e5ccd7517 (/lib/x86_64-linux-gnu/libc.so.6+0x3b517) 2025-12-04T21:46:58.3239737Z #9 0x5780031ce0d6 llvm::ScheduleDAGTopologicalSort::InitDAGTopologicalSorting() /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/CodeGen/ScheduleDAG.cpp:0:0 2025-12-04T21:46:58.3241434Z #10 0x578002f8e5e1 llvm::SwingSchedulerDAG::schedule() /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/CodeGen/MachinePipeliner.cpp:710:3 2025-12-04T21:46:58.3243241Z #11 0x578002f8cf7f llvm::MachinePipeliner::swingModuloScheduler(llvm::MachineLoop&) /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/CodeGen/MachinePipeliner.cpp:635:7 2025-12-04T21:46:58.3249321Z #12 0x578002f8aff2 llvm::MachinePipeliner::scheduleLoop(llvm::MachineLoop&) /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/CodeGen/MachinePipeliner.cpp:434:15 ``` https://github.com/llvm/llvm-project/pull/170749 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] DAG: Check if deoptimize call is available before emitting it (PR #170940)
arsenm wrote: > Looks good, though I wonder if deoptimize is unavailable, where it comes from > then? (Why it is generated in IR?) Nothing is stopping you from writing IR with an intrinsic on it. And nothing may be keeping the optimize libcall alive if it depends on codegen. https://github.com/llvm/llvm-project/pull/170940 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [RFC][Clang] Allow plugins to hook into back-end (PR #165257)
https://github.com/aengelke closed https://github.com/llvm/llvm-project/pull/165257 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] release/21.x: [clang-format] Fix a crash in AlignArrayOfStructures (#167099) (PR #170967)
https://github.com/HazardyKnusperkeks approved this pull request. https://github.com/llvm/llvm-project/pull/170967 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] release/21.x: [clang-format] Don't swap `(const override)` with QAS_Right (#167191) (PR #170966)
https://github.com/HazardyKnusperkeks approved this pull request. https://github.com/llvm/llvm-project/pull/170966 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [ConstantTime][RISCV] Add comprehensive tests for ct.select (PR #166708)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166708
>From f00c9b274e9bf87110b8c013ac61f1d5a64a268f Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:00 -0500
Subject: [PATCH] [ConstantTime][RISCV] Add comprehensive tests for ct.select
Add comprehensive test suite for RISC-V fallback implementation:
- Edge cases (zero conditions, large integers, sign extension)
- Pattern matching (nested selects, chains)
- Vector support with RVV extensions
- Side effects and memory operations
The basic fallback test is in the core infrastructure PR.
---
.../RISCV/ctselect-fallback-edge-cases.ll | 214 +
.../RISCV/ctselect-fallback-patterns.ll | 383 +
.../RISCV/ctselect-fallback-vector-rvv.ll | 804 ++
.../CodeGen/RISCV/ctselect-side-effects.ll| 176
4 files changed, 1577 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-vector-rvv.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..af1be0c8f3ddc
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,214 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=riscv64 -O3 | FileCheck %s --check-prefix=RV64
+; RUN: llc < %s -mtriple=riscv32 -O3 | FileCheck %s --check-prefix=RV32
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; RV64-LABEL: test_ctselect_i1:
+; RV64: # %bb.0:
+; RV64-NEXT:and a1, a0, a1
+; RV64-NEXT:xori a0, a0, 1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_i1:
+; RV32: # %bb.0:
+; RV32-NEXT:and a1, a0, a1
+; RV32-NEXT:xori a0, a0, 1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; RV64-LABEL: test_ctselect_extremal_values:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:lui a1, 524288
+; RV64-NEXT:subw a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_extremal_values:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:lui a1, 524288
+; RV32-NEXT:addi a2, a0, -1
+; RV32-NEXT:neg a0, a0
+; RV32-NEXT:and a1, a2, a1
+; RV32-NEXT:slli a0, a0, 1
+; RV32-NEXT:srli a0, a0, 1
+; RV32-NEXT:or a0, a0, a1
+; RV32-NEXT:ret
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; RV64-LABEL: test_ctselect_null_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:slli a0, a0, 63
+; RV64-NEXT:srai a0, a0, 63
+; RV64-NEXT:and a0, a0, a1
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_null_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:slli a0, a0, 31
+; RV32-NEXT:srai a0, a0, 31
+; RV32-NEXT:and a0, a0, a1
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; RV64-LABEL: test_ctselect_function_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:neg a3, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a1, a3, a1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_function_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:neg a3, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a1, a3, a1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; RV64-LABEL: test_ctselect_ptr_cmp:
+; RV64: # %bb.0:
+; RV64-NEXT:xor a0, a0, a1
+; RV64-NEXT:snez a0, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a2, a0, a2
+; RV64-NEXT:not a0, a0
+; RV64-NEXT:and a0, a0, a3
+; RV64-NEXT:or a0, a2, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_ptr_cmp:
+; RV32: # %bb.0:
+; RV32-NEXT:xor a0, a0, a1
+; RV32-NEXT:snez a0, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a2, a0, a2
+; RV32-NEXT:not a0, a0
+; RV32-NEXT:
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for ARM64 (PR #166706)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166706
>From d56d121e45fef048b2a09e9a5af2a147d23d7be3 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 17:09:45 -0500
Subject: [PATCH] [LLVM][AArch64] Add native ct.select support for ARM64
This patch implements architecture-specific lowering for ct.select on AArch64
using CSEL (conditional select) instructions for constant-time selection.
Implementation details:
- Uses CSEL family of instructions for scalar integer types
- Uses FCSEL for floating-point types (F16, BF16, F32, F64)
- Post-RA MC lowering to convert pseudo-instructions to real CSEL/FCSEL
- Handles vector types appropriately
- Comprehensive test coverage for AArch64
The implementation includes:
- ISelLowering: Custom lowering to CTSELECT pseudo-instructions
- InstrInfo: Pseudo-instruction definitions and patterns
- MCInstLower: Post-RA lowering of pseudo-instructions to actual CSEL/FCSEL
- Proper handling of condition codes for constant-time guarantees
---
.../Target/AArch64/AArch64ISelLowering.cpp| 56 +
llvm/lib/Target/AArch64/AArch64ISelLowering.h | 11 +
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 200 --
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 40
.../lib/Target/AArch64/AArch64MCInstLower.cpp | 18 ++
llvm/test/CodeGen/AArch64/ctselect.ll | 153 ++
6 files changed, 368 insertions(+), 110 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/ctselect.ll
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 7199319ccdd9f..884f7ef59d5b0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -505,12 +505,36 @@ AArch64TargetLowering::AArch64TargetLowering(const
TargetMachine &TM,
setOperationAction(ISD::BR_CC, MVT::f64, Custom);
setOperationAction(ISD::SELECT, MVT::i32, Custom);
setOperationAction(ISD::SELECT, MVT::i64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i8, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i16, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i64, Custom);
if (Subtarget->hasFPARMv8()) {
setOperationAction(ISD::SELECT, MVT::f16, Custom);
setOperationAction(ISD::SELECT, MVT::bf16, Custom);
}
+ if (Subtarget->hasFullFP16()) {
+setOperationAction(ISD::CTSELECT, MVT::f16, Custom);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Custom);
+ } else {
+setOperationAction(ISD::CTSELECT, MVT::f16, Promote);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Promote);
+ }
setOperationAction(ISD::SELECT, MVT::f32, Custom);
setOperationAction(ISD::SELECT, MVT::f64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f64, Custom);
+ for (MVT VT : MVT::vector_valuetypes()) {
+MVT elemType = VT.getVectorElementType();
+if (elemType == MVT::i8 || elemType == MVT::i16) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else if ((elemType == MVT::f16 || elemType == MVT::bf16) &&
+ !Subtarget->hasFullFP16()) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else {
+ setOperationAction(ISD::CTSELECT, VT, Expand);
+}
+ }
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
setOperationAction(ISD::SELECT_CC, MVT::i64, Custom);
setOperationAction(ISD::SELECT_CC, MVT::f16, Custom);
@@ -3375,6 +3399,20 @@ void AArch64TargetLowering::fixupPtrauthDiscriminator(
IntDiscOp.setImm(IntDisc);
}
+MachineBasicBlock *AArch64TargetLowering::EmitCTSELECT(MachineInstr &MI,
+ MachineBasicBlock *MBB,
+ unsigned Opcode) const {
+ const TargetInstrInfo *TII = Subtarget->getInstrInfo();
+ DebugLoc DL = MI.getDebugLoc();
+ MachineInstrBuilder Builder = BuildMI(*MBB, MI, DL, TII->get(Opcode));
+ for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx) {
+Builder.add(MI.getOperand(Idx));
+ }
+ Builder->setFlag(MachineInstr::NoMerge);
+ MBB->remove_instr(&MI);
+ return MBB;
+}
+
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
MachineInstr &MI, MachineBasicBlock *BB) const {
@@ -7862,6 +7900,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op,
return LowerSELECT(Op, DAG);
case ISD::SELECT_CC:
return LowerSELECT_CC(Op, DAG);
+ case ISD::CTSELECT:
+return LowerCTSELECT(Op, DAG);
case ISD::JumpTable:
return LowerJumpTable(Op, DAG);
case ISD::BR_JT:
@@ -12439,6 +12479,22 @@ SDValue AArch64TargetLowering::LowerSELECT(SDValue Op,
return Res;
}
+SDValue AArch64TargetLowering::LowerCTSELECT(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDValue CCVal = Op->getOperand(0);
+ SDValue TVal = Op->getOper
[llvm-branch-commits] [clang] [ConstantTime][Clang] Add __builtin_ct_select for constant-time selection (PR #166703)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166703
>From cee34bcd91923a4b50c12e12931de09fff609c34 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 10:56:34 -0500
Subject: [PATCH] [ConstantTime][Clang] Add __builtin_ct_select for
constant-time selection
---
clang/docs/LanguageExtensions.rst | 44 ++
clang/include/clang/Basic/Builtins.td | 8 +
clang/lib/CodeGen/CGBuiltin.cpp | 13 +
clang/lib/Sema/SemaChecking.cpp | 64 ++
.../test/Sema/builtin-ct-select-edge-cases.c | 373 ++
clang/test/Sema/builtin-ct-select.c | 683 ++
6 files changed, 1185 insertions(+)
create mode 100644 clang/test/Sema/builtin-ct-select-edge-cases.c
create mode 100644 clang/test/Sema/builtin-ct-select.c
diff --git a/clang/docs/LanguageExtensions.rst
b/clang/docs/LanguageExtensions.rst
index 87d38e7d99e50..647ec19a0792f 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6798,3 +6798,47 @@ Clang fails to reject some code that should be rejected.
e.g.,
// own initializer rather than rejecting the code with an undeclared
identifier
// diagnostic.
auto x = x;
+
+.. _langext-__builtin_ct_select:
+
+``__builtin_ct_select``
+---
+
+``__builtin_ct_select`` performs a constant-time conditional selection between
+two values. Unlike the ternary operator ``?:``, this builtin is designed to
+execute in constant time regardless of the condition value, making it suitable
+for cryptographic and security-sensitive code where timing side-channels must
+be avoided.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ __builtin_ct_select(condition, true_value, false_value)
+
+**Examples**:
+
+.. code-block:: c++
+
+ // Select between two integers
+ int result = __builtin_ct_select(secret_bit, value_a, value_b);
+
+ // Select between two pointers
+ int *ptr = __builtin_ct_select(condition, ptr_a, ptr_b);
+
+ // Select between two floating-point values
+ double d = __builtin_ct_select(flag, 1.0, 2.0);
+
+**Description**:
+
+The first argument is an integer condition that is converted to a boolean
+(non-zero is true, zero is false). The second and third arguments must have
+the same scalar or vector type. The builtin returns the second argument if
+the condition is true, otherwise the third argument.
+
+The operation is guaranteed to be lowered to constant-time machine code that
+does not branch on the condition value, preventing timing-based side-channel
+attacks.
+
+Query for this feature with ``__has_builtin(__builtin_ct_select)``.
+
diff --git a/clang/include/clang/Basic/Builtins.td
b/clang/include/clang/Basic/Builtins.td
index 98fb2debad06d..ffc924c1b11e1 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5327,3 +5327,11 @@ def CountedByRef : Builtin {
let Attributes = [NoThrow, CustomTypeChecking];
let Prototype = "int(...)";
}
+
+// Constant-time select builtin
+def CtSelect : Builtin {
+ let Spellings = ["__builtin_ct_select"];
+ let Attributes = [NoThrow, Const, UnevaluatedArguments,
+ConstIgnoringExceptions, CustomTypeChecking];
+ let Prototype = "void(...)";
+}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 01be374422d93..03ed50368757b 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6343,6 +6343,19 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl
GD, unsigned BuiltinID,
auto Str = CGM.GetAddrOfConstantCString(Name, "");
return RValue::get(Str.getPointer());
}
+ case Builtin::BI__builtin_ct_select: {
+auto *Cond = EmitScalarExpr(E->getArg(0));
+auto *A = EmitScalarExpr(E->getArg(1));
+auto *B = EmitScalarExpr(E->getArg(2));
+
+if (Cond->getType()->getIntegerBitWidth() != 1)
+ Cond = Builder.CreateICmpNE(
+ Cond, llvm::ConstantInt::get(Cond->getType(), 0), "cond.bool");
+
+llvm::Function *Fn =
+CGM.getIntrinsic(llvm::Intrinsic::ct_select, {A->getType()});
+return RValue::get(Builder.CreateCall(Fn, {Cond, A, B}));
+ }
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 02c838bc4a862..3f8a919c8f66d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3587,6 +3587,70 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl,
unsigned BuiltinID,
if (BuiltinCountedByRef(TheCall))
return ExprError();
break;
+
+ case Builtin::BI__builtin_ct_select: {
+if (TheCall->getNumArgs() != 3) {
+ // Simple argument count check without complex diagnostics
+ if (TheCall->getNumArgs() < 3) {
+return Diag(TheCall->getEndLoc(),
+diag::err_typecheck_call_too_few_args_at_least)
+ << 0 << 3 << TheCall->getNumArgs()
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for ARM64 (PR #166706)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166706
>From d56d121e45fef048b2a09e9a5af2a147d23d7be3 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 17:09:45 -0500
Subject: [PATCH] [LLVM][AArch64] Add native ct.select support for ARM64
This patch implements architecture-specific lowering for ct.select on AArch64
using CSEL (conditional select) instructions for constant-time selection.
Implementation details:
- Uses CSEL family of instructions for scalar integer types
- Uses FCSEL for floating-point types (F16, BF16, F32, F64)
- Post-RA MC lowering to convert pseudo-instructions to real CSEL/FCSEL
- Handles vector types appropriately
- Comprehensive test coverage for AArch64
The implementation includes:
- ISelLowering: Custom lowering to CTSELECT pseudo-instructions
- InstrInfo: Pseudo-instruction definitions and patterns
- MCInstLower: Post-RA lowering of pseudo-instructions to actual CSEL/FCSEL
- Proper handling of condition codes for constant-time guarantees
---
.../Target/AArch64/AArch64ISelLowering.cpp| 56 +
llvm/lib/Target/AArch64/AArch64ISelLowering.h | 11 +
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 200 --
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 40
.../lib/Target/AArch64/AArch64MCInstLower.cpp | 18 ++
llvm/test/CodeGen/AArch64/ctselect.ll | 153 ++
6 files changed, 368 insertions(+), 110 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/ctselect.ll
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 7199319ccdd9f..884f7ef59d5b0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -505,12 +505,36 @@ AArch64TargetLowering::AArch64TargetLowering(const
TargetMachine &TM,
setOperationAction(ISD::BR_CC, MVT::f64, Custom);
setOperationAction(ISD::SELECT, MVT::i32, Custom);
setOperationAction(ISD::SELECT, MVT::i64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i8, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i16, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i64, Custom);
if (Subtarget->hasFPARMv8()) {
setOperationAction(ISD::SELECT, MVT::f16, Custom);
setOperationAction(ISD::SELECT, MVT::bf16, Custom);
}
+ if (Subtarget->hasFullFP16()) {
+setOperationAction(ISD::CTSELECT, MVT::f16, Custom);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Custom);
+ } else {
+setOperationAction(ISD::CTSELECT, MVT::f16, Promote);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Promote);
+ }
setOperationAction(ISD::SELECT, MVT::f32, Custom);
setOperationAction(ISD::SELECT, MVT::f64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f64, Custom);
+ for (MVT VT : MVT::vector_valuetypes()) {
+MVT elemType = VT.getVectorElementType();
+if (elemType == MVT::i8 || elemType == MVT::i16) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else if ((elemType == MVT::f16 || elemType == MVT::bf16) &&
+ !Subtarget->hasFullFP16()) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else {
+ setOperationAction(ISD::CTSELECT, VT, Expand);
+}
+ }
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
setOperationAction(ISD::SELECT_CC, MVT::i64, Custom);
setOperationAction(ISD::SELECT_CC, MVT::f16, Custom);
@@ -3375,6 +3399,20 @@ void AArch64TargetLowering::fixupPtrauthDiscriminator(
IntDiscOp.setImm(IntDisc);
}
+MachineBasicBlock *AArch64TargetLowering::EmitCTSELECT(MachineInstr &MI,
+ MachineBasicBlock *MBB,
+ unsigned Opcode) const {
+ const TargetInstrInfo *TII = Subtarget->getInstrInfo();
+ DebugLoc DL = MI.getDebugLoc();
+ MachineInstrBuilder Builder = BuildMI(*MBB, MI, DL, TII->get(Opcode));
+ for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx) {
+Builder.add(MI.getOperand(Idx));
+ }
+ Builder->setFlag(MachineInstr::NoMerge);
+ MBB->remove_instr(&MI);
+ return MBB;
+}
+
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
MachineInstr &MI, MachineBasicBlock *BB) const {
@@ -7862,6 +7900,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op,
return LowerSELECT(Op, DAG);
case ISD::SELECT_CC:
return LowerSELECT_CC(Op, DAG);
+ case ISD::CTSELECT:
+return LowerCTSELECT(Op, DAG);
case ISD::JumpTable:
return LowerJumpTable(Op, DAG);
case ISD::BR_JT:
@@ -12439,6 +12479,22 @@ SDValue AArch64TargetLowering::LowerSELECT(SDValue Op,
return Res;
}
+SDValue AArch64TargetLowering::LowerCTSELECT(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDValue CCVal = Op->getOperand(0);
+ SDValue TVal = Op->getOper
[llvm-branch-commits] [llvm] [ConstantTime][WebAssembly] Add comprehensive tests for ct.select (PR #166709)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166709
>From a5038c441b949cfbe47276a3d04a20633a2e4803 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:03:23 -0500
Subject: [PATCH] [ConstantTime][WebAssembly] Add comprehensive tests for
ct.select
---
.../ctselect-fallback-edge-cases.ll | 376 +
.../WebAssembly/ctselect-fallback-patterns.ll | 641
.../WebAssembly/ctselect-fallback-vector.ll | 714 ++
.../CodeGen/WebAssembly/ctselect-fallback.ll | 552 ++
.../WebAssembly/ctselect-side-effects.ll | 226 ++
5 files changed, 2509 insertions(+)
create mode 100644
llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..b0f7f2807debd
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,376 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W32
+; RUN: llc < %s -mtriple=wasm64-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W64
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; W32-LABEL: test_ctselect_i1:
+; W32: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:local.get 0
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.xor
+; W32-NEXT:local.get 2
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_i1:
+; W64: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:local.get 0
+; W64-NEXT:local.get 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.xor
+; W64-NEXT:local.get 2
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; W32-LABEL: test_ctselect_extremal_values:
+; W32: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.tee 0
+; W32-NEXT:i32.sub
+; W32-NEXT:i32.const 2147483647
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const -1
+; W32-NEXT:i32.add
+; W32-NEXT:i32.const -2147483648
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_extremal_values:
+; W64: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i32.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.tee 0
+; W64-NEXT:i32.sub
+; W64-NEXT:i32.const 2147483647
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const -1
+; W64-NEXT:i32.add
+; W64-NEXT:i32.const -2147483648
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; W32-LABEL: test_ctselect_null_ptr:
+; W32: .functype test_ctselect_null_ptr (i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:i32.sub
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_null_ptr:
+; W64: .functype test_ctselect_null_ptr (i32, i64) -> (i64)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i64.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i64.extend_i32_u
+; W64-NEXT:i64.const 1
+; W64-NEXT:i64.and
+; W64-NEXT:i64.sub
+; W64-NEXT:local.get 1
+; W64-NEXT:i64.and
+; W64-NEXT:# fallthrough-return
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; W32-
[llvm-branch-commits] [llvm] [ConstantTime][RISCV] Add comprehensive tests for ct.select (PR #166708)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166708
>From f00c9b274e9bf87110b8c013ac61f1d5a64a268f Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:00 -0500
Subject: [PATCH] [ConstantTime][RISCV] Add comprehensive tests for ct.select
Add comprehensive test suite for RISC-V fallback implementation:
- Edge cases (zero conditions, large integers, sign extension)
- Pattern matching (nested selects, chains)
- Vector support with RVV extensions
- Side effects and memory operations
The basic fallback test is in the core infrastructure PR.
---
.../RISCV/ctselect-fallback-edge-cases.ll | 214 +
.../RISCV/ctselect-fallback-patterns.ll | 383 +
.../RISCV/ctselect-fallback-vector-rvv.ll | 804 ++
.../CodeGen/RISCV/ctselect-side-effects.ll| 176
4 files changed, 1577 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-vector-rvv.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..af1be0c8f3ddc
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,214 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=riscv64 -O3 | FileCheck %s --check-prefix=RV64
+; RUN: llc < %s -mtriple=riscv32 -O3 | FileCheck %s --check-prefix=RV32
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; RV64-LABEL: test_ctselect_i1:
+; RV64: # %bb.0:
+; RV64-NEXT:and a1, a0, a1
+; RV64-NEXT:xori a0, a0, 1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_i1:
+; RV32: # %bb.0:
+; RV32-NEXT:and a1, a0, a1
+; RV32-NEXT:xori a0, a0, 1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; RV64-LABEL: test_ctselect_extremal_values:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:lui a1, 524288
+; RV64-NEXT:subw a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_extremal_values:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:lui a1, 524288
+; RV32-NEXT:addi a2, a0, -1
+; RV32-NEXT:neg a0, a0
+; RV32-NEXT:and a1, a2, a1
+; RV32-NEXT:slli a0, a0, 1
+; RV32-NEXT:srli a0, a0, 1
+; RV32-NEXT:or a0, a0, a1
+; RV32-NEXT:ret
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; RV64-LABEL: test_ctselect_null_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:slli a0, a0, 63
+; RV64-NEXT:srai a0, a0, 63
+; RV64-NEXT:and a0, a0, a1
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_null_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:slli a0, a0, 31
+; RV32-NEXT:srai a0, a0, 31
+; RV32-NEXT:and a0, a0, a1
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; RV64-LABEL: test_ctselect_function_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:neg a3, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a1, a3, a1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_function_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:neg a3, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a1, a3, a1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; RV64-LABEL: test_ctselect_ptr_cmp:
+; RV64: # %bb.0:
+; RV64-NEXT:xor a0, a0, a1
+; RV64-NEXT:snez a0, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a2, a0, a2
+; RV64-NEXT:not a0, a0
+; RV64-NEXT:and a0, a0, a3
+; RV64-NEXT:or a0, a2, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_ptr_cmp:
+; RV32: # %bb.0:
+; RV32-NEXT:xor a0, a0, a1
+; RV32-NEXT:snez a0, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a2, a0, a2
+; RV32-NEXT:not a0, a0
+; RV32-NEXT:
[llvm-branch-commits] [TableGen] Slightly improve error location for a fatal error (PR #170790)
@@ -1316,11 +1316,18 @@ CodeGenRegBank::getOrCreateSubClass(const
CodeGenRegisterClass *RC,
return {&RegClasses.back(), true};
}
-CodeGenRegisterClass *CodeGenRegBank::getRegClass(const Record *Def) const {
+CodeGenRegisterClass *CodeGenRegBank::getRegClass(const Record *Def,
s-barannikov wrote:
Yes, that's what I meant.
What else can it be? Won't it hit the PrintFatalError if it is not a
RegisterClass? The function comment says it should be.
https://github.com/llvm/llvm-project/pull/170790
___
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [ConstantTime][MIPS] Add comprehensive tests for ct.select (PR #166705)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166705
>From 010fb44a5aea2f28bca67533588f4a6f7431010d Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:26 -0500
Subject: [PATCH] [LLVM][MIPS] Add comprehensive tests for ct.select
---
.../Mips/ctselect-fallback-edge-cases.ll | 244 +
.../Mips/ctselect-fallback-patterns.ll| 426 +
.../CodeGen/Mips/ctselect-fallback-vector.ll | 830 ++
llvm/test/CodeGen/Mips/ctselect-fallback.ll | 371
.../CodeGen/Mips/ctselect-side-effects.ll | 183
5 files changed, 2054 insertions(+)
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..f1831a625d4a4
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,244 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=mipsel-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M32
+; RUN: llc < %s -mtriple=mips64el-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M64
+
+; Portable edge case tests
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; M32-LABEL: test_ctselect_i1:
+; M32: # %bb.0:
+; M32-NEXT:xori $2, $4, 1
+; M32-NEXT:and $1, $4, $5
+; M32-NEXT:and $2, $2, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $1, $2
+;
+; M64-LABEL: test_ctselect_i1:
+; M64: # %bb.0:
+; M64-NEXT:sll $2, $4, 0
+; M64-NEXT:sll $1, $6, 0
+; M64-NEXT:xori $2, $2, 1
+; M64-NEXT:and $1, $2, $1
+; M64-NEXT:and $2, $4, $5
+; M64-NEXT:sll $2, $2, 0
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; M32-LABEL: test_ctselect_extremal_values:
+; M32: # %bb.0:
+; M32-NEXT:lui $3, 32767
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:ori $3, $3, 65535
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $3
+; M32-NEXT:lui $3, 32768
+; M32-NEXT:and $1, $1, $3
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_extremal_values:
+; M64: # %bb.0:
+; M64-NEXT:sll $1, $4, 0
+; M64-NEXT:lui $3, 32767
+; M64-NEXT:andi $1, $1, 1
+; M64-NEXT:ori $3, $3, 65535
+; M64-NEXT:negu $2, $1
+; M64-NEXT:addiu $1, $1, -1
+; M64-NEXT:and $2, $2, $3
+; M64-NEXT:lui $3, 32768
+; M64-NEXT:and $1, $1, $3
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; M32-LABEL: test_ctselect_null_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $1, $1
+; M32-NEXT:jr $ra
+; M32-NEXT:and $2, $1, $5
+;
+; M64-LABEL: test_ctselect_null_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $1, $1
+; M64-NEXT:jr $ra
+; M64-NEXT:and $2, $1, $5
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; M32-LABEL: test_ctselect_function_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $5
+; M32-NEXT:and $1, $1, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_function_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $2, $1
+; M64-NEXT:daddiu $1, $1, -1
+; M64-NEXT:and $2, $2, $5
+; M64-NEXT:and $1, $1, $6
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; M32-LABEL: test_ctselect_ptr_cmp:
+; M32: # %bb.0:
+; M32-NEXT:xor $1, $4, $5
+; M32-NEXT:sltu $1, $zero, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $1, $6
+; M32-NEXT:not $1, $1
+; M32-NEXT:and $1, $1, $7
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_ptr_cmp:
+; M64: # %bb.0:
+; M6
[llvm-branch-commits] [llvm] [ConstantTime][WebAssembly] Add comprehensive tests for ct.select (PR #166709)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166709
>From a5038c441b949cfbe47276a3d04a20633a2e4803 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:03:23 -0500
Subject: [PATCH] [ConstantTime][WebAssembly] Add comprehensive tests for
ct.select
---
.../ctselect-fallback-edge-cases.ll | 376 +
.../WebAssembly/ctselect-fallback-patterns.ll | 641
.../WebAssembly/ctselect-fallback-vector.ll | 714 ++
.../CodeGen/WebAssembly/ctselect-fallback.ll | 552 ++
.../WebAssembly/ctselect-side-effects.ll | 226 ++
5 files changed, 2509 insertions(+)
create mode 100644
llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..b0f7f2807debd
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,376 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W32
+; RUN: llc < %s -mtriple=wasm64-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W64
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; W32-LABEL: test_ctselect_i1:
+; W32: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:local.get 0
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.xor
+; W32-NEXT:local.get 2
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_i1:
+; W64: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:local.get 0
+; W64-NEXT:local.get 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.xor
+; W64-NEXT:local.get 2
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; W32-LABEL: test_ctselect_extremal_values:
+; W32: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.tee 0
+; W32-NEXT:i32.sub
+; W32-NEXT:i32.const 2147483647
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const -1
+; W32-NEXT:i32.add
+; W32-NEXT:i32.const -2147483648
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_extremal_values:
+; W64: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i32.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.tee 0
+; W64-NEXT:i32.sub
+; W64-NEXT:i32.const 2147483647
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const -1
+; W64-NEXT:i32.add
+; W64-NEXT:i32.const -2147483648
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; W32-LABEL: test_ctselect_null_ptr:
+; W32: .functype test_ctselect_null_ptr (i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:i32.sub
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_null_ptr:
+; W64: .functype test_ctselect_null_ptr (i32, i64) -> (i64)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i64.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i64.extend_i32_u
+; W64-NEXT:i64.const 1
+; W64-NEXT:i64.and
+; W64-NEXT:i64.sub
+; W64-NEXT:local.get 1
+; W64-NEXT:i64.and
+; W64-NEXT:# fallthrough-return
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; W32-
[llvm-branch-commits] [TableGen] Slightly improve error location for a fatal error (PR #170790)
@@ -1316,11 +1316,18 @@ CodeGenRegBank::getOrCreateSubClass(const
CodeGenRegisterClass *RC,
return {&RegClasses.back(), true};
}
-CodeGenRegisterClass *CodeGenRegBank::getRegClass(const Record *Def) const {
+CodeGenRegisterClass *CodeGenRegBank::getRegClass(const Record *Def,
+ ArrayRef Loc) const {
if (CodeGenRegisterClass *RC = Def2RC.lookup(Def))
return RC;
- PrintFatalError(Def->getLoc(), "Not a known RegisterClass!");
+ ArrayRef DiagLoc = Loc.empty() ? Def->getLoc() : Loc;
+ // TODO: Ideally we should update the API to allow resolving HwMode.
s-barannikov wrote:
Maybe in those cases we should resolve it manually in td?
https://github.com/llvm/llvm-project/pull/170790
___
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ConstantTime][Clang] Add __builtin_ct_select for constant-time selection (PR #166703)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166703
>From add5d106929bf3e96af6acb68fd4e9c7d552ffc0 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 10:56:34 -0500
Subject: [PATCH] [ConstantTime][Clang] Add __builtin_ct_select for
constant-time selection
---
clang/docs/LanguageExtensions.rst | 44 ++
clang/include/clang/Basic/Builtins.td | 8 +
clang/lib/CodeGen/CGBuiltin.cpp | 13 +
clang/lib/Sema/SemaChecking.cpp | 64 ++
.../test/Sema/builtin-ct-select-edge-cases.c | 373 ++
clang/test/Sema/builtin-ct-select.c | 683 ++
6 files changed, 1185 insertions(+)
create mode 100644 clang/test/Sema/builtin-ct-select-edge-cases.c
create mode 100644 clang/test/Sema/builtin-ct-select.c
diff --git a/clang/docs/LanguageExtensions.rst
b/clang/docs/LanguageExtensions.rst
index 87d38e7d99e50..647ec19a0792f 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6798,3 +6798,47 @@ Clang fails to reject some code that should be rejected.
e.g.,
// own initializer rather than rejecting the code with an undeclared
identifier
// diagnostic.
auto x = x;
+
+.. _langext-__builtin_ct_select:
+
+``__builtin_ct_select``
+---
+
+``__builtin_ct_select`` performs a constant-time conditional selection between
+two values. Unlike the ternary operator ``?:``, this builtin is designed to
+execute in constant time regardless of the condition value, making it suitable
+for cryptographic and security-sensitive code where timing side-channels must
+be avoided.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ __builtin_ct_select(condition, true_value, false_value)
+
+**Examples**:
+
+.. code-block:: c++
+
+ // Select between two integers
+ int result = __builtin_ct_select(secret_bit, value_a, value_b);
+
+ // Select between two pointers
+ int *ptr = __builtin_ct_select(condition, ptr_a, ptr_b);
+
+ // Select between two floating-point values
+ double d = __builtin_ct_select(flag, 1.0, 2.0);
+
+**Description**:
+
+The first argument is an integer condition that is converted to a boolean
+(non-zero is true, zero is false). The second and third arguments must have
+the same scalar or vector type. The builtin returns the second argument if
+the condition is true, otherwise the third argument.
+
+The operation is guaranteed to be lowered to constant-time machine code that
+does not branch on the condition value, preventing timing-based side-channel
+attacks.
+
+Query for this feature with ``__has_builtin(__builtin_ct_select)``.
+
diff --git a/clang/include/clang/Basic/Builtins.td
b/clang/include/clang/Basic/Builtins.td
index 98fb2debad06d..ffc924c1b11e1 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5327,3 +5327,11 @@ def CountedByRef : Builtin {
let Attributes = [NoThrow, CustomTypeChecking];
let Prototype = "int(...)";
}
+
+// Constant-time select builtin
+def CtSelect : Builtin {
+ let Spellings = ["__builtin_ct_select"];
+ let Attributes = [NoThrow, Const, UnevaluatedArguments,
+ConstIgnoringExceptions, CustomTypeChecking];
+ let Prototype = "void(...)";
+}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 01be374422d93..03ed50368757b 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6343,6 +6343,19 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl
GD, unsigned BuiltinID,
auto Str = CGM.GetAddrOfConstantCString(Name, "");
return RValue::get(Str.getPointer());
}
+ case Builtin::BI__builtin_ct_select: {
+auto *Cond = EmitScalarExpr(E->getArg(0));
+auto *A = EmitScalarExpr(E->getArg(1));
+auto *B = EmitScalarExpr(E->getArg(2));
+
+if (Cond->getType()->getIntegerBitWidth() != 1)
+ Cond = Builder.CreateICmpNE(
+ Cond, llvm::ConstantInt::get(Cond->getType(), 0), "cond.bool");
+
+llvm::Function *Fn =
+CGM.getIntrinsic(llvm::Intrinsic::ct_select, {A->getType()});
+return RValue::get(Builder.CreateCall(Fn, {Cond, A, B}));
+ }
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 02c838bc4a862..c438e8ffe067c 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3587,6 +3587,70 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl,
unsigned BuiltinID,
if (BuiltinCountedByRef(TheCall))
return ExprError();
break;
+
+ case Builtin::BI__builtin_ct_select: {
+if (TheCall->getNumArgs() != 3) {
+ // Simple argument count check without complex diagnostics
+ if (TheCall->getNumArgs() < 3) {
+return Diag(TheCall->getEndLoc(),
+diag::err_typecheck_call_too_few_args_at_least)
+ << 0 << 3 << TheCall->getNumArgs()
[llvm-branch-commits] [clang] [ConstantTime][Clang] Add __builtin_ct_select for constant-time selection (PR #166703)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166703
>From add5d106929bf3e96af6acb68fd4e9c7d552ffc0 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 10:56:34 -0500
Subject: [PATCH] [ConstantTime][Clang] Add __builtin_ct_select for
constant-time selection
---
clang/docs/LanguageExtensions.rst | 44 ++
clang/include/clang/Basic/Builtins.td | 8 +
clang/lib/CodeGen/CGBuiltin.cpp | 13 +
clang/lib/Sema/SemaChecking.cpp | 64 ++
.../test/Sema/builtin-ct-select-edge-cases.c | 373 ++
clang/test/Sema/builtin-ct-select.c | 683 ++
6 files changed, 1185 insertions(+)
create mode 100644 clang/test/Sema/builtin-ct-select-edge-cases.c
create mode 100644 clang/test/Sema/builtin-ct-select.c
diff --git a/clang/docs/LanguageExtensions.rst
b/clang/docs/LanguageExtensions.rst
index 87d38e7d99e50..647ec19a0792f 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6798,3 +6798,47 @@ Clang fails to reject some code that should be rejected.
e.g.,
// own initializer rather than rejecting the code with an undeclared
identifier
// diagnostic.
auto x = x;
+
+.. _langext-__builtin_ct_select:
+
+``__builtin_ct_select``
+---
+
+``__builtin_ct_select`` performs a constant-time conditional selection between
+two values. Unlike the ternary operator ``?:``, this builtin is designed to
+execute in constant time regardless of the condition value, making it suitable
+for cryptographic and security-sensitive code where timing side-channels must
+be avoided.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ __builtin_ct_select(condition, true_value, false_value)
+
+**Examples**:
+
+.. code-block:: c++
+
+ // Select between two integers
+ int result = __builtin_ct_select(secret_bit, value_a, value_b);
+
+ // Select between two pointers
+ int *ptr = __builtin_ct_select(condition, ptr_a, ptr_b);
+
+ // Select between two floating-point values
+ double d = __builtin_ct_select(flag, 1.0, 2.0);
+
+**Description**:
+
+The first argument is an integer condition that is converted to a boolean
+(non-zero is true, zero is false). The second and third arguments must have
+the same scalar or vector type. The builtin returns the second argument if
+the condition is true, otherwise the third argument.
+
+The operation is guaranteed to be lowered to constant-time machine code that
+does not branch on the condition value, preventing timing-based side-channel
+attacks.
+
+Query for this feature with ``__has_builtin(__builtin_ct_select)``.
+
diff --git a/clang/include/clang/Basic/Builtins.td
b/clang/include/clang/Basic/Builtins.td
index 98fb2debad06d..ffc924c1b11e1 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5327,3 +5327,11 @@ def CountedByRef : Builtin {
let Attributes = [NoThrow, CustomTypeChecking];
let Prototype = "int(...)";
}
+
+// Constant-time select builtin
+def CtSelect : Builtin {
+ let Spellings = ["__builtin_ct_select"];
+ let Attributes = [NoThrow, Const, UnevaluatedArguments,
+ConstIgnoringExceptions, CustomTypeChecking];
+ let Prototype = "void(...)";
+}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 01be374422d93..03ed50368757b 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6343,6 +6343,19 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl
GD, unsigned BuiltinID,
auto Str = CGM.GetAddrOfConstantCString(Name, "");
return RValue::get(Str.getPointer());
}
+ case Builtin::BI__builtin_ct_select: {
+auto *Cond = EmitScalarExpr(E->getArg(0));
+auto *A = EmitScalarExpr(E->getArg(1));
+auto *B = EmitScalarExpr(E->getArg(2));
+
+if (Cond->getType()->getIntegerBitWidth() != 1)
+ Cond = Builder.CreateICmpNE(
+ Cond, llvm::ConstantInt::get(Cond->getType(), 0), "cond.bool");
+
+llvm::Function *Fn =
+CGM.getIntrinsic(llvm::Intrinsic::ct_select, {A->getType()});
+return RValue::get(Builder.CreateCall(Fn, {Cond, A, B}));
+ }
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 02c838bc4a862..c438e8ffe067c 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3587,6 +3587,70 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl,
unsigned BuiltinID,
if (BuiltinCountedByRef(TheCall))
return ExprError();
break;
+
+ case Builtin::BI__builtin_ct_select: {
+if (TheCall->getNumArgs() != 3) {
+ // Simple argument count check without complex diagnostics
+ if (TheCall->getNumArgs() < 3) {
+return Diag(TheCall->getEndLoc(),
+diag::err_typecheck_call_too_few_args_at_least)
+ << 0 << 3 << TheCall->getNumArgs()
[llvm-branch-commits] [llvm] [ConstantTime][WebAssembly] Add comprehensive tests for ct.select (PR #166709)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166709
>From e629b7f54c7de636e86cdf9d43e7b064c93c8b63 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:03:23 -0500
Subject: [PATCH] [ConstantTime][WebAssembly] Add comprehensive tests for
ct.select
---
.../ctselect-fallback-edge-cases.ll | 376 +
.../WebAssembly/ctselect-fallback-patterns.ll | 641
.../WebAssembly/ctselect-fallback-vector.ll | 714 ++
.../CodeGen/WebAssembly/ctselect-fallback.ll | 552 ++
.../WebAssembly/ctselect-side-effects.ll | 226 ++
5 files changed, 2509 insertions(+)
create mode 100644
llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..b0f7f2807debd
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,376 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W32
+; RUN: llc < %s -mtriple=wasm64-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W64
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; W32-LABEL: test_ctselect_i1:
+; W32: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:local.get 0
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.xor
+; W32-NEXT:local.get 2
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_i1:
+; W64: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:local.get 0
+; W64-NEXT:local.get 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.xor
+; W64-NEXT:local.get 2
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; W32-LABEL: test_ctselect_extremal_values:
+; W32: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.tee 0
+; W32-NEXT:i32.sub
+; W32-NEXT:i32.const 2147483647
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const -1
+; W32-NEXT:i32.add
+; W32-NEXT:i32.const -2147483648
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_extremal_values:
+; W64: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i32.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.tee 0
+; W64-NEXT:i32.sub
+; W64-NEXT:i32.const 2147483647
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const -1
+; W64-NEXT:i32.add
+; W64-NEXT:i32.const -2147483648
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; W32-LABEL: test_ctselect_null_ptr:
+; W32: .functype test_ctselect_null_ptr (i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:i32.sub
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_null_ptr:
+; W64: .functype test_ctselect_null_ptr (i32, i64) -> (i64)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i64.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i64.extend_i32_u
+; W64-NEXT:i64.const 1
+; W64-NEXT:i64.and
+; W64-NEXT:i64.sub
+; W64-NEXT:local.get 1
+; W64-NEXT:i64.and
+; W64-NEXT:# fallthrough-return
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; W32-
[llvm-branch-commits] [llvm] [ConstantTime][MIPS] Add comprehensive tests for ct.select (PR #166705)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166705
>From 34fc2ff685bebd625f4d5730b41f053356ada52b Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:26 -0500
Subject: [PATCH] [LLVM][MIPS] Add comprehensive tests for ct.select
---
.../Mips/ctselect-fallback-edge-cases.ll | 244 +
.../Mips/ctselect-fallback-patterns.ll| 426 +
.../CodeGen/Mips/ctselect-fallback-vector.ll | 830 ++
llvm/test/CodeGen/Mips/ctselect-fallback.ll | 371
.../CodeGen/Mips/ctselect-side-effects.ll | 183
5 files changed, 2054 insertions(+)
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..f1831a625d4a4
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,244 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=mipsel-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M32
+; RUN: llc < %s -mtriple=mips64el-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M64
+
+; Portable edge case tests
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; M32-LABEL: test_ctselect_i1:
+; M32: # %bb.0:
+; M32-NEXT:xori $2, $4, 1
+; M32-NEXT:and $1, $4, $5
+; M32-NEXT:and $2, $2, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $1, $2
+;
+; M64-LABEL: test_ctselect_i1:
+; M64: # %bb.0:
+; M64-NEXT:sll $2, $4, 0
+; M64-NEXT:sll $1, $6, 0
+; M64-NEXT:xori $2, $2, 1
+; M64-NEXT:and $1, $2, $1
+; M64-NEXT:and $2, $4, $5
+; M64-NEXT:sll $2, $2, 0
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; M32-LABEL: test_ctselect_extremal_values:
+; M32: # %bb.0:
+; M32-NEXT:lui $3, 32767
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:ori $3, $3, 65535
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $3
+; M32-NEXT:lui $3, 32768
+; M32-NEXT:and $1, $1, $3
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_extremal_values:
+; M64: # %bb.0:
+; M64-NEXT:sll $1, $4, 0
+; M64-NEXT:lui $3, 32767
+; M64-NEXT:andi $1, $1, 1
+; M64-NEXT:ori $3, $3, 65535
+; M64-NEXT:negu $2, $1
+; M64-NEXT:addiu $1, $1, -1
+; M64-NEXT:and $2, $2, $3
+; M64-NEXT:lui $3, 32768
+; M64-NEXT:and $1, $1, $3
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; M32-LABEL: test_ctselect_null_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $1, $1
+; M32-NEXT:jr $ra
+; M32-NEXT:and $2, $1, $5
+;
+; M64-LABEL: test_ctselect_null_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $1, $1
+; M64-NEXT:jr $ra
+; M64-NEXT:and $2, $1, $5
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; M32-LABEL: test_ctselect_function_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $5
+; M32-NEXT:and $1, $1, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_function_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $2, $1
+; M64-NEXT:daddiu $1, $1, -1
+; M64-NEXT:and $2, $2, $5
+; M64-NEXT:and $1, $1, $6
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; M32-LABEL: test_ctselect_ptr_cmp:
+; M32: # %bb.0:
+; M32-NEXT:xor $1, $4, $5
+; M32-NEXT:sltu $1, $zero, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $1, $6
+; M32-NEXT:not $1, $1
+; M32-NEXT:and $1, $1, $7
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_ptr_cmp:
+; M64: # %bb.0:
+; M6
[llvm-branch-commits] [llvm] [ConstantTime][RISCV] Add comprehensive tests for ct.select (PR #166708)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166708
>From b68d05c33c4713dd3bc9458b25fa8975d8aef471 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:00 -0500
Subject: [PATCH] [ConstantTime][RISCV] Add comprehensive tests for ct.select
Add comprehensive test suite for RISC-V fallback implementation:
- Edge cases (zero conditions, large integers, sign extension)
- Pattern matching (nested selects, chains)
- Vector support with RVV extensions
- Side effects and memory operations
The basic fallback test is in the core infrastructure PR.
---
.../RISCV/ctselect-fallback-edge-cases.ll | 214 +
.../RISCV/ctselect-fallback-patterns.ll | 383 +
.../RISCV/ctselect-fallback-vector-rvv.ll | 804 ++
.../CodeGen/RISCV/ctselect-side-effects.ll| 176
4 files changed, 1577 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-vector-rvv.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..af1be0c8f3ddc
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,214 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=riscv64 -O3 | FileCheck %s --check-prefix=RV64
+; RUN: llc < %s -mtriple=riscv32 -O3 | FileCheck %s --check-prefix=RV32
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; RV64-LABEL: test_ctselect_i1:
+; RV64: # %bb.0:
+; RV64-NEXT:and a1, a0, a1
+; RV64-NEXT:xori a0, a0, 1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_i1:
+; RV32: # %bb.0:
+; RV32-NEXT:and a1, a0, a1
+; RV32-NEXT:xori a0, a0, 1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; RV64-LABEL: test_ctselect_extremal_values:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:lui a1, 524288
+; RV64-NEXT:subw a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_extremal_values:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:lui a1, 524288
+; RV32-NEXT:addi a2, a0, -1
+; RV32-NEXT:neg a0, a0
+; RV32-NEXT:and a1, a2, a1
+; RV32-NEXT:slli a0, a0, 1
+; RV32-NEXT:srli a0, a0, 1
+; RV32-NEXT:or a0, a0, a1
+; RV32-NEXT:ret
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; RV64-LABEL: test_ctselect_null_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:slli a0, a0, 63
+; RV64-NEXT:srai a0, a0, 63
+; RV64-NEXT:and a0, a0, a1
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_null_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:slli a0, a0, 31
+; RV32-NEXT:srai a0, a0, 31
+; RV32-NEXT:and a0, a0, a1
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; RV64-LABEL: test_ctselect_function_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:neg a3, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a1, a3, a1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_function_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:neg a3, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a1, a3, a1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; RV64-LABEL: test_ctselect_ptr_cmp:
+; RV64: # %bb.0:
+; RV64-NEXT:xor a0, a0, a1
+; RV64-NEXT:snez a0, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a2, a0, a2
+; RV64-NEXT:not a0, a0
+; RV64-NEXT:and a0, a0, a3
+; RV64-NEXT:or a0, a2, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_ptr_cmp:
+; RV32: # %bb.0:
+; RV32-NEXT:xor a0, a0, a1
+; RV32-NEXT:snez a0, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a2, a0, a2
+; RV32-NEXT:not a0, a0
+; RV32-NEXT:
[llvm-branch-commits] [llvm] [ConstantTime][RISCV] Add comprehensive tests for ct.select (PR #166708)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166708
>From b68d05c33c4713dd3bc9458b25fa8975d8aef471 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:00 -0500
Subject: [PATCH] [ConstantTime][RISCV] Add comprehensive tests for ct.select
Add comprehensive test suite for RISC-V fallback implementation:
- Edge cases (zero conditions, large integers, sign extension)
- Pattern matching (nested selects, chains)
- Vector support with RVV extensions
- Side effects and memory operations
The basic fallback test is in the core infrastructure PR.
---
.../RISCV/ctselect-fallback-edge-cases.ll | 214 +
.../RISCV/ctselect-fallback-patterns.ll | 383 +
.../RISCV/ctselect-fallback-vector-rvv.ll | 804 ++
.../CodeGen/RISCV/ctselect-side-effects.ll| 176
4 files changed, 1577 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-vector-rvv.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..af1be0c8f3ddc
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,214 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=riscv64 -O3 | FileCheck %s --check-prefix=RV64
+; RUN: llc < %s -mtriple=riscv32 -O3 | FileCheck %s --check-prefix=RV32
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; RV64-LABEL: test_ctselect_i1:
+; RV64: # %bb.0:
+; RV64-NEXT:and a1, a0, a1
+; RV64-NEXT:xori a0, a0, 1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_i1:
+; RV32: # %bb.0:
+; RV32-NEXT:and a1, a0, a1
+; RV32-NEXT:xori a0, a0, 1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; RV64-LABEL: test_ctselect_extremal_values:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:lui a1, 524288
+; RV64-NEXT:subw a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_extremal_values:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:lui a1, 524288
+; RV32-NEXT:addi a2, a0, -1
+; RV32-NEXT:neg a0, a0
+; RV32-NEXT:and a1, a2, a1
+; RV32-NEXT:slli a0, a0, 1
+; RV32-NEXT:srli a0, a0, 1
+; RV32-NEXT:or a0, a0, a1
+; RV32-NEXT:ret
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; RV64-LABEL: test_ctselect_null_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:slli a0, a0, 63
+; RV64-NEXT:srai a0, a0, 63
+; RV64-NEXT:and a0, a0, a1
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_null_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:slli a0, a0, 31
+; RV32-NEXT:srai a0, a0, 31
+; RV32-NEXT:and a0, a0, a1
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; RV64-LABEL: test_ctselect_function_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:neg a3, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a1, a3, a1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_function_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:neg a3, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a1, a3, a1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; RV64-LABEL: test_ctselect_ptr_cmp:
+; RV64: # %bb.0:
+; RV64-NEXT:xor a0, a0, a1
+; RV64-NEXT:snez a0, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a2, a0, a2
+; RV64-NEXT:not a0, a0
+; RV64-NEXT:and a0, a0, a3
+; RV64-NEXT:or a0, a2, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_ptr_cmp:
+; RV32: # %bb.0:
+; RV32-NEXT:xor a0, a0, a1
+; RV32-NEXT:snez a0, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a2, a0, a2
+; RV32-NEXT:not a0, a0
+; RV32-NEXT:
[llvm-branch-commits] [llvm] [ConstantTime][WebAssembly] Add comprehensive tests for ct.select (PR #166709)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166709
>From e629b7f54c7de636e86cdf9d43e7b064c93c8b63 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:03:23 -0500
Subject: [PATCH] [ConstantTime][WebAssembly] Add comprehensive tests for
ct.select
---
.../ctselect-fallback-edge-cases.ll | 376 +
.../WebAssembly/ctselect-fallback-patterns.ll | 641
.../WebAssembly/ctselect-fallback-vector.ll | 714 ++
.../CodeGen/WebAssembly/ctselect-fallback.ll | 552 ++
.../WebAssembly/ctselect-side-effects.ll | 226 ++
5 files changed, 2509 insertions(+)
create mode 100644
llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..b0f7f2807debd
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,376 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W32
+; RUN: llc < %s -mtriple=wasm64-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W64
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; W32-LABEL: test_ctselect_i1:
+; W32: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:local.get 0
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.xor
+; W32-NEXT:local.get 2
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_i1:
+; W64: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:local.get 0
+; W64-NEXT:local.get 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.xor
+; W64-NEXT:local.get 2
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; W32-LABEL: test_ctselect_extremal_values:
+; W32: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.tee 0
+; W32-NEXT:i32.sub
+; W32-NEXT:i32.const 2147483647
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const -1
+; W32-NEXT:i32.add
+; W32-NEXT:i32.const -2147483648
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_extremal_values:
+; W64: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i32.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.tee 0
+; W64-NEXT:i32.sub
+; W64-NEXT:i32.const 2147483647
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const -1
+; W64-NEXT:i32.add
+; W64-NEXT:i32.const -2147483648
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; W32-LABEL: test_ctselect_null_ptr:
+; W32: .functype test_ctselect_null_ptr (i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:i32.sub
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_null_ptr:
+; W64: .functype test_ctselect_null_ptr (i32, i64) -> (i64)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i64.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i64.extend_i32_u
+; W64-NEXT:i64.const 1
+; W64-NEXT:i64.and
+; W64-NEXT:i64.sub
+; W64-NEXT:local.get 1
+; W64-NEXT:i64.and
+; W64-NEXT:# fallthrough-return
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; W32-
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for ARM64 (PR #166706)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166706
>From ad3a33442bb613f844dff50aab13b9f0075ac06d Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 17:09:45 -0500
Subject: [PATCH] [LLVM][AArch64] Add native ct.select support for ARM64
This patch implements architecture-specific lowering for ct.select on AArch64
using CSEL (conditional select) instructions for constant-time selection.
Implementation details:
- Uses CSEL family of instructions for scalar integer types
- Uses FCSEL for floating-point types (F16, BF16, F32, F64)
- Post-RA MC lowering to convert pseudo-instructions to real CSEL/FCSEL
- Handles vector types appropriately
- Comprehensive test coverage for AArch64
The implementation includes:
- ISelLowering: Custom lowering to CTSELECT pseudo-instructions
- InstrInfo: Pseudo-instruction definitions and patterns
- MCInstLower: Post-RA lowering of pseudo-instructions to actual CSEL/FCSEL
- Proper handling of condition codes for constant-time guarantees
---
.../Target/AArch64/AArch64ISelLowering.cpp| 56 +
llvm/lib/Target/AArch64/AArch64ISelLowering.h | 11 +
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 200 --
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 40
.../lib/Target/AArch64/AArch64MCInstLower.cpp | 18 ++
llvm/test/CodeGen/AArch64/ctselect.ll | 153 ++
6 files changed, 368 insertions(+), 110 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/ctselect.ll
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 7199319ccdd9f..884f7ef59d5b0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -505,12 +505,36 @@ AArch64TargetLowering::AArch64TargetLowering(const
TargetMachine &TM,
setOperationAction(ISD::BR_CC, MVT::f64, Custom);
setOperationAction(ISD::SELECT, MVT::i32, Custom);
setOperationAction(ISD::SELECT, MVT::i64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i8, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i16, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i64, Custom);
if (Subtarget->hasFPARMv8()) {
setOperationAction(ISD::SELECT, MVT::f16, Custom);
setOperationAction(ISD::SELECT, MVT::bf16, Custom);
}
+ if (Subtarget->hasFullFP16()) {
+setOperationAction(ISD::CTSELECT, MVT::f16, Custom);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Custom);
+ } else {
+setOperationAction(ISD::CTSELECT, MVT::f16, Promote);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Promote);
+ }
setOperationAction(ISD::SELECT, MVT::f32, Custom);
setOperationAction(ISD::SELECT, MVT::f64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f64, Custom);
+ for (MVT VT : MVT::vector_valuetypes()) {
+MVT elemType = VT.getVectorElementType();
+if (elemType == MVT::i8 || elemType == MVT::i16) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else if ((elemType == MVT::f16 || elemType == MVT::bf16) &&
+ !Subtarget->hasFullFP16()) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else {
+ setOperationAction(ISD::CTSELECT, VT, Expand);
+}
+ }
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
setOperationAction(ISD::SELECT_CC, MVT::i64, Custom);
setOperationAction(ISD::SELECT_CC, MVT::f16, Custom);
@@ -3375,6 +3399,20 @@ void AArch64TargetLowering::fixupPtrauthDiscriminator(
IntDiscOp.setImm(IntDisc);
}
+MachineBasicBlock *AArch64TargetLowering::EmitCTSELECT(MachineInstr &MI,
+ MachineBasicBlock *MBB,
+ unsigned Opcode) const {
+ const TargetInstrInfo *TII = Subtarget->getInstrInfo();
+ DebugLoc DL = MI.getDebugLoc();
+ MachineInstrBuilder Builder = BuildMI(*MBB, MI, DL, TII->get(Opcode));
+ for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx) {
+Builder.add(MI.getOperand(Idx));
+ }
+ Builder->setFlag(MachineInstr::NoMerge);
+ MBB->remove_instr(&MI);
+ return MBB;
+}
+
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
MachineInstr &MI, MachineBasicBlock *BB) const {
@@ -7862,6 +7900,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op,
return LowerSELECT(Op, DAG);
case ISD::SELECT_CC:
return LowerSELECT_CC(Op, DAG);
+ case ISD::CTSELECT:
+return LowerCTSELECT(Op, DAG);
case ISD::JumpTable:
return LowerJumpTable(Op, DAG);
case ISD::BR_JT:
@@ -12439,6 +12479,22 @@ SDValue AArch64TargetLowering::LowerSELECT(SDValue Op,
return Res;
}
+SDValue AArch64TargetLowering::LowerCTSELECT(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDValue CCVal = Op->getOperand(0);
+ SDValue TVal = Op->getOper
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for ARM64 (PR #166706)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166706
>From ad3a33442bb613f844dff50aab13b9f0075ac06d Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 17:09:45 -0500
Subject: [PATCH] [LLVM][AArch64] Add native ct.select support for ARM64
This patch implements architecture-specific lowering for ct.select on AArch64
using CSEL (conditional select) instructions for constant-time selection.
Implementation details:
- Uses CSEL family of instructions for scalar integer types
- Uses FCSEL for floating-point types (F16, BF16, F32, F64)
- Post-RA MC lowering to convert pseudo-instructions to real CSEL/FCSEL
- Handles vector types appropriately
- Comprehensive test coverage for AArch64
The implementation includes:
- ISelLowering: Custom lowering to CTSELECT pseudo-instructions
- InstrInfo: Pseudo-instruction definitions and patterns
- MCInstLower: Post-RA lowering of pseudo-instructions to actual CSEL/FCSEL
- Proper handling of condition codes for constant-time guarantees
---
.../Target/AArch64/AArch64ISelLowering.cpp| 56 +
llvm/lib/Target/AArch64/AArch64ISelLowering.h | 11 +
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 200 --
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 40
.../lib/Target/AArch64/AArch64MCInstLower.cpp | 18 ++
llvm/test/CodeGen/AArch64/ctselect.ll | 153 ++
6 files changed, 368 insertions(+), 110 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/ctselect.ll
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 7199319ccdd9f..884f7ef59d5b0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -505,12 +505,36 @@ AArch64TargetLowering::AArch64TargetLowering(const
TargetMachine &TM,
setOperationAction(ISD::BR_CC, MVT::f64, Custom);
setOperationAction(ISD::SELECT, MVT::i32, Custom);
setOperationAction(ISD::SELECT, MVT::i64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i8, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i16, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i64, Custom);
if (Subtarget->hasFPARMv8()) {
setOperationAction(ISD::SELECT, MVT::f16, Custom);
setOperationAction(ISD::SELECT, MVT::bf16, Custom);
}
+ if (Subtarget->hasFullFP16()) {
+setOperationAction(ISD::CTSELECT, MVT::f16, Custom);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Custom);
+ } else {
+setOperationAction(ISD::CTSELECT, MVT::f16, Promote);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Promote);
+ }
setOperationAction(ISD::SELECT, MVT::f32, Custom);
setOperationAction(ISD::SELECT, MVT::f64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f64, Custom);
+ for (MVT VT : MVT::vector_valuetypes()) {
+MVT elemType = VT.getVectorElementType();
+if (elemType == MVT::i8 || elemType == MVT::i16) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else if ((elemType == MVT::f16 || elemType == MVT::bf16) &&
+ !Subtarget->hasFullFP16()) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else {
+ setOperationAction(ISD::CTSELECT, VT, Expand);
+}
+ }
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
setOperationAction(ISD::SELECT_CC, MVT::i64, Custom);
setOperationAction(ISD::SELECT_CC, MVT::f16, Custom);
@@ -3375,6 +3399,20 @@ void AArch64TargetLowering::fixupPtrauthDiscriminator(
IntDiscOp.setImm(IntDisc);
}
+MachineBasicBlock *AArch64TargetLowering::EmitCTSELECT(MachineInstr &MI,
+ MachineBasicBlock *MBB,
+ unsigned Opcode) const {
+ const TargetInstrInfo *TII = Subtarget->getInstrInfo();
+ DebugLoc DL = MI.getDebugLoc();
+ MachineInstrBuilder Builder = BuildMI(*MBB, MI, DL, TII->get(Opcode));
+ for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx) {
+Builder.add(MI.getOperand(Idx));
+ }
+ Builder->setFlag(MachineInstr::NoMerge);
+ MBB->remove_instr(&MI);
+ return MBB;
+}
+
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
MachineInstr &MI, MachineBasicBlock *BB) const {
@@ -7862,6 +7900,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op,
return LowerSELECT(Op, DAG);
case ISD::SELECT_CC:
return LowerSELECT_CC(Op, DAG);
+ case ISD::CTSELECT:
+return LowerCTSELECT(Op, DAG);
case ISD::JumpTable:
return LowerJumpTable(Op, DAG);
case ISD::BR_JT:
@@ -12439,6 +12479,22 @@ SDValue AArch64TargetLowering::LowerSELECT(SDValue Op,
return Res;
}
+SDValue AArch64TargetLowering::LowerCTSELECT(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDValue CCVal = Op->getOperand(0);
+ SDValue TVal = Op->getOper
[llvm-branch-commits] [llvm] [ConstantTime][MIPS] Add comprehensive tests for ct.select (PR #166705)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166705
>From 34fc2ff685bebd625f4d5730b41f053356ada52b Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:26 -0500
Subject: [PATCH] [LLVM][MIPS] Add comprehensive tests for ct.select
---
.../Mips/ctselect-fallback-edge-cases.ll | 244 +
.../Mips/ctselect-fallback-patterns.ll| 426 +
.../CodeGen/Mips/ctselect-fallback-vector.ll | 830 ++
llvm/test/CodeGen/Mips/ctselect-fallback.ll | 371
.../CodeGen/Mips/ctselect-side-effects.ll | 183
5 files changed, 2054 insertions(+)
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..f1831a625d4a4
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,244 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=mipsel-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M32
+; RUN: llc < %s -mtriple=mips64el-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M64
+
+; Portable edge case tests
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; M32-LABEL: test_ctselect_i1:
+; M32: # %bb.0:
+; M32-NEXT:xori $2, $4, 1
+; M32-NEXT:and $1, $4, $5
+; M32-NEXT:and $2, $2, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $1, $2
+;
+; M64-LABEL: test_ctselect_i1:
+; M64: # %bb.0:
+; M64-NEXT:sll $2, $4, 0
+; M64-NEXT:sll $1, $6, 0
+; M64-NEXT:xori $2, $2, 1
+; M64-NEXT:and $1, $2, $1
+; M64-NEXT:and $2, $4, $5
+; M64-NEXT:sll $2, $2, 0
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; M32-LABEL: test_ctselect_extremal_values:
+; M32: # %bb.0:
+; M32-NEXT:lui $3, 32767
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:ori $3, $3, 65535
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $3
+; M32-NEXT:lui $3, 32768
+; M32-NEXT:and $1, $1, $3
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_extremal_values:
+; M64: # %bb.0:
+; M64-NEXT:sll $1, $4, 0
+; M64-NEXT:lui $3, 32767
+; M64-NEXT:andi $1, $1, 1
+; M64-NEXT:ori $3, $3, 65535
+; M64-NEXT:negu $2, $1
+; M64-NEXT:addiu $1, $1, -1
+; M64-NEXT:and $2, $2, $3
+; M64-NEXT:lui $3, 32768
+; M64-NEXT:and $1, $1, $3
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; M32-LABEL: test_ctselect_null_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $1, $1
+; M32-NEXT:jr $ra
+; M32-NEXT:and $2, $1, $5
+;
+; M64-LABEL: test_ctselect_null_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $1, $1
+; M64-NEXT:jr $ra
+; M64-NEXT:and $2, $1, $5
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; M32-LABEL: test_ctselect_function_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $5
+; M32-NEXT:and $1, $1, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_function_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $2, $1
+; M64-NEXT:daddiu $1, $1, -1
+; M64-NEXT:and $2, $2, $5
+; M64-NEXT:and $1, $1, $6
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; M32-LABEL: test_ctselect_ptr_cmp:
+; M32: # %bb.0:
+; M32-NEXT:xor $1, $4, $5
+; M32-NEXT:sltu $1, $zero, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $1, $6
+; M32-NEXT:not $1, $1
+; M32-NEXT:and $1, $1, $7
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_ptr_cmp:
+; M64: # %bb.0:
+; M6
[llvm-branch-commits] [clang] [ConstantTime][Clang] Add __builtin_ct_select for constant-time selection (PR #166703)
https://github.com/wizardengineer converted_to_draft https://github.com/llvm/llvm-project/pull/166703 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for ARM64 (PR #166706)
https://github.com/wizardengineer converted_to_draft https://github.com/llvm/llvm-project/pull/166706 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ConstantTime][Clang] Add __builtin_ct_select for constant-time selection (PR #166703)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166703
>From 6091c90ca822b4c78827606684d8e5f85ee3cf5f Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 10:56:34 -0500
Subject: [PATCH] [ConstantTime][Clang] Add __builtin_ct_select for
constant-time selection
---
clang/docs/LanguageExtensions.rst | 44 ++
clang/include/clang/Basic/Builtins.td | 8 +
clang/lib/CodeGen/CGBuiltin.cpp | 13 +
clang/lib/Sema/SemaChecking.cpp | 64 ++
.../test/Sema/builtin-ct-select-edge-cases.c | 373 ++
clang/test/Sema/builtin-ct-select.c | 683 ++
6 files changed, 1185 insertions(+)
create mode 100644 clang/test/Sema/builtin-ct-select-edge-cases.c
create mode 100644 clang/test/Sema/builtin-ct-select.c
diff --git a/clang/docs/LanguageExtensions.rst
b/clang/docs/LanguageExtensions.rst
index 87d38e7d99e50..647ec19a0792f 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6798,3 +6798,47 @@ Clang fails to reject some code that should be rejected.
e.g.,
// own initializer rather than rejecting the code with an undeclared
identifier
// diagnostic.
auto x = x;
+
+.. _langext-__builtin_ct_select:
+
+``__builtin_ct_select``
+---
+
+``__builtin_ct_select`` performs a constant-time conditional selection between
+two values. Unlike the ternary operator ``?:``, this builtin is designed to
+execute in constant time regardless of the condition value, making it suitable
+for cryptographic and security-sensitive code where timing side-channels must
+be avoided.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ __builtin_ct_select(condition, true_value, false_value)
+
+**Examples**:
+
+.. code-block:: c++
+
+ // Select between two integers
+ int result = __builtin_ct_select(secret_bit, value_a, value_b);
+
+ // Select between two pointers
+ int *ptr = __builtin_ct_select(condition, ptr_a, ptr_b);
+
+ // Select between two floating-point values
+ double d = __builtin_ct_select(flag, 1.0, 2.0);
+
+**Description**:
+
+The first argument is an integer condition that is converted to a boolean
+(non-zero is true, zero is false). The second and third arguments must have
+the same scalar or vector type. The builtin returns the second argument if
+the condition is true, otherwise the third argument.
+
+The operation is guaranteed to be lowered to constant-time machine code that
+does not branch on the condition value, preventing timing-based side-channel
+attacks.
+
+Query for this feature with ``__has_builtin(__builtin_ct_select)``.
+
diff --git a/clang/include/clang/Basic/Builtins.td
b/clang/include/clang/Basic/Builtins.td
index 98fb2debad06d..ffc924c1b11e1 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5327,3 +5327,11 @@ def CountedByRef : Builtin {
let Attributes = [NoThrow, CustomTypeChecking];
let Prototype = "int(...)";
}
+
+// Constant-time select builtin
+def CtSelect : Builtin {
+ let Spellings = ["__builtin_ct_select"];
+ let Attributes = [NoThrow, Const, UnevaluatedArguments,
+ConstIgnoringExceptions, CustomTypeChecking];
+ let Prototype = "void(...)";
+}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 01be374422d93..03ed50368757b 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6343,6 +6343,19 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl
GD, unsigned BuiltinID,
auto Str = CGM.GetAddrOfConstantCString(Name, "");
return RValue::get(Str.getPointer());
}
+ case Builtin::BI__builtin_ct_select: {
+auto *Cond = EmitScalarExpr(E->getArg(0));
+auto *A = EmitScalarExpr(E->getArg(1));
+auto *B = EmitScalarExpr(E->getArg(2));
+
+if (Cond->getType()->getIntegerBitWidth() != 1)
+ Cond = Builder.CreateICmpNE(
+ Cond, llvm::ConstantInt::get(Cond->getType(), 0), "cond.bool");
+
+llvm::Function *Fn =
+CGM.getIntrinsic(llvm::Intrinsic::ct_select, {A->getType()});
+return RValue::get(Builder.CreateCall(Fn, {Cond, A, B}));
+ }
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 02c838bc4a862..3f8a919c8f66d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3587,6 +3587,70 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl,
unsigned BuiltinID,
if (BuiltinCountedByRef(TheCall))
return ExprError();
break;
+
+ case Builtin::BI__builtin_ct_select: {
+if (TheCall->getNumArgs() != 3) {
+ // Simple argument count check without complex diagnostics
+ if (TheCall->getNumArgs() < 3) {
+return Diag(TheCall->getEndLoc(),
+diag::err_typecheck_call_too_few_args_at_least)
+ << 0 << 3 << TheCall->getNumArgs()
[llvm-branch-commits] [llvm] [ConstantTime][RISCV] Add comprehensive tests for ct.select (PR #166708)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166708
>From 0d548a5b91307bcd353ef89e67e6198dae5d6847 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:00 -0500
Subject: [PATCH] [ConstantTime][RISCV] Add comprehensive tests for ct.select
Add comprehensive test suite for RISC-V fallback implementation:
- Edge cases (zero conditions, large integers, sign extension)
- Pattern matching (nested selects, chains)
- Vector support with RVV extensions
- Side effects and memory operations
The basic fallback test is in the core infrastructure PR.
---
.../RISCV/ctselect-fallback-edge-cases.ll | 214 +
.../RISCV/ctselect-fallback-patterns.ll | 383 +
.../RISCV/ctselect-fallback-vector-rvv.ll | 804 ++
.../CodeGen/RISCV/ctselect-side-effects.ll| 176
4 files changed, 1577 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-vector-rvv.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..af1be0c8f3ddc
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,214 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=riscv64 -O3 | FileCheck %s --check-prefix=RV64
+; RUN: llc < %s -mtriple=riscv32 -O3 | FileCheck %s --check-prefix=RV32
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; RV64-LABEL: test_ctselect_i1:
+; RV64: # %bb.0:
+; RV64-NEXT:and a1, a0, a1
+; RV64-NEXT:xori a0, a0, 1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_i1:
+; RV32: # %bb.0:
+; RV32-NEXT:and a1, a0, a1
+; RV32-NEXT:xori a0, a0, 1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; RV64-LABEL: test_ctselect_extremal_values:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:lui a1, 524288
+; RV64-NEXT:subw a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_extremal_values:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:lui a1, 524288
+; RV32-NEXT:addi a2, a0, -1
+; RV32-NEXT:neg a0, a0
+; RV32-NEXT:and a1, a2, a1
+; RV32-NEXT:slli a0, a0, 1
+; RV32-NEXT:srli a0, a0, 1
+; RV32-NEXT:or a0, a0, a1
+; RV32-NEXT:ret
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; RV64-LABEL: test_ctselect_null_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:slli a0, a0, 63
+; RV64-NEXT:srai a0, a0, 63
+; RV64-NEXT:and a0, a0, a1
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_null_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:slli a0, a0, 31
+; RV32-NEXT:srai a0, a0, 31
+; RV32-NEXT:and a0, a0, a1
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; RV64-LABEL: test_ctselect_function_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:neg a3, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a1, a3, a1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_function_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:neg a3, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a1, a3, a1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; RV64-LABEL: test_ctselect_ptr_cmp:
+; RV64: # %bb.0:
+; RV64-NEXT:xor a0, a0, a1
+; RV64-NEXT:snez a0, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a2, a0, a2
+; RV64-NEXT:not a0, a0
+; RV64-NEXT:and a0, a0, a3
+; RV64-NEXT:or a0, a2, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_ptr_cmp:
+; RV32: # %bb.0:
+; RV32-NEXT:xor a0, a0, a1
+; RV32-NEXT:snez a0, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a2, a0, a2
+; RV32-NEXT:not a0, a0
+; RV32-NEXT:
[llvm-branch-commits] [llvm] [ConstantTime][WebAssembly] Add comprehensive tests for ct.select (PR #166709)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166709
>From 2ba35b0579eca946d8bb9bdcdef9f8150ccc88aa Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:03:23 -0500
Subject: [PATCH] [ConstantTime][WebAssembly] Add comprehensive tests for
ct.select
---
.../ctselect-fallback-edge-cases.ll | 376 +
.../WebAssembly/ctselect-fallback-patterns.ll | 641
.../WebAssembly/ctselect-fallback-vector.ll | 714 ++
.../CodeGen/WebAssembly/ctselect-fallback.ll | 552 ++
.../WebAssembly/ctselect-side-effects.ll | 226 ++
5 files changed, 2509 insertions(+)
create mode 100644
llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..b0f7f2807debd
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,376 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W32
+; RUN: llc < %s -mtriple=wasm64-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W64
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; W32-LABEL: test_ctselect_i1:
+; W32: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:local.get 0
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.xor
+; W32-NEXT:local.get 2
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_i1:
+; W64: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:local.get 0
+; W64-NEXT:local.get 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.xor
+; W64-NEXT:local.get 2
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; W32-LABEL: test_ctselect_extremal_values:
+; W32: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.tee 0
+; W32-NEXT:i32.sub
+; W32-NEXT:i32.const 2147483647
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const -1
+; W32-NEXT:i32.add
+; W32-NEXT:i32.const -2147483648
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_extremal_values:
+; W64: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i32.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.tee 0
+; W64-NEXT:i32.sub
+; W64-NEXT:i32.const 2147483647
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const -1
+; W64-NEXT:i32.add
+; W64-NEXT:i32.const -2147483648
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; W32-LABEL: test_ctselect_null_ptr:
+; W32: .functype test_ctselect_null_ptr (i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:i32.sub
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_null_ptr:
+; W64: .functype test_ctselect_null_ptr (i32, i64) -> (i64)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i64.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i64.extend_i32_u
+; W64-NEXT:i64.const 1
+; W64-NEXT:i64.and
+; W64-NEXT:i64.sub
+; W64-NEXT:local.get 1
+; W64-NEXT:i64.and
+; W64-NEXT:# fallthrough-return
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; W32-
[llvm-branch-commits] [llvm] [ConstantTime][WebAssembly] Add comprehensive tests for ct.select (PR #166709)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166709
>From 2ba35b0579eca946d8bb9bdcdef9f8150ccc88aa Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:03:23 -0500
Subject: [PATCH] [ConstantTime][WebAssembly] Add comprehensive tests for
ct.select
---
.../ctselect-fallback-edge-cases.ll | 376 +
.../WebAssembly/ctselect-fallback-patterns.ll | 641
.../WebAssembly/ctselect-fallback-vector.ll | 714 ++
.../CodeGen/WebAssembly/ctselect-fallback.ll | 552 ++
.../WebAssembly/ctselect-side-effects.ll | 226 ++
5 files changed, 2509 insertions(+)
create mode 100644
llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..b0f7f2807debd
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,376 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W32
+; RUN: llc < %s -mtriple=wasm64-unknown-unknown -O3 -filetype=asm | FileCheck
%s --check-prefix=W64
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; W32-LABEL: test_ctselect_i1:
+; W32: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:local.get 0
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.xor
+; W32-NEXT:local.get 2
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_i1:
+; W64: .functype test_ctselect_i1 (i32, i32, i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:local.get 0
+; W64-NEXT:local.get 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.xor
+; W64-NEXT:local.get 2
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; W32-LABEL: test_ctselect_extremal_values:
+; W32: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:local.tee 0
+; W32-NEXT:i32.sub
+; W32-NEXT:i32.const 2147483647
+; W32-NEXT:i32.and
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const -1
+; W32-NEXT:i32.add
+; W32-NEXT:i32.const -2147483648
+; W32-NEXT:i32.and
+; W32-NEXT:i32.or
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_extremal_values:
+; W64: .functype test_ctselect_extremal_values (i32) -> (i32)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i32.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const 1
+; W64-NEXT:i32.and
+; W64-NEXT:local.tee 0
+; W64-NEXT:i32.sub
+; W64-NEXT:i32.const 2147483647
+; W64-NEXT:i32.and
+; W64-NEXT:local.get 0
+; W64-NEXT:i32.const -1
+; W64-NEXT:i32.add
+; W64-NEXT:i32.const -2147483648
+; W64-NEXT:i32.and
+; W64-NEXT:i32.or
+; W64-NEXT:# fallthrough-return
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; W32-LABEL: test_ctselect_null_ptr:
+; W32: .functype test_ctselect_null_ptr (i32, i32) -> (i32)
+; W32-NEXT: # %bb.0:
+; W32-NEXT:i32.const 0
+; W32-NEXT:local.get 0
+; W32-NEXT:i32.const 1
+; W32-NEXT:i32.and
+; W32-NEXT:i32.sub
+; W32-NEXT:local.get 1
+; W32-NEXT:i32.and
+; W32-NEXT:# fallthrough-return
+;
+; W64-LABEL: test_ctselect_null_ptr:
+; W64: .functype test_ctselect_null_ptr (i32, i64) -> (i64)
+; W64-NEXT: # %bb.0:
+; W64-NEXT:i64.const 0
+; W64-NEXT:local.get 0
+; W64-NEXT:i64.extend_i32_u
+; W64-NEXT:i64.const 1
+; W64-NEXT:i64.and
+; W64-NEXT:i64.sub
+; W64-NEXT:local.get 1
+; W64-NEXT:i64.and
+; W64-NEXT:# fallthrough-return
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; W32-
[llvm-branch-commits] [llvm] [ConstantTime][MIPS] Add comprehensive tests for ct.select (PR #166705)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166705
>From b49b9cb5740c7e5a7c54dd5d90414fb481934f6a Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:26 -0500
Subject: [PATCH] [LLVM][MIPS] Add comprehensive tests for ct.select
---
.../Mips/ctselect-fallback-edge-cases.ll | 244 +
.../Mips/ctselect-fallback-patterns.ll| 426 +
.../CodeGen/Mips/ctselect-fallback-vector.ll | 830 ++
llvm/test/CodeGen/Mips/ctselect-fallback.ll | 371
.../CodeGen/Mips/ctselect-side-effects.ll | 183
5 files changed, 2054 insertions(+)
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..f1831a625d4a4
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,244 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=mipsel-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M32
+; RUN: llc < %s -mtriple=mips64el-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M64
+
+; Portable edge case tests
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; M32-LABEL: test_ctselect_i1:
+; M32: # %bb.0:
+; M32-NEXT:xori $2, $4, 1
+; M32-NEXT:and $1, $4, $5
+; M32-NEXT:and $2, $2, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $1, $2
+;
+; M64-LABEL: test_ctselect_i1:
+; M64: # %bb.0:
+; M64-NEXT:sll $2, $4, 0
+; M64-NEXT:sll $1, $6, 0
+; M64-NEXT:xori $2, $2, 1
+; M64-NEXT:and $1, $2, $1
+; M64-NEXT:and $2, $4, $5
+; M64-NEXT:sll $2, $2, 0
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; M32-LABEL: test_ctselect_extremal_values:
+; M32: # %bb.0:
+; M32-NEXT:lui $3, 32767
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:ori $3, $3, 65535
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $3
+; M32-NEXT:lui $3, 32768
+; M32-NEXT:and $1, $1, $3
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_extremal_values:
+; M64: # %bb.0:
+; M64-NEXT:sll $1, $4, 0
+; M64-NEXT:lui $3, 32767
+; M64-NEXT:andi $1, $1, 1
+; M64-NEXT:ori $3, $3, 65535
+; M64-NEXT:negu $2, $1
+; M64-NEXT:addiu $1, $1, -1
+; M64-NEXT:and $2, $2, $3
+; M64-NEXT:lui $3, 32768
+; M64-NEXT:and $1, $1, $3
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; M32-LABEL: test_ctselect_null_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $1, $1
+; M32-NEXT:jr $ra
+; M32-NEXT:and $2, $1, $5
+;
+; M64-LABEL: test_ctselect_null_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $1, $1
+; M64-NEXT:jr $ra
+; M64-NEXT:and $2, $1, $5
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; M32-LABEL: test_ctselect_function_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $5
+; M32-NEXT:and $1, $1, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_function_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $2, $1
+; M64-NEXT:daddiu $1, $1, -1
+; M64-NEXT:and $2, $2, $5
+; M64-NEXT:and $1, $1, $6
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; M32-LABEL: test_ctselect_ptr_cmp:
+; M32: # %bb.0:
+; M32-NEXT:xor $1, $4, $5
+; M32-NEXT:sltu $1, $zero, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $1, $6
+; M32-NEXT:not $1, $1
+; M32-NEXT:and $1, $1, $7
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_ptr_cmp:
+; M64: # %bb.0:
+; M6
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for ARM64 (PR #166706)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166706
>From a709071b6b356df84b4579cd09ee32e7895e1b5a Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 17:09:45 -0500
Subject: [PATCH] [LLVM][AArch64] Add native ct.select support for ARM64
This patch implements architecture-specific lowering for ct.select on AArch64
using CSEL (conditional select) instructions for constant-time selection.
Implementation details:
- Uses CSEL family of instructions for scalar integer types
- Uses FCSEL for floating-point types (F16, BF16, F32, F64)
- Post-RA MC lowering to convert pseudo-instructions to real CSEL/FCSEL
- Handles vector types appropriately
- Comprehensive test coverage for AArch64
The implementation includes:
- ISelLowering: Custom lowering to CTSELECT pseudo-instructions
- InstrInfo: Pseudo-instruction definitions and patterns
- MCInstLower: Post-RA lowering of pseudo-instructions to actual CSEL/FCSEL
- Proper handling of condition codes for constant-time guarantees
---
.../Target/AArch64/AArch64ISelLowering.cpp| 56 +++
llvm/lib/Target/AArch64/AArch64ISelLowering.h | 11 ++
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 39 -
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 40 +
.../lib/Target/AArch64/AArch64MCInstLower.cpp | 18 +++
llvm/test/CodeGen/AArch64/ctselect.ll | 153 ++
6 files changed, 313 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/ctselect.ll
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 7199319ccdd9f..884f7ef59d5b0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -505,12 +505,36 @@ AArch64TargetLowering::AArch64TargetLowering(const
TargetMachine &TM,
setOperationAction(ISD::BR_CC, MVT::f64, Custom);
setOperationAction(ISD::SELECT, MVT::i32, Custom);
setOperationAction(ISD::SELECT, MVT::i64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i8, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i16, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i64, Custom);
if (Subtarget->hasFPARMv8()) {
setOperationAction(ISD::SELECT, MVT::f16, Custom);
setOperationAction(ISD::SELECT, MVT::bf16, Custom);
}
+ if (Subtarget->hasFullFP16()) {
+setOperationAction(ISD::CTSELECT, MVT::f16, Custom);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Custom);
+ } else {
+setOperationAction(ISD::CTSELECT, MVT::f16, Promote);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Promote);
+ }
setOperationAction(ISD::SELECT, MVT::f32, Custom);
setOperationAction(ISD::SELECT, MVT::f64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f64, Custom);
+ for (MVT VT : MVT::vector_valuetypes()) {
+MVT elemType = VT.getVectorElementType();
+if (elemType == MVT::i8 || elemType == MVT::i16) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else if ((elemType == MVT::f16 || elemType == MVT::bf16) &&
+ !Subtarget->hasFullFP16()) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else {
+ setOperationAction(ISD::CTSELECT, VT, Expand);
+}
+ }
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
setOperationAction(ISD::SELECT_CC, MVT::i64, Custom);
setOperationAction(ISD::SELECT_CC, MVT::f16, Custom);
@@ -3375,6 +3399,20 @@ void AArch64TargetLowering::fixupPtrauthDiscriminator(
IntDiscOp.setImm(IntDisc);
}
+MachineBasicBlock *AArch64TargetLowering::EmitCTSELECT(MachineInstr &MI,
+ MachineBasicBlock *MBB,
+ unsigned Opcode) const {
+ const TargetInstrInfo *TII = Subtarget->getInstrInfo();
+ DebugLoc DL = MI.getDebugLoc();
+ MachineInstrBuilder Builder = BuildMI(*MBB, MI, DL, TII->get(Opcode));
+ for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx) {
+Builder.add(MI.getOperand(Idx));
+ }
+ Builder->setFlag(MachineInstr::NoMerge);
+ MBB->remove_instr(&MI);
+ return MBB;
+}
+
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
MachineInstr &MI, MachineBasicBlock *BB) const {
@@ -7862,6 +7900,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op,
return LowerSELECT(Op, DAG);
case ISD::SELECT_CC:
return LowerSELECT_CC(Op, DAG);
+ case ISD::CTSELECT:
+return LowerCTSELECT(Op, DAG);
case ISD::JumpTable:
return LowerJumpTable(Op, DAG);
case ISD::BR_JT:
@@ -12439,6 +12479,22 @@ SDValue AArch64TargetLowering::LowerSELECT(SDValue Op,
return Res;
}
+SDValue AArch64TargetLowering::LowerCTSELECT(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDValue CCVal = Op->getOperand(0);
+ SDValue TVal = Op->getOperand(1)
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for ARM32 and Thumb (PR #166707)
https://github.com/wizardengineer converted_to_draft https://github.com/llvm/llvm-project/pull/166707 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for ARM64 (PR #166706)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166706
>From bd1047afec312170e4d49c98b29b5ee25dae6b3e Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 17:09:45 -0500
Subject: [PATCH] [LLVM][AArch64] Add native ct.select support for ARM64
This patch implements architecture-specific lowering for ct.select on AArch64
using CSEL (conditional select) instructions for constant-time selection.
Implementation details:
- Uses CSEL family of instructions for scalar integer types
- Uses FCSEL for floating-point types (F16, BF16, F32, F64)
- Post-RA MC lowering to convert pseudo-instructions to real CSEL/FCSEL
- Handles vector types appropriately
- Comprehensive test coverage for AArch64
The implementation includes:
- ISelLowering: Custom lowering to CTSELECT pseudo-instructions
- InstrInfo: Pseudo-instruction definitions and patterns
- MCInstLower: Post-RA lowering of pseudo-instructions to actual CSEL/FCSEL
- Proper handling of condition codes for constant-time guarantees
---
.../Target/AArch64/AArch64ISelLowering.cpp| 56 +++
llvm/lib/Target/AArch64/AArch64ISelLowering.h | 11 ++
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 39 -
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 40 +
.../lib/Target/AArch64/AArch64MCInstLower.cpp | 18 +++
llvm/test/CodeGen/AArch64/ctselect.ll | 153 ++
6 files changed, 313 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/ctselect.ll
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 7199319ccdd9f..884f7ef59d5b0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -505,12 +505,36 @@ AArch64TargetLowering::AArch64TargetLowering(const
TargetMachine &TM,
setOperationAction(ISD::BR_CC, MVT::f64, Custom);
setOperationAction(ISD::SELECT, MVT::i32, Custom);
setOperationAction(ISD::SELECT, MVT::i64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i8, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i16, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i64, Custom);
if (Subtarget->hasFPARMv8()) {
setOperationAction(ISD::SELECT, MVT::f16, Custom);
setOperationAction(ISD::SELECT, MVT::bf16, Custom);
}
+ if (Subtarget->hasFullFP16()) {
+setOperationAction(ISD::CTSELECT, MVT::f16, Custom);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Custom);
+ } else {
+setOperationAction(ISD::CTSELECT, MVT::f16, Promote);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Promote);
+ }
setOperationAction(ISD::SELECT, MVT::f32, Custom);
setOperationAction(ISD::SELECT, MVT::f64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f64, Custom);
+ for (MVT VT : MVT::vector_valuetypes()) {
+MVT elemType = VT.getVectorElementType();
+if (elemType == MVT::i8 || elemType == MVT::i16) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else if ((elemType == MVT::f16 || elemType == MVT::bf16) &&
+ !Subtarget->hasFullFP16()) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else {
+ setOperationAction(ISD::CTSELECT, VT, Expand);
+}
+ }
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
setOperationAction(ISD::SELECT_CC, MVT::i64, Custom);
setOperationAction(ISD::SELECT_CC, MVT::f16, Custom);
@@ -3375,6 +3399,20 @@ void AArch64TargetLowering::fixupPtrauthDiscriminator(
IntDiscOp.setImm(IntDisc);
}
+MachineBasicBlock *AArch64TargetLowering::EmitCTSELECT(MachineInstr &MI,
+ MachineBasicBlock *MBB,
+ unsigned Opcode) const {
+ const TargetInstrInfo *TII = Subtarget->getInstrInfo();
+ DebugLoc DL = MI.getDebugLoc();
+ MachineInstrBuilder Builder = BuildMI(*MBB, MI, DL, TII->get(Opcode));
+ for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx) {
+Builder.add(MI.getOperand(Idx));
+ }
+ Builder->setFlag(MachineInstr::NoMerge);
+ MBB->remove_instr(&MI);
+ return MBB;
+}
+
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
MachineInstr &MI, MachineBasicBlock *BB) const {
@@ -7862,6 +7900,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op,
return LowerSELECT(Op, DAG);
case ISD::SELECT_CC:
return LowerSELECT_CC(Op, DAG);
+ case ISD::CTSELECT:
+return LowerCTSELECT(Op, DAG);
case ISD::JumpTable:
return LowerJumpTable(Op, DAG);
case ISD::BR_JT:
@@ -12439,6 +12479,22 @@ SDValue AArch64TargetLowering::LowerSELECT(SDValue Op,
return Res;
}
+SDValue AArch64TargetLowering::LowerCTSELECT(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDValue CCVal = Op->getOperand(0);
+ SDValue TVal = Op->getOperand(1)
[llvm-branch-commits] [llvm] [ConstantTime][MIPS] Add comprehensive tests for ct.select (PR #166705)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166705
>From b49b9cb5740c7e5a7c54dd5d90414fb481934f6a Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:26 -0500
Subject: [PATCH] [LLVM][MIPS] Add comprehensive tests for ct.select
---
.../Mips/ctselect-fallback-edge-cases.ll | 244 +
.../Mips/ctselect-fallback-patterns.ll| 426 +
.../CodeGen/Mips/ctselect-fallback-vector.ll | 830 ++
llvm/test/CodeGen/Mips/ctselect-fallback.ll | 371
.../CodeGen/Mips/ctselect-side-effects.ll | 183
5 files changed, 2054 insertions(+)
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback-vector.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-fallback.ll
create mode 100644 llvm/test/CodeGen/Mips/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..f1831a625d4a4
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,244 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=mipsel-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M32
+; RUN: llc < %s -mtriple=mips64el-unknown-linux-gnu -O3 | FileCheck %s
--check-prefix=M64
+
+; Portable edge case tests
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; M32-LABEL: test_ctselect_i1:
+; M32: # %bb.0:
+; M32-NEXT:xori $2, $4, 1
+; M32-NEXT:and $1, $4, $5
+; M32-NEXT:and $2, $2, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $1, $2
+;
+; M64-LABEL: test_ctselect_i1:
+; M64: # %bb.0:
+; M64-NEXT:sll $2, $4, 0
+; M64-NEXT:sll $1, $6, 0
+; M64-NEXT:xori $2, $2, 1
+; M64-NEXT:and $1, $2, $1
+; M64-NEXT:and $2, $4, $5
+; M64-NEXT:sll $2, $2, 0
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; M32-LABEL: test_ctselect_extremal_values:
+; M32: # %bb.0:
+; M32-NEXT:lui $3, 32767
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:ori $3, $3, 65535
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $3
+; M32-NEXT:lui $3, 32768
+; M32-NEXT:and $1, $1, $3
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_extremal_values:
+; M64: # %bb.0:
+; M64-NEXT:sll $1, $4, 0
+; M64-NEXT:lui $3, 32767
+; M64-NEXT:andi $1, $1, 1
+; M64-NEXT:ori $3, $3, 65535
+; M64-NEXT:negu $2, $1
+; M64-NEXT:addiu $1, $1, -1
+; M64-NEXT:and $2, $2, $3
+; M64-NEXT:lui $3, 32768
+; M64-NEXT:and $1, $1, $3
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; M32-LABEL: test_ctselect_null_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $1, $1
+; M32-NEXT:jr $ra
+; M32-NEXT:and $2, $1, $5
+;
+; M64-LABEL: test_ctselect_null_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $1, $1
+; M64-NEXT:jr $ra
+; M64-NEXT:and $2, $1, $5
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; M32-LABEL: test_ctselect_function_ptr:
+; M32: # %bb.0:
+; M32-NEXT:andi $1, $4, 1
+; M32-NEXT:negu $2, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $2, $5
+; M32-NEXT:and $1, $1, $6
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_function_ptr:
+; M64: # %bb.0:
+; M64-NEXT:andi $1, $4, 1
+; M64-NEXT:dnegu $2, $1
+; M64-NEXT:daddiu $1, $1, -1
+; M64-NEXT:and $2, $2, $5
+; M64-NEXT:and $1, $1, $6
+; M64-NEXT:jr $ra
+; M64-NEXT:or $2, $2, $1
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; M32-LABEL: test_ctselect_ptr_cmp:
+; M32: # %bb.0:
+; M32-NEXT:xor $1, $4, $5
+; M32-NEXT:sltu $1, $zero, $1
+; M32-NEXT:addiu $1, $1, -1
+; M32-NEXT:and $2, $1, $6
+; M32-NEXT:not $1, $1
+; M32-NEXT:and $1, $1, $7
+; M32-NEXT:jr $ra
+; M32-NEXT:or $2, $2, $1
+;
+; M64-LABEL: test_ctselect_ptr_cmp:
+; M64: # %bb.0:
+; M6
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for ARM64 (PR #166706)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166706
>From bd1047afec312170e4d49c98b29b5ee25dae6b3e Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 17:09:45 -0500
Subject: [PATCH] [LLVM][AArch64] Add native ct.select support for ARM64
This patch implements architecture-specific lowering for ct.select on AArch64
using CSEL (conditional select) instructions for constant-time selection.
Implementation details:
- Uses CSEL family of instructions for scalar integer types
- Uses FCSEL for floating-point types (F16, BF16, F32, F64)
- Post-RA MC lowering to convert pseudo-instructions to real CSEL/FCSEL
- Handles vector types appropriately
- Comprehensive test coverage for AArch64
The implementation includes:
- ISelLowering: Custom lowering to CTSELECT pseudo-instructions
- InstrInfo: Pseudo-instruction definitions and patterns
- MCInstLower: Post-RA lowering of pseudo-instructions to actual CSEL/FCSEL
- Proper handling of condition codes for constant-time guarantees
---
.../Target/AArch64/AArch64ISelLowering.cpp| 56 +++
llvm/lib/Target/AArch64/AArch64ISelLowering.h | 11 ++
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 39 -
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 40 +
.../lib/Target/AArch64/AArch64MCInstLower.cpp | 18 +++
llvm/test/CodeGen/AArch64/ctselect.ll | 153 ++
6 files changed, 313 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/ctselect.ll
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 7199319ccdd9f..884f7ef59d5b0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -505,12 +505,36 @@ AArch64TargetLowering::AArch64TargetLowering(const
TargetMachine &TM,
setOperationAction(ISD::BR_CC, MVT::f64, Custom);
setOperationAction(ISD::SELECT, MVT::i32, Custom);
setOperationAction(ISD::SELECT, MVT::i64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i8, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i16, Promote);
+ setOperationAction(ISD::CTSELECT, MVT::i32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::i64, Custom);
if (Subtarget->hasFPARMv8()) {
setOperationAction(ISD::SELECT, MVT::f16, Custom);
setOperationAction(ISD::SELECT, MVT::bf16, Custom);
}
+ if (Subtarget->hasFullFP16()) {
+setOperationAction(ISD::CTSELECT, MVT::f16, Custom);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Custom);
+ } else {
+setOperationAction(ISD::CTSELECT, MVT::f16, Promote);
+setOperationAction(ISD::CTSELECT, MVT::bf16, Promote);
+ }
setOperationAction(ISD::SELECT, MVT::f32, Custom);
setOperationAction(ISD::SELECT, MVT::f64, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f32, Custom);
+ setOperationAction(ISD::CTSELECT, MVT::f64, Custom);
+ for (MVT VT : MVT::vector_valuetypes()) {
+MVT elemType = VT.getVectorElementType();
+if (elemType == MVT::i8 || elemType == MVT::i16) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else if ((elemType == MVT::f16 || elemType == MVT::bf16) &&
+ !Subtarget->hasFullFP16()) {
+ setOperationAction(ISD::CTSELECT, VT, Promote);
+} else {
+ setOperationAction(ISD::CTSELECT, VT, Expand);
+}
+ }
setOperationAction(ISD::SELECT_CC, MVT::i32, Custom);
setOperationAction(ISD::SELECT_CC, MVT::i64, Custom);
setOperationAction(ISD::SELECT_CC, MVT::f16, Custom);
@@ -3375,6 +3399,20 @@ void AArch64TargetLowering::fixupPtrauthDiscriminator(
IntDiscOp.setImm(IntDisc);
}
+MachineBasicBlock *AArch64TargetLowering::EmitCTSELECT(MachineInstr &MI,
+ MachineBasicBlock *MBB,
+ unsigned Opcode) const {
+ const TargetInstrInfo *TII = Subtarget->getInstrInfo();
+ DebugLoc DL = MI.getDebugLoc();
+ MachineInstrBuilder Builder = BuildMI(*MBB, MI, DL, TII->get(Opcode));
+ for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx) {
+Builder.add(MI.getOperand(Idx));
+ }
+ Builder->setFlag(MachineInstr::NoMerge);
+ MBB->remove_instr(&MI);
+ return MBB;
+}
+
MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter(
MachineInstr &MI, MachineBasicBlock *BB) const {
@@ -7862,6 +7900,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op,
return LowerSELECT(Op, DAG);
case ISD::SELECT_CC:
return LowerSELECT_CC(Op, DAG);
+ case ISD::CTSELECT:
+return LowerCTSELECT(Op, DAG);
case ISD::JumpTable:
return LowerJumpTable(Op, DAG);
case ISD::BR_JT:
@@ -12439,6 +12479,22 @@ SDValue AArch64TargetLowering::LowerSELECT(SDValue Op,
return Res;
}
+SDValue AArch64TargetLowering::LowerCTSELECT(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDValue CCVal = Op->getOperand(0);
+ SDValue TVal = Op->getOperand(1)
[llvm-branch-commits] [llvm] [ConstantTime][RISCV] Add comprehensive tests for ct.select (PR #166708)
https://github.com/wizardengineer updated
https://github.com/llvm/llvm-project/pull/166708
>From 0d548a5b91307bcd353ef89e67e6198dae5d6847 Mon Sep 17 00:00:00 2001
From: wizardengineer
Date: Wed, 5 Nov 2025 11:01:00 -0500
Subject: [PATCH] [ConstantTime][RISCV] Add comprehensive tests for ct.select
Add comprehensive test suite for RISC-V fallback implementation:
- Edge cases (zero conditions, large integers, sign extension)
- Pattern matching (nested selects, chains)
- Vector support with RVV extensions
- Side effects and memory operations
The basic fallback test is in the core infrastructure PR.
---
.../RISCV/ctselect-fallback-edge-cases.ll | 214 +
.../RISCV/ctselect-fallback-patterns.ll | 383 +
.../RISCV/ctselect-fallback-vector-rvv.ll | 804 ++
.../CodeGen/RISCV/ctselect-side-effects.ll| 176
4 files changed, 1577 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-patterns.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-fallback-vector-rvv.ll
create mode 100644 llvm/test/CodeGen/RISCV/ctselect-side-effects.ll
diff --git a/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
new file mode 100644
index 0..af1be0c8f3ddc
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/ctselect-fallback-edge-cases.ll
@@ -0,0 +1,214 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 5
+; RUN: llc < %s -mtriple=riscv64 -O3 | FileCheck %s --check-prefix=RV64
+; RUN: llc < %s -mtriple=riscv32 -O3 | FileCheck %s --check-prefix=RV32
+
+; Test with small integer types
+define i1 @test_ctselect_i1(i1 %cond, i1 %a, i1 %b) {
+; RV64-LABEL: test_ctselect_i1:
+; RV64: # %bb.0:
+; RV64-NEXT:and a1, a0, a1
+; RV64-NEXT:xori a0, a0, 1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_i1:
+; RV32: # %bb.0:
+; RV32-NEXT:and a1, a0, a1
+; RV32-NEXT:xori a0, a0, 1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call i1 @llvm.ct.select.i1(i1 %cond, i1 %a, i1 %b)
+ ret i1 %result
+}
+
+; Test with extremal values
+define i32 @test_ctselect_extremal_values(i1 %cond) {
+; RV64-LABEL: test_ctselect_extremal_values:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:lui a1, 524288
+; RV64-NEXT:subw a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_extremal_values:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:lui a1, 524288
+; RV32-NEXT:addi a2, a0, -1
+; RV32-NEXT:neg a0, a0
+; RV32-NEXT:and a1, a2, a1
+; RV32-NEXT:slli a0, a0, 1
+; RV32-NEXT:srli a0, a0, 1
+; RV32-NEXT:or a0, a0, a1
+; RV32-NEXT:ret
+ %result = call i32 @llvm.ct.select.i32(i1 %cond, i32 2147483647, i32
-2147483648)
+ ret i32 %result
+}
+
+; Test with null pointers
+define ptr @test_ctselect_null_ptr(i1 %cond, ptr %ptr) {
+; RV64-LABEL: test_ctselect_null_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:slli a0, a0, 63
+; RV64-NEXT:srai a0, a0, 63
+; RV64-NEXT:and a0, a0, a1
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_null_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:slli a0, a0, 31
+; RV32-NEXT:srai a0, a0, 31
+; RV32-NEXT:and a0, a0, a1
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %ptr, ptr null)
+ ret ptr %result
+}
+
+; Test with function pointers
+define ptr @test_ctselect_function_ptr(i1 %cond, ptr %func1, ptr %func2) {
+; RV64-LABEL: test_ctselect_function_ptr:
+; RV64: # %bb.0:
+; RV64-NEXT:andi a0, a0, 1
+; RV64-NEXT:neg a3, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a1, a3, a1
+; RV64-NEXT:and a0, a0, a2
+; RV64-NEXT:or a0, a1, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_function_ptr:
+; RV32: # %bb.0:
+; RV32-NEXT:andi a0, a0, 1
+; RV32-NEXT:neg a3, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a1, a3, a1
+; RV32-NEXT:and a0, a0, a2
+; RV32-NEXT:or a0, a1, a0
+; RV32-NEXT:ret
+ %result = call ptr @llvm.ct.select.p0(i1 %cond, ptr %func1, ptr %func2)
+ ret ptr %result
+}
+
+; Test with condition from icmp on pointers
+define ptr @test_ctselect_ptr_cmp(ptr %p1, ptr %p2, ptr %a, ptr %b) {
+; RV64-LABEL: test_ctselect_ptr_cmp:
+; RV64: # %bb.0:
+; RV64-NEXT:xor a0, a0, a1
+; RV64-NEXT:snez a0, a0
+; RV64-NEXT:addi a0, a0, -1
+; RV64-NEXT:and a2, a0, a2
+; RV64-NEXT:not a0, a0
+; RV64-NEXT:and a0, a0, a3
+; RV64-NEXT:or a0, a2, a0
+; RV64-NEXT:ret
+;
+; RV32-LABEL: test_ctselect_ptr_cmp:
+; RV32: # %bb.0:
+; RV32-NEXT:xor a0, a0, a1
+; RV32-NEXT:snez a0, a0
+; RV32-NEXT:addi a0, a0, -1
+; RV32-NEXT:and a2, a0, a2
+; RV32-NEXT:not a0, a0
+; RV32-NEXT:
[llvm-branch-commits] [llvm] [ConstantTime][WebAssembly] Add comprehensive tests for ct.select (PR #166709)
https://github.com/wizardengineer converted_to_draft https://github.com/llvm/llvm-project/pull/166709 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [ConstantTime][MIPS] Add comprehensive tests for ct.select (PR #166705)
https://github.com/wizardengineer converted_to_draft https://github.com/llvm/llvm-project/pull/166705 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [ConstantTime][RISCV] Add comprehensive tests for ct.select (PR #166708)
https://github.com/wizardengineer converted_to_draft https://github.com/llvm/llvm-project/pull/166708 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [llvm] [ConstantTime] Native ct.select support for X86 and i386 (PR #166704)
https://github.com/wizardengineer converted_to_draft https://github.com/llvm/llvm-project/pull/166704 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [libcxx] Prepare libcxx and libcxxabi for pointer field protection. (PR #151651)
huixie90 wrote: `std::trivially_relocate` and friends have been removed from C++26. and the new equivalent of `std::trivially_relocate` in C++29 is very likely to be based on `memcpy` or `memmov` . I am not sure how PFP can work with the future `std::trivially_relocate` at all to be honest https://github.com/llvm/llvm-project/pull/151651 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
