[LLVMdev] Missed optimisation opportunities?
Philip Reames
listmail at philipreames.com
Wed Apr 3 22:36:42 PDT 2013
On 03/30/2013 03:36 AM, Jeremy Lakeman wrote:
> I'm writing a front end for an existing interpreted language with
> slightly odd semantics for primitive values.
> Similar to the values in a database table, any value could be null, even
> for non-pointer types.
> For example a boolean variable could be true, false, or null.
> To model this behaviour, I'm passing an {i1, [type]} around for every
> numeric type. And using insertvalue / extractvalue all over the place.
>
> So when compiling this simple CS example;
>
> function long fib (long al_x)
> long ll_i=0, ll_ret=0, ll_last=0, ll_last2=1
>
> if isnull(al_x) then
> return al_x
> end if
>
> ll_i=1
> do while ll_i<=al_x
> ll_ret=ll_last+ll_last2
> ll_last2=ll_last
> ll_last=ll_ret
> ll_i++
> loop
> return ll_ret
> end function
>
> With the following function passes;
> FunctionPasses->add(new DataLayout(*JITEngine->getDataLayout()));
> FunctionPasses->add(createBasicAliasAnalysisPass());
> FunctionPasses->add(createPromoteMemoryToRegisterPass());
> FunctionPasses->add(createInstructionCombiningPass());
> FunctionPasses->add(createReassociatePass());
> FunctionPasses->add(createGVNPass());
> FunctionPasses->add(createCFGSimplificationPass());
>
> Which does a pretty good job of cleaning up most of the messy & verbose
> code generated by my front end.
>
> I end up with the following bitcode;
>
> define x86_stdcallcc { i1, i32 } @fib({ i1, i32 } %arg_al_x) {
> entry:
> %"3_4" = extractvalue { i1, i32 } %arg_al_x, 0
> br i1 %"3_4", label %Line15_Offset120, label %Line8_Offset38
>
> Line8_Offset38: ; preds =
> %Line9_Offset52, %entry
> %ll_last2.0 = phi { i1, i32 } [ %ll_last.0, %Line9_Offset52 ], [ { i1
> false, i32 1 }, %entry ]
> %ll_last.0 = phi { i1, i32 } [ %"9_64_composed", %Line9_Offset52 ], [
> zeroinitializer, %entry ]
> %ll_i.0 = phi { i1, i32 } [ %"12_98", %Line9_Offset52 ], [ { i1
> false, i32 1 }, %entry ]
> %"8_38_isnull" = extractvalue { i1, i32 } %ll_i.0, 0
> %"8_38_value" = extractvalue { i1, i32 } %ll_i.0, 1
> %"8_42_value" = extractvalue { i1, i32 } %arg_al_x, 1
> %"8_46_value" = icmp sle i32 %"8_38_value", %"8_42_value"
> %"8_48_not_null" = xor i1 %"8_38_isnull", true
> %"8_48_and_not_null" = and i1 %"8_46_value", %"8_48_not_null"
> br i1 %"8_48_and_not_null", label %Line9_Offset52, label
> %Line15_Offset120
>
> Line9_Offset52: ; preds = %Line8_Offset38
> %"9_60_isnull" = extractvalue { i1, i32 } %ll_last2.0, 0
> %"9_56_isnull" = extractvalue { i1, i32 } %ll_last.0, 0
> %"9_64_isnull" = or i1 %"9_56_isnull", %"9_60_isnull"
> %"9_56_value" = extractvalue { i1, i32 } %ll_last.0, 1
> %"9_60_value" = extractvalue { i1, i32 } %ll_last2.0, 1
> %"9_64" = add i32 %"9_56_value", %"9_60_value"
> %0 = insertvalue { i1, i32 } zeroinitializer, i1 %"9_64_isnull", 0
> %"9_64_composed" = insertvalue { i1, i32 } %0, i32 %"9_64", 1
> %"12_98_value" = add i32 %"8_38_value", 1
> %1 = insertvalue { i1, i32 } zeroinitializer, i1 %"8_38_isnull", 0
> %"12_98" = insertvalue { i1, i32 } %1, i32 %"12_98_value", 1
> br label %Line8_Offset38
>
> Line15_Offset120: ; preds =
> %Line8_Offset38, %entry
> %_return_value.0 = phi { i1, i32 } [ %arg_al_x, %entry ], [
> %ll_last.0, %Line8_Offset38 ]
> ret { i1, i32 } %_return_value.0
> }
>
> None of the local variables in this example can ever be null. Each of
> these extractvalue expressions will always evaluate to i1 0;
>
> %"8_38_isnull" = extractvalue { i1, i32 } %ll_i.0, 0
> %"9_60_isnull" = extractvalue { i1, i32 } %ll_last2.0, 0
> %"9_56_isnull" = extractvalue { i1, i32 } %ll_last.0, 0
>
> Is there an existing function pass I could add that would clean this up?
> Or is this example just slightly too complex?
>
>
> _______________________________________________
> LLVM Developers mailing list
> LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
>
Note that I am not an expert in this code and haven't actually tested
any of what I'm about to suggest. I'm purely speculating after a quick
read through code I'm otherwise unfamiliar with...
The key fact missed here seems to be that the input to the phi
(ll_last2.0) are both insertvalue instructions or constant values (and
thus implicitly one). With this information, the phi node could be
pushed through the insertvector to do a phi of the non-constant part of
the value and an insertvalue to add the constant piece. This actually
seems fairly similar to the FoldPHIArgGEPIntoPHI transformation which is
currently done for GEPs by InstrCombine. It might be worth looking into
whether there's an analogous transformation for insertelement you could
add. Once this was done, the extractvalue(insertvalue) patterns should
remove the redundant constants.
While this would be profitable in your case, I'm unclear whether this
would be a generally profitable transformation to make.
Another option would be to manually destruct your records into their
component pieces. This would introduce separate phi nodes for each
field and should enable the null checks to be mostly removed in this
example.
Philip
More information about the llvm-dev
mailing list