From ba4da8e0d32a4a4fc0e4df8744fef6039f9b1975 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 13 May 2020 16:09:30 +0200 Subject: [PATCH] LICM: enable more stores to moved out of a loop Even if a store is not dominating the loop exits, it makes sense to move it out of the loop if the pre-header also as a store to the same memory location. When this is done, dead-store-elimination can then most likely remove the store in the pre-header. --- lib/SILOptimizer/LoopTransforms/LICM.cpp | 35 ++++++++++++++++++++ test/SILOptimizer/licm.sil | 41 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 171a10abd863..a70f93d764c2 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -963,6 +963,41 @@ static bool storesCommonlyDominateLoopExits(SILValue addr, SILLoop *loop, if (stores.count(header) != 0) return true; + // Also a store in the pre-header dominates all exists. Although the situation + // is a bit different here: the store in the pre-header remains - it's not + // (re)moved by the LICM transformation. + // But even if the loop-stores are not dominating the loop exits, it + // makes sense to move them out of the loop if this case. When this is done, + // dead-store-elimination can then most likely eliminate the store in the + // pre-header. + // + // pre_header: + // store %v1 to %addr + // header: + // cond_br %cond, then, tail + // then: + // store %v2 to %addr // a conditional store in the loop + // br tail + // tail: + // cond_br %loop_cond, header, exit + // exit: + // + // will be transformed to + // + // pre_header: + // store %v1 to %addr // <- can be removed by DSE afterwards + // header: + // cond_br %cond, then, tail + // then: + // br tail + // tail(%phi): + // cond_br %loop_cond, header, exit + // exit: + // store %phi to %addr + // + if (stores.count(loop->getLoopPreheader()) != 0) + return true; + // Propagate the store-is-not-alive flag through the control flow in the loop, // starting at the header. SmallPtrSet storesNotAlive; diff --git a/test/SILOptimizer/licm.sil b/test/SILOptimizer/licm.sil index 2a6452d43630..153834ffc0f6 100644 --- a/test/SILOptimizer/licm.sil +++ b/test/SILOptimizer/licm.sil @@ -502,6 +502,47 @@ bb6: return %12 : $() } +// CHECK-LABEL: sil @hoist_when_store_is_in_preheader +// CHECK: bb0(%0 : $*Int32, %1 : $Int32): +// CHECK: store +// CHECK: load +// CHECK: bb1(%{{[0-9]+}} : $Int32): +// CHECK-NOT: load +// CHECK-NOT: store +// CHECK: bb4([[P:%[0-9]+]] : $Int32): +// CHECK: bb6: +// CHECK: store [[P]] to %0 +// CHECK: } // end sil function 'hoist_when_store_is_in_preheader' +sil @hoist_when_store_is_in_preheader : $@convention(thin) (@inout Int32, Int32) -> () { +bb0(%0 : $*Int32, %1 : $Int32): + %8 = struct_element_addr %0 : $*Int32, #Int32._value + %9 = struct_extract %1 : $Int32, #Int32._value + %10 = integer_literal $Builtin.Int1, 0 + store %1 to %0 : $*Int32 + br bb1 + +bb1: + %17 = load %8 : $*Builtin.Int32 + %18 = builtin "sadd_with_overflow_Int64"(%17 : $Builtin.Int32, %9 : $Builtin.Int32, %10 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) + %19 = tuple_extract %18 : $(Builtin.Int32, Builtin.Int1), 0 + %20 = struct $Int32 (%19 : $Builtin.Int32) + cond_br undef, bb2, bb3 +bb2: + br bb4 +bb3: + store %20 to %0 : $*Int32 + br bb4 +bb4: + cond_br undef, bb5, bb6 + +bb5: + br bb1 + +bb6: + %12 = tuple () + return %12 : $() +} + // CHECK-LABEL: sil @hoist_loads_and_stores_multiple_exits // CHECK: [[V1:%[0-9]+]] = load %0 // CHECK: br bb1([[V1]] : $Int32)