Skip to content

Commit 3b010c9

Browse files
authored
[Clang] Add elementwise ldexp builtin function (#166296)
This PR adds __builtin_elementwise_ldexp. It can be used for implementing OpenCL ldexp builtin with vector inputs.
1 parent d18b796 commit 3b010c9

File tree

7 files changed

+135
-12
lines changed

7 files changed

+135
-12
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,8 @@ of different sizes and signs is forbidden in binary and ternary builtins.
807807
T __builtin_elementwise_exp(T x) returns the base-e exponential, e^x, of the specified value floating point types
808808
T __builtin_elementwise_exp2(T x) returns the base-2 exponential, 2^x, of the specified value floating point types
809809
T __builtin_elementwise_exp10(T x) returns the base-10 exponential, 10^x, of the specified value floating point types
810+
T __builtin_elementwise_ldexp(T x, IntT y) returns the product of x and 2 raised to the power y. T: floating point types,
811+
y must be an integer type matching the shape of x. IntT: integer types
810812

811813
T __builtin_elementwise_sqrt(T x) return the square root of a floating-point number floating point types
812814
T __builtin_elementwise_roundeven(T x) round x to the nearest integer value in floating point format, floating point types

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ C23 Feature Support
214214

215215
Non-comprehensive list of changes in this release
216216
-------------------------------------------------
217+
- Added ``__builtin_elementwise_ldexp``.
218+
217219
- Added ``__builtin_elementwise_fshl`` and ``__builtin_elementwise_fshr``.
218220

219221
- ``__builtin_elementwise_abs`` can now be used in constant expression.

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,12 @@ def ElementwiseExp10 : Builtin {
14181418
let Prototype = "void(...)";
14191419
}
14201420

1421+
def ElementwiseLdexp : Builtin {
1422+
let Spellings = ["__builtin_elementwise_ldexp"];
1423+
let Attributes = [NoThrow, Const, CustomTypeChecking];
1424+
let Prototype = "void(...)";
1425+
}
1426+
14211427
def ElementwiseFloor : Builtin {
14221428
let Spellings = ["__builtin_elementwise_floor"];
14231429
let Attributes = [NoThrow, Const, CustomTypeChecking];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3992,6 +3992,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
39923992
case Builtin::BI__builtin_elementwise_exp10:
39933993
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
39943994
*this, E, Intrinsic::exp10, "elt.exp10"));
3995+
case Builtin::BI__builtin_elementwise_ldexp: {
3996+
Value *Src = EmitScalarExpr(E->getArg(0));
3997+
Value *Exp = EmitScalarExpr(E->getArg(1));
3998+
Value *Result = Builder.CreateLdexp(Src, Exp, {}, "elt.ldexp");
3999+
return RValue::get(Result);
4000+
}
39954001
case Builtin::BI__builtin_elementwise_log:
39964002
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
39974003
*this, E, Intrinsic::log, "elt.log"));

clang/lib/Sema/SemaChecking.cpp

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2609,6 +2609,18 @@ static ExprResult BuiltinInvoke(Sema &S, CallExpr *TheCall) {
26092609
Args.drop_front(), TheCall->getRParenLoc());
26102610
}
26112611

2612+
// Performs a similar job to Sema::UsualUnaryConversions, but without any
2613+
// implicit promotion of integral/enumeration types.
2614+
static ExprResult BuiltinVectorMathConversions(Sema &S, Expr *E) {
2615+
// First, convert to an r-value.
2616+
ExprResult Res = S.DefaultFunctionArrayLvalueConversion(E);
2617+
if (Res.isInvalid())
2618+
return ExprError();
2619+
2620+
// Promote floating-point types.
2621+
return S.UsualUnaryFPConversions(Res.get());
2622+
}
2623+
26122624
ExprResult
26132625
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
26142626
CallExpr *TheCall) {
@@ -3273,6 +3285,46 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
32733285
return ExprError();
32743286
break;
32753287

3288+
case Builtin::BI__builtin_elementwise_ldexp: {
3289+
if (checkArgCount(TheCall, 2))
3290+
return ExprError();
3291+
3292+
ExprResult A = BuiltinVectorMathConversions(*this, TheCall->getArg(0));
3293+
if (A.isInvalid())
3294+
return ExprError();
3295+
QualType TyA = A.get()->getType();
3296+
if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA,
3297+
EltwiseBuiltinArgTyRestriction::FloatTy, 1))
3298+
return ExprError();
3299+
3300+
ExprResult Exp = UsualUnaryConversions(TheCall->getArg(1));
3301+
if (Exp.isInvalid())
3302+
return ExprError();
3303+
QualType TyExp = Exp.get()->getType();
3304+
if (checkMathBuiltinElementType(*this, Exp.get()->getBeginLoc(), TyExp,
3305+
EltwiseBuiltinArgTyRestriction::IntegerTy,
3306+
2))
3307+
return ExprError();
3308+
3309+
// Check the two arguments are either scalars or vectors of equal length.
3310+
const auto *Vec0 = TyA->getAs<VectorType>();
3311+
const auto *Vec1 = TyExp->getAs<VectorType>();
3312+
unsigned Arg0Length = Vec0 ? Vec0->getNumElements() : 0;
3313+
unsigned Arg1Length = Vec1 ? Vec1->getNumElements() : 0;
3314+
if (Arg0Length != Arg1Length) {
3315+
Diag(Exp.get()->getBeginLoc(),
3316+
diag::err_typecheck_vector_lengths_not_equal)
3317+
<< TyA << TyExp << A.get()->getSourceRange()
3318+
<< Exp.get()->getSourceRange();
3319+
return ExprError();
3320+
}
3321+
3322+
TheCall->setArg(0, A.get());
3323+
TheCall->setArg(1, Exp.get());
3324+
TheCall->setType(TyA);
3325+
break;
3326+
}
3327+
32763328
// These builtins restrict the element type to floating point
32773329
// types only, and take in two arguments.
32783330
case Builtin::BI__builtin_elementwise_minnum:
@@ -15992,18 +16044,6 @@ void Sema::CheckAddressOfPackedMember(Expr *rhs) {
1599216044
_2, _3, _4));
1599316045
}
1599416046

15995-
// Performs a similar job to Sema::UsualUnaryConversions, but without any
15996-
// implicit promotion of integral/enumeration types.
15997-
static ExprResult BuiltinVectorMathConversions(Sema &S, Expr *E) {
15998-
// First, convert to an r-value.
15999-
ExprResult Res = S.DefaultFunctionArrayLvalueConversion(E);
16000-
if (Res.isInvalid())
16001-
return ExprError();
16002-
16003-
// Promote floating-point types.
16004-
return S.UsualUnaryFPConversions(Res.get());
16005-
}
16006-
1600716047
bool Sema::PrepareBuiltinElementwiseMathOneArgCall(
1600816048
CallExpr *TheCall, EltwiseBuiltinArgTyRestriction ArgTyRestr) {
1600916049
if (checkArgCount(TheCall, 1))

clang/test/CodeGen/builtins-elementwise-math.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ typedef half half2 __attribute__((ext_vector_type(2)));
66
typedef float float2 __attribute__((ext_vector_type(2)));
77
typedef float float4 __attribute__((ext_vector_type(4)));
88
typedef short int si8 __attribute__((ext_vector_type(8)));
9+
typedef int int4 __attribute__((ext_vector_type(4)));
910
typedef unsigned int u4 __attribute__((ext_vector_type(4)));
1011
typedef double double2 __attribute__((ext_vector_type(2)));
1112
typedef double double3 __attribute__((ext_vector_type(3)));
@@ -729,6 +730,36 @@ void test_builtin_elementwise_exp10(float f1, float f2, double d1, double d2,
729730
vf2 = __builtin_elementwise_exp10(vf1);
730731
}
731732

733+
void test_builtin_elementwise_ldexp(float f1, float f2, double d1, double d2,
734+
float4 vf1, float4 vf2, int i1, int4 vi1, short s1, long l1) {
735+
// CHECK-LABEL: define void @test_builtin_elementwise_ldexp(
736+
// CHECK: [[F1:%.+]] = load float, ptr %f1.addr, align 4
737+
// CHECK: [[I1:%.+]] = load i32, ptr %i1.addr, align 4
738+
// CHECK-NEXT: call float @llvm.ldexp.f32.i32(float [[F1]], i32 [[I1]])
739+
f2 = __builtin_elementwise_ldexp(f1, i1);
740+
741+
// CHECK: [[F2:%.+]] = load float, ptr %f1.addr, align 4
742+
// CHECK: [[S1:%.+]] = load i16, ptr %s1.addr, align 2
743+
// CHECK: [[Ext1:%.+]] = sext i16 [[S1]] to i32
744+
// CHECK-NEXT: call float @llvm.ldexp.f32.i32(float [[F2]], i32 [[Ext1]])
745+
f2 = __builtin_elementwise_ldexp(f1, s1);
746+
747+
// CHECK: [[F3:%.+]] = load float, ptr %f1.addr, align 4
748+
// CHECK: [[L1:%.+]] = load i64, ptr %l1.addr, align 8
749+
// CHECK-NEXT: call float @llvm.ldexp.f32.i64(float [[F3]], i64 [[L1]])
750+
f2 = __builtin_elementwise_ldexp(f1, l1);
751+
752+
// CHECK: [[D1:%.+]] = load double, ptr %d1.addr, align 8
753+
// CHECK: [[I2:%.+]] = load i32, ptr %i1.addr, align 4
754+
// CHECK-NEXT: call double @llvm.ldexp.f64.i32(double [[D1]], i32 [[I2]])
755+
d2 = __builtin_elementwise_ldexp(d1, i1);
756+
757+
// CHECK: [[VF1:%.+]] = load <4 x float>, ptr %vf1.addr, align 16
758+
// CHECK: [[VI1:%.+]] = load <4 x i32>, ptr %vi1.addr, align 16
759+
// CHECK-NEXT: call <4 x float> @llvm.ldexp.v4f32.v4i32(<4 x float> [[VF1]], <4 x i32> [[VI1]])
760+
vf2 = __builtin_elementwise_ldexp(vf1, vi1);
761+
}
762+
732763
void test_builtin_elementwise_floor(float f1, float f2, double d1, double d2,
733764
float4 vf1, float4 vf2) {
734765
// CHECK-LABEL: define void @test_builtin_elementwise_floor(

clang/test/Sema/builtins-elementwise-math.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,42 @@ void test_builtin_elementwise_exp10(int i, float f, double d, float4 v, int3 iv,
645645
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'unsigned4' (vector of 4 'unsigned int' values))}}
646646
}
647647

648+
void test_builtin_elementwise_ldexp(int i, float f, double d, float4 v, int3 iv, unsigned u, unsigned4 uv) {
649+
650+
struct Foo s = __builtin_elementwise_ldexp(f, i);
651+
// expected-error@-1 {{initializing 'struct Foo' with an expression of incompatible type 'float'}}
652+
653+
f = __builtin_elementwise_ldexp();
654+
// expected-error@-1 {{too few arguments to function call, expected 2, have 0}}
655+
656+
f = __builtin_elementwise_ldexp(f);
657+
// expected-error@-1 {{too few arguments to function call, expected 2, have 1}}
658+
659+
f = __builtin_elementwise_ldexp(f, i, i);
660+
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}}
661+
662+
f = __builtin_elementwise_ldexp(i, i);
663+
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'int')}}
664+
665+
f = __builtin_elementwise_ldexp(f, f);
666+
// expected-error@-1 {{2nd argument must be a scalar or vector of integer types (was 'float')}}
667+
668+
f = __builtin_elementwise_ldexp(v, iv);
669+
// 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))}}
670+
671+
v = __builtin_elementwise_ldexp(v, i);
672+
// expected-error@-1 {{vector operands do not have the same number of elements ('float4' (vector of 4 'float' values) and 'int')}}
673+
674+
v = __builtin_elementwise_ldexp(f, iv);
675+
// expected-error@-1 {{vector operands do not have the same number of elements ('float' and 'int3' (vector of 3 'int' values))}}
676+
677+
f = __builtin_elementwise_ldexp(u, i);
678+
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'unsigned int')}}
679+
680+
f = __builtin_elementwise_ldexp(uv, i);
681+
// expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was 'unsigned4' (vector of 4 'unsigned int' values))}}
682+
}
683+
648684
void test_builtin_elementwise_floor(int i, float f, double d, float4 v, int3 iv, unsigned u, unsigned4 uv) {
649685

650686
struct Foo s = __builtin_elementwise_floor(f);

0 commit comments

Comments
 (0)