mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 03:02:36 +01:00
85bd37fc58
Global to local demotion can speed up programs that use globals a lot. It is particularly useful with LTO, when the entire call graph is known and most functions have been internalized. For a global to be demoted, it must only be accessed by one function and that function: 1. Must never recurse directly or indirectly, else the GV would be clobbered. 2. Must never rely on the value in GV at the start of the function (apart from the initializer). GlobalOpt can already do this, but it is hamstrung and only ever tries to demote globals inside "main", because C++ gives extra guarantees about how main is called - once and only once. In LTO mode, we can often prove the first property (if the function is internal by this point, we know enough about the callgraph to determine if it could possibly recurse). FunctionAttrs now infers the "norecurse" attribute for this reason. The second property can be proven for a subset of functions by proving that all loads from GV are dominated by a store to GV. This is conservative in the name of compile time - this only requires a DominatorTree which is fairly cheap in the grand scheme of things. We could do more fancy stuff with MemoryDependenceAnalysis too to catch more cases but this appears to catch most of the useful ones in my testing. llvm-svn: 253168
55 lines
1.5 KiB
LLVM
55 lines
1.5 KiB
LLVM
; RUN: opt %s -mergefunc -globalopt -S -o - | FileCheck %s
|
|
|
|
; Make sure we don't crash on this example. This test is supposed to test that
|
|
; MergeFunctions clears its GlobalNumbers value map. If this map still contains
|
|
; entries when running globalopt and the MergeFunctions instance is still alive
|
|
; the optimization of @G would cause an assert because globalopt would do an
|
|
; RAUW on @G which still exists as an entry in the GlobalNumbers ValueMap which
|
|
; causes an assert in the ValueHandle call back because we are RAUWing with a
|
|
; different type (AllocaInst) than its key type (GlobalValue).
|
|
|
|
@G = internal global i8** null
|
|
@G2 = internal global i8** null
|
|
|
|
define i32 @main(i32 %argc, i8** %argv) norecurse {
|
|
; CHECK: alloca
|
|
store i8** %argv, i8*** @G
|
|
ret i32 0
|
|
}
|
|
|
|
define internal i8** @dead1(i64 %p) {
|
|
call void @right(i64 %p)
|
|
call void @right(i64 %p)
|
|
call void @right(i64 %p)
|
|
call void @right(i64 %p)
|
|
%tmp = load i8**, i8*** @G
|
|
ret i8** %tmp
|
|
}
|
|
|
|
define internal i8** @dead2(i64 %p) {
|
|
call void @right(i64 %p)
|
|
call void @right(i64 %p)
|
|
call void @right(i64 %p)
|
|
call void @right(i64 %p)
|
|
%tmp = load i8**, i8*** @G2
|
|
ret i8** %tmp
|
|
}
|
|
|
|
define void @left(i64 %p) {
|
|
entry-block:
|
|
call void @right(i64 %p)
|
|
call void @right(i64 %p)
|
|
call void @right(i64 %p)
|
|
call void @right(i64 %p)
|
|
ret void
|
|
}
|
|
|
|
define void @right(i64 %p) {
|
|
entry-block:
|
|
call void @left(i64 %p)
|
|
call void @left(i64 %p)
|
|
call void @left(i64 %p)
|
|
call void @left(i64 %p)
|
|
ret void
|
|
}
|