Skip to content

Commit 3f5c25f

Browse files
authored
[clang] Implement C2y stdc_memreverse8 and stdc_memreverse8u{8,16,32,64} builtins (#197358)
Implements the C2y <stdbit.h> memory reversal functions stdc_memreverse8 and stdc_memreverse8u{8,16,32,64}. The typed variants lower to llvm.bswap and support constexpr evaluation.
1 parent 55611de commit 3f5c25f

15 files changed

Lines changed: 330 additions & 3 deletions

File tree

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,11 @@ C2y Feature Support
232232
``stdc_rotate_left_{uc,us,ui,ul,ull}`` and
233233
``stdc_rotate_right_{uc,us,ui,ul,ull}``.
234234

235+
- Implemented C2y ``<stdbit.h>`` memory reversal functions:
236+
``__builtin_stdc_memreverse8`` / ``stdc_memreverse8`` (in-place byte
237+
reversal of a byte array) and ``stdc_memreverse8u{8,16,32,64}`` (byte-swap
238+
of an exact-width unsigned integer value, usable in constant expressions).
239+
235240
C23 Feature Support
236241
^^^^^^^^^^^^^^^^^^^
237242
- Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349)

clang/include/clang/Basic/Builtins.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
// L -> long (e.g. Li for 'long int', Ld for 'long double')
5151
// LL -> long long (e.g. LLi for 'long long int', LLd for __float128)
5252
// LLL -> __int128_t (e.g. LLLi)
53+
// B -> int8_t (byte-width integer, e.g. Bi for 'int8_t', UBi for 'uint8_t')
54+
// T -> int16_t (short-width integer, e.g. Ti for 'int16_t', UTi for 'uint16_t')
5355
// Z -> int32_t (require a native 32-bit integer type on the target)
5456
// W -> int64_t (require a native 64-bit integer type on the target)
5557
// N -> 'int' size if target is LP64, 'L' otherwise.

clang/include/clang/Basic/Builtins.td

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ class IntBitUtilTemplate : Template<
3434
"unsigned long int", "unsigned long long int"],
3535
["_uc", "_us", "_ui", "_ul", "_ull"]>;
3636

37+
class MemReverse8Template : Template<
38+
["uint8_t", "uint16_t", "uint32_t", "uint64_t"],
39+
["u8", "u16", "u32", "u64"]>;
40+
3741
class MSInt8_16_32Template : Template<["char", "short", "msint32_t"],
3842
["8", "16", ""]>;
3943

@@ -986,6 +990,19 @@ def StdcRotateRightTyped : LibBuiltin<"stdbit.h", "C2Y_LANG">, IntBitUtilTemplat
986990
let Prototype = "T(T, unsigned int)";
987991
}
988992

993+
def StdcMemReverse8: LibBuiltin<"stdbit.h", "C2Y_LANG"> {
994+
let Spellings = ["stdc_memreverse8"];
995+
let Attributes = [NoThrow, NonNull<NonOptimizing, [1]>];
996+
let Prototype = "void(size_t, unsigned char*)";
997+
let AddBuiltinPrefixedAlias = 1;
998+
}
999+
1000+
def StdcMemReverse8Typed : LibBuiltin<"stdbit.h", "C2Y_LANG">, MemReverse8Template {
1001+
let Spellings = ["stdc_memreverse8"];
1002+
let Attributes = [NoThrow, Pure, Const, Constexpr];
1003+
let Prototype = "T(T)";
1004+
}
1005+
9891006

9901007
// Random GCC builtins
9911008
// FIXME: The builtins marked FunctionWithBuiltinPrefix below should be

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11530,6 +11530,8 @@ def err_builtin_setjmp_unsupported : Error<
1153011530
def err_builtin_longjmp_invalid_val : Error<
1153111531
"argument to __builtin_longjmp must be a constant 1">;
1153211532
def err_builtin_requires_language : Error<"'%0' is only available in %1">;
11533+
def err_builtin_requires_char_bit_8 : Error<
11534+
"'%0' is only available on targets where 'CHAR_BIT == 8'">;
1153311535

1153411536
def err_constant_integer_arg_type : Error<
1153511537
"argument to %0 must be a constant integer">;

clang/lib/AST/ASTContext.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12583,6 +12583,7 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
1258312583
// Modifiers.
1258412584
int HowLong = 0;
1258512585
bool Signed = false, Unsigned = false;
12586+
bool IsChar = false, IsShort = false;
1258612587
RequiresICE = false;
1258712588

1258812589
// Read the prefixed modifiers first.
@@ -12606,8 +12607,29 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
1260612607
assert(!Unsigned && "Can't use 'U' modifier multiple times!");
1260712608
Unsigned = true;
1260812609
break;
12610+
case 'B':
12611+
// This modifier represents int8 type (byte-width).
12612+
assert(!IsSpecial &&
12613+
"Can't use two 'N', 'W', 'Z', 'O', 'B', or 'T' modifiers!");
12614+
assert(HowLong == 0 && "Can't use both 'L' and 'B' modifiers!");
12615+
#ifndef NDEBUG
12616+
IsSpecial = true;
12617+
#endif
12618+
IsChar = true;
12619+
break;
12620+
case 'T':
12621+
// This modifier represents int16 type (short-width).
12622+
assert(!IsSpecial &&
12623+
"Can't use two 'N', 'W', 'Z', 'O', 'B', or 'T' modifiers!");
12624+
assert(HowLong == 0 && "Can't use both 'L' and 'T' modifiers!");
12625+
#ifndef NDEBUG
12626+
IsSpecial = true;
12627+
#endif
12628+
IsShort = true;
12629+
break;
1260912630
case 'L':
12610-
assert(!IsSpecial && "Can't use 'L' with 'W', 'N', 'Z' or 'O' modifiers");
12631+
assert(!IsSpecial &&
12632+
"Can't use 'L' with 'W', 'N', 'Z', 'O', 'B', or 'T' modifiers");
1261112633
assert(HowLong <= 2 && "Can't have LLLL modifier");
1261212634
++HowLong;
1261312635
break;
@@ -12723,7 +12745,11 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
1272312745
Type = Context.ShortTy;
1272412746
break;
1272512747
case 'i':
12726-
if (HowLong == 3)
12748+
if (IsChar)
12749+
Type = Unsigned ? Context.UnsignedCharTy : Context.SignedCharTy;
12750+
else if (IsShort)
12751+
Type = Unsigned ? Context.UnsignedShortTy : Context.ShortTy;
12752+
else if (HowLong == 3)
1272712753
Type = Unsigned ? Context.UnsignedInt128Ty : Context.Int128Ty;
1272812754
else if (HowLong == 2)
1272912755
Type = Unsigned ? Context.UnsignedLongLongTy : Context.LongLongTy;

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4957,6 +4957,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
49574957
case Builtin::BI__builtin_bswap16:
49584958
case Builtin::BI__builtin_bswap32:
49594959
case Builtin::BI__builtin_bswap64:
4960+
case Builtin::BIstdc_memreverse8u8:
4961+
case Builtin::BIstdc_memreverse8u16:
4962+
case Builtin::BIstdc_memreverse8u32:
4963+
case Builtin::BIstdc_memreverse8u64:
49604964
return interp__builtin_bswap(S, OpPC, Frame, Call);
49614965

49624966
case Builtin::BI__atomic_always_lock_free:

clang/lib/AST/ExprConstant.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16562,7 +16562,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1656216562
case Builtin::BI__builtin_bswapg:
1656316563
case Builtin::BI__builtin_bswap16:
1656416564
case Builtin::BI__builtin_bswap32:
16565-
case Builtin::BI__builtin_bswap64: {
16565+
case Builtin::BI__builtin_bswap64:
16566+
case Builtin::BIstdc_memreverse8u8:
16567+
case Builtin::BIstdc_memreverse8u16:
16568+
case Builtin::BIstdc_memreverse8u32:
16569+
case Builtin::BIstdc_memreverse8u64: {
1656616570
APSInt Val;
1656716571
if (!EvaluateInteger(E->getArg(0), Val, Info))
1656816572
return false;

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4230,6 +4230,41 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
42304230
return RValue::get(Phi);
42314231
}
42324232

4233+
// stdc_memreverse8u8 is a no-op (single byte, nothing to swap).
4234+
case Builtin::BIstdc_memreverse8u8:
4235+
return RValue::get(EmitScalarExpr(E->getArg(0)));
4236+
4237+
case Builtin::BIstdc_memreverse8u16:
4238+
case Builtin::BIstdc_memreverse8u32:
4239+
case Builtin::BIstdc_memreverse8u64:
4240+
return RValue::get(
4241+
emitBuiltinWithOneOverloadedType<1>(*this, E, Intrinsic::bswap));
4242+
4243+
case Builtin::BIstdc_memreverse8:
4244+
case Builtin::BI__builtin_stdc_memreverse8: {
4245+
Expr::EvalResult R;
4246+
if (E->getArg(0)->EvaluateAsInt(R, getContext())) {
4247+
uint64_t Size = R.Val.getInt().getZExtValue();
4248+
if (Size <= 1) {
4249+
EmitIgnoredExpr(E->getArg(1));
4250+
return RValue::get(nullptr);
4251+
}
4252+
if (Size == 2 || Size == 4 || Size == 8) {
4253+
llvm::Type *IntTy = Builder.getIntNTy(Size * 8);
4254+
Address PtrAddr = EmitPointerWithAlignment(E->getArg(1));
4255+
Address Addr = PtrAddr.withElementType(IntTy);
4256+
Value *Val = Builder.CreateLoad(Addr);
4257+
Function *F = CGM.getIntrinsic(Intrinsic::bswap, IntTy);
4258+
Value *Swapped = Builder.CreateCall(F, Val);
4259+
Builder.CreateStore(Swapped, Addr);
4260+
return RValue::get(nullptr);
4261+
}
4262+
}
4263+
4264+
// General case: fall back to the library function stdc_memreverse8.
4265+
break;
4266+
}
4267+
42334268
case Builtin::BI__builtin_constant_p: {
42344269
llvm::Type *ResultType = ConvertType(E->getType());
42354270

clang/lib/Sema/SemaChecking.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3933,6 +3933,19 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
39333933
return ExprError();
39343934
break;
39353935

3936+
case Builtin::BI__builtin_stdc_memreverse8:
3937+
case Builtin::BIstdc_memreverse8:
3938+
case Builtin::BIstdc_memreverse8u8:
3939+
case Builtin::BIstdc_memreverse8u16:
3940+
case Builtin::BIstdc_memreverse8u32:
3941+
case Builtin::BIstdc_memreverse8u64:
3942+
if (Context.getTargetInfo().getCharWidth() != 8) {
3943+
Diag(TheCall->getBeginLoc(), diag::err_builtin_requires_char_bit_8)
3944+
<< TheCall->getDirectCallee()->getName();
3945+
return ExprError();
3946+
}
3947+
break;
3948+
39363949
case Builtin::BI__builtin_stdc_bit_floor:
39373950
case Builtin::BI__builtin_stdc_bit_ceil:
39383951
if (BuiltinStdCBuiltin(*this, TheCall, QualType()))

clang/test/CodeGen/Inputs/stdbit.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,10 @@ unsigned int stdc_rotate_right_ui(unsigned int, unsigned int);
112112
unsigned long stdc_rotate_right_ul(unsigned long, unsigned int);
113113
unsigned long long stdc_rotate_right_ull(unsigned long long, unsigned int);
114114

115+
void stdc_memreverse8(__SIZE_TYPE__, unsigned char *);
116+
__UINT8_TYPE__ stdc_memreverse8u8(__UINT8_TYPE__);
117+
__UINT16_TYPE__ stdc_memreverse8u16(__UINT16_TYPE__);
118+
__UINT32_TYPE__ stdc_memreverse8u32(__UINT32_TYPE__);
119+
__UINT64_TYPE__ stdc_memreverse8u64(__UINT64_TYPE__);
120+
115121
#endif

0 commit comments

Comments
 (0)