Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,8 @@ of different sizes and signs is forbidden in binary and ternary builtins.
T __builtin_elementwise_exp(T x) returns the base-e exponential, e^x, of the specified value floating point types
T __builtin_elementwise_exp2(T x) returns the base-2 exponential, 2^x, of the specified value floating point types
T __builtin_elementwise_exp10(T x) returns the base-10 exponential, 10^x, of the specified value floating point types
T __builtin_elementwise_ldexp(T x, IntT y) returns the product of x and 2 raised to the power y. The floating point types
number of elements in y must equal the number of elements in x.
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

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

The documentation for __builtin_elementwise_ldexp should specify what IntT represents. It should clarify that y must be an integer type (scalar or vector) to match the semantic checking implementation.

Suggested change
T __builtin_elementwise_ldexp(T x, IntT y) returns the product of x and 2 raised to the power y. The floating point types
number of elements in y must equal the number of elements in x.
T __builtin_elementwise_ldexp(T x, IntT y) returns the product of x and 2 raised to the power y. The T: floating point types,
number of elements in y must equal the number of elements in x. IntT: integer types (scalar or vector)
y must be an integer type (scalar or vector) matching the shape of x.

Copilot uses AI. Check for mistakes.

T __builtin_elementwise_sqrt(T x) return the square root of a floating-point number floating point types
T __builtin_elementwise_roundeven(T x) round x to the nearest integer value in floating point format, floating point types
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ C23 Feature Support

Non-comprehensive list of changes in this release
-------------------------------------------------
- Added ``__builtin_elementwise_ldexp``.

- Added ``__builtin_elementwise_fshl`` and ``__builtin_elementwise_fshr``.

- ``__builtin_elementwise_abs`` can now be used in constant expression.
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,12 @@ def ElementwiseExp10 : Builtin {
let Prototype = "void(...)";
}

def ElementwiseLdexp : Builtin {
let Spellings = ["__builtin_elementwise_ldexp"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwiseFloor : Builtin {
let Spellings = ["__builtin_elementwise_floor"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3992,6 +3992,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI__builtin_elementwise_exp10:
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
*this, E, Intrinsic::exp10, "elt.exp10"));
case Builtin::BI__builtin_elementwise_ldexp: {
Value *Src = EmitScalarExpr(E->getArg(0));
Value *Exp = EmitScalarExpr(E->getArg(1));
Value *Result = Builder.CreateLdexp(Src, Exp, {}, "elt.ldexp");
return RValue::get(Result);
}
case Builtin::BI__builtin_elementwise_log:
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
*this, E, Intrinsic::log, "elt.log"));
Expand Down
64 changes: 52 additions & 12 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2609,6 +2609,18 @@ static ExprResult BuiltinInvoke(Sema &S, CallExpr *TheCall) {
Args.drop_front(), TheCall->getRParenLoc());
}

// Performs a similar job to Sema::UsualUnaryConversions, but without any
// implicit promotion of integral/enumeration types.
static ExprResult BuiltinVectorMathConversions(Sema &S, Expr *E) {
// First, convert to an r-value.
ExprResult Res = S.DefaultFunctionArrayLvalueConversion(E);
if (Res.isInvalid())
return ExprError();

// Promote floating-point types.
return S.UsualUnaryFPConversions(Res.get());
}

ExprResult
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
CallExpr *TheCall) {
Expand Down Expand Up @@ -3273,6 +3285,46 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return ExprError();
break;

case Builtin::BI__builtin_elementwise_ldexp: {
if (checkArgCount(TheCall, 2))
return ExprError();

ExprResult A = BuiltinVectorMathConversions(*this, TheCall->getArg(0));
if (A.isInvalid())
return ExprError();
QualType TyA = A.get()->getType();
if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA,
EltwiseBuiltinArgTyRestriction::FloatTy, 1))
return ExprError();

ExprResult Exp = UsualUnaryConversions(TheCall->getArg(1));
if (Exp.isInvalid())
return ExprError();
QualType TyExp = Exp.get()->getType();
if (checkMathBuiltinElementType(*this, Exp.get()->getBeginLoc(), TyExp,
EltwiseBuiltinArgTyRestriction::IntegerTy,
2))
return ExprError();

// Check the two arguments are either scalars or vectors of equal length.
const auto *Vec0 = TyA->getAs<VectorType>();
const auto *Vec1 = TyExp->getAs<VectorType>();
unsigned Arg0Length = Vec0 ? Vec0->getNumElements() : 0;
unsigned Arg1Length = Vec1 ? Vec1->getNumElements() : 0;
if (Arg0Length != Arg1Length) {
Diag(Exp.get()->getBeginLoc(),
diag::err_typecheck_vector_lengths_not_equal)
<< TyA << TyExp << A.get()->getSourceRange()
<< Exp.get()->getSourceRange();
return ExprError();
}

TheCall->setArg(0, A.get());
TheCall->setArg(1, Exp.get());
TheCall->setType(TyA);
break;
}

// These builtins restrict the element type to floating point
// types only, and take in two arguments.
case Builtin::BI__builtin_elementwise_minnum:
Expand Down Expand Up @@ -15992,18 +16044,6 @@ void Sema::CheckAddressOfPackedMember(Expr *rhs) {
_2, _3, _4));
}

// Performs a similar job to Sema::UsualUnaryConversions, but without any
// implicit promotion of integral/enumeration types.
static ExprResult BuiltinVectorMathConversions(Sema &S, Expr *E) {
// First, convert to an r-value.
ExprResult Res = S.DefaultFunctionArrayLvalueConversion(E);
if (Res.isInvalid())
return ExprError();

// Promote floating-point types.
return S.UsualUnaryFPConversions(Res.get());
}

bool Sema::PrepareBuiltinElementwiseMathOneArgCall(
CallExpr *TheCall, EltwiseBuiltinArgTyRestriction ArgTyRestr) {
if (checkArgCount(TheCall, 1))
Expand Down
31 changes: 31 additions & 0 deletions clang/test/CodeGen/builtins-elementwise-math.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ typedef half half2 __attribute__((ext_vector_type(2)));
typedef float float2 __attribute__((ext_vector_type(2)));
typedef float float4 __attribute__((ext_vector_type(4)));
typedef short int si8 __attribute__((ext_vector_type(8)));
typedef int int4 __attribute__((ext_vector_type(4)));
typedef unsigned int u4 __attribute__((ext_vector_type(4)));
typedef double double2 __attribute__((ext_vector_type(2)));
typedef double double3 __attribute__((ext_vector_type(3)));
Expand Down Expand Up @@ -729,6 +730,36 @@ void test_builtin_elementwise_exp10(float f1, float f2, double d1, double d2,
vf2 = __builtin_elementwise_exp10(vf1);
}

void test_builtin_elementwise_ldexp(float f1, float f2, double d1, double d2,
float4 vf1, float4 vf2, int i1, int4 vi1, short s1, long l1) {
// CHECK-LABEL: define void @test_builtin_elementwise_ldexp(
// CHECK: [[F1:%.+]] = load float, ptr %f1.addr, align 4
// CHECK: [[I1:%.+]] = load i32, ptr %i1.addr, align 4
// CHECK-NEXT: call float @llvm.ldexp.f32.i32(float [[F1]], i32 [[I1]])
f2 = __builtin_elementwise_ldexp(f1, i1);

// CHECK: [[F2:%.+]] = load float, ptr %f1.addr, align 4
// CHECK: [[S1:%.+]] = load i16, ptr %s1.addr, align 2
// CHECK: [[Ext1:%.+]] = sext i16 [[S1]] to i32
// CHECK-NEXT: call float @llvm.ldexp.f32.i32(float [[F2]], i32 [[Ext1]])
f2 = __builtin_elementwise_ldexp(f1, s1);

// CHECK: [[F3:%.+]] = load float, ptr %f1.addr, align 4
// CHECK: [[L1:%.+]] = load i64, ptr %l1.addr, align 8
// CHECK-NEXT: call float @llvm.ldexp.f32.i64(float [[F3]], i64 [[L1]])
f2 = __builtin_elementwise_ldexp(f1, l1);

// CHECK: [[D1:%.+]] = load double, ptr %d1.addr, align 8
// CHECK: [[I2:%.+]] = load i32, ptr %i1.addr, align 4
// CHECK-NEXT: call double @llvm.ldexp.f64.i32(double [[D1]], i32 [[I2]])
d2 = __builtin_elementwise_ldexp(d1, i1);

// CHECK: [[VF1:%.+]] = load <4 x float>, ptr %vf1.addr, align 16
// CHECK: [[VI1:%.+]] = load <4 x i32>, ptr %vi1.addr, align 16
// CHECK-NEXT: call <4 x float> @llvm.ldexp.v4f32.v4i32(<4 x float> [[VF1]], <4 x i32> [[VI1]])
vf2 = __builtin_elementwise_ldexp(vf1, vi1);
}

void test_builtin_elementwise_floor(float f1, float f2, double d1, double d2,
float4 vf1, float4 vf2) {
// CHECK-LABEL: define void @test_builtin_elementwise_floor(
Expand Down
36 changes: 36 additions & 0 deletions clang/test/Sema/builtins-elementwise-math.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,42 @@ void test_builtin_elementwise_exp10(int i, float f, double d, float4 v, int3 iv,
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'unsigned4' (vector of 4 'unsigned int' values))}}
}

void test_builtin_elementwise_ldexp(int i, float f, double d, float4 v, int3 iv, unsigned u, unsigned4 uv) {

struct Foo s = __builtin_elementwise_ldexp(f, i);
// expected-error@-1 {{initializing 'struct Foo' with an expression of incompatible type 'float'}}

f = __builtin_elementwise_ldexp();
// expected-error@-1 {{too few arguments to function call, expected 2, have 0}}

f = __builtin_elementwise_ldexp(f);
// expected-error@-1 {{too few arguments to function call, expected 2, have 1}}

f = __builtin_elementwise_ldexp(f, i, i);
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}}

f = __builtin_elementwise_ldexp(i, i);
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'int')}}

f = __builtin_elementwise_ldexp(f, f);
// expected-error@-1 {{2nd argument must be a scalar or vector of integer types (was 'float')}}

f = __builtin_elementwise_ldexp(v, iv);
// expected-error@-1 {{vector operands do not have the same number of elements ('float4' (vector of 4 'float' values) and 'int3' (vector of 3 'int' values))}}

v = __builtin_elementwise_ldexp(v, i);
// expected-error@-1 {{vector operands do not have the same number of elements ('float4' (vector of 4 'float' values) and 'int')}}

v = __builtin_elementwise_ldexp(f, iv);
// expected-error@-1 {{vector operands do not have the same number of elements ('float' and 'int3' (vector of 3 'int' values))}}

f = __builtin_elementwise_ldexp(u, i);
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'unsigned int')}}

f = __builtin_elementwise_ldexp(uv, i);
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'unsigned4' (vector of 4 'unsigned int' values))}}
}

void test_builtin_elementwise_floor(int i, float f, double d, float4 v, int3 iv, unsigned u, unsigned4 uv) {

struct Foo s = __builtin_elementwise_floor(f);
Expand Down
Loading