[clang] [clang][CodeGen] Emit prefetch hints and frame address depth as i32 (PR #221477)
Kiroo via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 5 11:35:31 PDT 2026
https://github.com/zlfn created https://github.com/llvm/llvm-project/pull/221477
The hint operands of `llvm.prefetch` and the depth operand of `llvm.frameaddress` and `llvm.returnaddress` are declared as i32, but clang emitted the values at the width of the C argument's type.
`__builtin_prefetch` is declared with a variadic prototype, `void(void const*, ...)`, so its hint arguments are never converted to a parameter type; only the default argument promotions apply. The depth argument is emitted at the type of `unsigned int`. Two cases end up with a width other than 32 bits:
- Targets whose `int` is 16 bits, such as MSP430 and AVR.
Plain `__builtin_prefetch(p, 1, 3)` or `__builtin_frame_address(0)` is hit this.
- An argument written with a type wider than 32 bits, on any target.
`__builtin_prefetch(p, 1L, 3L)` reproduces this on LP64 targets
`__builtin_prefetch(p, 1LL, 3LL)` reproduces this on 32 bit targets
Either way the emitted call does not match the intrinsic's signature. With assertions enabled this trips
`assert(... "Calling a function with a bad signature!")` in `CallInst::init`;
without them the verifier rejects the module:
```console
$ clang -cc1 -triple msp430 -emit-llvm t.c -o -
call void @llvm.prefetch.p0(ptr %0, i16 1, i16 3, i32 1)
%0 = call ptr @llvm.frameaddress.p0(i16 0)
fatal error: error in backend: Broken module found, compilation aborted!
```
Zero-extend or truncate them to i32 before building the intrinsic call. The conversion is a no-op where the widths already match, and folds back to a constant, since Sema already requires these arguments to be constants in range.
This also removes the FIXME that pointed at the mismatch.
>From 2f7d2c8829ea837e2a3d9083b94fb4ca82e4363e Mon Sep 17 00:00:00 2001
From: zlfn <ung at zlfn.space>
Date: Sun, 6 Sep 2026 03:20:04 +0900
Subject: [PATCH] [clang][CodeGen] Emit prefetch hints and frame address depth
as i32
The hint operands of llvm.prefetch and the depth operand of
llvm.frameaddress and llvm.returnaddress are declared as i32, but clang
emitted the values at the width of the C argument's type.
Zero-extend or truncate them to i32 before building the intrinsic call.
The conversion is a no-op where the widths already match, and folds back
to a constant, since Sema already requires these arguments to be
constants in range.
---
clang/lib/CodeGen/CGBuiltin.cpp | 5 ++++-
.../CodeGen/builtin-frame-address-arg-type.c | 16 ++++++++++++++
.../test/CodeGen/builtin-prefetch-arg-type.c | 22 +++++++++++++++++++
3 files changed, 42 insertions(+), 1 deletion(-)
create mode 100644 clang/test/CodeGen/builtin-frame-address-arg-type.c
create mode 100644 clang/test/CodeGen/builtin-prefetch-arg-type.c
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index f4598a8a54e1b..034c1cbfbdc40 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4117,12 +4117,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI__builtin_prefetch: {
Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0));
unsigned ICEArguments = (1 << 1) | (1 << 2);
- // FIXME: Technically these constants should of type 'int', yes?
RW = (E->getNumArgs() > 1) ? EmitScalarOrConstFoldImmArg(ICEArguments, 1, E)
: llvm::ConstantInt::get(Int32Ty, 0);
+ RW = Builder.CreateZExtOrTrunc(RW, Int32Ty);
Locality = (E->getNumArgs() > 2)
? EmitScalarOrConstFoldImmArg(ICEArguments, 2, E)
: llvm::ConstantInt::get(Int32Ty, 3);
+ Locality = Builder.CreateZExtOrTrunc(Locality, Int32Ty);
Value *Data = llvm::ConstantInt::get(Int32Ty, 1);
Function *F = CGM.getIntrinsic(Intrinsic::prefetch, Address->getType());
Builder.CreateCall(F, {Address, RW, Locality, Data});
@@ -5135,6 +5136,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI__builtin_return_address: {
Value *Depth = ConstantEmitter(*this).emitAbstract(E->getArg(0),
getContext().UnsignedIntTy);
+ Depth = Builder.CreateZExtOrTrunc(Depth, Int32Ty);
Function *F =
CGM.getIntrinsic(Intrinsic::returnaddress, {CGM.ProgramPtrTy});
return RValue::get(Builder.CreateCall(F, Depth));
@@ -5147,6 +5149,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI__builtin_frame_address: {
Value *Depth = ConstantEmitter(*this).emitAbstract(E->getArg(0),
getContext().UnsignedIntTy);
+ Depth = Builder.CreateZExtOrTrunc(Depth, Int32Ty);
Function *F = CGM.getIntrinsic(Intrinsic::frameaddress, AllocaInt8PtrTy);
return RValue::get(Builder.CreateCall(F, Depth));
}
diff --git a/clang/test/CodeGen/builtin-frame-address-arg-type.c b/clang/test/CodeGen/builtin-frame-address-arg-type.c
new file mode 100644
index 0000000000000..91cd94ec32072
--- /dev/null
+++ b/clang/test/CodeGen/builtin-frame-address-arg-type.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple msp430 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple avr -emit-llvm %s -o - | FileCheck %s
+
+// The depth operand of these intrinsics is i32 regardless of the width of
+// unsigned int on the target.
+
+void *test_return_address(void) {
+ // CHECK: call{{.*}} @llvm.returnaddress.p{{[0-9]}}(i32 0)
+ return __builtin_return_address(0);
+}
+
+void *test_frame_address(void) {
+ // CHECK: call{{.*}} @llvm.frameaddress.p{{[0-9]}}(i32 0)
+ return __builtin_frame_address(0);
+}
diff --git a/clang/test/CodeGen/builtin-prefetch-arg-type.c b/clang/test/CodeGen/builtin-prefetch-arg-type.c
new file mode 100644
index 0000000000000..c2481f1e77dde
--- /dev/null
+++ b/clang/test/CodeGen/builtin-prefetch-arg-type.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple msp430 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple avr -emit-llvm %s -o - | FileCheck %s
+
+// The hint operands of llvm.prefetch are i32 regardless of the type of the
+// corresponding C argument, which is narrower on targets with a 16-bit int and
+// wider when the caller passes a long.
+
+void test_int(void *p) {
+ // CHECK: call{{.*}} void @llvm.prefetch.p0(ptr {{%.+}}, i32 1, i32 3, i32 1)
+ __builtin_prefetch(p, 1, 3);
+}
+
+void test_long(void *p) {
+ // CHECK: call{{.*}} void @llvm.prefetch.p0(ptr {{%.+}}, i32 1, i32 3, i32 1)
+ __builtin_prefetch(p, 1L, 3L);
+}
+
+void test_defaulted_hints(void *p) {
+ // CHECK: call{{.*}} void @llvm.prefetch.p0(ptr {{%.+}}, i32 0, i32 3, i32 1)
+ __builtin_prefetch(p);
+}
More information about the cfe-commits
mailing list