Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
DISCONTINUED:openSUSE:11.1
gcc43
pr37868.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File pr37868.diff of Package gcc43
2008-10-22 Richard Guenther <rguenther@suse.de> PR tree-optimization/37868 * tree-ssa-structalias.c (set_uids_in_ptset): Add SFTs based on pointed to variable and access size. * gcc.dg/torture/pr37868.c: New testcase. * gcc.c-torture/execute/pr38048-1.c: Likewise. * gcc.c-torture/execute/pr38048-2.c: Likewise. Backport from mainline: 2008-07-07 Richard Guenther <rguenther@suse.de> * tree-ssa-structalias.c (struct variable_info): Add is_full_var flag. (new_var_info): Set it to false. (solution_set_add): Correctly handle pointers outside a var and inside a field. (type_safe): Treat variables with is_full_var properly. (do_sd_constraint): Likewise. (do_ds_constraint): Likewise. (process_constraint): Remove zeroing offset for !use_field_sensitive. (get_constraint_for_ptr_offset): New function. (get_constraint_for_component_ref): Handle is_full_vars properly. (get_constraint_for): Handle POINTER_PLUS_EXPR. (handle_ptr_arith): Remove. (find_func_aliases): Handle POINTER_PLUS_EXPR through generic get_constraint_for code. (create_function_info_for): For parameter and result varinfos set is_full_var flag. (create_variable_info_for): Set is_full_var flag whenever we just created a single varinfo for a decl. (init_alias_vars): Initialize use_field_sensitive from max-fields-for-field-sensitive parameter. * gcc.dg/torture/pta-ptrarith-1.c: New testcase. * gcc.dg/torture/pta-ptrarith-2.c: Likewise. Index: gcc-4_3-branch/gcc/testsuite/gcc.dg/torture/pta-ptrarith-1.c =================================================================== *** /dev/null 1970-01-01 00:00:00.000000000 +0000 --- gcc-4_3-branch/gcc/testsuite/gcc.dg/torture/pta-ptrarith-1.c 2008-10-22 15:28:30.000000000 +0200 *************** *** 0 **** --- 1,29 ---- + /* { dg-do run } */ + + struct Foo { + int *p; + }; + + void __attribute__((noinline)) + foo (void *p) + { + struct Foo *f = (struct Foo *)p - 1; + *f->p = 0; + } + + int bar (void) + { + struct Foo f; + int i = 1; + f.p = &i; + foo (&f + 1); + return i; + } + extern void abort (void); + int main() + { + if (bar () != 0) + abort (); + return 0; + } + Index: gcc-4_3-branch/gcc/testsuite/gcc.dg/torture/pta-ptrarith-2.c =================================================================== *** /dev/null 1970-01-01 00:00:00.000000000 +0000 --- gcc-4_3-branch/gcc/testsuite/gcc.dg/torture/pta-ptrarith-2.c 2008-10-22 15:28:30.000000000 +0200 *************** *** 0 **** --- 1,36 ---- + /* { dg-do run } */ + /* { dg-options "-fdump-tree-salias" } */ + /* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */ + + struct Foo { + int **p; + int **q; + }; + + int __attribute__((noinline)) + bar (void) + { + struct Foo f; + int j, i = 1; + char *p; + int *x = &i; + int *y = &j; + f.p = &y; + f.q = &x; + p = (char *)&f; + for (j = 0; j < sizeof (int *); ++j) + p++; + return ***(int ***)p; + } + extern void abort (void); + int main() + { + if (bar () != 1) + abort (); + return 0; + } + + /* In theory = { i } is the correct solution. But it's not easy to scan + for that reliably, so just use what we create now. */ + /* { dg-final { scan-tree-dump "= { i j }" "salias" } } */ + /* { dg-final { cleanup-tree-dump "salias" } } */ Index: gcc-4_3-branch/gcc/tree-ssa-structalias.c =================================================================== *** gcc-4_3-branch.orig/gcc/tree-ssa-structalias.c 2008-10-22 15:28:17.000000000 +0200 --- gcc-4_3-branch/gcc/tree-ssa-structalias.c 2008-10-22 18:12:58.000000000 +0200 *************** *** 241,246 **** --- 241,249 ---- /* True for variables that have unions somewhere in them. */ unsigned int has_union:1; + /* True for (sub-)fields that represent a whole variable. */ + unsigned int is_full_var : 1; + /* True if this is a heap variable. */ unsigned int is_heap_var:1; *************** *** 363,368 **** --- 366,372 ---- ret->is_heap_var = false; ret->is_special_var = false; ret->is_unknown_size_var = false; + ret->is_full_var = false; ret->has_union = false; var = t; if (TREE_CODE (var) == SSA_NAME) *************** *** 742,764 **** EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi) { ! /* If this is a properly sized variable, only add offset if it's ! less than end. Otherwise, it is globbed to a single ! variable. */ ! if ((get_varinfo (i)->offset + offset) < get_varinfo (i)->fullsize) { ! unsigned HOST_WIDE_INT fieldoffset = get_varinfo (i)->offset + offset; ! varinfo_t v = first_vi_for_offset (get_varinfo (i), fieldoffset); if (!v) ! continue; bitmap_set_bit (result, v->id); ! } ! else if (get_varinfo (i)->is_artificial_var ! || get_varinfo (i)->has_union ! || get_varinfo (i)->is_unknown_size_var) ! { ! bitmap_set_bit (result, i); } } --- 746,778 ---- EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi) { ! varinfo_t vi = get_varinfo (i); ! /* If this is a variable with just one field just set its bit ! in the result. */ ! if (vi->is_artificial_var ! || vi->is_unknown_size_var ! || vi->has_union ! || vi->is_full_var) ! bitmap_set_bit (result, i); ! else { ! unsigned HOST_WIDE_INT fieldoffset = vi->offset + offset; ! varinfo_t v = first_vi_for_offset (vi, fieldoffset); ! /* If the result is outside of the variable use the last field. */ if (!v) ! { ! v = vi; ! while (v->next != NULL) ! v = v->next; ! } bitmap_set_bit (result, v->id); ! /* If the result is not exactly at fieldoffset include the next ! field as well. See get_constraint_for_ptr_offset for more ! rationale. */ ! if (v->offset != fieldoffset ! && v->next != NULL) ! bitmap_set_bit (result, v->next->id); } } *************** *** 1371,1377 **** 0. */ if (ninfo->is_special_var || ninfo->is_artificial_var ! || ninfo->is_unknown_size_var) { *offset = 0; return true; --- 1385,1392 ---- 0. */ if (ninfo->is_special_var || ninfo->is_artificial_var ! || ninfo->is_unknown_size_var ! || ninfo->is_full_var) { *offset = 0; return true; *************** *** 1411,1416 **** --- 1426,1432 ---- unsigned int t; v = first_vi_for_offset (get_varinfo (j), fieldoffset); + /* If the access is outside of the variable we can ignore it. */ if (!v) continue; t = find (v->id); *************** *** 1460,1468 **** unsigned HOST_WIDE_INT fieldoffset = jvi->offset + loff; varinfo_t v; ! v = first_vi_for_offset (get_varinfo (j), fieldoffset); ! if (!v) ! continue; t = find (v->id); if (!bitmap_bit_p (get_varinfo (t)->solution, anything_id)) --- 1476,1489 ---- unsigned HOST_WIDE_INT fieldoffset = jvi->offset + loff; varinfo_t v; ! v = get_varinfo (j); ! if (!v->is_full_var) ! { ! v = first_vi_for_offset (v, fieldoffset); ! /* If the access is outside of the variable we can ignore it. */ ! if (!v) ! continue; ! } t = find (v->id); if (!bitmap_bit_p (get_varinfo (t)->solution, anything_id)) *************** *** 1491,1496 **** --- 1512,1518 ---- bitmap tmp; v = first_vi_for_offset (get_varinfo (j), fieldoffset); + /* If the access is outside of the variable we can ignore it. */ if (!v) continue; t = find (v->id); *************** *** 2519,2530 **** gcc_assert (rhs.var < VEC_length (varinfo_t, varmap)); gcc_assert (lhs.var < VEC_length (varinfo_t, varmap)); - if (!use_field_sensitive) - { - t->rhs.offset = 0; - t->lhs.offset = 0; - } - /* ANYTHING == ANYTHING is pointless. */ if (lhs.var == anything_id && rhs.var == anything_id) return; --- 2541,2546 ---- *************** *** 2593,2600 **** tree type = TREE_TYPE (t); if (POINTER_TYPE_P (type) ! || AGGREGATE_TYPE_P (type) ! || TREE_CODE (type) == COMPLEX_TYPE) return true; return false; --- 2609,2615 ---- tree type = TREE_TYPE (t); if (POINTER_TYPE_P (type) ! || AGGREGATE_TYPE_P (type)) return true; return false; *************** *** 2635,2640 **** --- 2650,2769 ---- return false; } + + /* Get constraint expressions for offsetting PTR by OFFSET. Stores the + resulting constraint expressions in *RESULTS. */ + + static void + get_constraint_for_ptr_offset (tree ptr, tree offset, + VEC (ce_s, heap) **results) + { + struct constraint_expr *c; + unsigned int j, n; + unsigned HOST_WIDE_INT rhsunitoffset, rhsoffset; + + /* If we do not do field-sensitive PTA adding offsets to pointers + does not change the points-to solution. */ + if (!use_field_sensitive) + { + get_constraint_for (ptr, results); + return; + } + + /* If the offset is not a non-negative integer constant that fits + in a HOST_WIDE_INT, we have to fall back to a conservative + solution which includes all sub-fields of all pointed-to + variables of ptr. + ??? As we do not have the ability to express this, fall back + to anything. */ + if (!host_integerp (offset, 1)) + { + struct constraint_expr temp; + temp.var = anything_id; + temp.type = SCALAR; + temp.offset = 0; + VEC_safe_push (ce_s, heap, *results, &temp); + return; + } + + /* Make sure the bit-offset also fits. */ + rhsunitoffset = TREE_INT_CST_LOW (offset); + rhsoffset = rhsunitoffset * BITS_PER_UNIT; + if (rhsunitoffset != rhsoffset / BITS_PER_UNIT) + { + struct constraint_expr temp; + temp.var = anything_id; + temp.type = SCALAR; + temp.offset = 0; + VEC_safe_push (ce_s, heap, *results, &temp); + return; + } + + get_constraint_for (ptr, results); + if (rhsoffset == 0) + return; + + /* As we are eventually appending to the solution do not use + VEC_iterate here. */ + n = VEC_length (ce_s, *results); + for (j = 0; j < n; j++) + { + varinfo_t curr; + c = VEC_index (ce_s, *results, j); + curr = get_varinfo (c->var); + + if (c->type == ADDRESSOF + && !curr->is_full_var) + { + varinfo_t temp, curr = get_varinfo (c->var); + + /* Search the sub-field which overlaps with the + pointed-to offset. As we deal with positive offsets + only, we can start the search from the current variable. */ + temp = first_vi_for_offset (curr, curr->offset + rhsoffset); + + /* If the result is outside of the variable we have to provide + a conservative result, as the variable is still reachable + from the resulting pointer (even though it technically + cannot point to anything). The last sub-field is such + a conservative result. + ??? If we always had a sub-field for &object + 1 then + we could represent this in a more precise way. */ + if (temp == NULL) + { + temp = curr; + while (temp->next != NULL) + temp = temp->next; + continue; + } + + /* If the found variable is not exactly at the pointed to + result, we have to include the next variable in the + solution as well. Otherwise two increments by offset / 2 + do not result in the same or a conservative superset + solution. */ + if (temp->offset != curr->offset + rhsoffset + && temp->next != NULL) + { + struct constraint_expr c2; + c2.var = temp->next->id; + c2.type = ADDRESSOF; + c2.offset = 0; + VEC_safe_push (ce_s, heap, *results, &c2); + } + c->var = temp->id; + c->offset = 0; + } + else if (c->type == ADDRESSOF + /* If this varinfo represents a full variable just use it. */ + && curr->is_full_var) + c->offset = 0; + else + c->offset = rhsoffset; + } + } + + /* Given a COMPONENT_REF T, return the constraint_expr for it. */ static void *************** *** 2682,2688 **** if (TREE_CODE (t) != ADDR_EXPR && result->type == ADDRESSOF) result->type = SCALAR; ! if (result->type == SCALAR) { /* In languages like C, you can access one past the end of an array. You aren't allowed to dereference it, so we can --- 2811,2821 ---- if (TREE_CODE (t) != ADDR_EXPR && result->type == ADDRESSOF) result->type = SCALAR; ! if (result->type == SCALAR ! && get_varinfo (result->var)->is_full_var) ! /* For single-field vars do not bother about the offset. */ ! result->offset = 0; ! else if (result->type == SCALAR) { /* In languages like C, you can access one past the end of an array. You aren't allowed to dereference it, so we can *************** *** 2801,2833 **** struct constraint_expr *c; unsigned int i; tree exp = TREE_OPERAND (t, 0); - tree pttype = TREE_TYPE (TREE_TYPE (t)); get_constraint_for (exp, results); - - /* Complex types are special. Taking the address of one - allows you to access either part of it through that - pointer. */ - if (VEC_length (ce_s, *results) == 1 && - TREE_CODE (pttype) == COMPLEX_TYPE) - { - struct constraint_expr *origrhs; - varinfo_t origvar; - struct constraint_expr tmp; - - gcc_assert (VEC_length (ce_s, *results) == 1); - origrhs = VEC_last (ce_s, *results); - tmp = *origrhs; - VEC_pop (ce_s, *results); - origvar = get_varinfo (origrhs->var); - for (; origvar; origvar = origvar->next) - { - tmp.var = origvar->id; - VEC_safe_push (ce_s, heap, *results, &tmp); - } - } - for (i = 0; VEC_iterate (ce_s, *results, i, c); i++) { if (c->type == DEREF) --- 2934,2942 ---- *************** *** 2943,2948 **** --- 3052,3067 ---- } } } + case tcc_binary: + { + if (TREE_CODE (t) == POINTER_PLUS_EXPR) + { + get_constraint_for_ptr_offset (TREE_OPERAND (t, 0), + TREE_OPERAND (t, 1), results); + return; + } + break; + } case tcc_exceptional: { switch (TREE_CODE (t)) *************** *** 3512,3590 **** } - /* Handle pointer arithmetic EXPR when creating aliasing constraints. - Expressions of the type PTR + CST can be handled in two ways: - - 1- If the constraint for PTR is ADDRESSOF for a non-structure - variable, then we can use it directly because adding or - subtracting a constant may not alter the original ADDRESSOF - constraint (i.e., pointer arithmetic may not legally go outside - an object's boundaries). - - 2- If the constraint for PTR is ADDRESSOF for a structure variable, - then if CST is a compile-time constant that can be used as an - offset, we can determine which sub-variable will be pointed-to - by the expression. - - Return true if the expression is handled. For any other kind of - expression, return false so that each operand can be added as a - separate constraint by the caller. */ - - static bool - handle_ptr_arith (VEC (ce_s, heap) *lhsc, tree expr) - { - tree op0, op1; - struct constraint_expr *c, *c2; - unsigned int i = 0; - unsigned int j = 0; - VEC (ce_s, heap) *temp = NULL; - unsigned HOST_WIDE_INT rhsunitoffset, rhsoffset; - - if (TREE_CODE (expr) != POINTER_PLUS_EXPR) - return false; - - op0 = TREE_OPERAND (expr, 0); - op1 = TREE_OPERAND (expr, 1); - gcc_assert (POINTER_TYPE_P (TREE_TYPE (op0))); - - /* If the offset is not a non-negative integer constant that fits - in a HOST_WIDE_INT, we cannot handle it here. */ - if (!host_integerp (op1, 1)) - return false; - - /* Make sure the bit-offset also fits. */ - rhsunitoffset = TREE_INT_CST_LOW (op1); - rhsoffset = rhsunitoffset * BITS_PER_UNIT; - if (rhsunitoffset != rhsoffset / BITS_PER_UNIT) - return false; - - get_constraint_for (op0, &temp); - - for (i = 0; VEC_iterate (ce_s, lhsc, i, c); i++) - for (j = 0; VEC_iterate (ce_s, temp, j, c2); j++) - { - if (c2->type == ADDRESSOF && rhsoffset != 0) - { - varinfo_t temp = get_varinfo (c2->var); - - /* An access one after the end of an array is valid, - so simply punt on accesses we cannot resolve. */ - temp = first_vi_for_offset (temp, rhsoffset); - if (temp == NULL) - continue; - c2->var = temp->id; - c2->offset = 0; - } - else - c2->offset = rhsoffset; - process_constraint (new_constraint (*c, *c2)); - } - - VEC_free (ce_s, heap, temp); - - return true; - } - /* For non-IPA mode, generate constraints necessary for a call on the RHS. */ --- 3631,3636 ---- *************** *** 3815,3824 **** tree rhsop = GIMPLE_STMT_OPERAND (t, 1); int i; ! if ((AGGREGATE_TYPE_P (TREE_TYPE (lhsop)) ! || TREE_CODE (TREE_TYPE (lhsop)) == COMPLEX_TYPE) ! && (AGGREGATE_TYPE_P (TREE_TYPE (rhsop)) ! || TREE_CODE (TREE_TYPE (lhsop)) == COMPLEX_TYPE)) { do_structure_copy (lhsop, rhsop); } --- 3861,3868 ---- tree rhsop = GIMPLE_STMT_OPERAND (t, 1); int i; ! if (AGGREGATE_TYPE_P (TREE_TYPE (lhsop)) ! && AGGREGATE_TYPE_P (TREE_TYPE (rhsop))) { do_structure_copy (lhsop, rhsop); } *************** *** 3842,3847 **** --- 3886,3892 ---- case tcc_expression: case tcc_vl_exp: case tcc_unary: + case tcc_binary: { unsigned int j; *************** *** 3854,3874 **** for (k = 0; VEC_iterate (ce_s, rhsc, k, c2); k++) process_constraint (new_constraint (*c, *c2)); } - } break; - case tcc_binary: - { - /* For pointer arithmetic of the form - PTR + CST, we can simply use PTR's - constraint because pointer arithmetic is - not allowed to go out of bounds. */ - if (handle_ptr_arith (lhsc, rhsop)) - break; - } - /* FALLTHRU */ - /* Otherwise, walk each operand. Notice that we can't use the operand interface because we need to process expressions other than simple operands --- 3899,3907 ---- *************** *** 4257,4264 **** stats.total_vars++; /* If it's varargs, we don't know how many arguments it has, so we ! can't do much. ! */ if (is_varargs) { vi->fullsize = ~0; --- 4290,4296 ---- stats.total_vars++; /* If it's varargs, we don't know how many arguments it has, so we ! can't do much. */ if (is_varargs) { vi->fullsize = ~0; *************** *** 4292,4297 **** --- 4324,4330 ---- VEC_safe_push (varinfo_t, heap, varmap, argvi); argvi->offset = i; argvi->size = 1; + argvi->is_full_var = true; argvi->fullsize = vi->fullsize; argvi->has_union = false; insert_into_field_list_sorted (vi, argvi); *************** *** 4329,4334 **** --- 4362,4368 ---- resultvi->offset = i; resultvi->size = 1; resultvi->fullsize = vi->fullsize; + resultvi->is_full_var = true; resultvi->has_union = false; insert_into_field_list_sorted (vi, resultvi); stats.total_vars ++; *************** *** 4464,4469 **** --- 4498,4504 ---- vi->is_unknown_size_var = 1; vi->fullsize = ~0; vi->size = ~0; + vi->is_full_var = true; VEC_free (fieldoff_s, heap, fieldstack); return index; } *************** *** 4502,4507 **** --- 4537,4544 ---- stats.total_vars++; } } + else + vi->is_full_var = true; VEC_free (fieldoff_s, heap, fieldstack); *************** *** 4749,4765 **** && (sv = get_subvars_for_var (vi->decl))) { /* If VI->DECL is an aggregate for which we created ! SFTs, add the SFT corresponding to VI->OFFSET. If we didn't do field-sensitive PTA we need to to add all overlapping SFTs. */ unsigned int j; ! tree sft = get_first_overlapping_subvar (sv, vi->offset, ! vi->size, &j); gcc_assert (sft); for (; VEC_iterate (tree, sv, j, sft); ++j) { if (SFT_OFFSET (sft) > vi->offset ! && vi->size <= SFT_OFFSET (sft) - vi->offset) break; bitmap_set_bit (into, DECL_UID (sft)); --- 4786,4807 ---- && (sv = get_subvars_for_var (vi->decl))) { /* If VI->DECL is an aggregate for which we created ! SFTs, add the SFT corresponding to VI->OFFSET (the ! pointed-to location) and the access size. If we didn't do field-sensitive PTA we need to to add all overlapping SFTs. */ unsigned int j; ! unsigned HOST_WIDE_INT size = -1; ! tree sft, tsize = TYPE_SIZE (TREE_TYPE (TREE_TYPE (ptr))); ! if (tsize ! && host_integerp (tsize, 1)) ! size = TREE_INT_CST_LOW (tsize); ! sft = get_first_overlapping_subvar (sv, vi->offset, size, &j); gcc_assert (sft); for (; VEC_iterate (tree, sv, j, sft); ++j) { if (SFT_OFFSET (sft) > vi->offset ! && size <= SFT_OFFSET (sft) - vi->offset) break; bitmap_set_bit (into, DECL_UID (sft)); *************** *** 5156,5161 **** --- 5198,5205 ---- static void init_alias_vars (void) { + use_field_sensitive = (MAX_FIELDS_FOR_FIELD_SENSITIVE > 1); + bitmap_obstack_initialize (&pta_obstack); bitmap_obstack_initialize (&oldpta_obstack); bitmap_obstack_initialize (&predbitmap_obstack); Index: gcc-4_3-branch/gcc/testsuite/gcc.dg/torture/pr37868.c =================================================================== *** /dev/null 1970-01-01 00:00:00.000000000 +0000 --- gcc-4_3-branch/gcc/testsuite/gcc.dg/torture/pr37868.c 2008-10-22 15:28:30.000000000 +0200 *************** *** 0 **** --- 1,28 ---- + /* { dg-do run } */ + /* { dg-options "-fno-strict-aliasing" } */ + + extern void abort (void); + + struct X { + unsigned char pad : 4; + unsigned int a : 32; + unsigned int b : 24; + unsigned int c : 6; + } __attribute__((packed)); + + int main (void) + { + struct X x; + unsigned int bad_bits; + + x.pad = -1; + x.a = -1; + x.b = -1; + x.c = -1; + + bad_bits = ((unsigned int)-1) ^ *(1+(unsigned int *) &x); + if (bad_bits != 0) + abort (); + return 0; + } + Index: gcc-4_3-branch/gcc/testsuite/gcc.c-torture/execute/pr38048-1.c =================================================================== *** gcc-4_3-branch/gcc/testsuite/gcc.c-torture/execute/pr38048-1.c (revision 0) --- gcc-4_3-branch/gcc/testsuite/gcc.c-torture/execute/pr38048-1.c (revision 0) *************** *** 0 **** --- 1,22 ---- + extern void abort(void); + + int foo () + { + int mat[2][1]; + int (*a)[1] = mat; + int det = 0; + int i; + mat[0][0] = 1; + mat[1][0] = 2; + for (i = 0; i < 2; ++i) + det += a[i][0]; + return det; + } + + int main() + { + if (foo () != 3) + abort (); + return 0; + } + Index: gcc-4_3-branch/gcc/testsuite/gcc.c-torture/execute/pr38048-2.c =================================================================== *** gcc-4_3-branch/gcc/testsuite/gcc.c-torture/execute/pr38048-2.c (revision 0) --- gcc-4_3-branch/gcc/testsuite/gcc.c-torture/execute/pr38048-2.c (revision 0) *************** *** 0 **** --- 1,28 ---- + extern void abort (void); + + static int inv_J(int a[][2]) + { + int i, j; + int det = 0.0; + for (j=0; j<2; ++j) + det += a[j][0] + a[j][1]; + return det; + } + + int foo() + { + int mat[2][2]; + mat[0][0] = 1; + mat[0][1] = 2; + mat[1][0] = 4; + mat[1][1] = 8; + return inv_J(mat); + } + + int main() + { + if (foo () != 15) + abort (); + return 0; + } +
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor