Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -5278,3 +5278,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(...)";
}
37 changes: 36 additions & 1 deletion clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
#include "TargetInfo.h"
#include "clang/AST/OSLog.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Intrinsics.h"
Expand Down Expand Up @@ -6450,6 +6451,40 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
auto Str = CGM.GetAddrOfConstantCString(Name, "");
return RValue::get(Str.getPointer());
}
case Builtin::BI__builtin_ct_select: {
if (E->getNumArgs() != 3) {
CGM.getDiags().Report(E->getBeginLoc(),
E->getNumArgs() > 3
? diag::err_typecheck_call_too_many_args
: diag::err_typecheck_call_too_few_args);
return GetUndefRValue(E->getType());
Copy link
Collaborator

Choose a reason for hiding this comment

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

We shouldn't be generating diagnostics here; Sema should have already generated the relevant diagnostics, and if Sema generates a diagnostic, we don't reach codegen.

}

auto *Cond = EmitScalarExpr(E->getArg(0));
auto *A = EmitScalarExpr(E->getArg(1));
auto *B = EmitScalarExpr(E->getArg(2));

// Verify types match
if (A->getType() != B->getType()) {
CGM.getDiags().Report(E->getBeginLoc(),
diag::err_typecheck_convert_incompatible);
return GetUndefRValue(E->getType());
}

// Verify condition is integer type
if (!Cond->getType()->isIntegerTy()) {
CGM.getDiags().Report(E->getBeginLoc(), diag::err_typecheck_expect_int);
return GetUndefRValue(E->getType());
}

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
Expand Down
89 changes: 89 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3494,6 +3494,95 @@ 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() << 0
<< TheCall->getCallee()->getSourceRange();
} else {
return Diag(TheCall->getEndLoc(),
diag::err_typecheck_call_too_many_args)
<< 0 << 3 << TheCall->getNumArgs() << 0
<< TheCall->getCallee()->getSourceRange();
}
}
auto *Cond = TheCall->getArg(0);
auto *A = TheCall->getArg(1);
auto *B = TheCall->getArg(2);

QualType CondTy = Cond->getType();
if (!CondTy->isIntegerType()) {
return Diag(Cond->getBeginLoc(), diag::err_typecheck_cond_expect_scalar)
<< CondTy << Cond->getSourceRange();
}

QualType ATy = A->getType();
QualType BTy = B->getType();

// check for scalar or vector scalar type
if ((!ATy->isScalarType() && !ATy->isVectorType()) ||
(!BTy->isScalarType() && !BTy->isVectorType())) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You probably want to do array/function promotion before this check?

return Diag(A->getBeginLoc(),
diag::err_typecheck_cond_incompatible_operands)
<< ATy << BTy << A->getSourceRange() << B->getSourceRange();
}

// Check if both operands have the same type or can be implicitly converted
QualType ResultTy;
if (Context.hasSameType(ATy, BTy)) {
ResultTy = ATy;
} else {
// Try to find a common type using the same logic as conditional
// expressions
Copy link
Collaborator

Choose a reason for hiding this comment

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

The type computation for conditional expressions is complicated; I'd prefer not to extend it to contexts where we don't need it.

ExprResult ARes = ExprResult(A);
ExprResult BRes = ExprResult(B);

// For arithmetic types, allow promotions within the same category only
if (ATy->isArithmeticType() && BTy->isArithmeticType()) {
// Check if both are integer types or both are floating types
bool AIsInteger = ATy->isIntegerType();
bool BIsInteger = BTy->isIntegerType();
bool AIsFloating = ATy->isFloatingType();
bool BIsFloating = BTy->isFloatingType();

if ((AIsInteger && BIsInteger) || (AIsFloating && BIsFloating)) {
// Both are in the same category, allow usual arithmetic conversions
ResultTy = UsualArithmeticConversions(
ARes, BRes, TheCall->getBeginLoc(), ArithConvKind::Conditional);
if (ARes.isInvalid() || BRes.isInvalid() || ResultTy.isNull()) {
return Diag(A->getBeginLoc(),
diag::err_typecheck_cond_incompatible_operands)
<< ATy << BTy << A->getSourceRange() << B->getSourceRange();
}
// Update the arguments with any necessary implicit casts
TheCall->setArg(1, ARes.get());
TheCall->setArg(2, BRes.get());
} else {
// Different categories (int vs float), not allowed
return Diag(A->getBeginLoc(),
diag::err_typecheck_cond_incompatible_operands)
<< ATy << BTy << A->getSourceRange() << B->getSourceRange();
}
} else {
// For non-arithmetic types, they must be exactly the same
return Diag(A->getBeginLoc(),
diag::err_typecheck_cond_incompatible_operands)
<< ATy << BTy << A->getSourceRange() << B->getSourceRange();
}
}

ExprResult CondRes = PerformContextuallyConvertToBool(Cond);
if (CondRes.isInvalid())
return ExprError();

TheCall->setArg(0, CondRes.get());
TheCall->setType(ResultTy);
return TheCall;
} break;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unnecessary break.

}

if (getLangOpts().HLSL && HLSL().CheckBuiltinFunctionCall(BuiltinID, TheCall))
Expand Down
Loading
Loading