/* Tree lowering pass. Lowers GIMPLE into unstructured form.
- Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This file is part of GCC.
/* A TREE_LIST of label and return statements to be moved to the end
of the function. */
tree return_statements;
+
+ /* True if the function calls __builtin_setjmp. */
+ bool calls_builtin_setjmp;
};
static void lower_stmt (tree_stmt_iterator *, struct lower_data *);
static void lower_bind_expr (tree_stmt_iterator *, struct lower_data *);
static void lower_cond_expr (tree_stmt_iterator *, struct lower_data *);
static void lower_return_expr (tree_stmt_iterator *, struct lower_data *);
+static void lower_builtin_setjmp (tree_stmt_iterator *);
-/* Lowers the body of current_function_decl. */
+/* Lower the body of current_function_decl. */
static unsigned int
lower_function_body (void)
tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
}
+ /* If the function calls __builtin_setjmp, we need to emit the computed
+ goto that will serve as the unique dispatcher for all the receivers. */
+ if (data.calls_builtin_setjmp)
+ {
+ tree disp_label, disp_var, arg;
+
+ /* Build 'DISP_LABEL:' and insert. */
+ disp_label = create_artificial_label ();
+ /* This mark will create forward edges from every call site. */
+ DECL_NONLOCAL (disp_label) = 1;
+ current_function_has_nonlocal_label = 1;
+ x = build1 (LABEL_EXPR, void_type_node, disp_label);
+ tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
+
+ /* Build 'DISP_VAR = __builtin_setjmp_dispatcher (DISP_LABEL);'
+ and insert. */
+ disp_var = create_tmp_var (ptr_type_node, "setjmpvar");
+ arg = build_addr (disp_label, current_function_decl);
+ t = implicit_built_in_decls[BUILT_IN_SETJMP_DISPATCHER];
+ t = build_call_expr (t, 1, arg);
+ x = build_gimple_modify_stmt (disp_var, t);
+
+ /* Build 'goto DISP_VAR;' and insert. */
+ tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
+ x = build1 (GOTO_EXPR, void_type_node, disp_var);
+ tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
+ }
+
gcc_assert (data.block == DECL_INITIAL (current_function_decl));
BLOCK_SUBBLOCKS (data.block)
= blocks_nreverse (BLOCK_SUBBLOCKS (data.block));
};
-/* Lowers the EXPR. Unlike gimplification the statements are not relowered
+/* Lower the EXPR. Unlike gimplification the statements are not relowered
when they are changed -- if this has to be done, the lowering routine must
do it explicitly. DATA is passed through the recursion. */
static void
lower_omp_directive (tree_stmt_iterator *tsi, struct lower_data *data)
{
- tree clause, stmt;
+ tree stmt;
stmt = tsi_stmt (*tsi);
- clause = (TREE_CODE (stmt) >= OMP_PARALLEL && TREE_CODE (stmt) <= OMP_SINGLE)
- ? OMP_CLAUSES (stmt)
- : NULL_TREE;
-
lower_stmt_body (OMP_BODY (stmt), data);
tsi_link_before (tsi, stmt, TSI_SAME_STMT);
tsi_link_before (tsi, OMP_BODY (stmt), TSI_SAME_STMT);
}
-/* Lowers statement TSI. DATA is passed through the recursion. */
+/* Lower statement TSI. DATA is passed through the recursion. */
static void
lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data)
case NOP_EXPR:
case ASM_EXPR:
- case MODIFY_EXPR:
- case CALL_EXPR:
case GOTO_EXPR:
case LABEL_EXPR:
case SWITCH_EXPR:
- case OMP_RETURN_EXPR:
- break;
-
- case OMP_PARALLEL:
case OMP_FOR:
case OMP_SECTIONS:
case OMP_SECTION:
case OMP_MASTER:
case OMP_ORDERED:
case OMP_CRITICAL:
+ case OMP_RETURN:
+ case OMP_CONTINUE:
+ break;
+
+ case GIMPLE_MODIFY_STMT:
+ if (TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CALL_EXPR)
+ stmt = GIMPLE_STMT_OPERAND (stmt, 1);
+ else
+ break;
+ /* FALLTHRU */
+
+ case CALL_EXPR:
+ {
+ tree decl = get_callee_fndecl (stmt);
+ if (decl
+ && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (decl) == BUILT_IN_SETJMP)
+ {
+ data->calls_builtin_setjmp = true;
+ lower_builtin_setjmp (tsi);
+ return;
+ }
+ }
+ break;
+
+ case OMP_PARALLEL:
lower_omp_directive (tsi, data);
return;
tsi_next (tsi);
}
-/* Lowers a bind_expr TSI. DATA is passed through the recursion. */
+/* Lower a bind_expr TSI. DATA is passed through the recursion. */
static void
lower_bind_expr (tree_stmt_iterator *tsi, struct lower_data *data)
return (block_may_fallthru (TREE_OPERAND (stmt, 0))
&& block_may_fallthru (TREE_OPERAND (stmt, 1)));
- case MODIFY_EXPR:
- if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR)
- stmt = TREE_OPERAND (stmt, 1);
+ case GIMPLE_MODIFY_STMT:
+ if (TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CALL_EXPR)
+ stmt = GIMPLE_STMT_OPERAND (stmt, 1);
else
return true;
/* FALLTHRU */
}
}
-/* Lowers a cond_expr TSI. DATA is passed through the recursion. */
+/* Lower a cond_expr TSI. DATA is passed through the recursion. */
static void
lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data)
tsi_next (tsi);
}
+/* Lower a return_expr TSI. DATA is passed through the recursion. */
+
static void
lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data)
{
/* Extract the value being returned. */
value = TREE_OPERAND (stmt, 0);
- if (value && TREE_CODE (value) == MODIFY_EXPR)
- value = TREE_OPERAND (value, 1);
+ if (value && TREE_CODE (value) == GIMPLE_MODIFY_STMT)
+ value = GIMPLE_STMT_OPERAND (value, 1);
/* Match this up with an existing return statement that's been created. */
for (t = data->return_statements; t ; t = TREE_CHAIN (t))
{
tree tvalue = TREE_OPERAND (TREE_VALUE (t), 0);
- if (tvalue && TREE_CODE (tvalue) == MODIFY_EXPR)
- tvalue = TREE_OPERAND (tvalue, 1);
+ if (tvalue && TREE_CODE (tvalue) == GIMPLE_MODIFY_STMT)
+ tvalue = GIMPLE_STMT_OPERAND (tvalue, 1);
if (value == tvalue)
{
tsi_link_before (tsi, t, TSI_SAME_STMT);
tsi_delink (tsi);
}
+
+/* Lower a __builtin_setjmp TSI.
+
+ __builtin_setjmp is passed a pointer to an array of five words (not
+ all will be used on all machines). It operates similarly to the C
+ library function of the same name, but is more efficient.
+
+ It is lowered into 3 other builtins, namely __builtin_setjmp_setup,
+ __builtin_setjmp_dispatcher and __builtin_setjmp_receiver, but with
+ __builtin_setjmp_dispatcher shared among all the instances; that's
+ why it is only emitted at the end by lower_function_body.
+
+ After full lowering, the body of the function should look like:
+
+ {
+ void * setjmpvar.0;
+ int D.1844;
+ int D.2844;
+
+ [...]
+
+ __builtin_setjmp_setup (&buf, &<D1847>);
+ D.1844 = 0;
+ goto <D1846>;
+ <D1847>:;
+ __builtin_setjmp_receiver (&<D1847>);
+ D.1844 = 1;
+ <D1846>:;
+ if (D.1844 == 0) goto <D1848>; else goto <D1849>;
+
+ [...]
+
+ __builtin_setjmp_setup (&buf, &<D2847>);
+ D.2844 = 0;
+ goto <D2846>;
+ <D2847>:;
+ __builtin_setjmp_receiver (&<D2847>);
+ D.2844 = 1;
+ <D2846>:;
+ if (D.2844 == 0) goto <D2848>; else goto <D2849>;
+
+ [...]
+
+ <D3850>:;
+ return;
+ <D3853>: [non-local];
+ setjmpvar.0 = __builtin_setjmp_dispatcher (&<D3853>);
+ goto setjmpvar.0;
+ }
+
+ The dispatcher block will be both the unique destination of all the
+ abnormal call edges and the unique source of all the abnormal edges
+ to the receivers, thus keeping the complexity explosion localized. */
+
+static void
+lower_builtin_setjmp (tree_stmt_iterator *tsi)
+{
+ tree stmt = tsi_stmt (*tsi);
+ tree cont_label = create_artificial_label ();
+ tree next_label = create_artificial_label ();
+ tree dest, t, arg;
+
+ /* NEXT_LABEL is the label __builtin_longjmp will jump to. Its address is
+ passed to both __builtin_setjmp_setup and __builtin_setjmp_receiver. */
+ FORCED_LABEL (next_label) = 1;
+
+ if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT)
+ {
+ dest = GIMPLE_STMT_OPERAND (stmt, 0);
+ stmt = GIMPLE_STMT_OPERAND (stmt, 1);
+ }
+ else
+ dest = NULL_TREE;
+
+ /* Build '__builtin_setjmp_setup (BUF, NEXT_LABEL)' and insert. */
+ arg = build_addr (next_label, current_function_decl);
+ t = implicit_built_in_decls[BUILT_IN_SETJMP_SETUP];
+ t = build_call_expr (t, 2, CALL_EXPR_ARG (stmt, 0), arg);
+ SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
+ tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+ /* Build 'DEST = 0' and insert. */
+ if (dest)
+ {
+ t = build_gimple_modify_stmt (dest, fold_convert (TREE_TYPE (dest),
+ integer_zero_node));
+ SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
+ tsi_link_before (tsi, t, TSI_SAME_STMT);
+ }
+
+ /* Build 'goto CONT_LABEL' and insert. */
+ t = build1 (GOTO_EXPR, void_type_node, cont_label);
+ tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+ /* Build 'NEXT_LABEL:' and insert. */
+ t = build1 (LABEL_EXPR, void_type_node, next_label);
+ tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+ /* Build '__builtin_setjmp_receiver (NEXT_LABEL)' and insert. */
+ arg = build_addr (next_label, current_function_decl);
+ t = implicit_built_in_decls[BUILT_IN_SETJMP_RECEIVER];
+ t = build_call_expr (t, 1, arg);
+ SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
+ tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+ /* Build 'DEST = 1' and insert. */
+ if (dest)
+ {
+ t = build_gimple_modify_stmt (dest, fold_convert (TREE_TYPE (dest),
+ integer_one_node));
+ SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
+ tsi_link_before (tsi, t, TSI_SAME_STMT);
+ }
+
+ /* Build 'CONT_LABEL:' and insert. */
+ t = build1 (LABEL_EXPR, void_type_node, cont_label);
+ tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+ /* Remove the call to __builtin_setjmp. */
+ tsi_delink (tsi);
+}
\f
/* Record the variables in VARS into function FN. */