[PATCH] D132275: [clang] Create alloca to pass into static lambda
Leonard Chan via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 8 17:51:34 PDT 2023
leonardchan added a comment.
Ok here's some of my findings. So there's a step in the attributor where it replaces some instructions with `unreachable`. One step is:
*** IR Dump Before AttributorPass on [module] ***
; Function Attrs: inlinehint minsize nounwind optsize
define linkonce_odr dso_local i8 @_ZZN2pw3rpc8internal12NanopbMethod16SynchronousUnaryIXadL_ZN2ax3bud7bt_core20global_state_manager18GlobalStateManager20PropagateGlobalStateERK26_global_state_StateR18_pw_protobuf_EmptyEEEES2_jRKNS1_17NanopbMethodSerdeEENUlRNS0_7ServiceEPKvPvE_8__invokeESI_SK_SL_(ptr noundef nonnull align 4 dereferenceable(16) %0, ptr noundef %1, ptr noundef %2) #3 comdat align 2 !dbg !12909 {
call void @llvm.dbg.value(metadata ptr %0, metadata !12913, metadata !DIExpression()), !dbg !12916
call void @llvm.dbg.value(metadata ptr %1, metadata !12914, metadata !DIExpression()), !dbg !12916
call void @llvm.dbg.value(metadata ptr %2, metadata !12915, metadata !DIExpression()), !dbg !12916
%4 = call i8 @_ZZN2pw3rpc8internal12NanopbMethod16SynchronousUnaryIXadL_ZN2ax3bud7bt_core20global_state_manager18GlobalStateManager20PropagateGlobalStateERK26_global_state_StateR18_pw_protobuf_EmptyEEEES2_jRKNS1_17NanopbMethodSerdeEENKUlRNS0_7ServiceEPKvPvE_clESI_SK_SL_(ptr noundef nonnull align 1 dereferenceable(1) undef, ptr noundef nonnull align 4 dereferenceable(16) %0, ptr noundef %1, ptr noundef %2) #12, !dbg !12917
ret i8 %4, !dbg !12917
}
*** IR Dump After AttributorPass on [module] ***
; Function Attrs: inlinehint minsize nounwind optsize
define linkonce_odr dso_local i8 @_ZZN2pw3rpc8internal12NanopbMethod16SynchronousUnaryIXadL_ZN2ax3bud7bt_core20global_state_manager18GlobalStateManager20PropagateGlobalStateERK26_global_state_StateR18_pw_protobuf_EmptyEEEES2_jRKNS1_17NanopbMethodSerdeEENUlRNS0_7ServiceEPKvPvE_8__invokeESI_SK_SL_(ptr noundef nonnull align 4 dereferenceable(16) %0, ptr noundef %1, ptr noundef %2) #3 comdat align 2 !dbg !12906 {
call void @llvm.dbg.value(metadata ptr %0, metadata !12910, metadata !DIExpression()), !dbg !12913
call void @llvm.dbg.value(metadata ptr %1, metadata !12911, metadata !DIExpression()), !dbg !12913
call void @llvm.dbg.value(metadata ptr %2, metadata !12912, metadata !DIExpression()), !dbg !12913
unreachable, !dbg !12914
}
The first argument in the call is an `undef` but the argument type is also marked as `noundef`, so this is unreachable. With this patch, in the final IR, the function will instead accept some non-undef argument:
define linkonce_odr dso_local i8 @_ZZN2pw3rpc8internal12NanopbMethod16SynchronousUnaryIXadL_ZN2ax3bud7bt_core20global_state_manager18GlobalStateManager20PropagateGlobalStateERK26_global_state_StateR18_pw_protobuf_EmptyEEEES2_jRKNS1_17NanopbMethodSerdeEENUlRNS0_7ServiceEPKvPvE_8__invokeESI_SK_SL_(ptr noundef nonnull align 4 dereferenceable(16) %0, ptr noundef nonnull %1, ptr noundef %2) #3 comdat align 2 !dbg !14764 {
;; .. a bunch of llvm.dbg.values
tail call void @_ZN2ax3bud7bt_core20global_state_manager18GlobalStateManager12ReceiveStateERK26_global_state_State(ptr noundef nonnull align 8 dereferenceable(3864) %0, ptr noundef nonnull align 8 dereferenceable(528) %1) #17, !dbg !14808
ret i8 0, !dbg !14809
}
Without this patch, before any passes run, this call originally took an `undef`:
define linkonce_odr dso_local i8 @_ZZN2pw3rpc8internal12NanopbMethod16SynchronousUnaryIXadL_ZN2ax3bud7bt_core20global_state_manager18GlobalStateManager20PropagateGlobalStateERK26_global_state_StateR18_pw_protobuf_EmptyEEEES2_jRKNS1_17NanopbMethodSerdeEENUlRNS0_7ServiceEPKvPv
E_8__invokeESI_SK_SL_(ptr noundef nonnull align 4 dereferenceable(16) %0, ptr noundef %1, ptr noundef %2) #3 comdat align 2 !dbg !14493 {
%4 = alloca %"class.pw::Status", align 1
%5 = alloca ptr, align 4
%6 = alloca ptr, align 4
%7 = alloca ptr, align 4
%8 = alloca %"class.pw::Status", align 1
store ptr %0, ptr %5, align 4, !tbaa !8211
call void @llvm.dbg.declare(metadata ptr %5, metadata !14497, metadata !DIExpression()), !dbg !14500
store ptr %1, ptr %6, align 4, !tbaa !8211
call void @llvm.dbg.declare(metadata ptr %6, metadata !14498, metadata !DIExpression()), !dbg !14500
store ptr %2, ptr %7, align 4, !tbaa !8211
call void @llvm.dbg.declare(metadata ptr %7, metadata !14499, metadata !DIExpression()), !dbg !14500
%9 = load ptr, ptr %5, align 4, !dbg !14501
%10 = load ptr, ptr %6, align 4, !dbg !14501, !tbaa !8211
%11 = load ptr, ptr %7, align 4, !dbg !14501, !tbaa !8211
%12 = call i8 @_ZZN2pw3rpc8internal12NanopbMethod16SynchronousUnaryIXadL_ZN2ax3bud7bt_core20global_state_manager18GlobalStateManager20PropagateGlobalStateERK26_global_state_StateR18_pw_protobuf_EmptyEEEES2_jRKNS1_17NanopbMethodSerdeEENKUlRNS0_7ServiceEPKvPvE_clESI_SK_SL_(p
tr noundef nonnull align 1 dereferenceable(1) undef, ptr noundef nonnull align 4 dereferenceable(16) %9, ptr noundef %10, ptr noundef %11) #11, !dbg !14501
%13 = getelementptr inbounds %"class.pw::Status", ptr %8, i32 0, i32 0, !dbg !14501
store i8 %12, ptr %13, align 1, !dbg !14501
call void @llvm.memcpy.p0.p0.i32(ptr align 1 %4, ptr align 1 %8, i32 1, i1 false), !dbg !14501, !tbaa.struct !9432
%14 = getelementptr inbounds %"class.pw::Status", ptr %4, i32 0, i32 0, !dbg !14501
%15 = load i8, ptr %14, align 1, !dbg !14501
ret i8 %15, !dbg !14501
}
However, with this patch, it accepts an `%8 = alloca %class.anon.61, align 1`:
define linkonce_odr dso_local i8 @_ZZN2pw3rpc8internal12NanopbMethod16SynchronousUnaryIXadL_ZN2ax3bud7bt_core20global_state_manager18GlobalStateManager20PropagateGlobalStateERK26_global_state_StateR18_pw_protobuf_EmptyEEEES2_jRKNS1_17NanopbMethodSerdeEENUlRNS0_7ServiceEPKvPvE_8__invokeESI_SK_SL_(ptr noundef nonnull align 4 dereferenceable(16) %0, ptr noundef %1, ptr noundef %2) #3 comdat align 2 !dbg !14493 {
%4 = alloca %"class.pw::Status", align 1
%5 = alloca ptr, align 4
%6 = alloca ptr, align 4
%7 = alloca ptr, align 4
%8 = alloca %class.anon.61, align 1
%9 = alloca %"class.pw::Status", align 1
store ptr %0, ptr %5, align 4, !tbaa !8211
call void @llvm.dbg.declare(metadata ptr %5, metadata !14497, metadata !DIExpression()), !dbg !14500
store ptr %1, ptr %6, align 4, !tbaa !8211
call void @llvm.dbg.declare(metadata ptr %6, metadata !14498, metadata !DIExpression()), !dbg !14500
store ptr %2, ptr %7, align 4, !tbaa !8211
call void @llvm.dbg.declare(metadata ptr %7, metadata !14499, metadata !DIExpression()), !dbg !14500
%10 = load ptr, ptr %5, align 4, !dbg !14501
%11 = load ptr, ptr %6, align 4, !dbg !14501, !tbaa !8211
%12 = load ptr, ptr %7, align 4, !dbg !14501, !tbaa !8211
%13 = call i8 @_ZZN2pw3rpc8internal12NanopbMethod16SynchronousUnaryIXadL_ZN2ax3bud7bt_core20global_state_manager18GlobalStateManager20PropagateGlobalStateERK26_global_state_StateR18_pw_protobuf_EmptyEEEES2_jRKNS1_17NanopbMethodSerdeEENKUlRNS0_7ServiceEPKvPvE_clESI_SK_SL_(ptr noundef nonnull align 1 dereferenceable(1) %8, ptr noundef nonnull align 4 dereferenceable(16) %10, ptr noundef %11, ptr noundef %12) #11, !dbg !14501
%14 = getelementptr inbounds %"class.pw::Status", ptr %9, i32 0, i32 0, !dbg !14501
store i8 %13, ptr %14, align 1, !dbg !14501
call void @llvm.memcpy.p0.p0.i32(ptr align 1 %4, ptr align 1 %9, i32 1, i1 false), !dbg !14501, !tbaa.struct !9432
%15 = getelementptr inbounds %"class.pw::Status", ptr %4, i32 0, i32 0, !dbg !14501
%16 = load i8, ptr %15, align 1, !dbg !14501
ret i8 %16, !dbg !14501
}
It seems like the attributor is WAI. The size changes likely come from certain functions being removed altogether since this function is the only caller of a bunch of other functions, but since it's only user is replaced with `unreachable`, then all those functions get removed. So it looks like this patch keeps the call and all those functions alive by passing in a regular alloca instead of an `undef`.
It sounds like @efriedma's prediction is right in that this alloca isn't deleted early enough. Not sure what the best approach from here might be in ensuring that the call here still be removed so we can reclaim space.
> Is the lambda in question defined in an inline function (linkonce_odr)? Maybe we should make EmitLambdaDelegatingInvokeBody emit an always_inline hint; it should be rare that a lambda is both called directly and converted to a function pointer.
The lambda in question is:
template <auto kMethod>
static constexpr NanopbMethod SynchronousUnary(
uint32_t id, const NanopbMethodSerde& serde) {
constexpr SynchronousUnaryFunction wrapper =
[](Service& service, const void* req, void* resp) {
return CallMethodImplFunction<kMethod>(
service,
*static_cast<const Request<kMethod>*>(req),
*static_cast<Response<kMethod>*>(resp));
};
return NanopbMethod(
id,
SynchronousUnaryInvoker<AllocateSpaceFor<Request<kMethod>>(),
AllocateSpaceFor<Response<kMethod>>()>,
Function{.synchronous_unary = wrapper},
serde);
}
I can provide more context/snippets if needed.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D132275/new/
https://reviews.llvm.org/D132275
More information about the cfe-commits
mailing list