From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 65100 invoked by alias); 27 Sep 2017 23:59:34 -0000 Mailing-List: contact jit-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Post: List-Help: List-Subscribe: Sender: jit-owner@gcc.gnu.org Received: (qmail 64267 invoked by uid 89); 27 Sep 2017 23:59:34 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Checked: by ClamAV 0.99.2 on sourceware.org X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,RP_MATCHES_RCVD,SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=FINAL, H*Ad:U*jit, Within, inject X-Spam-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,RP_MATCHES_RCVD,SPF_HELO_PASS autolearn=ham version=3.3.2 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on sourceware.org X-Spam-Level: X-Spam-User: qpsmtpd, 2 recipients X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 27 Sep 2017 23:59:31 +0000 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EF667C0BC1B9; Wed, 27 Sep 2017 23:59:29 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com EF667C0BC1B9 Authentication-Results: ext-mx07.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx07.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=dmalcolm@redhat.com Received: from c64.redhat.com (ovpn-112-11.phx2.redhat.com [10.3.112.11]) by smtp.corp.redhat.com (Postfix) with ESMTP id A694581896; Wed, 27 Sep 2017 23:59:28 +0000 (UTC) From: David Malcolm To: Bartosz Szreder Cc: jit@gcc.gnu.org, gcc-patches@gcc.gnu.org, David Malcolm Subject: [committed] jit: implement gcc_jit_function_get_address Date: Sun, 01 Jan 2017 00:00:00 -0000 Message-Id: <1506556779-19217-1-git-send-email-dmalcolm@redhat.com> In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.31]); Wed, 27 Sep 2017 23:59:30 +0000 (UTC) X-IsSubscribed: yes X-SW-Source: 2017-q3/txt/msg00020.txt.bz2 On Fri, 2017-09-22 at 11:21 +0200, Bartosz Szreder wrote: > Hello David, > > > > 1. The documentation doesn't mention existence of > > > gcc_jit_context_new_function_ptr_type() as a mechanism of > > > handling > > > function pointers, yet contains > > > gcc_jit_context_new_call_through_ptr(). > > > > [...] > > > > It's just missing documentation. I'm working on fixing it. > > Have a look at gcc/testsuite/jit.dg/test-calling-function-ptr.c > > Thanks, this is very helpful! > > > > - What is the proper way of obtaining a function pointer to be > > > passed > > > to gcc_jit_context_new_call_through_ptr()? There doesn't seem to > > > be > > > any counterpart to gcc_jit_lvalue_get_address() for functions. As > > > the > > > name suggests, gcc_jit_lvalue_get_address() works on an L-value > > > and > > > gcc_jit_function type isn't an ancestor of gcc_jit_lvalue in the > > > internal type system, therefore upcasting is impossible. > > > > [...] > > > > If it's a function that's being compiled as part of the same > > gcc_jit_context, then I think you've identified a weakness in the > > current API. As you say, one fix would be to make gcc_jit_function > > be > > a subclass of gcc_jit_lvalue (and add casting functions); I don't > > yet > > know how easy that would be to implement. > > Yes, this is the exact use case I'm thinking of. > > From an user's point of view, I believe making gcc_jit_function a > subclass of gcc_jit_lvalue makes for an awkward design. Maybe > something like this would be easier to implement without upending the > current hierarchy: > > gcc_jit_rvalue *gcc_jit_context_function_get_address(gcc_jit_context > *ctxt, gcc_jit_function *fun); > > Thanks, > Bartosz Szreder Thanks. I ended up adding the following: extern gcc_jit_rvalue * gcc_jit_function_get_address (gcc_jit_function *fn, gcc_jit_location *loc); FWIW I initially attempted to make functions be a subclass of rvalue internally (within jit-recording.*, but ran into difficulties with function::write_reproducer, which was having to do double-duty: printing the body of the function AND printing the name of the function); doing it all as a new API of functions ended up being simpler. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu; takes jit.sum from 9789 to 9889 PASS results. Committed to trunk as r253244. gcc/jit/ChangeLog: * docs/cp/topics/expressions.rst (Function pointers): New section. * docs/topics/compatibility.rst (LIBGCCJIT_ABI_9): New tag. * docs/topics/expressions.rst (Function pointers): New section. * docs/_build/texinfo/libgccjit.texi: Regenerate. * jit-common.h (class gcc::jit::recording::function_pointer): New forward decl. * jit-playback.c (gcc::jit::playback::function::get_address): New method. * jit-playback.h (gcc::jit::playback::function::get_address): New method decl. * jit-recording.c: Within namespace gcc::jit::recording... (function::function): Initialize new field "m_fn_ptr_type". (function::get_address): New method. (function_pointer::replay_into): New method. (function_pointer::visit_children): New method. (function_pointer::make_debug_string): New method. (function_pointer::write_reproducer): New method. * jit-recording.h: Within namespace gcc::jit::recording... (function::get_address): New method. (function): Add field "m_fn_ptr_type". (class function_pointer): New subclass of rvalue. * libgccjit++.h (gccjit::function::get_address): New method. * libgccjit.c (gcc_jit_function_get_address): New function. * libgccjit.h (LIBGCCJIT_HAVE_gcc_jit_function_get_address): New macro. (gcc_jit_function_get_address): New API entrypoint. * libgccjit.map (LIBGCCJIT_ABI_9): New tag. gcc/testsuite/ChangeLog: * jit.dg/all-non-failing-tests.h: Add test-returning-function-ptr.c. * jit.dg/test-returning-function-ptr.c: New test case. --- gcc/jit/docs/cp/topics/expressions.rst | 9 ++ gcc/jit/docs/topics/compatibility.rst | 7 + gcc/jit/docs/topics/expressions.rst | 17 +++ gcc/jit/jit-common.h | 1 + gcc/jit/jit-playback.c | 14 ++ gcc/jit/jit-playback.h | 3 + gcc/jit/jit-recording.c | 77 ++++++++++- gcc/jit/jit-recording.h | 29 +++++ gcc/jit/libgccjit++.h | 9 ++ gcc/jit/libgccjit.c | 20 +++ gcc/jit/libgccjit.h | 15 +++ gcc/jit/libgccjit.map | 5 + gcc/testsuite/jit.dg/all-non-failing-tests.h | 10 ++ gcc/testsuite/jit.dg/test-returning-function-ptr.c | 143 +++++++++++++++++++++ 14 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/jit.dg/test-returning-function-ptr.c diff --git a/gcc/jit/docs/cp/topics/expressions.rst b/gcc/jit/docs/cp/topics/expressions.rst index 2f317fff..147d065 100644 --- a/gcc/jit/docs/cp/topics/expressions.rst +++ b/gcc/jit/docs/cp/topics/expressions.rst @@ -459,6 +459,15 @@ Function calls /* Add "(void)printf (arg0, arg1);". */ block.add_eval (ctxt.new_call (printf_func, arg0, arg1)); +Function pointers +***************** + +.. function:: gccjit::rvalue \ + gccjit::function::get_address (gccjit::location loc) + + Get the address of a function as an rvalue, of function pointer + type. + Type-coercion ************* diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst index 1d5fbc2..8408939 100644 --- a/gcc/jit/docs/topics/compatibility.rst +++ b/gcc/jit/docs/topics/compatibility.rst @@ -156,3 +156,10 @@ entrypoints: ------------------- ``LIBGCCJIT_ABI_8`` covers the addition of :func:`gcc_jit_type_get_vector` + +.. _LIBGCCJIT_ABI_9: + +``LIBGCCJIT_ABI_9`` +------------------- +``LIBGCCJIT_ABI_9`` covers the addition of +:func:`gcc_jit_function_get_address` diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst index 2534699..f5c2d0f 100644 --- a/gcc/jit/docs/topics/expressions.rst +++ b/gcc/jit/docs/topics/expressions.rst @@ -449,6 +449,23 @@ Function calls #ifdef LIBGCCJIT_HAVE_gcc_jit_rvalue_set_bool_require_tail_call +Function pointers +***************** + +.. function:: gcc_jit_rvalue *\ + gcc_jit_function_get_address (gcc_jit_function *fn,\ + gcc_jit_location *loc) + + Get the address of a function as an rvalue, of function pointer + type. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_9`; you can test + for its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_gcc_jit_function_get_address + Type-coercion ************* diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h index 1fc558c..c931b3f 100644 --- a/gcc/jit/jit-common.h +++ b/gcc/jit/jit-common.h @@ -127,6 +127,7 @@ namespace recording { class global; class param; class base_call; + class function_pointer; class statement; class case_; diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c index 19b6fe2..5798179 100644 --- a/gcc/jit/jit-playback.c +++ b/gcc/jit/jit-playback.c @@ -1356,6 +1356,20 @@ new_block (const char *name) return result; } +/* Construct a playback::rvalue instance wrapping an ADDR_EXPR for + this playback::function. */ + +playback::rvalue * +playback::function::get_address (location *loc) +{ + tree t_fndecl = as_fndecl (); + tree t_fntype = TREE_TYPE (t_fndecl); + tree t_fnptr = build1 (ADDR_EXPR, build_pointer_type (t_fntype), t_fndecl); + if (loc) + m_ctxt->set_tree_location (t_fnptr, loc); + return new rvalue (m_ctxt, t_fnptr); +} + /* Build a statement list for the function as a whole out of the lists of statements for the individual blocks, building labels for each block. */ diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h index 7dc7315..746f5da 100644 --- a/gcc/jit/jit-playback.h +++ b/gcc/jit/jit-playback.h @@ -443,6 +443,9 @@ public: block* new_block (const char *name); + rvalue * + get_address (location *loc); + void build_stmt_list (); diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c index 0e7f46e0..8481280 100644 --- a/gcc/jit/jit-recording.c +++ b/gcc/jit/jit-recording.c @@ -3452,7 +3452,8 @@ recording::function::function (context *ctxt, m_is_variadic (is_variadic), m_builtin_id (builtin_id), m_locals (), - m_blocks () + m_blocks (), + m_fn_ptr_type (NULL) { for (int i = 0; i< num_params; i++) { @@ -3725,6 +3726,35 @@ recording::function::dump_to_dot (const char *path) fclose (fp); } +/* Implements the post-error-checking part of + gcc_jit_function_get_address. */ + +recording::rvalue * +recording::function::get_address (recording::location *loc) +{ + /* Lazily create and cache the function pointer type. */ + if (!m_fn_ptr_type) + { + /* Make a recording::function_type for this function. */ + auto_vec param_types (m_params.length ()); + unsigned i; + recording::param *param; + FOR_EACH_VEC_ELT (m_params, i, param) + param_types.safe_push (param->get_type ()); + recording::function_type *fn_type + = m_ctxt->new_function_type (m_return_type, + m_params.length (), + param_types.address (), + m_is_variadic); + m_fn_ptr_type = fn_type->get_pointer (); + } + gcc_assert (m_fn_ptr_type); + + rvalue *result = new function_pointer (get_context (), loc, this, m_fn_ptr_type); + m_ctxt->record (result); + return result; +} + /* Implementation of recording::memento::make_debug_string for functions. */ @@ -5400,6 +5430,51 @@ recording::get_address_of_lvalue::write_reproducer (reproducer &r) r.get_identifier (m_loc)); } +/* The implementation of class gcc::jit::recording::function_pointer. */ + +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::function_pointer. */ + +void +recording::function_pointer::replay_into (replayer *r) +{ + set_playback_obj ( + m_fn->playback_function ()-> + get_address (playback_location (r, m_loc))); +} + +void +recording::function_pointer::visit_children (rvalue_visitor *) +{ + /* Empty. */ +} + +/* Implementation of recording::memento::make_debug_string for + getting the address of an lvalue. */ + +recording::string * +recording::function_pointer::make_debug_string () +{ + return string::from_printf (m_ctxt, + "%s", + m_fn->get_debug_string ()); +} + +/* Implementation of recording::memento::write_reproducer for + function_pointer. */ + +void +recording::function_pointer::write_reproducer (reproducer &r) +{ + const char *id = r.make_identifier (this, "address_of"); + r.write (" gcc_jit_rvalue *%s =\n" + " gcc_jit_function_get_address (%s, /* gcc_jit_function *fn */\n" + " %s); /* gcc_jit_location *loc */\n", + id, + r.get_identifier (m_fn), + r.get_identifier (m_loc)); +} + /* The implementation of class gcc::jit::recording::local. */ /* Implementation of pure virtual hook recording::memento::replay_into diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h index 248765d..8918124 100644 --- a/gcc/jit/jit-recording.h +++ b/gcc/jit/jit-recording.h @@ -1145,6 +1145,8 @@ public: void dump_to_dot (const char *path); + rvalue *get_address (location *loc); + private: string * make_debug_string () FINAL OVERRIDE; void write_reproducer (reproducer &r) FINAL OVERRIDE; @@ -1159,6 +1161,7 @@ private: enum built_in_function m_builtin_id; auto_vec m_locals; auto_vec m_blocks; + type *m_fn_ptr_type; }; class block : public memento @@ -1699,6 +1702,32 @@ private: lvalue *m_lvalue; }; +class function_pointer : public rvalue +{ +public: + function_pointer (context *ctxt, + location *loc, + function *fn, + type *type) + : rvalue (ctxt, loc, type), + m_fn (fn) {} + + void replay_into (replayer *r) FINAL OVERRIDE; + + void visit_children (rvalue_visitor *v) FINAL OVERRIDE; + +private: + string * make_debug_string () FINAL OVERRIDE; + void write_reproducer (reproducer &r) FINAL OVERRIDE; + enum precedence get_precedence () const FINAL OVERRIDE + { + return PRECEDENCE_UNARY; + } + +private: + function *m_fn; +}; + class local : public lvalue { public: diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h index a83ccf6..82997c3 100644 --- a/gcc/jit/libgccjit++.h +++ b/gcc/jit/libgccjit++.h @@ -368,6 +368,8 @@ namespace gccjit const std::string &name, location loc = location ()); + rvalue get_address (location loc = location ()); + /* A series of overloaded operator () with various numbers of arguments for a very terse way of creating a call to this function. The call is created within the same context as the function itself, which may @@ -1392,6 +1394,13 @@ function::new_local (type type_, name.c_str ())); } +inline rvalue +function::get_address (location loc) +{ + return rvalue (gcc_jit_function_get_address (get_inner_function (), + loc.get_inner_location ())); +} + inline function block::get_function () const { diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c index 6e352c6..37cb695 100644 --- a/gcc/jit/libgccjit.c +++ b/gcc/jit/libgccjit.c @@ -3022,3 +3022,23 @@ gcc_jit_type_get_vector (gcc_jit_type *type, size_t num_units) return (gcc_jit_type *)type->get_vector (num_units); } + +/* Public entrypoint. See description in libgccjit.h. + + After error-checking, the real work is done by the + gcc::jit::recording::function::get_address method, in + jit-recording.c. */ + +gcc_jit_rvalue * +gcc_jit_function_get_address (gcc_jit_function *fn, + gcc_jit_location *loc) +{ + RETURN_NULL_IF_FAIL (fn, NULL, NULL, "NULL function"); + + gcc::jit::recording::context *ctxt = fn->m_ctxt; + + JIT_LOG_FUNC (ctxt->get_logger ()); + /* LOC can be NULL. */ + + return (gcc_jit_rvalue *)fn->get_address (loc); +} diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h index b863b07..18c03fb 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -1418,6 +1418,21 @@ gcc_jit_type_get_aligned (gcc_jit_type *type, extern gcc_jit_type * gcc_jit_type_get_vector (gcc_jit_type *type, size_t num_units); + +#define LIBGCCJIT_HAVE_gcc_jit_function_get_address + +/* Get the address of a function as an rvalue, of function pointer + type. + + This API entrypoint was added in LIBGCCJIT_ABI_9; you can test for its + presence using + #ifdef LIBGCCJIT_HAVE_gcc_jit_function_get_address +*/ +extern gcc_jit_rvalue * +gcc_jit_function_get_address (gcc_jit_function *fn, + gcc_jit_location *loc); + + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map index 08760e3..616e364 100644 --- a/gcc/jit/libgccjit.map +++ b/gcc/jit/libgccjit.map @@ -160,3 +160,8 @@ LIBGCCJIT_ABI_8 { global: gcc_jit_type_get_vector; } LIBGCCJIT_ABI_7; + +LIBGCCJIT_ABI_9 { + global: + gcc_jit_function_get_address; +} LIBGCCJIT_ABI_8; diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h index acfcc40..bf02e12 100644 --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h @@ -71,6 +71,13 @@ #undef create_code #undef verify_code +/* test-returning-function-ptr.c */ +#define create_code create_code_calling_internal_function +#define verify_code verify_code_calling_internal_function +#include "test-returning-function-ptr.c" +#undef create_code +#undef verify_code + /* test-compound-assignment.c */ #define create_code create_code_compound_assignment #define verify_code verify_code_compound_assignment @@ -283,6 +290,9 @@ const struct testcase testcases[] = { {"calling_function_ptr", create_code_calling_function_ptr, verify_code_calling_function_ptr}, + {"calling_internal_function", + create_code_calling_internal_function, + verify_code_calling_internal_function}, {"compound_assignment", create_code_compound_assignment, verify_code_compound_assignment}, diff --git a/gcc/testsuite/jit.dg/test-returning-function-ptr.c b/gcc/testsuite/jit.dg/test-returning-function-ptr.c new file mode 100644 index 0000000..2d4f01e --- /dev/null +++ b/gcc/testsuite/jit.dg/test-returning-function-ptr.c @@ -0,0 +1,143 @@ +#include +#include + +#include "libgccjit.h" + +#include "harness.h" + +#ifdef __cplusplus +extern "C" { +#endif + + extern void + internally_called_function (int i, int j, int k); + +#ifdef __cplusplus +} +#endif + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + extern void internally_called_function (int i, int j, int k); + + static void + internal_test_caller (int a) + { + internally_called_function (a * 3, a * 4, a * 5); + } + + void (*) (int) + get_test_caller (void) + { + return internal_test_caller; + } + */ + int i; + 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); + + /* Declare the imported function. */ + gcc_jit_param *params[3]; + params[0] = + gcc_jit_context_new_param (ctxt, NULL, int_type, "i"); + params[1] = + gcc_jit_context_new_param (ctxt, NULL, int_type, "j"); + params[2] = + gcc_jit_context_new_param (ctxt, NULL, int_type, "k"); + gcc_jit_function *called_fn = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_IMPORTED, + void_type, + "internally_called_function", + 3, params, + 0); + + /* Build the test_caller fn. */ + gcc_jit_param *param_a = + gcc_jit_context_new_param (ctxt, NULL, int_type, "a"); + gcc_jit_function *test_caller = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + void_type, + "internal_test_caller", + 1, ¶m_a, + 0); + /* "a * 3, a * 4, a * 5" */ + gcc_jit_rvalue *args[3]; + for (i = 0; i < 3; i++) + args[i] + = gcc_jit_context_new_binary_op + (ctxt, NULL, + GCC_JIT_BINARY_OP_MULT, + int_type, + gcc_jit_param_as_rvalue (param_a), + gcc_jit_context_new_rvalue_from_int (ctxt, + int_type, + (i + 3) )); + gcc_jit_block *block = gcc_jit_function_new_block (test_caller, NULL); + gcc_jit_block_add_eval ( + block, NULL, + gcc_jit_context_new_call (ctxt, + NULL, + called_fn, + 3, args)); + gcc_jit_block_end_with_void_return (block, NULL); + + gcc_jit_rvalue *fn_ptr + = gcc_jit_function_get_address (test_caller, NULL); + + gcc_jit_type *fn_ptr_type + = gcc_jit_rvalue_get_type (fn_ptr); + + /* Build the get_test_caller fn. */ + gcc_jit_function *get_test_caller = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + fn_ptr_type, + "get_test_caller", + 0, NULL, + 0); + block = gcc_jit_function_new_block (get_test_caller, NULL); + gcc_jit_block_end_with_return (block, NULL, fn_ptr); +} + +static int called_with[3]; + +extern void +internally_called_function (int i, int j, int k) +{ + called_with[0] = i; + called_with[1] = j; + called_with[2] = k; +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + typedef void (*test_caller_type) (int); + typedef test_caller_type (*get_test_caller_type) (void); + CHECK_NON_NULL (result); + + get_test_caller_type get_test_caller = + (get_test_caller_type)gcc_jit_result_get_code (result, "get_test_caller"); + CHECK_NON_NULL (get_test_caller); + + test_caller_type test_caller = (test_caller_type)get_test_caller (); + CHECK_NON_NULL (test_caller); + + called_with[0] = 0; + called_with[1] = 0; + called_with[2] = 0; + + /* Call the JIT-generated function. */ + test_caller (5); + + /* Verify that it correctly called "internally_called_function". */ + CHECK_VALUE (called_with[0], 15); + CHECK_VALUE (called_with[1], 20); + CHECK_VALUE (called_with[2], 25); +} -- 1.8.5.3