[llvm-commits] CVS: llvm-gcc/gcc/llvm-expand.c

Chris Lattner lattner at cs.uiuc.edu
Wed Feb 23 09:53:02 PST 2005



Changes in directory llvm-gcc/gcc:

llvm-expand.c updated: 1.86 -> 1.87
---
Log message:

GCC seriously abuses COND_EXPR nodes (essentially ?: expressions) internally,
building zillions of them for no apparent reason.  Instead of always expanding
these into control flow and letting the llvm optimizer put them back together 
into selects, do so explicitly when we are certain this is safe.

For example, in this simple example:

_Bool X;
void test(int A, int B) {
  X |= A > B;
}

llvm-gcc used to spew:

void %test(int %A, int %B) {  
entry:
        %A_addr = alloca int             ; ty=int*
        %B_addr = alloca int             ; ty=int*
        %mem_tmp.0 = alloca int          ; ty=int*
        %mem_tmp.1 = alloca bool                 ; ty=bool*
        %mem_tmp.2 = alloca bool                 ; ty=bool*
        %mem_tmp.3 = alloca bool                 ; ty=bool*
        store int %A, int* %A_addr
        store int %B, int* %B_addr
        %tmp.0 = load bool* %X           ; ty=bool
        %tmp.1 = cast bool %tmp.0 to int                 ; ty=int
        store int %tmp.1, int* %mem_tmp.0
        %tmp.2 = load int* %A_addr               ; ty=int
        %tmp.3 = load int* %B_addr               ; ty=int
        %tmp.4 = setgt int %tmp.2, %tmp.3                ; ty=bool
        %tmp.5 = cast bool %tmp.4 to int                 ; ty=int
        br bool %tmp.4, label %cond_true.0, label %cond_false.0
cond_true.0:
        %se_val.0 = load int* %mem_tmp.0                 ; ty=int
        %tmp.6 = or int %se_val.0, 1             ; ty=int
        %tmp.7 = setne int %tmp.6, 0             ; ty=bool
        %tmp.8 = cast bool %tmp.7 to int                 ; ty=int
        br bool %tmp.7, label %cond_true.1, label %cond_false.1
cond_true.1:
        store bool true, bool* %mem_tmp.2
        br label %cond_continue.1
cond_false.1:
        store bool false, bool* %mem_tmp.2
        br label %cond_continue.1
cond_continue.1:
        %tmp.9 = load bool* %mem_tmp.2           ; ty=bool
        store bool %tmp.9, bool* %mem_tmp.1
        br label %cond_continue.0
cond_false.0:
        %se_val.1 = load int* %mem_tmp.0                 ; ty=int
        %tmp.10 = setne int %se_val.1, 0                 ; ty=bool
        %tmp.11 = cast bool %tmp.10 to int               ; ty=int
        br bool %tmp.10, label %cond_true.2, label %cond_false.2
cond_true.2:
        store bool true, bool* %mem_tmp.3
        br label %cond_continue.2
cond_false.2:
        store bool false, bool* %mem_tmp.3
        br label %cond_continue.2
cond_continue.2:
        %tmp.12 = load bool* %mem_tmp.3          ; ty=bool
        store bool %tmp.12, bool* %mem_tmp.1
        br label %cond_continue.0
cond_continue.0:
        %tmp.13 = load bool* %mem_tmp.1          ; ty=bool
        store bool %tmp.13, bool* %X
        br label %return
return:
        ret void
}

Now it spews:

void %test(int %A, int %B) {  
entry:
        %A_addr = alloca int             ; ty=int*
        %B_addr = alloca int             ; ty=int*
        %mem_tmp.0 = alloca int          ; ty=int*
        %mem_tmp.1 = alloca bool                 ; ty=bool*
        store int %A, int* %A_addr
        store int %B, int* %B_addr
        %tmp.0 = load bool* %X           ; ty=bool
        %tmp.1 = cast bool %tmp.0 to int                 ; ty=int
        store int %tmp.1, int* %mem_tmp.0
        %tmp.2 = load int* %A_addr               ; ty=int
        %tmp.3 = load int* %B_addr               ; ty=int
        %tmp.4 = setgt int %tmp.2, %tmp.3                ; ty=bool
        %tmp.5 = cast bool %tmp.4 to int                 ; ty=int
        br bool %tmp.4, label %cond_true, label %cond_false
cond_true:
        %se_val.0 = load int* %mem_tmp.0                 ; ty=int
        %tmp.6 = or int %se_val.0, 1             ; ty=int
        %tmp.7 = setne int %tmp.6, 0             ; ty=bool
        %tmp.8 = cast bool %tmp.7 to int                 ; ty=int
        %tmp.9 = select bool %tmp.7, bool true, bool false               ; ty=bool
        store bool %tmp.9, bool* %mem_tmp.1
        br label %cond_continue
cond_false:
        %se_val.1 = load int* %mem_tmp.0                 ; ty=int
        %tmp.10 = setne int %se_val.1, 0                 ; ty=bool
        %tmp.11 = cast bool %tmp.10 to int               ; ty=int
        %tmp.12 = select bool %tmp.10, bool true, bool false             ; ty=bool
        store bool %tmp.12, bool* %mem_tmp.1
        br label %cond_continue
cond_continue:
        %tmp.13 = load bool* %mem_tmp.1          ; ty=bool
        store bool %tmp.13, bool* %X
        br label %return
return:
        ret void
}


Note the two select instructions generated, which eliminates two mem_tmp
allocas and some basic blocks among other things.  Also note that the 
two selects are trivially turnable into casts by the llvm optimizer.

gccas reduces this to:

void %test(int %A, int %B) {
        %tmp.0 = load bool* %X          ; <bool> [#uses=1]
        %tmp.4 = setgt int %A, %B               ; <bool> [#uses=1]
        %mem_tmp.1.0 = or bool %tmp.4, %tmp.0           ; <bool> [#uses=1]
        store bool %mem_tmp.1.0, bool* %X
        ret void
}



---
Diffs of the changes:  (+56 -12)

 llvm-expand.c |   68 +++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 56 insertions(+), 12 deletions(-)


Index: llvm-gcc/gcc/llvm-expand.c
diff -u llvm-gcc/gcc/llvm-expand.c:1.86 llvm-gcc/gcc/llvm-expand.c:1.87
--- llvm-gcc/gcc/llvm-expand.c:1.86	Sun Feb 20 22:04:14 2005
+++ llvm-gcc/gcc/llvm-expand.c	Wed Feb 23 11:52:46 2005
@@ -4288,7 +4288,6 @@
                                               llvm_value *op0, llvm_value *op1){
   enum InstOpcode Opcode;
   llvm_value *Compare;
-  llvm_instruction *Select;
   const char *Name;
 
   assert(op0->Ty == op1->Ty && "Operands must have same type!");
@@ -4306,11 +4305,7 @@
   if (TREE_CODE(exp) == ABS_EXPR)
     op1 = append_inst(Fn, create_binary_inst("abs.pos", O_Sub, op1, op0));
 
-  Select = llvm_instruction_new(op0->Ty, Name, O_Select, 3);
-  Select->Operands[0] = Compare;
-  Select->Operands[1] = op0;
-  Select->Operands[2] = op1;
-  return append_inst(Fn, Select);
+  return append_inst(Fn, create_select_inst(Name, Compare, op0, op1));
 }
 
 static llvm_function *CreateIntrinsicFnWithType(const char *Name,
@@ -5905,6 +5900,28 @@
     InitializeComplex(Fn, DestLoc, ResultR, ResultI, 0);
 }
 
+/* isSimpleEnoughToEvaluateUnconditionally - Return true if this expression is
+ * simple enough to evaluate unconditionally in a case where it would normally
+ * be only conditionally executed.
+ */
+static int isSimpleEnoughToEvaluateUnconditionally(tree exp) {
+  switch (TREE_CODE(exp)) {
+  default: return 0;
+  case REAL_CST: 
+  case INTEGER_CST: return 1;
+  case NOP_EXPR:
+    return isSimpleEnoughToEvaluateUnconditionally(TREE_OPERAND(exp, 0));
+  case VAR_DECL:
+  case PARM_DECL:
+    /* Allow variables that will probably be promoted to ssa registers. */
+
+    /* Must be defined in this function. */
+    if (!DECL_CONTEXT(exp) || TREE_CODE(DECL_CONTEXT(exp)) != FUNCTION_DECL)
+      return false;
+    /* Must not be static, must not have its address taken. */
+    return !TREE_STATIC(exp) && !TREE_ADDRESSABLE(exp) && !DECL_EXTERNAL(exp);
+  }
+}
 
 /* llvm_expand_expr: generate code for computing expression EXP.  If the
  * expression produces a scalar value (which can be held in an LLVM register),
@@ -6538,19 +6555,46 @@
   }
   case COND_EXPR: {      /* ?: expression */
     /* Allocate a new temporary to hold the result of the expression */
-    llvm_basicblock *TrueBlock = llvm_basicblock_new("cond_true");
-    llvm_basicblock *FalseBlock = llvm_basicblock_new("cond_false");
-    llvm_basicblock *ContinueBlock = llvm_basicblock_new("cond_continue");
+    llvm_basicblock *TrueBlock;
+    llvm_basicblock *FalseBlock;
+    llvm_basicblock *ContinueBlock;
 
-    /* Expand condition and branch */
+    /* Expand condition */
     llvm_value *Cond = llvm_expand_expr(Fn, TREE_OPERAND(exp, 0), 0);
+    Cond = cast_if_type_not_equal(Fn, Cond, BoolTy);
+
+    /* If we can prove that the two operands to this conditional are very simple
+     * (aka, safe and profitable to evaluate unconditionally), emit a select
+     * instruction instead of a conditional branch sequence.
+     */
+    if (!DestLoc) { /* Only handle scalars */
+      if (isSimpleEnoughToEvaluateUnconditionally(TREE_OPERAND(exp, 1)) &&
+          isSimpleEnoughToEvaluateUnconditionally(TREE_OPERAND(exp, 2))) {
+        /* Okay, this is simple enough, expand the LHS and RHS unconditionally.
+         */
+        llvm_value *LHS = llvm_expand_expr(Fn, TREE_OPERAND(exp, 1), 0);
+        llvm_value *RHS = llvm_expand_expr(Fn, TREE_OPERAND(exp, 2), 0);
+        LHS = cast_if_type_not_equal(Fn, LHS, DestTy);
+        RHS = cast_if_type_not_equal(Fn, RHS, DestTy);
+        return append_inst(Fn, create_select_inst("tmp", Cond, LHS, RHS));
+      }
+    }
+
+    /* If this is a complicated ?: expression, expand this as a conditional
+     * branch with the values evalutated in the arms of the conditionals.
+     */
+
+    TrueBlock = llvm_basicblock_new("cond_true");
+    FalseBlock = llvm_basicblock_new("cond_false");
+    ContinueBlock = llvm_basicblock_new("cond_continue");
+    append_inst(Fn, create_cond_branch(Cond, TrueBlock, FalseBlock));
+
     if (DestLoc) {       /* This is an aggregate ?: expression */
       Result = DestLoc;
     } else {
       Result = DestTy != VoidTy ? D2V(make_temporary_alloca(Fn, DestTy)) : 0;
     }
-    Cond = cast_if_type_not_equal(Fn, Cond, BoolTy);
-    append_inst(Fn, create_cond_branch(Cond, TrueBlock, FalseBlock));
+
     
     /* Add the true block as the fall through */
     llvm_emit_label(Fn, TrueBlock);






More information about the llvm-commits mailing list