Mercurial > octave
view libinterp/corefcn/compile.cc @ 32396:fc3f9660d5f2
doc: Remove "Internal function" from VM function descriptions
author | Arun Giridhar <arungiridhar@gmail.com> |
---|---|
date | Sun, 08 Oct 2023 15:17:47 -0400 |
parents | d738b7d335b5 |
children | 9281146e836b |
line wrap: on
line source
//////////////////////////////////////////////////////////////////////// // // Copyright (C) 2023 The Octave Project Developers // // See the file COPYRIGHT.md in the top-level directory of this // distribution or <https://octave.org/copyright/>. // // This file is part of Octave. // // Octave is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Octave is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Octave; see the file COPYING. If not, see // <https://www.gnu.org/licenses/>. // //////////////////////////////////////////////////////////////////////// #if defined (HAVE_CONFIG_H) # include "config.h" #endif #include "ovl.h" #include "ov.h" #include "defun.h" #include "variables.h" #include "interpreter.h" #include "pt-bytecode-vm.h" #include "pt-bytecode-walk.h" OCTAVE_BEGIN_NAMESPACE(octave) // If TRUE, use VM evaluator rather than tree walker. // FIXME: Use OCTAVE_ENABLE_VM_EVALUATOR define to set it to true when // the VM has been tested properly. bool Vvm_enable = false; // Cleverly hidden in pt-bytecode-vm.cc to prevent inlining here extern "C" void dummy_mark_1 (void); extern "C" void dummy_mark_2 (void); DEFUN (__dummy_mark_1__, , , doc: /* -*- texinfo -*- @deftypefn {} {} __dummy_mark_1__ () Dummy function that calls the c-function void dummy_mark_1 (void) that does nothing. Usefull for e.g. marking start and end for Callgrind analyzis or as an entry point for gdb. @end deftypefn */) { dummy_mark_1 (); return {}; } DEFUN (__dummy_mark_2__, , , doc: /* -*- texinfo -*- @deftypefn {} {} __dummy_mark_2__ () Dummy function that calls the c-function void dummy_mark_2 (void) that does nothing. Usefull for e.g. marking start and end for Callgrind analyzis or as an entry point for gdb. @end deftypefn */) { dummy_mark_2 (); return {}; } DEFUN (vm_clear_cache, , , doc: /* -*- texinfo -*- @deftypefn {} {@var{val} =} vm_clear_cache () Clear the VM evaluator's internal cache. @end deftypefn */) { octave::load_path::signal_clear_fcn_cache (); return octave_value {true}; } DEFUN (vm_print_trace, , , doc: /* -*- texinfo -*- @deftypefn {} {@var{prints_trace} =} vm_print_trace ()) Print a debug trace from the VM. Toggles on or off each call. There has to be a breakpoint set in some file for the trace to actually print anything. Returns true if a trace will be printed from now on, false otherwise. @end deftypefn */) { vm::m_trace_enabled = !vm::m_trace_enabled; return octave_value {vm::m_trace_enabled}; } DEFUN (__ref_count__, args, , doc: /* -*- texinfo -*- @deftypefn {} {@var{count} =} __ref_count__ (@var{obj})) Returns reference count for an object. @end deftypefn */) { int nargin = args.length (); if (nargin != 1) print_usage (); octave_value ov = args (0); return octave_value {ov.get_count ()}; } DEFMETHOD (vm_is_executing, interp, , , doc: /* -*- texinfo -*- @deftypefn {} {@var{is_executing} =} vm_is_executing ()) Returns true if the VM is executing the function calling vm_is_executing (). False otherwise. @end deftypefn */) { auto frame = interp.get_evaluator ().get_current_stack_frame (); if (!frame) error ("Invalid current frame"); auto caller_frame = frame->static_link (); if (!caller_frame) error ("Invalid caller frame"); bool bytecode_running = caller_frame->is_bytecode_fcn_frame (); return octave_value {bytecode_running}; } DEFMETHOD (vm_profile, interp, args, , doc: /* -*- texinfo -*- @deftypefn {} {} vm_profile on @deftypefnx {} {} vm_profile off @deftypefnx {} {} vm_profile resume @deftypefnx {} {} vm_profile clear @deftypefnx {} {@var{T} =} vm_profile ("info") @deftypefnx {} {} vm_profile Profile code running in the VM. @table @code @item profile on Start the profiler, clearing all previously collected data if there is any. @item profile off Stop profiling. The collected data can later be retrieved and examined with @code{T = profile ("info")}. @item profile clear Clear all collected profiler data. @item profile resume Restart profiling without clearing the old data. All newly collected statistics are added to the existing ones. @item profile Toggles between profiling and printing the result of the profiler. Clears the profiler on each print. @item info Prints the profiler data. Not that output to a variable is not implemented yet. @end table @end deftypefn */) { int nargin = args.length (); auto &evaler = interp.get_evaluator (); std::string arg0; if (nargin >= 1) arg0 = args (0).string_value (); if (!arg0.size ()) { if (!vm::m_vm_profiler) { vm::m_vm_profiler = std::make_shared<vm_profiler> (); vm::m_profiler_enabled = true; evaler.vm_set_profiler_active (true); } else { evaler.vm_set_profiler_active (false); vm::m_profiler_enabled = false; auto p = vm::m_vm_profiler; vm::m_vm_profiler = nullptr; auto cpy = *p; cpy.print_to_stdout (); } } else if (arg0 == "on") { vm::m_profiler_enabled = false; vm::m_vm_profiler = std::make_shared<vm_profiler> (); vm::m_profiler_enabled = true; evaler.vm_set_profiler_active (true); } else if (arg0 == "resume") { if (!vm::m_vm_profiler) vm::m_vm_profiler = std::make_shared<vm_profiler> (); vm::m_profiler_enabled = true; evaler.vm_set_profiler_active (true); } else if (arg0 == "off") { evaler.vm_set_profiler_active (false); vm::m_profiler_enabled = false; } else if (arg0 == "clear") { evaler.vm_set_profiler_active (false); vm::m_profiler_enabled = false; vm::m_vm_profiler = nullptr; } else if (arg0 == "info") { auto p_vm_profiler = vm::m_vm_profiler; if (p_vm_profiler) { auto cpy = *p_vm_profiler; cpy.print_to_stdout (); } else warning ("Nothing recorded."); } else print_usage (); return octave_value {true}; } DEFMETHOD (vm_print_bytecode, interp, args, , doc: /* -*- texinfo -*- @deftypefn {} {@var{success} =} vm_print_bytecode (@var{fn_name})) @deftypefnx {} {@var{success} =} vm_print_bytecode (@var{fn_handle})) Prints the bytecode of a function name or function handle, if any. @end deftypefn */) { int nargin = args.length (); if (nargin != 1) print_usage (); octave_value ov; if (args (0).is_string ()) { std::string fn_name = args(0).string_value (); symbol_table& symtab = interp.get_symbol_table (); ov = symtab.find_function (fn_name); if (!ov.is_defined ()) { error ("Function not defined: %s", fn_name.c_str ()); } } else ov = args (0); octave_user_code *ufn = nullptr; octave_fcn_handle *h = nullptr; if (ov.is_function_handle ()) { h = ov.fcn_handle_value (); if (!h) error ("Invalid function handle"); ufn = h->user_function_value (); } else ufn = ov.user_code_value (); std::string fn_name = ufn->name (); if (!ufn || (!ufn->is_user_function () && !ufn->is_user_script ())) { error ("Function not a user function or script: %s", fn_name.c_str ()); } // Nested functions need to be compiled via their parent bool is_nested = ufn->is_nested_function (); bool try_compile = !ufn->is_compiled () && Vvm_enable && !is_nested; if (try_compile && h && h->is_anonymous ()) h->compile (); else if (try_compile) vm::maybe_compile_or_compiled (ufn, 0); else if (!ufn->is_compiled ()) error ("Function not compiled: %s", fn_name.c_str ()); if (!ufn->is_compiled ()) error ("Function can't be compiled: %s", fn_name.c_str ()); auto bc = ufn->get_bytecode (); print_bytecode (bc); return octave_value {true}; } DEFMETHOD (vm_is_compiled, interp, args, , doc: /* -*- texinfo -*- @deftypefn {} {@var{is_compiled} =} vm_is_compiled (@var{fn_name}) @deftypefnx {} {@var{is_compiled} =} vm_is_compiled (@var{fn_handle}) Returns true if the specified function name or function handle is compiled. False otherwise. @end deftypefn */) { int nargin = args.length (); if (nargin != 1) print_usage (); std::string fcn_to_compile; octave_fcn_handle *handle_to_compile = nullptr; bool do_handle = false; if (args (0).is_string ()) fcn_to_compile = args (0).string_value (); else if (args (0).is_function_handle ()) { handle_to_compile = args (0).fcn_handle_value (); do_handle = true; } else error ("First argument need to be a function name or function handle."); try { if (do_handle) { octave_user_function *ufn = handle_to_compile->user_function_value (); if (!ufn) return octave_value {false}; return octave_value {ufn->is_compiled ()}; } else { std::string name = fcn_to_compile; symbol_table& symtab = interp.get_symbol_table (); octave_value ov = symtab.find_function (name); if (!ov.is_defined ()) return octave_value {false}; octave_user_code *ufn = ov.user_code_value (); if (!ufn) return octave_value {false}; return octave_value {ufn->is_compiled ()}; } } catch (execution_exception &) { return octave_value {false}; } } DEFMETHOD (vm_compile, interp, args, , doc: /* -*- texinfo -*- @deftypefn {} {@var{success} =} vm_compile (@var{fn_name}) @deftypefnx {} {@var{success} =} vm_compile (@var{fn_name}, "clear") @deftypefnx {} {@var{success} =} vm_compile (@var{fn_name}, "print") Compile the specified function to bytecode. The compiled function and its subfunctions will be executed by the VM when called. Returns true on success, otherwise false. Don't recompile or clear the bytecode of a running function with vm_compile. The @qcode{"print"} option prints the bytecode after compilation. The @qcode{"clear"} option removes the bytecode from the function instead. @end deftypefn */) { int nargin = args.length (); if (! nargin) print_usage (); std::string fcn_to_compile; octave_fcn_handle *handle_to_compile = nullptr; bool do_clear = false; bool do_print = false; if (args (0).is_string ()) fcn_to_compile = args (0).string_value (); else if (args (0).is_function_handle ()) handle_to_compile = args (0).fcn_handle_value (); else error ("First argument need to be a function name or function handle."); for (int i = 1; i < nargin; i++) { auto arg = args(i); if (! arg.is_string()) error ("Non string argument"); std::string arg_s = arg.string_value (); if (arg_s == "clear") do_clear = true; if (arg_s == "print") do_print = true; } if (do_clear && handle_to_compile) { octave_user_function *ufn = handle_to_compile->user_function_value (); if (!ufn) error ("Invalid function handle"); ufn->clear_bytecode (); return octave_value {true}; } else if (do_clear) { std::string name = fcn_to_compile; symbol_table& symtab = interp.get_symbol_table (); octave_value ov = symtab.find_function (name); if (!ov.is_defined ()) { error ("Function not defined: %s", name.c_str ()); } octave_user_code *ufn = ov.user_code_value (); if (!ufn || (!ufn->is_user_function () && !ufn->is_user_script ())) { error ("Function not an user function or script: %s", name.c_str ()); } ufn->clear_bytecode (); return octave_value {true}; } if (handle_to_compile) { octave_user_function *ufn = handle_to_compile->user_function_value (); if (!ufn) error ("Invalid function handle"); if (ufn->is_nested_function ()) error ("Nested functions need to be compiled via their parent"); // Anonymous functions need to be compiled via their handle // to get the locals. if (handle_to_compile->is_anonymous ()) { handle_to_compile->compile (); if (do_print && ufn->is_compiled ()) { auto bc = ufn->get_bytecode (); print_bytecode (bc); } return octave_value {true}; } else { // Throws on errors compile_user_function (*ufn, do_print); return octave_value {true}; } } else { std::string name = fcn_to_compile; symbol_table& symtab = interp.get_symbol_table (); octave_value ov = symtab.find_function (name); if (!ov.is_defined ()) { error ("Function not defined: %s", name.c_str ()); } if (!ov.is_user_function () && !ov.is_user_script ()) { error ("Function is not a user function or script: %s", name.c_str ()); } octave_user_code *ufn = ov.user_code_value (); if (!ufn || (!ufn->is_user_function () && !ufn->is_user_script ())) { error ("Function is not really user function or script: %s", name.c_str ()); } if (ufn->is_nested_function ()) error ("Nested functions need to be compiled via their parent"); // Throws on errors compile_user_function (*ufn, do_print); } return octave_value {true}; } DEFUN (vm_enable, args, nargout, doc: /* -*- texinfo -*- @deftypefn {} {@var{val} =} vm_enable () @deftypefnx {} {@var{old_val} =} vm_enable (@var{new_val}) @deftypefnx {} {@var{old_val} =} vm_enable (@var{new_val}, "local") Query or set whether Octave automatically compiles functions to bytecode and executes them in a virtual machine (VM). Note that the virtual machine feature is experimental. The default value is currently false, while the VM is still experimental. Users need to explicitly call @code{vm_enable (1)} to enable it. In future, this will be set to the value of the OCTAVE_ENABLE_VM_EVALUATOR flag that was set when building Octave. When false, Octave uses a traditional tree walker to evaluate statements parsed from m-code. When true, Octave translates parsed statements to an intermediate representation that is then evaluated by a virtual machine. When called from inside a function with the @qcode{"local"} option, the setting is changed locally for the function and any subroutines it calls. The original setting is restored when exiting the function. Once compiled to bytecode, the function will always be evaluated by the VM no matter the state of @qcode{"vm_enable"}, until the bytecode is cleared, by e.g. @qcode{"clear all"} or an modification to the function's m-file. @seealso{vm_compile} @end deftypefn */) { return set_internal_variable (Vvm_enable, args, nargout, "vm_enable"); } OCTAVE_END_NAMESPACE(octave)