[swift-dev] SR-5403 / Memory Optimization Opportunity (Load/Store forwarding)
Johannes Weiß
johannesweiss at apple.com
Tue Jul 11 12:16:40 CDT 2017
Hi Michael,
> [...]
>> Now you advise to run the '-debug-only=sil-redundant-load-elim' so I tried
>>
>> sil-opt [...] -debug-only=sil-redundant-load-elim
>>
>> but it doesn't seem happy with that. Did I misunderstand how to pass this option?
>
> What do you mean by it doesn't seem happy?
I was using a too old/wrong version of sil-opt (the one from Xcode 9 beta N) which didn't have that option. Resolved by using the one from my own swift build.
> [...]
>> is this roughly what you had in mind?
>
> Nope. I was suggesting that you write the SIL by hand. It will be much easier.
Ha, that's a much better idea, thanks for your help, much appreciated! No idea why I didn't think of that. This is today's update:
--- SNIP (whole file attached as test-load-forwarding.sil) ---
// bar()
sil hidden @bar : $@convention(thin) () -> () {
bb0:
alloc_global @MyIntStorage
%int_storage = global_addr @MyIntStorage : $*Int
%value_raw = integer_literal $Builtin.Int64, 42
%value = struct $Int (%value_raw : $Builtin.Int64)
store %value to %int_storage : $*Int
%f_buz = function_ref @buz : $@convention(thin) (@in_guaranteed Int) -> ()
%r1 = apply %f_buz(%int_storage) : $@convention(thin) (@in_guaranteed Int) -> ()
%value_again = load %int_storage : $*Int
%f_test = function_ref @testNumber12345 : $@convention(thin) (Int) -> ()
%r2 = apply %f_test(%value_again) : $@convention(thin) (Int) -> ()
// just to test an illegal function
%f_bad = function_ref @bad : $@convention(thin) (@in_guaranteed Int) -> ()
%r3 = apply %f_bad(%int_storage) : $@convention(thin) (@in_guaranteed Int) -> ()
%value_again2 = load %int_storage : $*Int
%r4 = apply %f_test(%value_again2) : $@convention(thin) (Int) -> ()
return %r2 : $()
} // end sil function 'bar'
--- SNAP ---
So I make a global Int storage, store the number 42 to it, invoke buz() which takes an @in_guaranteed Int, then load it again (this is redundant as @in_guaranteed isn't allowed to modify it). I also pass the loaded number to the function testNumber12345() which just tests that it's the number 12345 (which it isn't but I wanted to be sure it's actually consumed)
Then (because I believe the RLE actually works) I also introduced a function `bad` which is a bit like `buz` but it does actually write to the *Int which I believe is illegal (right?).
Now let's see what we get with sil-opt
I run the following command:
sil-opt -verify -assume-parsing-unqualified-ownership-sil -redundant-load-elim -debug-only=sil-redundant-load-elim -debug test-load-forwarding.sil
which gives me the following output (my annotations marked as [JW: ... ])
--- SNIP (whole file attached as test-load-forwarding.sil-opt) ---
*** RLE on function: testNumber12345 ***
*** RLE on function: buz ***
*** RLE on function: bad ***
*** RLE on function: bar ***
LSLocation #0 %1 = global_addr @MyIntStorage : $*Int // users: %12, %11, %7, %6, %4
Projection Path [$*Int
Field: var _value: Int64 of: $*Builtin.Int64]
PROCESS bb0 for RLE.
WRITE alloc_global @MyIntStorage // id: %0
WRITE %6 = apply %5(%1) : $@convention(thin) (@in_guaranteed Int) -> ()
FORWARD %3 = struct $Int (%2 : $Builtin.Int64) // user: %4
to %7 = load %1 : $*Int // user: %9
WRITE %9 = apply %8(%7) : $@convention(thin) (Int) -> () // user: %14
WRITE %11 = apply %10(%1) : $@convention(thin) (@in_guaranteed Int) -> ()
WRITE %13 = apply %8(%12) : $@convention(thin) (Int) -> ()
Replacing %7 = load %1 : $*Int // user: %9
With %3 = struct $Int (%2 : $Builtin.Int64) // user: %4
[JW: ^^^^ this looks pretty promising to me, don't understand all the output but looks like it's replacing a load :) ]
*** RLE on function: main ***
sil_stage canonical
[...]
// bar
sil hidden @bar : $@convention(thin) () -> () {
bb0:
alloc_global @MyIntStorage // id: %0
%1 = global_addr @MyIntStorage : $*Int // users: %11, %10, %6, %4
%2 = integer_literal $Builtin.Int64, 42 // user: %3
%3 = struct $Int (%2 : $Builtin.Int64) // users: %8, %4
store %3 to %1 : $*Int // id: %4
// function_ref buz
%5 = function_ref @buz : $@convention(thin) (@in_guaranteed Int) -> () // user: %6
%6 = apply %5(%1) : $@convention(thin) (@in_guaranteed Int) -> ()
// function_ref testNumber12345
%7 = function_ref @testNumber12345 : $@convention(thin) (Int) -> () // users: %12, %8
[JW: Indeed, it seems to have optimised out the redundant load. So that works! ]
%8 = apply %7(%3) : $@convention(thin) (Int) -> () // user: %13
// function_ref bad
%9 = function_ref @bad : $@convention(thin) (@in_guaranteed Int) -> () // user: %10
%10 = apply %9(%1) : $@convention(thin) (@in_guaranteed Int) -> ()
[JW: Wow, very clever, it didn't optimise out this load despite me giving the wrong guarantee that it's not actually modified (or am I misunderstanding something]
%11 = load %1 : $*Int // user: %12
%12 = apply %7(%11) : $@convention(thin) (Int) -> ()
return %8 : $() // id: %13
} // end sil function 'bar'
[...]
--- SNAP ---
Long story short, I think the RLE actually works for the test case I created. It's even clever enough to see through my invalid function bad() which modified the storage despite its claim that it doesn't. I might also be misunderstanding something.
Does that all make sense?
Thanks,
Johannes
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test-load-forwarding.sil
Type: application/octet-stream
Size: 3003 bytes
Desc: not available
URL: <https://lists.swift.org/pipermail/swift-dev/attachments/20170711/09b123fd/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test-load-forwarding.sil-opt
Type: application/octet-stream
Size: 4602 bytes
Desc: not available
URL: <https://lists.swift.org/pipermail/swift-dev/attachments/20170711/09b123fd/attachment-0001.obj>
-------------- next part --------------
>
>>
>> Thanks,
>> Johannes
>>
>> [1]: https://bugs.swift.org/browse/SR-5403
More information about the swift-dev
mailing list