From f6f2b01933268f73bf439ead94a1a72416a36cd5 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 12 Jan 2015 22:25:37 +0000 Subject: [PATCH] libgccjit: detect various kinds of errors relating to params and locals gcc/jit/ChangeLog: * jit-recording.c (class gcc::jit::rvalue_usage_validator): New. (gcc::jit::rvalue_usage_validator::rvalue_usage_validator): New ctor. (gcc::jit::rvalue_usage_validator::visit): New function. (gcc::jit::recording::rvalue::verify_valid_within_stmt): New function. (gcc::jit::recording::rvalue::set_scope): New function. (gcc::jit::recording::function::function): Call set_scope on each param, issuing errors for any params that already have a function. (gcc::jit::recording::block::add_eval): Return the new statement; update the comment given that some error-checking now happens after this returns. (gcc::jit::recording::block::add_assignment): Likewise. (gcc::jit::recording::block::add_assignment_op): Likewise. (gcc::jit::recording::block::add_comment): Likewise. (gcc::jit::recording::block::end_with_conditional): Likewise. (gcc::jit::recording::block::end_with_jump): Likewise. (gcc::jit::recording::block::end_with_return): Likewise. (gcc::jit::recording::block::validate): Add a comment. (gcc::jit::recording::unary_op::visit_children): New function. (gcc::jit::recording::binary_op::visit_children): New function. (gcc::jit::recording::comparison::visit_children): New function. (gcc::jit::recording::cast::visit_children): New function. (gcc::jit::recording::call::visit_children): New function. (gcc::jit::recording::call_through_ptr::visit_children): New function. (gcc::jit::recording::array_access::visit_children): New function. (gcc::jit::recording::access_field_of_lvalue::visit_children): New function. (gcc::jit::recording::access_field_rvalue::visit_children): New function. (gcc::jit::recording::dereference_field_rvalue::visit_children): New function. (gcc::jit::recording::dereference_rvalue::visit_children): New function. (gcc::jit::recording::get_address_of_lvalue::visit_children): New function. * jit-recording.h: Within namespace gcc::jit::recording... (class rvalue_visitor): New. (rvalue::rvalue): Initialize m_scope. (rvalue::get_loc): New accessor. (rvalue::verify_valid_within_stmt): New function. (rvalue::visit_children): New pure virtual function. (rvalue::set_scope): New function. (rvalue::get_scope): New function. (rvalue::dyn_cast_param): New function. (rvalue::m_scope): New field. (param::visit_children): New empty function. (param::dyn_cast_param): New function. (function::get_loc): New function. (block::add_eval): Return the new statement. (block::add_assignment): Likewise. (block::add_assignment_op): Likewise. (block::add_comment): Likewise. (block::end_with_conditional): Likewise. (block::end_with_jump): Likewise. (block::end_with_return): Likewise. (global::visit_children): New function. (memento_of_new_rvalue_from_const::visit_children): New function. (memento_of_new_string_literal::visit_children): New function. (unary_op::visit_children): New function. (binary_op::visit_children): New function. (comparison::visit_children): New function. (cast::visit_children): New function. (call::visit_children): New function. (call_through_ptr::visit_children): New function. (array_access::visit_children): New function. (access_field_of_lvalue::visit_children): New function. (access_field_rvalue::visit_children): New function. (dereference_field_rvalue::visit_children): New function. (dereference_rvalue::visit_children): New function. (get_address_of_lvalue::visit_children): New function. (local::local): Call set_scope. (local::visit_children): New function. (statement::get_block): Make public. * libgccjit.c (RETURN_VAL_IF_FAIL_PRINTF5): New macro. (RETURN_NULL_IF_FAIL_PRINTF5): New macro. (gcc_jit_context_new_function): Verify that each param has not yet been used for creating another function. (gcc_jit_block_add_eval): After creating the stmt, verify that the rvalue expression tree is valid to use within it. (gcc_jit_block_add_assignment): Likewise for the lvalue and rvalue expression trees. (gcc_jit_block_add_assignment_op): Likewise. (gcc_jit_block_end_with_conditional): Likewise for the boolval expression tree. (gcc_jit_block_end_with_return): Likewise for the rvalue expression tree. (gcc_jit_block_end_with_void_return): Remove return of "void", now that block::end_with_return is now non-void. gcc/testsuite/ChangeLog: * jit.dg/test-error-local-used-from-other-function.c: New test case. * jit.dg/test-error-param-reuse.c: New test case. * jit.dg/test-error-param-sharing.c: New test case. * jit.dg/test-error-param-used-from-other-function.c: New test case. * jit.dg/test-error-param-used-without-a-function.c: New test case. From-SVN: r219498 --- gcc/jit/ChangeLog | 93 ++++++ gcc/jit/jit-recording.c | 295 ++++++++++++++++-- gcc/jit/jit-recording.h | 90 +++++- gcc/jit/libgccjit.c | 83 ++++- gcc/testsuite/ChangeLog | 11 + ...est-error-local-used-from-other-function.c | 70 +++++ gcc/testsuite/jit.dg/test-error-param-reuse.c | 48 +++ .../jit.dg/test-error-param-sharing.c | 61 ++++ ...est-error-param-used-from-other-function.c | 74 +++++ ...test-error-param-used-without-a-function.c | 56 ++++ 10 files changed, 840 insertions(+), 41 deletions(-) create mode 100644 gcc/testsuite/jit.dg/test-error-local-used-from-other-function.c create mode 100644 gcc/testsuite/jit.dg/test-error-param-reuse.c create mode 100644 gcc/testsuite/jit.dg/test-error-param-sharing.c create mode 100644 gcc/testsuite/jit.dg/test-error-param-used-from-other-function.c create mode 100644 gcc/testsuite/jit.dg/test-error-param-used-without-a-function.c diff --git a/gcc/jit/ChangeLog b/gcc/jit/ChangeLog index 1ccbdf986e2..dbc1dc692d0 100644 --- a/gcc/jit/ChangeLog +++ b/gcc/jit/ChangeLog @@ -1,3 +1,96 @@ +2015-01-12 David Malcolm + + * jit-recording.c (class gcc::jit::rvalue_usage_validator): New. + (gcc::jit::rvalue_usage_validator::rvalue_usage_validator): New + ctor. + (gcc::jit::rvalue_usage_validator::visit): New function. + (gcc::jit::recording::rvalue::verify_valid_within_stmt): New + function. + (gcc::jit::recording::rvalue::set_scope): New function. + (gcc::jit::recording::function::function): Call set_scope on each + param, issuing errors for any params that already have a function. + (gcc::jit::recording::block::add_eval): Return the new statement; + update the comment given that some error-checking now happens after + this returns. + (gcc::jit::recording::block::add_assignment): Likewise. + (gcc::jit::recording::block::add_assignment_op): Likewise. + (gcc::jit::recording::block::add_comment): Likewise. + (gcc::jit::recording::block::end_with_conditional): Likewise. + (gcc::jit::recording::block::end_with_jump): Likewise. + (gcc::jit::recording::block::end_with_return): Likewise. + (gcc::jit::recording::block::validate): Add a comment. + (gcc::jit::recording::unary_op::visit_children): New function. + (gcc::jit::recording::binary_op::visit_children): New function. + (gcc::jit::recording::comparison::visit_children): New function. + (gcc::jit::recording::cast::visit_children): New function. + (gcc::jit::recording::call::visit_children): New function. + (gcc::jit::recording::call_through_ptr::visit_children): New function. + (gcc::jit::recording::array_access::visit_children): New function. + (gcc::jit::recording::access_field_of_lvalue::visit_children): New + function. + (gcc::jit::recording::access_field_rvalue::visit_children): New + function. + (gcc::jit::recording::dereference_field_rvalue::visit_children): + New function. + (gcc::jit::recording::dereference_rvalue::visit_children): New + function. + (gcc::jit::recording::get_address_of_lvalue::visit_children): New + function. + * jit-recording.h: Within namespace gcc::jit::recording... + (class rvalue_visitor): New. + (rvalue::rvalue): Initialize m_scope. + (rvalue::get_loc): New accessor. + (rvalue::verify_valid_within_stmt): New function. + (rvalue::visit_children): New pure virtual function. + (rvalue::set_scope): New function. + (rvalue::get_scope): New function. + (rvalue::dyn_cast_param): New function. + (rvalue::m_scope): New field. + (param::visit_children): New empty function. + (param::dyn_cast_param): New function. + (function::get_loc): New function. + (block::add_eval): Return the new statement. + (block::add_assignment): Likewise. + (block::add_assignment_op): Likewise. + (block::add_comment): Likewise. + (block::end_with_conditional): Likewise. + (block::end_with_jump): Likewise. + (block::end_with_return): Likewise. + (global::visit_children): New function. + (memento_of_new_rvalue_from_const::visit_children): + New function. + (memento_of_new_string_literal::visit_children): New function. + (unary_op::visit_children): New function. + (binary_op::visit_children): New function. + (comparison::visit_children): New function. + (cast::visit_children): New function. + (call::visit_children): New function. + (call_through_ptr::visit_children): New function. + (array_access::visit_children): New function. + (access_field_of_lvalue::visit_children): New function. + (access_field_rvalue::visit_children): New function. + (dereference_field_rvalue::visit_children): New function. + (dereference_rvalue::visit_children): New function. + (get_address_of_lvalue::visit_children): New function. + (local::local): Call set_scope. + (local::visit_children): New function. + (statement::get_block): Make public. + * libgccjit.c (RETURN_VAL_IF_FAIL_PRINTF5): New macro. + (RETURN_NULL_IF_FAIL_PRINTF5): New macro. + (gcc_jit_context_new_function): Verify that each param has + not yet been used for creating another function. + (gcc_jit_block_add_eval): After creating the stmt, verify + that the rvalue expression tree is valid to use within it. + (gcc_jit_block_add_assignment): Likewise for the lvalue and + rvalue expression trees. + (gcc_jit_block_add_assignment_op): Likewise. + (gcc_jit_block_end_with_conditional): Likewise for the boolval + expression tree. + (gcc_jit_block_end_with_return): Likewise for the rvalue + expression tree. + (gcc_jit_block_end_with_void_return): Remove return of "void", + now that block::end_with_return is now non-void. + 2015-01-12 David Malcolm * jit-playback.c (gcc::jit::playback::context::read_dump_file): diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c index 20fd2d2f82b..ec247e5a70b 100644 --- a/gcc/jit/jit-recording.c +++ b/gcc/jit/jit-recording.c @@ -2034,6 +2034,115 @@ recording::rvalue::dereference (recording::location *loc) return result; } +/* An rvalue visitor, for validating that every rvalue within an expression + trees within "STMT" has the correct scope (e.g. no access to locals + of a different function). */ + +class rvalue_usage_validator : public recording::rvalue_visitor +{ + public: + rvalue_usage_validator (const char *api_funcname, + recording::context *ctxt, + recording::statement *stmt); + + void + visit (recording::rvalue *rvalue); + + private: + const char *m_api_funcname; + recording::context *m_ctxt; + recording::statement *m_stmt; +}; + +/* The trivial constructor for rvalue_usage_validator. */ + +rvalue_usage_validator::rvalue_usage_validator (const char *api_funcname, + recording::context *ctxt, + recording::statement *stmt) + : m_api_funcname (api_funcname), + m_ctxt (ctxt), + m_stmt (stmt) +{ +} + +/* Verify that the given rvalue is in the correct scope. */ + +void +rvalue_usage_validator::visit (recording::rvalue *rvalue) +{ + gcc_assert (m_stmt->get_block ()); + recording::function *stmt_scope = m_stmt->get_block ()->get_function (); + + /* Most rvalues don't have a scope (only locals and params). */ + if (rvalue->get_scope ()) + { + if (rvalue->get_scope () != stmt_scope) + m_ctxt->add_error + (rvalue->get_loc (), + "%s:" + " rvalue %s (type: %s)" + " has scope limited to function %s" + " but was used within function %s" + " (in statement: %s)", + m_api_funcname, + rvalue->get_debug_string (), + rvalue->get_type ()->get_debug_string (), + rvalue->get_scope ()->get_debug_string (), + stmt_scope->get_debug_string (), + m_stmt->get_debug_string ()); + } + else + { + if (rvalue->dyn_cast_param ()) + m_ctxt->add_error + (rvalue->get_loc (), + "%s:" + " param %s (type: %s)" + " was used within function %s" + " (in statement: %s)" + " but is not associated with any function", + m_api_funcname, + rvalue->get_debug_string (), + rvalue->get_type ()->get_debug_string (), + stmt_scope->get_debug_string (), + m_stmt->get_debug_string ()); + } +} + +/* Verify that it's valid to use this rvalue (and all expressions + in the tree below it) within the given statement. + + For example, we must reject attempts to use a local from one + function within a different function here, or we'll get + an ICE deep inside toplev::main. */ + +void +recording::rvalue::verify_valid_within_stmt (const char *api_funcname, statement *s) +{ + rvalue_usage_validator v (api_funcname, + s->get_context (), + s); + + /* Verify that it's OK to use this rvalue within s. */ + v.visit (this); + + /* Traverse the expression tree below "this", verifying all rvalues + within it. */ + visit_children (&v); +} + +/* Set the scope of this rvalue to be the given function. This can only + be done once on a given rvalue. */ + +void +recording::rvalue::set_scope (function *scope) +{ + gcc_assert (scope); + gcc_assert (NULL == m_scope); + m_scope = scope; +} + + /* The implementation of class gcc::jit::recording::lvalue. */ /* Create a recording::new_access_field_of_lvalue instance and add it to @@ -2106,7 +2215,40 @@ recording::function::function (context *ctxt, m_blocks () { for (int i = 0; i< num_params; i++) - m_params.safe_push (params[i]); + { + param *param = params[i]; + gcc_assert (param); + + /* Associate each param with this function. + + Verify that the param doesn't already have a function. */ + if (param->get_scope ()) + { + /* We've already rejected attempts to reuse a param between + different functions (within gcc_jit_context_new_function), so + if the param *does* already have a function, it must be being + reused within the params array for this function. We must + produce an error for this reuse (blocking the compile), since + otherwise we'd have an ICE later on. */ + gcc_assert (this == param->get_scope ()); + ctxt->add_error + (loc, + "gcc_jit_context_new_function:" + " parameter %s (type: %s)" + " is used more than once when creating function %s", + param->get_debug_string (), + param->get_type ()->get_debug_string (), + name->c_str ()); + } + else + { + /* The normal, non-error case: associate this function with the + param. */ + param->set_scope (this); + } + + m_params.safe_push (param); + } } /* Implementation of pure virtual hook recording::memento::replay_into @@ -2366,26 +2508,25 @@ recording::function::make_debug_string () the block's context's list of mementos, and to the block's list of statements. - Implements the post-error-checking part of - gcc_jit_block_add_eval. */ + Implements the heart of gcc_jit_block_add_eval. */ -void +recording::statement * recording::block::add_eval (recording::location *loc, recording::rvalue *rvalue) { statement *result = new eval (this, loc, rvalue); m_ctxt->record (result); m_statements.safe_push (result); + return result; } /* Create a recording::assignment instance and add it to the block's context's list of mementos, and to the block's list of statements. - Implements the post-error-checking part of - gcc_jit_block_add_assignment. */ + Implements the heart of gcc_jit_block_add_assignment. */ -void +recording::statement * recording::block::add_assignment (recording::location *loc, recording::lvalue *lvalue, recording::rvalue *rvalue) @@ -2393,16 +2534,16 @@ recording::block::add_assignment (recording::location *loc, statement *result = new assignment (this, loc, lvalue, rvalue); m_ctxt->record (result); m_statements.safe_push (result); + return result; } /* Create a recording::assignment_op instance and add it to the block's context's list of mementos, and to the block's list of statements. - Implements the post-error-checking part of - gcc_jit_block_add_assignment_op. */ + Implements the heart of gcc_jit_block_add_assignment_op. */ -void +recording::statement * recording::block::add_assignment_op (recording::location *loc, recording::lvalue *lvalue, enum gcc_jit_binary_op op, @@ -2411,32 +2552,32 @@ recording::block::add_assignment_op (recording::location *loc, statement *result = new assignment_op (this, loc, lvalue, op, rvalue); m_ctxt->record (result); m_statements.safe_push (result); + return result; } /* Create a recording::comment instance and add it to the block's context's list of mementos, and to the block's list of statements. - Implements the post-error-checking part of - gcc_jit_block_add_comment. */ + Implements the heart of gcc_jit_block_add_comment. */ -void +recording::statement * recording::block::add_comment (recording::location *loc, const char *text) { statement *result = new comment (this, loc, new_string (text)); m_ctxt->record (result); m_statements.safe_push (result); + return result; } /* Create a recording::end_with_conditional instance and add it to the block's context's list of mementos, and to the block's list of statements. - Implements the post-error-checking part of - gcc_jit_block_end_with_conditional. */ + Implements the heart of gcc_jit_block_end_with_conditional. */ -void +recording::statement * recording::block::end_with_conditional (recording::location *loc, recording::rvalue *boolval, recording::block *on_true, @@ -2446,16 +2587,16 @@ recording::block::end_with_conditional (recording::location *loc, m_ctxt->record (result); m_statements.safe_push (result); m_has_been_terminated = true; + return result; } /* Create a recording::end_with_jump instance and add it to the block's context's list of mementos, and to the block's list of statements. - Implements the post-error-checking part of - gcc_jit_block_end_with_jump. */ + Implements the heart of gcc_jit_block_end_with_jump. */ -void +recording::statement * recording::block::end_with_jump (recording::location *loc, recording::block *target) { @@ -2463,6 +2604,7 @@ recording::block::end_with_jump (recording::location *loc, m_ctxt->record (result); m_statements.safe_push (result); m_has_been_terminated = true; + return result; } /* Create a recording::end_with_return instance and add it to @@ -2473,7 +2615,7 @@ recording::block::end_with_jump (recording::location *loc, gcc_jit_block_end_with_return and gcc_jit_block_end_with_void_return. */ -void +recording::statement * recording::block::end_with_return (recording::location *loc, recording::rvalue *rvalue) { @@ -2484,6 +2626,7 @@ recording::block::end_with_return (recording::location *loc, m_ctxt->record (result); m_statements.safe_push (result); m_has_been_terminated = true; + return result; } /* Override the default implementation of @@ -2513,6 +2656,7 @@ recording::block::write_to_dump (dump &d) bool recording::block::validate () { + /* Check for termination. */ if (!has_been_terminated ()) { statement *stmt = get_last_statement (); @@ -2856,6 +3000,14 @@ recording::unary_op::replay_into (replayer *r) m_a->playback_rvalue ())); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::unary_op. */ +void +recording::unary_op::visit_children (rvalue_visitor *v) +{ + v->visit (m_a); +} + /* Implementation of recording::memento::make_debug_string for unary ops. */ @@ -2890,6 +3042,15 @@ recording::binary_op::replay_into (replayer *r) m_b->playback_rvalue ())); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::binary_op. */ +void +recording::binary_op::visit_children (rvalue_visitor *v) +{ + v->visit (m_a); + v->visit (m_b); +} + /* Implementation of recording::memento::make_debug_string for binary ops. */ @@ -2955,6 +3116,16 @@ recording::comparison::replay_into (replayer *r) m_b->playback_rvalue ())); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::comparison. */ + +void +recording::comparison::visit_children (rvalue_visitor *v) +{ + v->visit (m_a); + v->visit (m_b); +} + /* Implementation of pure virtual hook recording::memento::replay_into for recording::cast. */ @@ -2966,6 +3137,14 @@ recording::cast::replay_into (replayer *r) get_type ()->playback_type ())); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::cast. */ +void +recording::cast::visit_children (rvalue_visitor *v) +{ + v->visit (m_rvalue); +} + /* Implementation of recording::memento::make_debug_string for casts. */ @@ -3011,6 +3190,16 @@ recording::call::replay_into (replayer *r) &playback_args)); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::call. */ + +void +recording::call::visit_children (rvalue_visitor *v) +{ + for (unsigned i = 0; i< m_args.length (); i++) + v->visit (m_args[i]); +} + /* Implementation of recording::memento::make_debug_string for function calls. */ @@ -3088,6 +3277,17 @@ recording::call_through_ptr::replay_into (replayer *r) &playback_args)); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::call_through_ptr. */ + +void +recording::call_through_ptr::visit_children (rvalue_visitor *v) +{ + v->visit (m_fn_ptr); + for (unsigned i = 0; i< m_args.length (); i++) + v->visit (m_args[i]); +} + /* Implementation of recording::memento::make_debug_string for calls through function ptrs. */ @@ -3144,6 +3344,16 @@ recording::array_access::replay_into (replayer *r) m_index->playback_rvalue ())); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::array_access. */ + +void +recording::array_access::visit_children (rvalue_visitor *v) +{ + v->visit (m_ptr); + v->visit (m_index); +} + /* Implementation of recording::memento::make_debug_string for array accesses. */ @@ -3171,6 +3381,15 @@ recording::access_field_of_lvalue::replay_into (replayer *r) } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::access_field_of_lvalue. */ + +void +recording::access_field_of_lvalue::visit_children (rvalue_visitor *v) +{ + v->visit (m_lvalue); +} + /* Implementation of recording::memento::make_debug_string for accessing a field of an lvalue. */ @@ -3197,6 +3416,15 @@ recording::access_field_rvalue::replay_into (replayer *r) m_field->playback_field ())); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::access_field_rvalue. */ + +void +recording::access_field_rvalue::visit_children (rvalue_visitor *v) +{ + v->visit (m_rvalue); +} + /* Implementation of recording::memento::make_debug_string for accessing a field of an rvalue. */ @@ -3224,6 +3452,15 @@ recording::dereference_field_rvalue::replay_into (replayer *r) m_field->playback_field ())); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::dereference_field_rvalue. */ + +void +recording::dereference_field_rvalue::visit_children (rvalue_visitor *v) +{ + v->visit (m_rvalue); +} + /* Implementation of recording::memento::make_debug_string for dereferencing a field of an rvalue. */ @@ -3249,6 +3486,15 @@ recording::dereference_rvalue::replay_into (replayer *r) dereference (playback_location (r, m_loc))); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::dereference_rvalue. */ + +void +recording::dereference_rvalue::visit_children (rvalue_visitor *v) +{ + v->visit (m_rvalue); +} + /* Implementation of recording::memento::make_debug_string for dereferencing an rvalue. */ @@ -3273,6 +3519,15 @@ recording::get_address_of_lvalue::replay_into (replayer *r) get_address (playback_location (r, m_loc))); } +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::get_address_of_lvalue. */ + +void +recording::get_address_of_lvalue::visit_children (rvalue_visitor *v) +{ + v->visit (m_lvalue); +} + /* Implementation of recording::memento::make_debug_string for getting the address of an lvalue. */ diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h index 43e99bae0fd..812205c0a1b 100644 --- a/gcc/jit/jit-recording.h +++ b/gcc/jit/jit-recording.h @@ -800,6 +800,18 @@ private: fields *m_fields; }; +/* An abstract base class for operations that visit all rvalues within an + expression tree. + Currently the only implementation is class rvalue_usage_validator within + jit-recording.c. */ + +class rvalue_visitor +{ + public: + virtual ~rvalue_visitor () {} + virtual void visit (rvalue *rvalue) = 0; +}; + class rvalue : public memento { public: @@ -808,11 +820,14 @@ public: type *type_) : memento (ctxt), m_loc (loc), - m_type (type_) + m_type (type_), + m_scope (NULL) { gcc_assert (type_); } + location * get_loc () const { return m_loc; } + /* Get the recording::type of this rvalue. Implements the post-error-checking part of @@ -835,9 +850,23 @@ public: lvalue * dereference (location *loc); + void + verify_valid_within_stmt (const char *api_funcname, statement *s); + + virtual void visit_children (rvalue_visitor *v) = 0; + + void set_scope (function *scope); + function *get_scope () const { return m_scope; } + + /* Dynamic cast. */ + virtual param *dyn_cast_param () { return NULL; } + protected: location *m_loc; type *m_type; + + private: + function *m_scope; /* NULL for globals, non-NULL for locals/params */ }; class lvalue : public rvalue @@ -881,12 +910,16 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *) {} + playback::param * playback_param () const { return static_cast (m_playback_obj); } + param *dyn_cast_param () { return this; } + private: string * make_debug_string () { return m_name; } @@ -925,6 +958,7 @@ public: block* new_block (const char *name); + location *get_loc () const { return m_loc; } type *get_return_type () const { return m_return_type; } string * get_name () const { return m_name; } const vec &get_params () const { return m_params; } @@ -979,36 +1013,36 @@ public: bool has_been_terminated () { return m_has_been_terminated; } bool is_reachable () { return m_is_reachable; } - void + statement * add_eval (location *loc, rvalue *rvalue); - void + statement * add_assignment (location *loc, lvalue *lvalue, rvalue *rvalue); - void + statement * add_assignment_op (location *loc, lvalue *lvalue, enum gcc_jit_binary_op op, rvalue *rvalue); - void + statement * add_comment (location *loc, const char *text); - void + statement * end_with_conditional (location *loc, rvalue *boolval, block *on_true, block *on_false); - void + statement * end_with_jump (location *loc, block *target); - void + statement * end_with_return (location *loc, rvalue *rvalue); @@ -1063,6 +1097,8 @@ public: void replay_into (replayer *); + void visit_children (rvalue_visitor *) {} + void write_to_dump (dump &d); private: @@ -1086,6 +1122,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *) {} + private: string * make_debug_string (); @@ -1104,6 +1142,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *) {} + private: string * make_debug_string (); @@ -1126,6 +1166,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1149,6 +1191,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1173,6 +1217,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1195,6 +1241,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1213,6 +1261,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1232,6 +1282,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1254,6 +1306,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1276,6 +1330,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1298,6 +1354,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1320,6 +1378,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1339,6 +1399,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1358,6 +1420,8 @@ public: void replay_into (replayer *r); + void visit_children (rvalue_visitor *v); + private: string * make_debug_string (); @@ -1371,10 +1435,15 @@ public: local (function *func, location *loc, type *type_, string *name) : lvalue (func->m_ctxt, loc, type_), m_func (func), - m_name (name) {} + m_name (name) + { + set_scope (func); + } void replay_into (replayer *r); + void visit_children (rvalue_visitor *) {} + void write_to_dump (dump &d); private: @@ -1393,6 +1462,7 @@ public: void write_to_dump (dump &d); + block *get_block () const { return m_block; } location *get_loc () const { return m_loc; } protected: @@ -1401,8 +1471,6 @@ protected: m_block (b), m_loc (loc) {} - block *get_block () const { return m_block; } - playback::location * playback_location (replayer *r) const { diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c index d596d08cd3e..ad8ee752d29 100644 --- a/gcc/jit/libgccjit.c +++ b/gcc/jit/libgccjit.c @@ -172,6 +172,16 @@ struct gcc_jit_param : public gcc::jit::recording::param } \ JIT_END_STMT +#define RETURN_VAL_IF_FAIL_PRINTF5(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4) \ + JIT_BEGIN_STMT \ + if (!(TEST_EXPR)) \ + { \ + jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \ + __func__, (A0), (A1), (A2), (A3), (A4)); \ + return (RETURN_EXPR); \ + } \ + JIT_END_STMT + #define RETURN_VAL_IF_FAIL_PRINTF6(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5) \ JIT_BEGIN_STMT \ if (!(TEST_EXPR)) \ @@ -197,6 +207,9 @@ struct gcc_jit_param : public gcc::jit::recording::param #define RETURN_NULL_IF_FAIL_PRINTF4(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3) \ RETURN_VAL_IF_FAIL_PRINTF4 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3) +#define RETURN_NULL_IF_FAIL_PRINTF5(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4) \ + RETURN_VAL_IF_FAIL_PRINTF5 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4) + #define RETURN_NULL_IF_FAIL_PRINTF6(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5) \ RETURN_VAL_IF_FAIL_PRINTF6 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5) @@ -844,10 +857,23 @@ gcc_jit_context_new_function (gcc_jit_context *ctxt, ctxt, loc, "NULL params creating function %s", name); for (int i = 0; i < num_params; i++) - RETURN_NULL_IF_FAIL_PRINTF2 ( - params[i], - ctxt, loc, - "NULL parameter %i creating function %s", i, name); + { + RETURN_NULL_IF_FAIL_PRINTF2 ( + params[i], + ctxt, loc, + "NULL parameter %i creating function %s", i, name); + RETURN_NULL_IF_FAIL_PRINTF5 ( + (NULL == params[i]->get_scope ()), + ctxt, loc, + "parameter %i \"%s\"" + " (type: %s)" + " for function %s" + " was already used for function %s", + i, params[i]->get_debug_string (), + params[i]->get_type ()->get_debug_string (), + name, + params[i]->get_scope ()->get_debug_string ()); + } return (gcc_jit_function*) ctxt->new_function (loc, kind, return_type, name, @@ -1800,7 +1826,14 @@ gcc_jit_block_add_eval (gcc_jit_block *block, /* LOC can be NULL. */ RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue"); - return block->add_eval (loc, rvalue); + gcc::jit::recording::statement *stmt = block->add_eval (loc, rvalue); + + /* "stmt" should be good enough to be usable in error-messages, + but might still not be compilable; perform some more + error-checking here. We do this here so that the error messages + can contain a stringified version of "stmt", whilst appearing + as close as possible to the point of failure. */ + rvalue->verify_valid_within_stmt (__func__, stmt); } /* Public entrypoint. See description in libgccjit.h. @@ -1832,7 +1865,15 @@ gcc_jit_block_add_assignment (gcc_jit_block *block, rvalue->get_debug_string (), rvalue->get_type ()->get_debug_string ()); - return block->add_assignment (loc, lvalue, rvalue); + gcc::jit::recording::statement *stmt = block->add_assignment (loc, lvalue, rvalue); + + /* "stmt" should be good enough to be usable in error-messages, + but might still not be compilable; perform some more + error-checking here. We do this here so that the error messages + can contain a stringified version of "stmt", whilst appearing + as close as possible to the point of failure. */ + lvalue->verify_valid_within_stmt (__func__, stmt); + rvalue->verify_valid_within_stmt (__func__, stmt); } /* Public entrypoint. See description in libgccjit.h. @@ -1860,7 +1901,15 @@ gcc_jit_block_add_assignment_op (gcc_jit_block *block, op); RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue"); - return block->add_assignment_op (loc, lvalue, op, rvalue); + gcc::jit::recording::statement *stmt = block->add_assignment_op (loc, lvalue, op, rvalue); + + /* "stmt" should be good enough to be usable in error-messages, + but might still not be compilable; perform some more + error-checking here. We do this here so that the error messages + can contain a stringified version of "stmt", whilst appearing + as close as possible to the point of failure. */ + lvalue->verify_valid_within_stmt (__func__, stmt); + rvalue->verify_valid_within_stmt (__func__, stmt); } /* Internal helper function for determining if rvalue BOOLVAL is of @@ -1921,7 +1970,14 @@ gcc_jit_block_end_with_conditional (gcc_jit_block *block, on_false->get_debug_string (), on_false->get_function ()->get_debug_string ()); - return block->end_with_conditional (loc, boolval, on_true, on_false); + gcc::jit::recording::statement *stmt = block->end_with_conditional (loc, boolval, on_true, on_false); + + /* "stmt" should be good enough to be usable in error-messages, + but might still not be compilable; perform some more + error-checking here. We do this here so that the error messages + can contain a stringified version of "stmt", whilst appearing + as close as possible to the point of failure. */ + boolval->verify_valid_within_stmt (__func__, stmt); } /* Public entrypoint. See description in libgccjit.h. @@ -2003,7 +2059,14 @@ gcc_jit_block_end_with_return (gcc_jit_block *block, func->get_debug_string (), func->get_return_type ()->get_debug_string ()); - return block->end_with_return (loc, rvalue); + gcc::jit::recording::statement *stmt = block->end_with_return (loc, rvalue); + + /* "stmt" should be good enough to be usable in error-messages, + but might still not be compilable; perform some more + error-checking here. We do this here so that the error messages + can contain a stringified version of "stmt", whilst appearing + as close as possible to the point of failure. */ + rvalue->verify_valid_within_stmt (__func__, stmt); } /* Public entrypoint. See description in libgccjit.h. @@ -2029,7 +2092,7 @@ gcc_jit_block_end_with_void_return (gcc_jit_block *block, func->get_debug_string (), func->get_return_type ()->get_debug_string ()); - return block->end_with_return (loc, NULL); + block->end_with_return (loc, NULL); } /********************************************************************** diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 03e7acf7957..ba472c521b6 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2015-01-12 David Malcolm + + * jit.dg/test-error-local-used-from-other-function.c: New test + case. + * jit.dg/test-error-param-reuse.c: New test case. + * jit.dg/test-error-param-sharing.c: New test case. + * jit.dg/test-error-param-used-from-other-function.c: New test + case. + * jit.dg/test-error-param-used-without-a-function.c: New test + case. + 2015-01-12 Jakub Jelinek PR tree-optimization/64563 diff --git a/gcc/testsuite/jit.dg/test-error-local-used-from-other-function.c b/gcc/testsuite/jit.dg/test-error-local-used-from-other-function.c new file mode 100644 index 00000000000..fac47c853e8 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-error-local-used-from-other-function.c @@ -0,0 +1,70 @@ +#include +#include + +#include "libgccjit.h" + +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + + void + fn_one () + { + int i; + } + + int + fn_two () + { + return i; + } + + and verify that the API complains about the use of the local + from the other function. */ + gcc_jit_type *void_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + gcc_jit_function *fn_one = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + void_type, + "fn_one", + 0, NULL, + 0); + gcc_jit_lvalue *local = + gcc_jit_function_new_local (fn_one, NULL, int_type, "i"); + + gcc_jit_function *fn_two = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "fn_two", + 0, NULL, + 0); + + gcc_jit_block *block = gcc_jit_function_new_block (fn_two, NULL); + /* "return i;", using local i from the wrong function. */ + gcc_jit_block_end_with_return (block, + NULL, + gcc_jit_lvalue_as_rvalue (local)); +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + CHECK_VALUE (result, NULL); + + /* Verify that the correct error message was emitted. */ + CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt), + ("gcc_jit_block_end_with_return:" + " rvalue i (type: int)" + " has scope limited to function fn_one" + " but was used within function fn_two" + " (in statement: return i;)")); +} + diff --git a/gcc/testsuite/jit.dg/test-error-param-reuse.c b/gcc/testsuite/jit.dg/test-error-param-reuse.c new file mode 100644 index 00000000000..32cb0c015c1 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-error-param-reuse.c @@ -0,0 +1,48 @@ +#include +#include + +#include "libgccjit.h" + +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Verify that we get an error (rather than a crash) + if the client code reuses a gcc_jit_param * within + a function. */ + gcc_jit_type *void_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + /* Create a param. */ + gcc_jit_param *param = + gcc_jit_context_new_param (ctxt, NULL, int_type, "i"); + + /* Try to use it twice when creating "fn". */ + gcc_jit_param *params[2]; + params[0] = param; + params[1] = param; + + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_IMPORTED, + void_type, + "fn", + 2, params, + 0); +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + CHECK_VALUE (result, NULL); + + /* Verify that the correct error message was emitted. */ + CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt), + ("gcc_jit_context_new_function:" + " parameter i (type: int)" + " is used more than once when creating function" + " fn")) +} + diff --git a/gcc/testsuite/jit.dg/test-error-param-sharing.c b/gcc/testsuite/jit.dg/test-error-param-sharing.c new file mode 100644 index 00000000000..036049c5866 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-error-param-sharing.c @@ -0,0 +1,61 @@ +#include +#include + +#include "libgccjit.h" + +#include "harness.h" + +#ifdef __cplusplus +extern "C" { +#endif + + extern void + called_function (void *ptr); + +#ifdef __cplusplus +} +#endif + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Verify that we get an error (rather than a crash) + if the client code reuses a gcc_jit_param * for + two different functions. */ + gcc_jit_type *void_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + /* Create a param. */ + gcc_jit_param *param = + gcc_jit_context_new_param (ctxt, NULL, int_type, "i"); + + /* Try to use it for two different functions. */ + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + void_type, + "fn_one", + 1, ¶m, + 0); + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + void_type, + "fn_two", + 1, ¶m, + 0); +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + CHECK_VALUE (result, NULL); + + /* Verify that the correct error message was emitted. */ + CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt), + ("gcc_jit_context_new_function:" + " parameter 0 \"i\" (type: int)" + " for function fn_two" + " was already used for function fn_one")) +} + diff --git a/gcc/testsuite/jit.dg/test-error-param-used-from-other-function.c b/gcc/testsuite/jit.dg/test-error-param-used-from-other-function.c new file mode 100644 index 00000000000..84e898b1c95 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-error-param-used-from-other-function.c @@ -0,0 +1,74 @@ +#include +#include + +#include "libgccjit.h" + +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + + void + fn_one (int i) + { + } + + int + fn_two () + { + return i * 2; + } + + and verify that the API complains about the use of the param + from the other function. */ + gcc_jit_type *void_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + gcc_jit_param *param = + gcc_jit_context_new_param (ctxt, NULL, int_type, "i"); + + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + void_type, + "fn_one", + 1, ¶m, + 0); + gcc_jit_function *fn_two = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "fn_two", + 0, NULL, + 0); + + gcc_jit_block *block = gcc_jit_function_new_block (fn_two, NULL); + /* "return i * 2;", using param i from the wrong function. */ + gcc_jit_block_end_with_return ( + block, + NULL, + gcc_jit_context_new_binary_op ( + ctxt, NULL, + GCC_JIT_BINARY_OP_MULT, + int_type, + gcc_jit_param_as_rvalue (param), + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 2))); +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + CHECK_VALUE (result, NULL); + + /* Verify that the correct error message was emitted. */ + CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt), + ("gcc_jit_block_end_with_return:" + " rvalue i (type: int)" + " has scope limited to function fn_one" + " but was used within function fn_two" + " (in statement: return i * (int)2;)")); +} + diff --git a/gcc/testsuite/jit.dg/test-error-param-used-without-a-function.c b/gcc/testsuite/jit.dg/test-error-param-used-without-a-function.c new file mode 100644 index 00000000000..f369c6baf30 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-error-param-used-without-a-function.c @@ -0,0 +1,56 @@ +#include +#include + +#include "libgccjit.h" + +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + + int + test_fn () + { + return i; + } + + where "i" is a param that isn't associated with any function, + and verify that the API complains. */ + + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + gcc_jit_param *param = + gcc_jit_context_new_param (ctxt, NULL, int_type, "i"); + + gcc_jit_function *test_fn = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "test_fn", + 0, NULL, + 0); + + gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL); + /* "return i;", using param i from the wrong function. */ + gcc_jit_block_end_with_return (block, + NULL, + gcc_jit_param_as_rvalue (param)); +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + CHECK_VALUE (result, NULL); + + /* Verify that the correct error message was emitted. */ + CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt), + ("gcc_jit_block_end_with_return:" + " param i (type: int)" + " was used within function test_fn" + " (in statement: return i;)" + " but is not associated with any function")) +} + -- 2.30.2