[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Which ObjC2.0 features are missing in the latest GCC?
From: |
David Chisnall |
Subject: |
Re: Which ObjC2.0 features are missing in the latest GCC? |
Date: |
Mon, 25 Nov 2019 11:09:49 +0000 |
On 25 Nov 2019, at 10:18, H. Nikolaus Schaller <hns@goldelico.com> wrote:
>
> Fred mentioned that it could be possible to define some block wrapper macros
> if some time is invested.
> It that works out, we do not make our decisions depend on gcc *not*
> implementing something.
Fred made this claim, but he also added the caveat that it would likely be
limited to blocks with no captures. In this, I believe that he’s right. A
stateless block is:
- A function that’s used when you invoke the block.
- A fixed structure describing the block, its type encoding and a couple of
other things.
Stateful blocks also add a load of fields on the end that describe how to copy
and destroy each of the captures. For blocks to actually be useful, you need
state. You can’t, with C macros, implement something that defines a load of
functions inline. You probably could create a set of macros that would
simplify the effort in building the block byval structures, but it’s still
non-trivial.
Note that it is probably possible (though verbose) to implement this using C++
templates (though matching the ABI is tricky). I think most people reading
this have no idea how complex the block generation code is. When I implemented
Pragmatic Smalltalk, I write compiler support for a subset of the blocks ABI
and that was a few hundred lines of code for the compiler. Doing the same in
metaprogramming (especially with C macros) would be incredibly hard.
Consider this trivial compilation unit:
```
@interface X
- (void)doSomething;
@end
typedef void(^block)(void);
block callMethodOn(id x)
{
return ^() { [x doSomething]; };
}
```
Now, when I run this through clang, this is what I get out as LLVM IR (eliding
everything that isn’t directly related to blocks):
```
%struct.__block_descriptor = type { i64, i64 }
$__copy_helper_block_e8_32s = comdat any
$__destroy_helper_block_e8_32s = comdat any
@"__block_descriptor_40_e8_32s_e5_v8\01?0l" = linkonce_odr hidden unnamed_addr
constant { i64, i64, i8*, i8*, i8*, i8* } { i64 0, i64 40, i8* bitcast (void
(i8*, i8*)* @__copy_helper_block_e8_32s to i8*), i8* bitcast (void (i8*)*
@__destroy_helper_block_e8_32s to i8*), i8* getelementptr inbounds ([6 x i8],
[6 x i8]* @.str, i32 0, i32 0), i8* null }, align 8
; Function Attrs: nounwind uwtable
define dso_local void ()* @callMethodOn(i8*) local_unnamed_addr #0 {
%2 = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>, align
8
%3 = tail call i8* @llvm.objc.retain(i8* %0) #1
%4 = getelementptr inbounds <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>, <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>* %2, i64 0, i32 5
%5 = getelementptr inbounds <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>, <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>* %2, i64 0, i32 0
store i8* bitcast (i8** @_NSConcreteStackBlock to i8*), i8** %5, align 8
%6 = getelementptr inbounds <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>, <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>* %2, i64 0, i32 1
store i32 -1040187392, i32* %6, align 8
%7 = getelementptr inbounds <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>, <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>* %2, i64 0, i32 2
store i32 0, i32* %7, align 4
%8 = getelementptr inbounds <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>, <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>* %2, i64 0, i32 3
store i8* bitcast (void (i8*)* @__callMethodOn_block_invoke to i8*), i8** %8,
align 8
%9 = getelementptr inbounds <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>, <{ i8*, i32, i32, i8*,
%struct.__block_descriptor*, i8* }>* %2, i64 0, i32 4
store %struct.__block_descriptor* bitcast ({ i64, i64, i8*, i8*, i8*, i8* }*
@"__block_descriptor_40_e8_32s_e5_v8\01?0l" to %struct.__block_descriptor*),
%struct.__block_descriptor** %9, align 8
store i8* %0, i8** %4, align 8, !tbaa !2
%10 = bitcast <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %2
to void ()*
%11 = bitcast void ()* %10 to i8*
%12 = tail call i8* @llvm.objc.retain(i8* %0) #1
%13 = call i8* @llvm.objc.retainBlock(i8* nonnull %11) #1,
!clang.arc.copy_on_escape !5
%14 = bitcast i8* %13 to void ()*
%15 = load i8*, i8** %4, align 8
call void @llvm.objc.release(i8* %15) #1, !clang.imprecise_release !5
call void @llvm.objc.release(i8* %0) #1, !clang.imprecise_release !5
%16 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %13) #1
ret void ()* %14
}
; Function Attrs: nounwind
declare i8* @llvm.objc.retain(i8*) #1
; Function Attrs: uwtable
define internal void @__callMethodOn_block_invoke(i8* nocapture readonly) #2 {
%2 = getelementptr inbounds i8, i8* %0, i64 32
%3 = bitcast i8* %2 to i8**
%4 = load i8*, i8** %3, align 8, !tbaa !2
tail call void bitcast (i8* (i8*, ...)* @objc_msgSend to void (i8*,
i8*)*)(i8* %4, i8* bitcast ({ i8*, i8* }*
@".objc_selector_doSomething_v16\010:8" to i8*)), !GNUObjCMessageSend !6,
!clang.arc.no_objc_arc_exceptions !5
ret void
}
declare dso_local i8* @objc_msgSend(i8*, ...) local_unnamed_addr
; Function Attrs: uwtable
define linkonce_odr hidden void @__copy_helper_block_e8_32s(i8*, i8*)
unnamed_addr #2 comdat {
%3 = getelementptr inbounds i8, i8* %1, i64 32
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 8
%6 = tail call i8* @llvm.objc.retain(i8* %5) #1
ret void
}
; Function Attrs: uwtable
define linkonce_odr hidden void @__destroy_helper_block_e8_32s(i8*)
unnamed_addr #2 comdat {
%2 = getelementptr inbounds i8, i8* %0, i64 32
%3 = bitcast i8* %2 to i8**
%4 = load i8*, i8** %3, align 8
tail call void @llvm.objc.release(i8* %4) #1, !clang.imprecise_release !5
ret void
}
```
A few things to notice:
1. This single function generates 4 functions.
2. The copy and dispose helpers have to understand the offset of each of the
captures. This is fairly simple in this example, because there’s only one
capture.
3. To avoid bloating the binaries, the copy and destroy helpers have well-known
names and COMDATs, so the linker can eliminate duplicates.
4. The memory management around creating the block and its captures is
non-trivial. Note that this is compiled with ARC, blocks for Objective-C
implement a subset of ARC even in non-ARC mode because getting the memory
management right was considered too hard.
If, every time you want to write something as simple as the original
Objective-C source, you have to write something as complex as the LLVM output,
that’s your choice, but it’s definitely not one that I’d make.
David
- Re: Which ObjC2.0 features are missing in the latest GCC?, (continued)
- Re: Which ObjC2.0 features are missing in the latest GCC?, Gregory Casamento, 2019/11/25
- Re: Which ObjC2.0 features are missing in the latest GCC?, H. Nikolaus Schaller, 2019/11/25
- Re: Which ObjC2.0 features are missing in the latest GCC?, Gregory Casamento, 2019/11/25
- Re: Which ObjC2.0 features are missing in the latest GCC?, H. Nikolaus Schaller, 2019/11/25
- Re: Which ObjC2.0 features are missing in the latest GCC?, Maxthon Chan, 2019/11/25
- Re: Which ObjC2.0 features are missing in the latest GCC?, Gregory Casamento, 2019/11/25
- Re: Which ObjC2.0 features are missing in the latest GCC?, H. Nikolaus Schaller, 2019/11/25
- Re: Which ObjC2.0 features are missing in the latest GCC?,
David Chisnall <=
- Re: Which ObjC2.0 features are missing in the latest GCC?, H. Nikolaus Schaller, 2019/11/25
- Re: Which ObjC2.0 features are missing in the latest GCC?, David Chisnall, 2019/11/25
- Re: Which ObjC2.0 features are missing in the latest GCC?, Gregory Casamento, 2019/11/26
- Re: Which ObjC2.0 features are missing in the latest GCC?, Max Chan, 2019/11/26
- Re: Which ObjC2.0 features are missing in the latest GCC?, Pirmin Braun, 2019/11/26
- Re: Which ObjC2.0 features are missing in the latest GCC?, H. Nikolaus Schaller, 2019/11/26
- Re: Which ObjC2.0 features are missing in the latest GCC?, Andreas Fink, 2019/11/26
- Re: Which ObjC2.0 features are missing in the latest GCC?, Pirmin Braun, 2019/11/26
- Re: Which ObjC2.0 features are missing in the latest GCC?, Umberto Cerrato, 2019/11/26
- Re: Which ObjC2.0 features are missing in the latest GCC?, hasebastian, 2019/11/26