mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-06 07:49:02 +00:00
2160 lines
79 KiB
Mathematica
2160 lines
79 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2001-2012 The University of Melbourne.
|
|
% Copyright (C) 2014-2019, 2021-2023, 2025 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: program_representation.m
|
|
% Authors: zs, dougl
|
|
%
|
|
% This module defines the representation of procedure bodies used by the
|
|
% declarative debugger and the deep profiler.
|
|
%
|
|
% One of the things we want the declarative debugger to be able to do
|
|
% is to let the user specify which part of which output argument of an
|
|
% incorrect or inadmissible atom is suspicious, and then find out where
|
|
% that particular subterm came from, i.e. where it was bound. Doing this
|
|
% requires knowing what the bodies of that procedure and its descendants are.
|
|
%
|
|
% If the Mercury compiler is invoked with the right options, it will include
|
|
% in each procedure layout a pointer to a simplified representation of the goal
|
|
% that is the body of the corresponding procedure. We use a simplified
|
|
% representation partly because we want to insulate the code using procedure
|
|
% representations from irrelevant changes in HLDS types, and partly because
|
|
% we want to minimize the space taken in up in executables by these
|
|
% representations.
|
|
%
|
|
% The current representation is intended to contain all the information
|
|
% we are pretty sure can be usefully exploited by the declarative debugger
|
|
% and/or the deep profiler.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module mdbcomp.program_representation.
|
|
:- interface.
|
|
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.rtti_access.
|
|
:- import_module mdbcomp.sym_name.
|
|
|
|
:- import_module bool.
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module type_desc.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The representation of programs, as recorded by the compiler for use
|
|
% by tools such as the declarative debugger and the deep profiler.
|
|
%
|
|
|
|
:- type prog_rep(GoalAnnotation)
|
|
---> prog_rep(
|
|
module_map(GoalAnnotation)
|
|
).
|
|
|
|
:- type prog_rep == prog_rep(unit).
|
|
|
|
% A map of module names to module representations.
|
|
%
|
|
:- type module_map(GoalAnnotation) ==
|
|
map(string, module_rep(GoalAnnotation)).
|
|
:- type module_map == module_map(unit).
|
|
|
|
:- type module_rep(GoalAnnotation)
|
|
---> module_rep(
|
|
mr_name :: string, % The module name.
|
|
mr_string_table :: string_table,
|
|
mr_oisu_types :: list(oisu_type_procs),
|
|
mr_type_rep_table :: map(int, type_rep),
|
|
mr_procs :: proc_map(GoalAnnotation)
|
|
).
|
|
|
|
:- type module_rep == module_rep(unit).
|
|
|
|
:- type oisu_type_procs
|
|
---> oisu_type_procs(
|
|
otp_type_ctor :: string, % name of type_ctor; arity 0
|
|
otp_creators :: list(string_proc_label),
|
|
otp_mutators :: list(string_proc_label),
|
|
otp_destructors :: list(string_proc_label)
|
|
).
|
|
|
|
:- type type_rep
|
|
---> defined_type_rep(sym_name, list(type_rep))
|
|
; builtin_type_rep(builtin_type_rep)
|
|
; tuple_type_rep(list(type_rep))
|
|
; higher_order_type_rep(list(type_rep), maybe(type_rep))
|
|
; type_var_rep(int).
|
|
|
|
:- type encoded_type_table == map(int, type_rep).
|
|
|
|
:- type builtin_type_rep
|
|
---> builtin_type_int_rep
|
|
; builtin_type_uint_rep
|
|
; builtin_type_int8_rep
|
|
; builtin_type_uint8_rep
|
|
; builtin_type_int16_rep
|
|
; builtin_type_uint16_rep
|
|
; builtin_type_int32_rep
|
|
; builtin_type_uint32_rep
|
|
; builtin_type_int64_rep
|
|
; builtin_type_uint64_rep
|
|
; builtin_type_float_rep
|
|
; builtin_type_string_rep
|
|
; builtin_type_char_rep.
|
|
|
|
% A map of proc names to proc_reps.
|
|
%
|
|
:- type proc_map(GoalAnnotation) ==
|
|
map(string_proc_label, proc_rep(GoalAnnotation)).
|
|
:- type proc_map == proc_map(unit).
|
|
|
|
:- type proc_rep(GoalAnnotation)
|
|
---> proc_rep(
|
|
pr_id :: string_proc_label,
|
|
pr_defn :: proc_defn_rep(GoalAnnotation)
|
|
).
|
|
|
|
:- type proc_rep == proc_rep(unit).
|
|
|
|
% A string_proc_label is a data structure that uniquely identifies a
|
|
% procedure. It is a version of the proc_label type from prim_data.m
|
|
% that can be used outside the compiler, e.g. in RTTI data structures
|
|
% and in data files generated by deep profiling.
|
|
%
|
|
% When procedures are imported from one module to another, for example for
|
|
% inter-module optimisations, the def_module field may be different to the
|
|
% decl_module field. If this is the case, then the procedure has been
|
|
% imported into the def_module from the decl_module. This is also true
|
|
% for the type_module and def_module fields in the str_special_proc_label
|
|
% constructor.
|
|
%
|
|
:- type string_proc_label
|
|
---> str_ordinary_proc_label(
|
|
s_ord_pred_or_func :: pred_or_func,
|
|
s_ord_decl_module :: string,
|
|
s_ord_def_module :: string,
|
|
s_ord_name :: string,
|
|
s_ord_arity :: int,
|
|
s_ord_mode :: int
|
|
)
|
|
; str_special_proc_label(
|
|
s_spec_type_name :: string,
|
|
s_spec_type_module :: string,
|
|
s_spec_def_module :: string,
|
|
s_spec_pred_name :: string,
|
|
s_spec_arity :: int,
|
|
s_spec_mode :: int
|
|
).
|
|
|
|
:- type proclabel_kind_token
|
|
---> proclabel_user_predicate
|
|
; proclabel_user_function
|
|
; proclabel_special.
|
|
|
|
:- pred is_proclabel_kind(int::in, proclabel_kind_token::out) is semidet.
|
|
|
|
% A representation of the procedure definitions (clause heads and bodies)
|
|
% that we execute. These are generated by the compiler, which stores them
|
|
% in the form of a bytecode representation in a field of the proc_layout
|
|
% structures in the executable.
|
|
%
|
|
% Each element of this structure will correspond one-to-one
|
|
% to an element of the original HLDS at the code generation stage.
|
|
|
|
:- type proc_defn_rep(GoalAnnotation)
|
|
---> proc_defn_rep(
|
|
% The head variables, in order, including the ones introduced
|
|
% by the compiler.
|
|
pdr_head_vars :: list(head_var_rep),
|
|
|
|
% The procedure body.
|
|
pdr_goal :: goal_rep(GoalAnnotation),
|
|
|
|
% The variable name table.
|
|
pdr_var_name_table :: var_name_table,
|
|
|
|
% The variable type table, if present.
|
|
pdr_var_type_table :: maybe(var_type_table),
|
|
|
|
% The determinism of the procedure. Note that this may be
|
|
% looser than the determinism of the procedure's body goal.
|
|
pdr_detism :: detism_rep
|
|
).
|
|
|
|
:- type proc_defn_rep == proc_defn_rep(unit).
|
|
|
|
:- type goal_rep(GoalAnnotation)
|
|
---> goal_rep(
|
|
% The expression this goal represents.
|
|
goal_expr_rep :: goal_expr_rep(GoalAnnotation),
|
|
|
|
% The determinism of this goal.
|
|
goal_detism_rep :: detism_rep,
|
|
|
|
% This slot may be used to annotate the goal with some extra
|
|
% information. The deep profiling tools make use of this
|
|
% to associate coverage profiling data with goals.
|
|
goal_annotation :: GoalAnnotation
|
|
).
|
|
|
|
:- type goal_rep == goal_rep(unit).
|
|
|
|
:- type goal_expr_rep(GoalAnnotation)
|
|
---> conj_rep(
|
|
% The conjuncts in the original order.
|
|
list(goal_rep(GoalAnnotation))
|
|
)
|
|
; disj_rep(
|
|
% The disjuncts in the original order.
|
|
list(goal_rep(GoalAnnotation))
|
|
)
|
|
; switch_rep(
|
|
% The variable being switched on.
|
|
var_rep,
|
|
|
|
% Completeness of the switch.
|
|
switch_can_fail_rep,
|
|
|
|
% The switch arms in the original order.
|
|
list(case_rep(GoalAnnotation))
|
|
)
|
|
; ite_rep(
|
|
% The condition, the then branch and the else branch.
|
|
goal_rep(GoalAnnotation),
|
|
goal_rep(GoalAnnotation),
|
|
goal_rep(GoalAnnotation)
|
|
)
|
|
; negation_rep(
|
|
% The negated goal.
|
|
goal_rep(GoalAnnotation)
|
|
)
|
|
; scope_rep(
|
|
% The quantified goal.
|
|
goal_rep(GoalAnnotation),
|
|
|
|
maybe_cut
|
|
)
|
|
; atomic_goal_rep(
|
|
string, % Filename of context.
|
|
int, % Line number of context.
|
|
list(var_rep), % The sorted list of the variables
|
|
% bound by the atomic goal.
|
|
atomic_goal_rep
|
|
).
|
|
|
|
:- type case_rep(GoalAnnotation)
|
|
---> case_rep(
|
|
% The name and arity of the first function symbol for which
|
|
% this switch arm is applicable.
|
|
cr_main_cons_id :: cons_id_arity_rep,
|
|
|
|
% The names and arities of any other function symbols for
|
|
% this switch arm.
|
|
cr_other_cons_ids :: list(cons_id_arity_rep),
|
|
|
|
% The code of the switch arm.
|
|
cr_case_goal :: goal_rep(GoalAnnotation)
|
|
).
|
|
|
|
:- type case_rep == case_rep(unit).
|
|
|
|
:- type switch_can_fail_rep
|
|
---> switch_can_fail_rep
|
|
; switch_can_not_fail_rep.
|
|
|
|
:- type atomic_goal_rep
|
|
---> unify_construct_rep(
|
|
var_rep,
|
|
cons_id_rep,
|
|
list(var_rep)
|
|
)
|
|
; unify_deconstruct_rep(
|
|
var_rep,
|
|
cons_id_rep,
|
|
list(var_rep)
|
|
)
|
|
; partial_deconstruct_rep(
|
|
% A partial deconstruction of the form
|
|
% X = f(Y_1, Y_2, ..., Y_n)
|
|
% where X is more instantiated after the unification
|
|
% than before.
|
|
var_rep, % X
|
|
cons_id_rep, % f
|
|
list(maybe(var_rep))
|
|
% The list of Y_i's. Y_i's which are input
|
|
% are wrapped in `yes', while the other
|
|
% Y_i positions are `no'.
|
|
)
|
|
; partial_construct_rep(
|
|
% A partial construction of the form
|
|
% X = f(Y_1, Y_2, ..., Y_n)
|
|
% where X is free before the unification and bound,
|
|
% but not ground, after the unification.
|
|
var_rep, % X
|
|
cons_id_rep, % f
|
|
list(maybe(var_rep))
|
|
% The list of Y_i's. Y_i's which are input
|
|
% are wrapped in `yes', while the other
|
|
% Y_i positions are `no'.
|
|
)
|
|
; unify_assign_rep(
|
|
var_rep, % target
|
|
var_rep % source
|
|
)
|
|
; cast_rep(
|
|
var_rep, % target
|
|
var_rep % source
|
|
)
|
|
; unify_simple_test_rep(
|
|
var_rep,
|
|
var_rep
|
|
)
|
|
; pragma_foreign_code_rep(
|
|
list(var_rep) % arguments
|
|
)
|
|
; higher_order_call_rep(
|
|
var_rep, % the closure to call
|
|
list(var_rep) % the call's plain arguments
|
|
)
|
|
; method_call_rep(
|
|
var_rep, % typeclass info var
|
|
int, % method number
|
|
list(var_rep) % the call's plain arguments
|
|
)
|
|
; plain_call_rep(
|
|
string, % name of called pred's module
|
|
string, % name of the called pred
|
|
list(var_rep) % the call's arguments
|
|
)
|
|
; builtin_call_rep(
|
|
% This represents inline builtins only.
|
|
string, % name of called pred's module
|
|
string, % name of the called pred
|
|
list(var_rep) % the call's arguments
|
|
)
|
|
; event_call_rep(
|
|
string, % name of the event
|
|
list(var_rep) % the call's arguments
|
|
).
|
|
|
|
:- type var_rep == int.
|
|
|
|
:- type head_var_rep
|
|
---> head_var_rep(
|
|
head_var_var :: var_rep,
|
|
head_var_mode :: var_mode_rep
|
|
).
|
|
|
|
:- type var_mode_rep
|
|
---> var_mode_rep(
|
|
vm_initial_inst :: inst_rep,
|
|
vm_final_inst :: inst_rep
|
|
).
|
|
|
|
:- type inst_rep
|
|
---> ir_free_rep
|
|
; ir_ground_rep
|
|
; ir_other_rep.
|
|
% Instantiation states that are not understood by the bytecode
|
|
% representation are stored as ir_other_rep.
|
|
|
|
:- type cons_id_arity_rep
|
|
---> cons_id_arity_rep(
|
|
cons_id_rep,
|
|
int
|
|
).
|
|
|
|
:- type cons_id_rep == string.
|
|
|
|
:- type detism_rep
|
|
---> det_rep
|
|
; semidet_rep
|
|
; nondet_rep
|
|
; multidet_rep
|
|
; cc_nondet_rep
|
|
; cc_multidet_rep
|
|
; erroneous_rep
|
|
; failure_rep.
|
|
|
|
:- type solution_count_rep
|
|
---> at_most_zero_rep
|
|
; at_most_one_rep % Including committed choice.
|
|
; at_most_many_rep.
|
|
|
|
:- type can_fail_rep
|
|
---> can_fail_rep
|
|
; cannot_fail_rep.
|
|
|
|
:- type committed_choice
|
|
---> committed_choice
|
|
; not_committed_choice.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Operations on determinisms.
|
|
%
|
|
|
|
:- func detism_get_solutions(detism_rep) = solution_count_rep.
|
|
|
|
:- func detism_get_can_fail(detism_rep) = can_fail_rep.
|
|
|
|
:- pred detism_components(detism_rep, solution_count_rep, can_fail_rep).
|
|
:- mode detism_components(in, out, out) is det.
|
|
:- mode detism_components(out, in, in) is multi.
|
|
|
|
:- pred detism_committed_choice(detism_rep, committed_choice).
|
|
:- mode detism_committed_choice(in, out) is det.
|
|
:- mode detism_committed_choice(out, in) is multi.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Operations on variable name tables.
|
|
%
|
|
|
|
% A table of var_rep to string mappings.
|
|
%
|
|
% This table may not contain all the variables in the procedure. Variables
|
|
% created by the compiler are not included. The table may be empty if it is
|
|
% not required, such as when used with the declarative debugger.
|
|
%
|
|
:- type var_name_table.
|
|
|
|
% Retrieve the name for this variable if it is known, otherwise fail.
|
|
%
|
|
:- pred search_var_name(var_name_table::in, var_rep::in, string::out)
|
|
is semidet.
|
|
|
|
% Retrieve the name for this variable if it is known, otherwise,
|
|
% return `no'.
|
|
%
|
|
:- pred maybe_search_var_name(var_name_table::in, var_rep::in,
|
|
maybe(string)::out) is det.
|
|
|
|
% Lookup the name of a variable within the variable table. If the variable
|
|
% is unknown a distinct name is automatically generated.
|
|
%
|
|
:- pred lookup_var_name(var_name_table::in, var_rep::in, string::out) is det.
|
|
|
|
% A table mapping var_reps to representations of the variables' types.
|
|
% It is intended to be used by a program analysis for order-independent
|
|
% state update in the auto-parallelisation feedback tool.
|
|
%
|
|
% This table should exist in any procedure that is named in a oisu pragma.
|
|
% In other procedures, it may or may not be there (currently, it isn't).
|
|
%
|
|
:- type var_type_table == map(var_rep, type_rep).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Operations on goals.
|
|
%
|
|
|
|
% If the given atomic goal behaves like a call in the sense that it
|
|
% generates events as ordinary calls do, then return the list of variables
|
|
% that are passed as arguments.
|
|
%
|
|
:- func atomic_goal_generates_event_like_call(atomic_goal_rep) =
|
|
maybe(list(var_rep)).
|
|
|
|
% call_does_not_generate_events(ModuleName, PredName, Arity):
|
|
%
|
|
% Succeed iff a call to the named predicate will not generate events
|
|
% in a debugging grade.
|
|
%
|
|
:- pred call_does_not_generate_events(string::in, string::in, int::in)
|
|
is semidet.
|
|
|
|
% If the given goal generates internal events directly, then return yes;
|
|
% otherwise, return no.
|
|
%
|
|
:- func goal_generates_internal_event(goal_rep(unit)) = bool.
|
|
|
|
% The atomic goal's module, name and arity.
|
|
%
|
|
:- type atomic_goal_id
|
|
---> atomic_goal_id(string, string, int).
|
|
|
|
% Can we find out the atomic goal's name, module and arity from
|
|
% its atomic_goal_rep? If so, return them; otherwise, return no.
|
|
%
|
|
:- func atomic_goal_identifiable(atomic_goal_rep) = maybe(atomic_goal_id).
|
|
|
|
:- func head_var_to_var(head_var_rep) = var_rep.
|
|
|
|
% Extract the goal from a case, this is implemented here so it can be used
|
|
% as a higher order value.
|
|
%
|
|
:- pred case_get_goal(case_rep(T)::in, goal_rep(T)::out) is det.
|
|
|
|
% Transform the annotations on a goal representation. This may change
|
|
% not only the values of the annotations, but also their type.
|
|
%
|
|
:- pred transform_goal_rep(pred(T, U)::in(pred(in, out) is det),
|
|
goal_rep(T)::in, goal_rep(U)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Describe a call site.
|
|
%
|
|
:- type call_site
|
|
---> call_site(
|
|
caller :: string_proc_label,
|
|
slot :: int,
|
|
call_type_and_callee :: call_type_and_callee
|
|
).
|
|
|
|
% The type and callee of call. The callee is known only for plain calls.
|
|
%
|
|
:- type call_type_and_callee
|
|
---> callback_call
|
|
; higher_order_call
|
|
; method_call
|
|
; plain_call(string_proc_label)
|
|
; special_call.
|
|
|
|
% User-visible head variables are represented by a number from 1..N,
|
|
% where N is the user-visible arity.
|
|
%
|
|
% Both user-visible and compiler-generated head variables can be
|
|
% referred to via their position in the full list of head variables;
|
|
% the first head variable is at position 1.
|
|
|
|
:- type arg_pos
|
|
---> user_head_var(int)
|
|
% Nth in the list of arguments after filtering out
|
|
% non-user-visible vars.
|
|
|
|
; any_head_var(int)
|
|
% Nth in the list of all arguments.
|
|
|
|
; any_head_var_from_back(int).
|
|
% (M-N+1)th argument in the list of all arguments, where N is
|
|
% the value of the int in the constructor and M is the total number
|
|
% of arguments.
|
|
|
|
% A particular subterm within a term is represented by a term_path.
|
|
% This is the list of argument positions that need to be followed
|
|
% in order to travel from the root to the subterm. This list is in
|
|
% top-down order (i.e. the argument number in the top function symbol
|
|
% is first).
|
|
:- type term_path == list(int).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Returns type_of(_ : proc_defn_rep), for use in C code.
|
|
%
|
|
:- func proc_defn_rep_type = type_desc.
|
|
|
|
% Returns type_of(_ : goal_rep), for use in C code.
|
|
%
|
|
:- func goal_rep_type = type_desc.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Conversions between the internal form of program representations
|
|
% and their form as stored in bytecode.
|
|
%
|
|
|
|
% Construct a representation of the interface determinism of a
|
|
% procedure. The code we have chosen is not sequential; instead
|
|
% it encodes the various properties of each determinism.
|
|
% This must match the encoding of MR_Determinism in
|
|
% mercury_stack_layout.h.
|
|
%
|
|
% The 8 bit is set iff the context is first_solution.
|
|
% The 4 bit is set iff the min number of solutions is more than zero.
|
|
% The 2 bit is set iff the max number of solutions is more than zero.
|
|
% The 1 bit is set iff the max number of solutions is more than one.
|
|
%
|
|
:- func detism_rep(detism_rep) = int.
|
|
|
|
:- pred determinism_representation(detism_rep, int).
|
|
:- mode determinism_representation(in, out) is det.
|
|
:- mode determinism_representation(out, in) is semidet.
|
|
|
|
:- pred inst_representation(inst_rep, int).
|
|
:- mode inst_representation(in, out) is det.
|
|
:- mode inst_representation(out, in) is semidet.
|
|
|
|
:- type bytecode_goal_type
|
|
---> goal_conj
|
|
; goal_disj
|
|
; goal_switch
|
|
; goal_ite
|
|
; goal_neg
|
|
; goal_scope
|
|
; goal_construct
|
|
; goal_deconstruct
|
|
; goal_partial_construct
|
|
; goal_partial_deconstruct
|
|
; goal_assign
|
|
; goal_cast
|
|
; goal_simple_test
|
|
; goal_foreign
|
|
; goal_ho_call
|
|
; goal_method_call
|
|
; goal_plain_call
|
|
; goal_builtin_call
|
|
; goal_event_call.
|
|
|
|
:- func goal_type_to_byte(bytecode_goal_type) = int.
|
|
|
|
:- pred byte_to_goal_type(int::in, bytecode_goal_type::out) is semidet.
|
|
|
|
% We represent a variable number as
|
|
% - one byte if all variable numbers fit into one byte,
|
|
% - two bytes if all variable numbers fit into two bytes, but
|
|
% some do not fit into one byte, and
|
|
% - four bytes if some variable numbers do not fit into two bytes.
|
|
% This assumes that all variable numbers fit into four bytes.
|
|
%
|
|
:- type var_num_rep
|
|
---> var_num_1_byte
|
|
; var_num_2_bytes
|
|
; var_num_4_bytes.
|
|
|
|
% Describe whether a variable name table should be included in the
|
|
% bytecode. The variable name table actually adds the strings into the
|
|
% module's string table.
|
|
%
|
|
:- type maybe_include_var_name_table
|
|
---> do_not_include_var_name_table
|
|
; include_var_name_table.
|
|
|
|
% Describe whether references to the types of variables should be included
|
|
% in the variable table. The types themselves are in a separate table next
|
|
% to the module's string table.
|
|
%
|
|
:- type maybe_include_var_types
|
|
---> do_not_include_var_types
|
|
; include_var_types.
|
|
|
|
% This predicate is here only for reading Deep.procrep files in an
|
|
% old format, for backwards compatibility.
|
|
%
|
|
:- pred var_num_rep_byte(var_num_rep, int).
|
|
:- mode var_num_rep_byte(in, out) is det.
|
|
:- mode var_num_rep_byte(out, in) is semidet.
|
|
|
|
% This predicate is the replacement for var_num_rep_byte.
|
|
%
|
|
:- pred var_flag_byte(var_num_rep,
|
|
maybe_include_var_name_table, maybe_include_var_types, int).
|
|
:- mode var_flag_byte(in, in, in, out) is det.
|
|
:- mode var_flag_byte(out, out, out, in) is semidet.
|
|
|
|
% Represent whether a scope goal cuts away solutions or not.
|
|
%
|
|
:- pred cut_byte(maybe_cut, int).
|
|
:- mode cut_byte(in, out) is det.
|
|
:- mode cut_byte(out, in) is semidet.
|
|
|
|
:- pred can_fail_byte(switch_can_fail_rep, int).
|
|
:- mode can_fail_byte(in, out) is det.
|
|
:- mode can_fail_byte(out, in) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% read_prog_rep_file(FileName, Result, !IO)
|
|
%
|
|
:- pred read_prog_rep_file(string::in, io.res(prog_rep)::out, io::di, io::uo)
|
|
is det.
|
|
|
|
:- pred trace_read_proc_defn_rep(bytecode_bytes::in, label_layout::in,
|
|
proc_defn_rep::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Some predicates that operate on polymorphic values do not need
|
|
% the type_infos describing the types bound to the variables.
|
|
% It is of course faster not to pass type_infos to such predicates
|
|
% (especially since we may also be able to avoid constructing those
|
|
% type_infos), and it can also be easier for a compiler module
|
|
% (e.g. common.m, size_prof.m) that generates calls to such predicates
|
|
% not to have to create those type_infos.
|
|
%
|
|
% All the predicates for whose names no_type_info_builtin succeeds
|
|
% are defined by compiler implementors. They are all predicates
|
|
% implemented by foreign language code in the standard library.
|
|
% For some, but not all, the compiler generates code inline.
|
|
%
|
|
% If you are adding a predicate to no_type_info_builtin, remember that
|
|
% this will only affect code built by a compiler linked with the new
|
|
% mdbcomp library. For example, if you add a predicate P to
|
|
% no_type_info_builtin, the compiler building the stage 1 library
|
|
% won't yet know about P. The stage 1 compiler _will_ know about P,
|
|
% so stage 2 is when P will be compiled differently.
|
|
%
|
|
% XXX ARITY The last argument should be pred_form_arity.
|
|
%
|
|
:- pred no_type_info_builtin(module_name::in, string::in, int::in) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type coverage_point_info
|
|
---> coverage_point_info(
|
|
% Identifies the goal that this coverage point is near.
|
|
% If cp_type is cp_type_branch_arm, the coverage point is
|
|
% immediately before this goal, otherwise it is immediately
|
|
% after.
|
|
reverse_goal_path,
|
|
|
|
% The type of this coverage point.
|
|
cp_type
|
|
).
|
|
|
|
% This enumeration specifies the type of coverage point. A branch arm
|
|
% is an arm of an if-then-else, switch or disj goal. The coverage_after
|
|
% type is used to measure the coverage after the goal its coverage point
|
|
% refers to.
|
|
:- type cp_type
|
|
---> cp_type_coverage_after
|
|
; cp_type_branch_arm.
|
|
|
|
% Gives the value in C for this coverage point type.
|
|
%
|
|
:- pred coverage_point_type_c_value(cp_type::in, string::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module mdbcomp.builtin_modules.
|
|
|
|
:- import_module int.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
detism_get_solutions(Detism) = Solutions :-
|
|
detism_components(Detism, Solutions, _).
|
|
|
|
detism_get_can_fail(Detism) = CanFail :-
|
|
detism_components(Detism, _, CanFail).
|
|
|
|
detism_components(det_rep, at_most_one_rep, cannot_fail_rep).
|
|
detism_components(semidet_rep, at_most_one_rep, can_fail_rep).
|
|
detism_components(multidet_rep, at_most_many_rep, cannot_fail_rep).
|
|
detism_components(nondet_rep, at_most_many_rep, can_fail_rep).
|
|
detism_components(cc_multidet_rep, at_most_one_rep, cannot_fail_rep).
|
|
detism_components(cc_nondet_rep, at_most_one_rep, can_fail_rep).
|
|
detism_components(erroneous_rep, at_most_zero_rep, cannot_fail_rep).
|
|
detism_components(failure_rep, at_most_zero_rep, can_fail_rep).
|
|
|
|
detism_committed_choice(det_rep, not_committed_choice).
|
|
detism_committed_choice(semidet_rep, not_committed_choice).
|
|
detism_committed_choice(multidet_rep, not_committed_choice).
|
|
detism_committed_choice(nondet_rep, not_committed_choice).
|
|
detism_committed_choice(cc_multidet_rep, committed_choice).
|
|
detism_committed_choice(cc_nondet_rep, committed_choice).
|
|
detism_committed_choice(erroneous_rep, not_committed_choice).
|
|
detism_committed_choice(failure_rep, not_committed_choice).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type var_name_table == map(var_rep, string).
|
|
|
|
search_var_name(VarNameTable, VarRep, String) :-
|
|
map.search(VarNameTable, VarRep, String).
|
|
|
|
maybe_search_var_name(VarNameTable, VarRep, MaybeString) :-
|
|
( if search_var_name(VarNameTable, VarRep, String) then
|
|
MaybeString = yes(String)
|
|
else
|
|
MaybeString = no
|
|
).
|
|
|
|
lookup_var_name(VarNameTable, VarRep, String) :-
|
|
( if search_var_name(VarNameTable, VarRep, StringPrime) then
|
|
String = StringPrime
|
|
else
|
|
% Generate an automatic name for the variable.
|
|
String = string.format("V_%d", [i(VarRep)])
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
atomic_goal_generates_event_like_call(GoalRep) = Generates :-
|
|
(
|
|
( GoalRep = unify_construct_rep(_, _, _)
|
|
; GoalRep = unify_deconstruct_rep(_, _, _)
|
|
; GoalRep = partial_construct_rep(_, _, _)
|
|
; GoalRep = partial_deconstruct_rep(_, _, _)
|
|
; GoalRep = unify_assign_rep(_, _)
|
|
; GoalRep = unify_simple_test_rep(_, _)
|
|
; GoalRep = cast_rep(_, _)
|
|
; GoalRep = pragma_foreign_code_rep(_)
|
|
; GoalRep = builtin_call_rep(_, _, _)
|
|
; GoalRep = event_call_rep(_, _)
|
|
),
|
|
Generates = no
|
|
;
|
|
( GoalRep = higher_order_call_rep(_, Args)
|
|
; GoalRep = method_call_rep(_, _, Args)
|
|
),
|
|
Generates = yes(Args)
|
|
;
|
|
GoalRep = plain_call_rep(ModuleName, PredName, Args),
|
|
NumArgs = list.length(Args),
|
|
( if call_does_not_generate_events(ModuleName, PredName, NumArgs) then
|
|
Generates = no
|
|
else
|
|
Generates = yes(Args)
|
|
)
|
|
).
|
|
|
|
call_does_not_generate_events(ModuleName, PredName, Arity) :-
|
|
(
|
|
SymModuleName = string_to_sym_name(ModuleName),
|
|
non_traced_mercury_builtin_module(SymModuleName)
|
|
;
|
|
% The debugger cannot handle calls to polymorphic builtins that
|
|
% do not take a type_info argument, so such calls are not traced.
|
|
SymModuleName = string_to_sym_name(ModuleName),
|
|
no_type_info_builtin(SymModuleName, PredName, Arity)
|
|
;
|
|
pred_is_external(ModuleName, PredName, Arity)
|
|
;
|
|
% Events from compiler generated predicates are not included in the
|
|
% annotated trace at the moment.
|
|
( PredName = "__Unify__"
|
|
; PredName = "__Index__"
|
|
; PredName = "__Compare__"
|
|
)
|
|
).
|
|
|
|
goal_generates_internal_event(goal_rep(GoalExpr, _, _)) = InternalEvent :-
|
|
require_complete_switch [GoalExpr]
|
|
(
|
|
( GoalExpr = conj_rep(_)
|
|
; GoalExpr = scope_rep(_, _)
|
|
; GoalExpr = atomic_goal_rep(_, _, _, _)
|
|
% Atomic goals may generate interface events,
|
|
% but not internal events.
|
|
),
|
|
InternalEvent = no
|
|
;
|
|
( GoalExpr = disj_rep(_)
|
|
; GoalExpr = switch_rep(_, _, _)
|
|
; GoalExpr = ite_rep(_, _, _)
|
|
; GoalExpr = negation_rep(_)
|
|
),
|
|
InternalEvent = yes
|
|
).
|
|
|
|
atomic_goal_identifiable(AtomicGoalExpr) = Identifieable :-
|
|
(
|
|
( AtomicGoalExpr = unify_construct_rep(_, _, _)
|
|
; AtomicGoalExpr = unify_deconstruct_rep(_, _, _)
|
|
; AtomicGoalExpr = partial_construct_rep(_, _, _)
|
|
; AtomicGoalExpr = partial_deconstruct_rep(_, _, _)
|
|
; AtomicGoalExpr = unify_assign_rep(_, _)
|
|
; AtomicGoalExpr = unify_simple_test_rep(_, _)
|
|
; AtomicGoalExpr = cast_rep(_, _)
|
|
; AtomicGoalExpr = pragma_foreign_code_rep(_)
|
|
; AtomicGoalExpr = higher_order_call_rep(_, _)
|
|
; AtomicGoalExpr = method_call_rep(_, _, _)
|
|
; AtomicGoalExpr = event_call_rep(_, _)
|
|
),
|
|
Identifieable = no
|
|
;
|
|
AtomicGoalExpr = builtin_call_rep(Module, Name, Args),
|
|
Identifieable = yes(atomic_goal_id(Module, Name, length(Args)))
|
|
;
|
|
AtomicGoalExpr = plain_call_rep(Module, Name, Args),
|
|
Identifieable = yes(atomic_goal_id(Module, Name, length(Args)))
|
|
).
|
|
|
|
head_var_to_var(head_var_rep(Var, _)) = Var.
|
|
|
|
case_get_goal(case_rep(_, _, Goal), Goal).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
transform_goal_rep(Pred, Goal0, Goal) :-
|
|
Goal0 = goal_rep(Expr0, Detism, AnnotationT),
|
|
transform_goal_expr(Pred, Expr0, Expr),
|
|
Pred(AnnotationT, AnnotationU),
|
|
Goal = goal_rep(Expr, Detism, AnnotationU).
|
|
|
|
:- pred transform_goal_expr(pred(T, U)::in(pred(in, out) is det),
|
|
goal_expr_rep(T)::in, goal_expr_rep(U)::out) is det.
|
|
|
|
transform_goal_expr(Pred, Expr0, Expr) :-
|
|
(
|
|
Expr0 = conj_rep(Conjs0),
|
|
list.map(transform_goal_rep(Pred), Conjs0, Conjs),
|
|
Expr = conj_rep(Conjs)
|
|
;
|
|
Expr0 = disj_rep(Disjs0),
|
|
list.map(transform_goal_rep(Pred), Disjs0, Disjs),
|
|
Expr = disj_rep(Disjs)
|
|
;
|
|
Expr0 = switch_rep(Var, CanFail, Cases0),
|
|
map(transform_switch_case(Pred), Cases0, Cases),
|
|
Expr = switch_rep(Var, CanFail, Cases)
|
|
;
|
|
Expr0 = ite_rep(Cond0, Then0, Else0),
|
|
transform_goal_rep(Pred, Cond0, Cond),
|
|
transform_goal_rep(Pred, Then0, Then),
|
|
transform_goal_rep(Pred, Else0, Else),
|
|
Expr = ite_rep(Cond, Then, Else)
|
|
;
|
|
Expr0 = negation_rep(NegGoal0),
|
|
transform_goal_rep(Pred, NegGoal0, NegGoal),
|
|
Expr = negation_rep(NegGoal)
|
|
;
|
|
Expr0 = scope_rep(SubGoal0, MaybeCut),
|
|
transform_goal_rep(Pred, SubGoal0, SubGoal),
|
|
Expr = scope_rep(SubGoal, MaybeCut)
|
|
;
|
|
Expr0 = atomic_goal_rep(Filename, Lineno, BoundVars, AtomicGoal),
|
|
Expr = atomic_goal_rep(Filename, Lineno, BoundVars, AtomicGoal)
|
|
).
|
|
|
|
:- pred transform_switch_case(pred(T, U)::in(pred(in, out) is det),
|
|
case_rep(T)::in, case_rep(U)::out) is det.
|
|
|
|
transform_switch_case(Pred, Case0, Case) :-
|
|
Case0 = case_rep(MainConsId, OtherConsIds, Goal0),
|
|
transform_goal_rep(Pred, Goal0, Goal),
|
|
Case = case_rep(MainConsId, OtherConsIds, Goal).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_export("C", proc_defn_rep_type = out,
|
|
"ML_proc_defn_rep_type").
|
|
|
|
proc_defn_rep_type = type_of(_ : proc_defn_rep).
|
|
|
|
:- pragma foreign_export("C", goal_rep_type = out,
|
|
"ML_goal_rep_type").
|
|
|
|
goal_rep_type = type_of(_ : goal_rep).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
detism_rep(Detism) = Rep :-
|
|
determinism_representation(Detism, Rep).
|
|
|
|
% This encoding must match the encoding of MR_Determinism in
|
|
% runtime/mercury_stack_layout.h. The rationale for this encoding
|
|
% is documented there.
|
|
|
|
determinism_representation(det_rep, 6).
|
|
determinism_representation(semidet_rep, 2).
|
|
determinism_representation(nondet_rep, 3).
|
|
determinism_representation(multidet_rep, 7).
|
|
determinism_representation(erroneous_rep, 4).
|
|
determinism_representation(failure_rep, 0).
|
|
determinism_representation(cc_nondet_rep, 10).
|
|
determinism_representation(cc_multidet_rep, 14).
|
|
|
|
inst_representation(ir_free_rep, 0).
|
|
inst_representation(ir_ground_rep, 1).
|
|
inst_representation(ir_other_rep, 2).
|
|
|
|
goal_type_to_byte(Type) = TypeInt :-
|
|
goal_type_byte(TypeInt, Type).
|
|
|
|
byte_to_goal_type(TypeInt, Type) :-
|
|
goal_type_byte(TypeInt, Type).
|
|
|
|
:- pred goal_type_byte(int, bytecode_goal_type).
|
|
:- mode goal_type_byte(in, out) is semidet.
|
|
:- mode goal_type_byte(out, in) is det.
|
|
|
|
goal_type_byte(1, goal_conj).
|
|
goal_type_byte(2, goal_disj).
|
|
goal_type_byte(3, goal_switch).
|
|
goal_type_byte(4, goal_ite).
|
|
goal_type_byte(5, goal_neg).
|
|
goal_type_byte(6, goal_scope).
|
|
goal_type_byte(7, goal_construct).
|
|
goal_type_byte(8, goal_deconstruct).
|
|
goal_type_byte(9, goal_partial_construct).
|
|
goal_type_byte(10, goal_partial_deconstruct).
|
|
goal_type_byte(11, goal_assign).
|
|
goal_type_byte(12, goal_cast).
|
|
goal_type_byte(13, goal_simple_test).
|
|
goal_type_byte(14, goal_foreign).
|
|
goal_type_byte(15, goal_ho_call).
|
|
goal_type_byte(16, goal_method_call).
|
|
goal_type_byte(17, goal_plain_call).
|
|
goal_type_byte(18, goal_builtin_call).
|
|
goal_type_byte(19, goal_event_call).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
var_num_rep_byte(var_num_1_byte, 0).
|
|
var_num_rep_byte(var_num_2_bytes, 1).
|
|
var_num_rep_byte(var_num_4_bytes, 2).
|
|
|
|
var_flag_byte(var_num_1_byte,
|
|
do_not_include_var_name_table, do_not_include_var_types, 0).
|
|
var_flag_byte(var_num_1_byte,
|
|
do_not_include_var_name_table, include_var_types, 1).
|
|
var_flag_byte(var_num_1_byte,
|
|
include_var_name_table, do_not_include_var_types, 2).
|
|
var_flag_byte(var_num_1_byte,
|
|
include_var_name_table, include_var_types, 3).
|
|
var_flag_byte(var_num_2_bytes,
|
|
do_not_include_var_name_table, do_not_include_var_types, 4).
|
|
var_flag_byte(var_num_2_bytes,
|
|
do_not_include_var_name_table, include_var_types, 5).
|
|
var_flag_byte(var_num_2_bytes,
|
|
include_var_name_table, do_not_include_var_types, 6).
|
|
var_flag_byte(var_num_2_bytes,
|
|
include_var_name_table, include_var_types, 7).
|
|
var_flag_byte(var_num_4_bytes,
|
|
do_not_include_var_name_table, do_not_include_var_types, 8).
|
|
var_flag_byte(var_num_4_bytes,
|
|
do_not_include_var_name_table, include_var_types, 9).
|
|
var_flag_byte(var_num_4_bytes,
|
|
include_var_name_table, do_not_include_var_types, 10).
|
|
var_flag_byte(var_num_4_bytes,
|
|
include_var_name_table, include_var_types, 11).
|
|
|
|
cut_byte(scope_is_no_cut, 0).
|
|
cut_byte(scope_is_cut, 1).
|
|
|
|
can_fail_byte(switch_can_fail_rep, 0).
|
|
can_fail_byte(switch_can_not_fail_rep, 1).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The top level of the code that reads the representation of a whole program
|
|
% from a file.
|
|
%
|
|
|
|
read_prog_rep_file(FileName, Result, !IO) :-
|
|
read_file_as_bytecode(FileName, ReadResult, !IO),
|
|
(
|
|
ReadResult = error(Error),
|
|
Result = error(Error)
|
|
;
|
|
ReadResult = ok(ByteCode),
|
|
( if
|
|
some [!Pos] (
|
|
!:Pos = 0,
|
|
read_line(ByteCode, Line, !Pos),
|
|
( if Line = old_procrep_id_string then
|
|
ExpectNewFormat = no
|
|
else if Line = new_procrep_id_string then
|
|
ExpectNewFormat = yes
|
|
else
|
|
fail
|
|
),
|
|
read_module_reps(ExpectNewFormat, ByteCode,
|
|
map.init, ModuleReps, !Pos),
|
|
ByteCode = bytecode(_, Size),
|
|
!.Pos = Size
|
|
)
|
|
then
|
|
Result = ok(prog_rep(ModuleReps))
|
|
else
|
|
Msg = FileName ++ ": is not a valid program representation file",
|
|
Result = error(io.make_io_error(Msg))
|
|
)
|
|
).
|
|
|
|
% Return the string written out by MR_write_out_procrep_id_string.
|
|
%
|
|
:- func old_procrep_id_string = string.
|
|
:- func new_procrep_id_string = string.
|
|
|
|
old_procrep_id_string = "Mercury deep profiler procrep version 5\n".
|
|
new_procrep_id_string = "Mercury deep profiler procrep version 6\n".
|
|
|
|
:- pred read_file_as_bytecode(string::in, io.res(bytecode)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_file_as_bytecode(FileName, Result, !IO) :-
|
|
read_file_as_bytecode_2(FileName, ByteCode, Size, Error, !IO),
|
|
( if Size < 0 then
|
|
io.make_io_error_from_system_error(Error,
|
|
"opening " ++ FileName ++ ": ", IOError, !IO),
|
|
Result = error(IOError)
|
|
else
|
|
Result = ok(bytecode(ByteCode, Size))
|
|
).
|
|
|
|
:- pragma foreign_decl("C", "
|
|
#ifdef MR_HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#endif
|
|
").
|
|
|
|
:- pred read_file_as_bytecode_2(string::in, bytecode_bytes::out, int::out,
|
|
io.system_error::out, io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
read_file_as_bytecode_2(FileName::in, Bytes::out, Size::out, Error::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, thread_safe, promise_pure],
|
|
"
|
|
#if defined(MR_HAVE_SYS_STAT_H) && \
|
|
defined(MR_HAVE_STAT) && \
|
|
defined(MR_HAVE_OPEN)
|
|
|
|
struct stat statbuf;
|
|
|
|
if (stat(FileName, &statbuf) != 0) {
|
|
Bytes = NULL;
|
|
Size = -1;
|
|
Error = errno;
|
|
} else {
|
|
int fd;
|
|
char *buf;
|
|
|
|
Size = statbuf.st_size;
|
|
MR_allocate_aligned_string_msg(buf, Size, MR_ALLOC_ID);
|
|
fd = open(FileName, O_RDONLY, 0);
|
|
if (fd < 0) {
|
|
Bytes = NULL;
|
|
Size = -1;
|
|
Error = errno;
|
|
} else {
|
|
if (read(fd, buf, Size) != Size) {
|
|
Bytes = NULL;
|
|
Size = -1;
|
|
Error = errno;
|
|
} else {
|
|
if (close(fd) != 0) {
|
|
Bytes = NULL;
|
|
Size = -1;
|
|
Error = errno;
|
|
} else {
|
|
Bytes = (MR_uint_least8_t *) buf;
|
|
Error = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
MR_fatal_error(""read_file_as_bytecode: not supported on this platform"");
|
|
#endif
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The top level of the code that reads the representation of a procedure
|
|
% from that procedure's proc layout structure.
|
|
%
|
|
|
|
:- pragma foreign_export("C", trace_read_proc_defn_rep(in, in, out),
|
|
"MR_MDBCOMP_trace_read_proc_defn_rep").
|
|
|
|
trace_read_proc_defn_rep(Bytes, LabelLayout, ProcDefnRep) :-
|
|
ProcLayout = containing_proc_layout(LabelLayout),
|
|
( if containing_module_layout(ProcLayout, ModuleLayout) then
|
|
StringTable = module_string_table(ModuleLayout)
|
|
else
|
|
unexpected($pred, "no module layout")
|
|
),
|
|
some [!Pos] (
|
|
!:Pos = 0,
|
|
% The size of the bytecode is not recorded anywhere in the proc layout
|
|
% except at the start of the bytecode itself.
|
|
DummyByteCode = bytecode(Bytes, 4),
|
|
read_int32(DummyByteCode, Size, !Pos),
|
|
ByteCode = bytecode(Bytes, Size),
|
|
read_string_via_offset(ByteCode, StringTable, FileName, !Pos),
|
|
Info = read_proc_rep_info(FileName),
|
|
% The declarative debugger does not need variable type representations
|
|
% from the bytecode. It has access to actual type_infos in label
|
|
% layouts.
|
|
ExpectNewFormat = yes,
|
|
read_var_table(ExpectNewFormat, ByteCode, StringTable,
|
|
map.init, VarNumRep, VarNameTable, _MaybeVarTypeTable, !Pos),
|
|
read_head_vars(VarNumRep, ByteCode, HeadVars, !Pos),
|
|
read_goal(VarNumRep, ByteCode, StringTable, Info, Goal, !Pos),
|
|
read_determinism(ByteCode, Detism, !Pos),
|
|
ProcDefnRep = proc_defn_rep(HeadVars, Goal, VarNameTable, no, Detism),
|
|
expect(unify(!.Pos, Size), $pred, "limit mismatch")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Operations that parse the (remaining part of) the bytecode string
|
|
% as various components of a program representation.
|
|
%
|
|
|
|
:- pred read_module_reps(bool::in, bytecode::in,
|
|
module_map(unit)::in, module_map(unit)::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_module_reps(ExpectNewFormat, ByteCode, !ModuleReps, !Pos) :-
|
|
read_byte(ByteCode, MoreByte, !Pos),
|
|
is_more_modules(MoreByte, MoreModules),
|
|
(
|
|
MoreModules = no_more_modules
|
|
;
|
|
MoreModules = next_module,
|
|
read_module_rep(ExpectNewFormat, ByteCode, ModuleRep, !Pos),
|
|
map.det_insert(ModuleRep ^ mr_name, ModuleRep, !ModuleReps),
|
|
disable_warning [suspicious_recursion] (
|
|
read_module_reps(ExpectNewFormat, ByteCode, !ModuleReps, !Pos)
|
|
)
|
|
).
|
|
|
|
:- pred read_module_rep(bool::in, bytecode::in, module_rep(unit)::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_module_rep(ExpectNewFormat, ByteCode, ModuleRep, !Pos) :-
|
|
read_len_string(ByteCode, ModuleName, !Pos),
|
|
trace [io(!IO), compiletime(flag("debug_oisu_bytecode"))] (
|
|
io.output_stream(OutputStream, !IO),
|
|
io.format(OutputStream, "module rep for %s\n", [s(ModuleName)], !IO)
|
|
),
|
|
read_string_table(ByteCode, StringTable, !Pos),
|
|
(
|
|
ExpectNewFormat = no,
|
|
OISUTypes = [],
|
|
map.init(TypeTable)
|
|
;
|
|
ExpectNewFormat = yes,
|
|
read_num(ByteCode, NumOISUTypes, !Pos),
|
|
( if NumOISUTypes > 0 then
|
|
OISUStartPos = !.Pos,
|
|
read_int32(ByteCode, OISUSize, !Pos),
|
|
trace [io(!IO), compiletime(flag("debug_oisu_bytecode"))] (
|
|
io.output_stream(OutputStream, !IO),
|
|
io.format(OutputStream, "OISU num types %d\n",
|
|
[i(NumOISUTypes)], !IO),
|
|
io.format(OutputStream, "OISU bytecode size %d\n",
|
|
[i(OISUSize)], !IO)
|
|
),
|
|
read_n_items(read_oisu_type_procs(ByteCode), NumOISUTypes,
|
|
OISUTypes, !Pos),
|
|
expect(unify(!.Pos, OISUStartPos + OISUSize), $pred,
|
|
"oisu limit mismatch")
|
|
else
|
|
OISUTypes = []
|
|
),
|
|
read_num(ByteCode, NumTableTypes, !Pos),
|
|
( if NumTableTypes > 0 then
|
|
TypeStartPos = !.Pos,
|
|
read_int32(ByteCode, TypeSize, !Pos),
|
|
trace [io(!IO), compiletime(flag("debug_oisu_bytecode"))] (
|
|
io.output_stream(OutputStream, !IO),
|
|
io.format(OutputStream, "num types %d\n",
|
|
[i(NumOISUTypes)], !IO),
|
|
io.format(OutputStream, "type bytecode size %d\n",
|
|
[i(TypeSize)], !IO)
|
|
),
|
|
read_n_encoded_types(ByteCode, StringTable, 0, NumTableTypes,
|
|
map.init, TypeTable, !Pos),
|
|
expect(unify(!.Pos, TypeStartPos + TypeSize), $pred,
|
|
"type limit mismatch")
|
|
else
|
|
map.init(TypeTable)
|
|
)
|
|
),
|
|
read_proc_reps(ExpectNewFormat, ByteCode, StringTable, TypeTable,
|
|
map.init, ProcReps, !Pos),
|
|
ModuleRep = module_rep(ModuleName, StringTable, OISUTypes, TypeTable,
|
|
ProcReps).
|
|
|
|
%---------------------%
|
|
|
|
:- pred read_oisu_type_procs(bytecode::in, oisu_type_procs::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_oisu_type_procs(ByteCode, OISUTypeProcs, !Pos) :-
|
|
read_len_string(ByteCode, TypeCtorName, !Pos),
|
|
read_num(ByteCode, NumCreators, !Pos),
|
|
read_n_items(read_string_proc_label(ByteCode), NumCreators,
|
|
CreatorProcLabels, !Pos),
|
|
read_num(ByteCode, NumMutators, !Pos),
|
|
read_n_items(read_string_proc_label(ByteCode), NumMutators,
|
|
MutatorProcLabels, !Pos),
|
|
read_num(ByteCode, NumDestructors, !Pos),
|
|
read_n_items(read_string_proc_label(ByteCode), NumDestructors,
|
|
DestructorProcLabels, !Pos),
|
|
OISUTypeProcs = oisu_type_procs(TypeCtorName,
|
|
CreatorProcLabels, MutatorProcLabels, DestructorProcLabels).
|
|
|
|
%---------------------%
|
|
|
|
:- pred read_n_encoded_types(bytecode::in, string_table::in, int::in, int::in,
|
|
encoded_type_table::in, encoded_type_table::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_n_encoded_types(ByteCode, StringTable, CurTypeNum, NumTableTypes,
|
|
!TypeTable, !Pos) :-
|
|
( if CurTypeNum < NumTableTypes then
|
|
read_encoded_type(ByteCode, StringTable, !.TypeTable, TypeRep, !Pos),
|
|
map.det_insert(CurTypeNum, TypeRep, !TypeTable),
|
|
read_n_encoded_types(ByteCode, StringTable,
|
|
CurTypeNum + 1, NumTableTypes, !TypeTable, !Pos)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred read_encoded_type(bytecode::in, string_table::in,
|
|
encoded_type_table::in, type_rep::out, int::in, int::out) is semidet.
|
|
|
|
read_encoded_type(ByteCode, StringTable, TypeTable, TypeRep, !Pos) :-
|
|
% The encoding read here is created by add_type_to_table in compiler/
|
|
% prog_rep_table.m. The code here and there must be kept in sync.
|
|
read_byte(ByteCode, Selector, !Pos),
|
|
(
|
|
Selector = 0,
|
|
read_string_via_offset(ByteCode, StringTable, TypeCtorStr, !Pos),
|
|
TypeCtorSymName = string_to_sym_name(TypeCtorStr),
|
|
TypeRep = defined_type_rep(TypeCtorSymName, [])
|
|
;
|
|
Selector = 1,
|
|
read_string_via_offset(ByteCode, StringTable, TypeCtorStr, !Pos),
|
|
TypeCtorSymName = string_to_sym_name(TypeCtorStr),
|
|
read_num(ByteCode, TypeNumArg1, !Pos),
|
|
map.lookup(TypeTable, TypeNumArg1, TypeRepArg1),
|
|
TypeRep = defined_type_rep(TypeCtorSymName, [TypeRepArg1])
|
|
;
|
|
Selector = 2,
|
|
read_string_via_offset(ByteCode, StringTable, TypeCtorStr, !Pos),
|
|
TypeCtorSymName = string_to_sym_name(TypeCtorStr),
|
|
read_num(ByteCode, TypeNumArg1, !Pos),
|
|
read_num(ByteCode, TypeNumArg2, !Pos),
|
|
map.lookup(TypeTable, TypeNumArg1, TypeRepArg1),
|
|
map.lookup(TypeTable, TypeNumArg2, TypeRepArg2),
|
|
TypeRep = defined_type_rep(TypeCtorSymName, [TypeRepArg1, TypeRepArg2])
|
|
;
|
|
Selector = 3,
|
|
read_string_via_offset(ByteCode, StringTable, TypeCtorStr, !Pos),
|
|
TypeCtorSymName = string_to_sym_name(TypeCtorStr),
|
|
read_num(ByteCode, TypeNumArg1, !Pos),
|
|
read_num(ByteCode, TypeNumArg2, !Pos),
|
|
read_num(ByteCode, TypeNumArg3, !Pos),
|
|
map.lookup(TypeTable, TypeNumArg1, TypeRepArg1),
|
|
map.lookup(TypeTable, TypeNumArg2, TypeRepArg2),
|
|
map.lookup(TypeTable, TypeNumArg3, TypeRepArg3),
|
|
TypeRep = defined_type_rep(TypeCtorSymName,
|
|
[TypeRepArg1, TypeRepArg2, TypeRepArg3])
|
|
;
|
|
Selector = 4,
|
|
read_string_via_offset(ByteCode, StringTable, TypeCtorStr, !Pos),
|
|
TypeCtorSymName = string_to_sym_name(TypeCtorStr),
|
|
read_num(ByteCode, NumArgs, !Pos),
|
|
read_n_items(read_num(ByteCode), NumArgs, TypeNumArgs, !Pos),
|
|
list.map(map.lookup(TypeTable), TypeNumArgs, TypeRepArgs),
|
|
TypeRep = defined_type_rep(TypeCtorSymName, TypeRepArgs)
|
|
;
|
|
Selector = 5,
|
|
TypeRep = builtin_type_rep(builtin_type_int_rep)
|
|
;
|
|
Selector = 6,
|
|
TypeRep = builtin_type_rep(builtin_type_uint_rep)
|
|
;
|
|
Selector = 7,
|
|
TypeRep = builtin_type_rep(builtin_type_float_rep)
|
|
;
|
|
Selector = 8,
|
|
TypeRep = builtin_type_rep(builtin_type_string_rep)
|
|
;
|
|
Selector = 9,
|
|
TypeRep = builtin_type_rep(builtin_type_char_rep)
|
|
;
|
|
Selector = 10,
|
|
read_num(ByteCode, NumArgs, !Pos),
|
|
read_n_items(read_num(ByteCode), NumArgs, TypeNumArgs, !Pos),
|
|
list.map(map.lookup(TypeTable), TypeNumArgs, TypeRepArgs),
|
|
TypeRep = tuple_type_rep(TypeRepArgs)
|
|
;
|
|
Selector = 11,
|
|
read_num(ByteCode, NumArgs, !Pos),
|
|
read_n_items(read_num(ByteCode), NumArgs, TypeNumArgs, !Pos),
|
|
list.map(map.lookup(TypeTable), TypeNumArgs, TypeRepArgs),
|
|
TypeRep = higher_order_type_rep(TypeRepArgs, no)
|
|
;
|
|
Selector = 12,
|
|
read_num(ByteCode, NumArgs, !Pos),
|
|
read_n_items(read_num(ByteCode), NumArgs, TypeNumArgs, !Pos),
|
|
list.map(map.lookup(TypeTable), TypeNumArgs, TypeRepArgs),
|
|
read_num(ByteCode, TypeNumReturn, !Pos),
|
|
map.lookup(TypeTable, TypeNumReturn, TypeRepReturn),
|
|
TypeRep = higher_order_type_rep(TypeRepArgs, yes(TypeRepReturn))
|
|
;
|
|
Selector = 13,
|
|
read_num(ByteCode, VarNum, !Pos),
|
|
TypeRep = type_var_rep(VarNum)
|
|
;
|
|
% XXX in order to avoid bumping the deep profiler binary compatibility
|
|
% version number when the fixed size integers were added, the newly
|
|
% added types were assigned unused Selector values. The next time the
|
|
% format of the program representation file is changed for some
|
|
% unavoidable reason this should be tidied up.
|
|
Selector = 14,
|
|
TypeRep = builtin_type_rep(builtin_type_int8_rep)
|
|
;
|
|
Selector = 15,
|
|
TypeRep = builtin_type_rep(builtin_type_uint8_rep)
|
|
;
|
|
Selector = 16,
|
|
TypeRep = builtin_type_rep(builtin_type_int16_rep)
|
|
;
|
|
Selector = 17,
|
|
TypeRep = builtin_type_rep(builtin_type_uint16_rep)
|
|
;
|
|
Selector = 18,
|
|
TypeRep = builtin_type_rep(builtin_type_int32_rep)
|
|
;
|
|
Selector = 19,
|
|
TypeRep = builtin_type_rep(builtin_type_uint32_rep)
|
|
;
|
|
Selector = 20,
|
|
TypeRep = builtin_type_rep(builtin_type_int64_rep)
|
|
;
|
|
Selector = 21,
|
|
TypeRep = builtin_type_rep(builtin_type_uint64_rep)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred read_proc_reps(bool::in, bytecode::in, string_table::in,
|
|
encoded_type_table::in, proc_map(unit)::in, proc_map(unit)::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_proc_reps(ExpectNewFormat, ByteCode, StringTable, TypeTable, !ProcReps,
|
|
!Pos) :-
|
|
read_byte(ByteCode, MoreByte, !Pos),
|
|
is_more_procs(MoreByte, MoreProcs),
|
|
(
|
|
MoreProcs = no_more_procs
|
|
;
|
|
MoreProcs = next_proc,
|
|
read_proc_rep(ExpectNewFormat, ByteCode, StringTable, TypeTable,
|
|
ProcRep, !Pos),
|
|
map.det_insert(ProcRep ^ pr_id, ProcRep, !ProcReps),
|
|
disable_warning [suspicious_recursion] (
|
|
read_proc_reps(ExpectNewFormat, ByteCode, StringTable, TypeTable,
|
|
!ProcReps, !Pos)
|
|
)
|
|
).
|
|
|
|
:- pred read_proc_rep(bool::in, bytecode::in, string_table::in,
|
|
encoded_type_table::in, proc_rep(unit)::out, int::in, int::out) is semidet.
|
|
|
|
read_proc_rep(ExpectNewFormat, ByteCode, StringTable, TypeTable, ProcRep,
|
|
!Pos) :-
|
|
read_string_proc_label(ByteCode, ProcLabel, !Pos),
|
|
StartPos = !.Pos,
|
|
read_int32(ByteCode, Size, !Pos),
|
|
read_string_via_offset(ByteCode, StringTable, FileName, !Pos),
|
|
Info = read_proc_rep_info(FileName),
|
|
read_var_table(ExpectNewFormat, ByteCode, StringTable, TypeTable,
|
|
VarNumRep, VarNameTable, MaybeVarTypeTable, !Pos),
|
|
read_head_vars(VarNumRep, ByteCode, HeadVars, !Pos),
|
|
read_goal(VarNumRep, ByteCode, StringTable, Info, Goal, !Pos),
|
|
read_determinism(ByteCode, Detism, !Pos),
|
|
ProcDefnRep = proc_defn_rep(HeadVars, Goal, VarNameTable,
|
|
MaybeVarTypeTable, Detism),
|
|
expect(unify(!.Pos, StartPos + Size), $pred, "limit mismatch"),
|
|
ProcRep = proc_rep(ProcLabel, ProcDefnRep).
|
|
|
|
:- pred read_string_proc_label(bytecode::in, string_proc_label::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_string_proc_label(ByteCode, ProcLabel, !Pos) :-
|
|
read_byte(ByteCode, Byte, !Pos),
|
|
is_proclabel_kind(Byte, ProcLabelKind),
|
|
(
|
|
ProcLabelKind = proclabel_special,
|
|
read_len_string(ByteCode, TypeName, !Pos),
|
|
read_len_string(ByteCode, TypeModule, !Pos),
|
|
read_len_string(ByteCode, DefModule, !Pos),
|
|
read_len_string(ByteCode, PredName, !Pos),
|
|
read_num(ByteCode, Arity, !Pos),
|
|
read_num(ByteCode, ModeNum, !Pos),
|
|
ProcLabel = str_special_proc_label(TypeName, TypeModule, DefModule,
|
|
PredName, Arity, ModeNum)
|
|
;
|
|
(
|
|
ProcLabelKind = proclabel_user_predicate,
|
|
PredOrFunc = pf_predicate
|
|
;
|
|
ProcLabelKind = proclabel_user_function,
|
|
PredOrFunc = pf_function
|
|
),
|
|
read_len_string(ByteCode, DeclModule, !Pos),
|
|
read_len_string(ByteCode, DefModule, !Pos),
|
|
read_len_string(ByteCode, PredName, !Pos),
|
|
read_num(ByteCode, Arity, !Pos),
|
|
read_num(ByteCode, ModeNum, !Pos),
|
|
ProcLabel = str_ordinary_proc_label(PredOrFunc, DeclModule, DefModule,
|
|
PredName, Arity, ModeNum)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% Read the var table from the bytecode. The var table contains the names
|
|
% of all the variables used in the procedure representation, and may
|
|
% (or may not) also contain their types.
|
|
%
|
|
:- pred read_var_table(bool::in, bytecode::in, string_table::in,
|
|
encoded_type_table::in, var_num_rep::out, var_name_table::out,
|
|
maybe(var_type_table)::out, int::in, int::out) is semidet.
|
|
|
|
read_var_table(ExpectNewFormat, ByteCode, StringTable, TypeTable, VarNumRep,
|
|
VarNameTable, MaybeVarTypeTable, !Pos) :-
|
|
(
|
|
ExpectNewFormat = no,
|
|
read_var_num_rep(ByteCode, VarNumRep, !Pos),
|
|
read_int32(ByteCode, NumVarsInTable, !Pos),
|
|
read_var_name_table_entries(NumVarsInTable, VarNumRep, ByteCode,
|
|
StringTable, map.init, VarNameTable, !Pos),
|
|
MaybeVarTypeTable = no
|
|
;
|
|
ExpectNewFormat = yes,
|
|
read_var_flag(ByteCode, VarNumRep, IncludeVarNameTable,
|
|
IncludeVarTypes, !Pos),
|
|
(
|
|
IncludeVarNameTable = do_not_include_var_name_table,
|
|
expect(unify(IncludeVarTypes, do_not_include_var_types),
|
|
$pred, "var types but not names"),
|
|
map.init(VarNameTable),
|
|
MaybeVarTypeTable = no
|
|
;
|
|
IncludeVarNameTable = include_var_name_table,
|
|
(
|
|
IncludeVarTypes = do_not_include_var_types,
|
|
read_num(ByteCode, NumVarsInTable, !Pos),
|
|
read_var_name_table_entries(NumVarsInTable, VarNumRep,
|
|
ByteCode, StringTable, map.init, VarNameTable, !Pos),
|
|
MaybeVarTypeTable = no
|
|
;
|
|
IncludeVarTypes = include_var_types,
|
|
read_num(ByteCode, NumVarsInTable, !Pos),
|
|
read_var_name_type_table_entries(NumVarsInTable, VarNumRep,
|
|
ByteCode, StringTable, TypeTable, map.init, VarNameTable,
|
|
map.init, VarTypeTable, !Pos),
|
|
MaybeVarTypeTable = yes(VarTypeTable)
|
|
)
|
|
)
|
|
).
|
|
|
|
% Read entries from the variable name table until there are no more
|
|
% entries left to read.
|
|
%
|
|
:- pred read_var_name_table_entries(var_rep::in, var_num_rep::in,
|
|
bytecode::in, string_table::in, var_name_table::in, var_name_table::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_var_name_table_entries(NumVarsLeftInTable, VarNumRep,
|
|
ByteCode, StringTable, !VarNameTable, !Pos) :-
|
|
( if NumVarsLeftInTable > 0 then
|
|
read_var(VarNumRep, ByteCode, VarRep, !Pos),
|
|
read_string_via_offset(ByteCode, StringTable, VarName, !Pos),
|
|
map.det_insert(VarRep, VarName, !VarNameTable),
|
|
read_var_name_table_entries(NumVarsLeftInTable - 1, VarNumRep,
|
|
ByteCode, StringTable, !VarNameTable, !Pos)
|
|
else
|
|
% No more variables to read.
|
|
true
|
|
).
|
|
|
|
% Read entries from the variable name and type table until
|
|
% there are no more entries left to read.
|
|
%
|
|
:- pred read_var_name_type_table_entries(var_rep::in, var_num_rep::in,
|
|
bytecode::in, string_table::in, encoded_type_table::in,
|
|
var_name_table::in, var_name_table::out,
|
|
var_type_table::in, var_type_table::out, int::in, int::out) is semidet.
|
|
|
|
read_var_name_type_table_entries(NumVarsLeftInTable, VarNumRep,
|
|
ByteCode, StringTable, TypeTable, !VarNameTable, !VarTypeTable, !Pos) :-
|
|
( if NumVarsLeftInTable > 0 then
|
|
read_var(VarNumRep, ByteCode, VarRep, !Pos),
|
|
read_string_via_offset(ByteCode, StringTable, VarName, !Pos),
|
|
map.det_insert(VarRep, VarName, !VarNameTable),
|
|
read_num(ByteCode, TypeNum, !Pos),
|
|
map.lookup(TypeTable, TypeNum, TypeRep),
|
|
map.det_insert(VarRep, TypeRep, !VarTypeTable),
|
|
read_var_name_type_table_entries(NumVarsLeftInTable - 1, VarNumRep,
|
|
ByteCode, StringTable, TypeTable, !VarNameTable, !VarTypeTable,
|
|
!Pos)
|
|
else
|
|
% No more variables to read.
|
|
true
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- type read_proc_rep_info
|
|
---> read_proc_rep_info(
|
|
rpri_filename :: string
|
|
).
|
|
|
|
:- pred read_goal(var_num_rep::in, bytecode::in, string_table::in,
|
|
read_proc_rep_info::in, goal_rep::out, int::in, int::out) is semidet.
|
|
|
|
read_goal(VarNumRep, ByteCode, StringTable, Info, Goal, !Pos) :-
|
|
read_byte(ByteCode, GoalTypeByte, !Pos),
|
|
( if byte_to_goal_type(GoalTypeByte, GoalType) then
|
|
(
|
|
GoalType = goal_conj,
|
|
read_goals(VarNumRep, ByteCode, StringTable, Info, Goals, !Pos),
|
|
GoalExpr = conj_rep(Goals)
|
|
;
|
|
GoalType = goal_disj,
|
|
read_goals(VarNumRep, ByteCode, StringTable, Info, Goals, !Pos),
|
|
GoalExpr = disj_rep(Goals)
|
|
;
|
|
GoalType = goal_neg,
|
|
disable_warning [suspicious_recursion] (
|
|
read_goal(VarNumRep, ByteCode, StringTable, Info, SubGoal,
|
|
!Pos)
|
|
),
|
|
GoalExpr = negation_rep(SubGoal)
|
|
;
|
|
GoalType = goal_ite,
|
|
disable_warning [suspicious_recursion] (
|
|
read_goal(VarNumRep, ByteCode, StringTable, Info, Cond, !Pos),
|
|
read_goal(VarNumRep, ByteCode, StringTable, Info, Then, !Pos),
|
|
read_goal(VarNumRep, ByteCode, StringTable, Info, Else, !Pos)
|
|
),
|
|
GoalExpr = ite_rep(Cond, Then, Else)
|
|
;
|
|
GoalType = goal_switch,
|
|
read_switch_can_fail(ByteCode, CanFail, !Pos),
|
|
read_var(VarNumRep, ByteCode, Var, !Pos),
|
|
read_cases(VarNumRep, ByteCode, StringTable, Info, Cases, !Pos),
|
|
GoalExpr = switch_rep(Var, CanFail, Cases)
|
|
;
|
|
GoalType = goal_assign,
|
|
read_var(VarNumRep, ByteCode, Target, !Pos),
|
|
read_var(VarNumRep, ByteCode, Source, !Pos),
|
|
AtomicGoal = unify_assign_rep(Target, Source),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_construct,
|
|
read_var(VarNumRep, ByteCode, Var, !Pos),
|
|
read_cons_id(ByteCode, StringTable, ConsId, !Pos),
|
|
read_vars(VarNumRep, ByteCode, ArgVars, !Pos),
|
|
AtomicGoal = unify_construct_rep(Var, ConsId, ArgVars),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_deconstruct,
|
|
read_var(VarNumRep, ByteCode, Var, !Pos),
|
|
read_cons_id(ByteCode, StringTable, ConsId, !Pos),
|
|
read_vars(VarNumRep, ByteCode, ArgVars, !Pos),
|
|
AtomicGoal = unify_deconstruct_rep(Var, ConsId, ArgVars),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_partial_construct,
|
|
read_var(VarNumRep, ByteCode, Var, !Pos),
|
|
read_cons_id(ByteCode, StringTable, ConsId, !Pos),
|
|
read_maybe_vars(VarNumRep, ByteCode, MaybeVars, !Pos),
|
|
AtomicGoal = partial_construct_rep(Var, ConsId, MaybeVars),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_partial_deconstruct,
|
|
read_var(VarNumRep, ByteCode, Var, !Pos),
|
|
read_cons_id(ByteCode, StringTable, ConsId, !Pos),
|
|
read_maybe_vars(VarNumRep, ByteCode, MaybeVars, !Pos),
|
|
AtomicGoal = partial_deconstruct_rep(Var, ConsId, MaybeVars),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_simple_test,
|
|
read_var(VarNumRep, ByteCode, Var1, !Pos),
|
|
read_var(VarNumRep, ByteCode, Var2, !Pos),
|
|
AtomicGoal = unify_simple_test_rep(Var1, Var2),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_scope,
|
|
read_byte(ByteCode, MaybeCutByte, !Pos),
|
|
( if cut_byte(MaybeCutPrime, MaybeCutByte) then
|
|
MaybeCut = MaybeCutPrime
|
|
else
|
|
unexpected($pred, "bad maybe_cut")
|
|
),
|
|
disable_warning [suspicious_recursion] (
|
|
read_goal(VarNumRep, ByteCode, StringTable, Info, SubGoal,
|
|
!Pos)
|
|
),
|
|
GoalExpr = scope_rep(SubGoal, MaybeCut)
|
|
;
|
|
GoalType = goal_ho_call,
|
|
read_var(VarNumRep, ByteCode, Var, !Pos),
|
|
read_vars(VarNumRep, ByteCode, Args, !Pos),
|
|
AtomicGoal = higher_order_call_rep(Var, Args),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_method_call,
|
|
read_var(VarNumRep, ByteCode, Var, !Pos),
|
|
read_method_num(ByteCode, MethodNum, !Pos),
|
|
read_vars(VarNumRep, ByteCode, Args, !Pos),
|
|
AtomicGoal = method_call_rep(Var, MethodNum, Args),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_cast,
|
|
read_var(VarNumRep, ByteCode, OutputVar, !Pos),
|
|
read_var(VarNumRep, ByteCode, InputVar, !Pos),
|
|
AtomicGoal = cast_rep(OutputVar, InputVar),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_plain_call,
|
|
read_string_via_offset(ByteCode, StringTable, ModuleName, !Pos),
|
|
read_string_via_offset(ByteCode, StringTable, PredName, !Pos),
|
|
read_vars(VarNumRep, ByteCode, Args, !Pos),
|
|
AtomicGoal = plain_call_rep(ModuleName, PredName, Args),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_builtin_call,
|
|
read_string_via_offset(ByteCode, StringTable, ModuleName, !Pos),
|
|
read_string_via_offset(ByteCode, StringTable, PredName, !Pos),
|
|
read_vars(VarNumRep, ByteCode, Args, !Pos),
|
|
AtomicGoal = builtin_call_rep(ModuleName, PredName, Args),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_event_call,
|
|
read_string_via_offset(ByteCode, StringTable, EventName, !Pos),
|
|
read_vars(VarNumRep, ByteCode, Args, !Pos),
|
|
AtomicGoal = event_call_rep(EventName, Args),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable,
|
|
Info, AtomicGoal, GoalExpr, !Pos)
|
|
;
|
|
GoalType = goal_foreign,
|
|
read_vars(VarNumRep, ByteCode, Args, !Pos),
|
|
AtomicGoal = pragma_foreign_code_rep(Args),
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info,
|
|
AtomicGoal, GoalExpr, !Pos)
|
|
),
|
|
read_determinism(ByteCode, Detism, !Pos),
|
|
Goal = goal_rep(GoalExpr, Detism, unit)
|
|
else
|
|
unexpected($pred, "invalid goal type")
|
|
).
|
|
|
|
:- pred read_atomic_info(var_num_rep::in, bytecode::in, string_table::in,
|
|
read_proc_rep_info::in, atomic_goal_rep::in, goal_expr_rep(unit)::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_atomic_info(VarNumRep, ByteCode, StringTable, Info, AtomicGoal, GoalExpr,
|
|
!Pos) :-
|
|
read_string_via_offset(ByteCode, StringTable, FileName0, !Pos),
|
|
( if FileName0 = "" then
|
|
FileName = Info ^ rpri_filename
|
|
else
|
|
FileName = FileName0
|
|
),
|
|
read_lineno(ByteCode, LineNo, !Pos),
|
|
read_vars(VarNumRep, ByteCode, BoundVars, !Pos),
|
|
GoalExpr = atomic_goal_rep(FileName, LineNo, BoundVars, AtomicGoal).
|
|
|
|
:- pred read_goals(var_num_rep::in, bytecode::in, string_table::in,
|
|
read_proc_rep_info::in, list(goal_rep)::out, int::in, int::out) is semidet.
|
|
|
|
read_goals(VarNumRep, ByteCode, StringTable, Info, Goals, !Pos) :-
|
|
read_length(ByteCode, Len, !Pos),
|
|
read_n_items(read_goal(VarNumRep, ByteCode, StringTable, Info), Len, Goals,
|
|
!Pos).
|
|
|
|
:- pred read_cases(var_num_rep::in, bytecode::in, string_table::in,
|
|
read_proc_rep_info::in, list(case_rep(unit))::out, int::in, int::out)
|
|
is semidet.
|
|
|
|
read_cases(VarNumRep, ByteCode, StringTable, Info, Cases, !Pos) :-
|
|
read_length(ByteCode, Len, !Pos),
|
|
read_n_items(read_case(VarNumRep, ByteCode, StringTable, Info), Len, Cases,
|
|
!Pos).
|
|
|
|
:- pred read_case(var_num_rep::in, bytecode::in, string_table::in,
|
|
read_proc_rep_info::in, case_rep(unit)::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_case(VarNumRep, ByteCode, StringTable, Info, Case, !Pos) :-
|
|
read_cons_id_arity(ByteCode, StringTable, MainConsId, !Pos),
|
|
read_length(ByteCode, NumOtherConsIds, !Pos),
|
|
read_n_items(read_cons_id_arity(ByteCode, StringTable), NumOtherConsIds,
|
|
OtherConsIds, !Pos),
|
|
read_goal(VarNumRep, ByteCode, StringTable, Info, Goal, !Pos),
|
|
Case = case_rep(MainConsId, OtherConsIds, Goal).
|
|
|
|
:- pred read_cons_id_arity(bytecode::in, string_table::in,
|
|
cons_id_arity_rep::out, int::in, int::out) is semidet.
|
|
|
|
read_cons_id_arity(ByteCode, StringTable, ConsId, !Pos) :-
|
|
read_cons_id(ByteCode, StringTable, ConsIdFunctor, !Pos),
|
|
read_short(ByteCode, ConsIdArity, !Pos),
|
|
ConsId = cons_id_arity_rep(ConsIdFunctor, ConsIdArity).
|
|
|
|
:- pred read_vars(var_num_rep::in, bytecode::in, list(var_rep)::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_vars(VarNumRep, ByteCode, Vars, !Pos) :-
|
|
read_length(ByteCode, Len, !Pos),
|
|
read_n_items(read_var(VarNumRep, ByteCode), Len, Vars, !Pos).
|
|
|
|
:- pred read_var(var_num_rep::in, bytecode::in, var_rep::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_var(VarNumRep, ByteCode, Var, !Pos) :-
|
|
(
|
|
VarNumRep = var_num_1_byte,
|
|
read_byte(ByteCode, Var, !Pos)
|
|
;
|
|
VarNumRep = var_num_2_bytes,
|
|
read_short(ByteCode, Var, !Pos)
|
|
;
|
|
VarNumRep = var_num_4_bytes,
|
|
read_int32(ByteCode, Var, !Pos)
|
|
).
|
|
|
|
:- pred read_maybe_vars(var_num_rep::in, bytecode::in,
|
|
list(maybe(var_rep))::out, int::in, int::out) is semidet.
|
|
|
|
read_maybe_vars(VarNumRep, ByteCode, MaybeVars, !Pos) :-
|
|
read_length(ByteCode, Len, !Pos),
|
|
read_n_items(read_maybe_var(VarNumRep, ByteCode), Len, MaybeVars, !Pos).
|
|
|
|
:- pred read_maybe_var(var_num_rep::in, bytecode::in,
|
|
maybe(var_rep)::out, int::in, int::out) is semidet.
|
|
|
|
read_maybe_var(VarNumRep, ByteCode, MaybeVar, !Pos) :-
|
|
read_byte(ByteCode, YesOrNo, !Pos),
|
|
( if YesOrNo = 1 then
|
|
read_var(VarNumRep, ByteCode, Var, !Pos),
|
|
MaybeVar = yes(Var)
|
|
else if YesOrNo = 0 then
|
|
MaybeVar = no
|
|
else
|
|
unexpected($pred, "invalid yes or no flag")
|
|
).
|
|
|
|
:- pred read_head_vars(var_num_rep::in, bytecode::in,
|
|
list(head_var_rep)::out, int::in, int::out) is semidet.
|
|
|
|
read_head_vars(VarNumRep, ByteCode, HeadVars, !Pos) :-
|
|
read_length(ByteCode, Len, !Pos),
|
|
read_n_items(read_head_var(VarNumRep, ByteCode), Len, HeadVars, !Pos).
|
|
|
|
:- pred read_head_var(var_num_rep::in, bytecode::in, head_var_rep::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_head_var(VarNumRep, ByteCode, HeadVar, !Pos) :-
|
|
read_var(VarNumRep, ByteCode, Var, !Pos),
|
|
read_inst(ByteCode, InitialInst, !Pos),
|
|
read_inst(ByteCode, FinalInst, !Pos),
|
|
HeadVar = head_var_rep(Var, var_mode_rep(InitialInst, FinalInst)).
|
|
|
|
:- pred read_inst(bytecode::in, inst_rep::out, int::in, int::out) is semidet.
|
|
|
|
read_inst(ByteCode, Inst, !Pos) :-
|
|
read_byte(ByteCode, Byte, !Pos),
|
|
inst_representation(Inst, Byte).
|
|
|
|
:- pred read_length(bytecode::in, var_rep::out, int::in, int::out) is semidet.
|
|
|
|
read_length(ByteCode, Len, !Pos) :-
|
|
read_int32(ByteCode, Len, !Pos).
|
|
|
|
:- pred read_lineno(bytecode::in, int::out, int::in, int::out) is semidet.
|
|
|
|
read_lineno(ByteCode, LineNo, !Pos) :-
|
|
read_int32(ByteCode, LineNo, !Pos).
|
|
|
|
:- pred read_method_num(bytecode::in, int::out, int::in, int::out) is semidet.
|
|
|
|
read_method_num(ByteCode, MethodNum, !Pos) :-
|
|
read_short(ByteCode, MethodNum, !Pos).
|
|
|
|
:- pred read_cons_id(bytecode::in, string_table::in, cons_id_rep::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_cons_id(ByteCode, StringTable, ConsId, !Pos) :-
|
|
read_string_via_offset(ByteCode, StringTable, ConsId, !Pos).
|
|
|
|
:- pred read_var_num_rep(bytecode::in, var_num_rep::out, int::in, int::out)
|
|
is semidet.
|
|
|
|
read_var_num_rep(ByteCode, VarNumRep, !Pos) :-
|
|
read_byte(ByteCode, Byte, !Pos),
|
|
( if var_num_rep_byte(VarNumRepPrime, Byte) then
|
|
VarNumRep = VarNumRepPrime
|
|
else
|
|
unexpected($pred, "unknown var_num_rep")
|
|
).
|
|
|
|
:- pred read_var_flag(bytecode::in, var_num_rep::out,
|
|
maybe_include_var_name_table::out, maybe_include_var_types::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_var_flag(ByteCode, VarNumRep, IncludeVarNameTable, IncludeVarTypes,
|
|
!Pos) :-
|
|
read_byte(ByteCode, Byte, !Pos),
|
|
( if
|
|
var_flag_byte(VarNumRepPrime,
|
|
IncludeVarNameTablePrime, IncludeVarTypesPrime, Byte)
|
|
then
|
|
VarNumRep = VarNumRepPrime,
|
|
IncludeVarNameTable = IncludeVarNameTablePrime,
|
|
IncludeVarTypes = IncludeVarTypesPrime
|
|
else
|
|
unexpected($pred, "unknown var_flag_byte")
|
|
).
|
|
|
|
:- pred read_determinism(bytecode::in, detism_rep::out, int::in, int::out)
|
|
is semidet.
|
|
|
|
read_determinism(ByteCode, Detism, !Pos) :-
|
|
read_byte(ByteCode, DetismByte, !Pos),
|
|
( if determinism_representation(DetismPrime, DetismByte) then
|
|
Detism = DetismPrime
|
|
else
|
|
unexpected($pred, "bad detism")
|
|
).
|
|
|
|
:- pred read_switch_can_fail(bytecode::in, switch_can_fail_rep::out,
|
|
int::in, int::out) is semidet.
|
|
|
|
read_switch_can_fail(Bytecode, CanFail, !Pos) :-
|
|
read_byte(Bytecode, CanFailByte, !Pos),
|
|
( if
|
|
(
|
|
CanFailByte = 0,
|
|
CanFailPrime = switch_can_fail_rep
|
|
;
|
|
CanFailByte = 1,
|
|
CanFailPrime = switch_can_not_fail_rep
|
|
)
|
|
then
|
|
CanFail = CanFailPrime
|
|
else
|
|
unexpected($pred, "bad switch_can_fail")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type more_modules
|
|
---> no_more_modules
|
|
; next_module.
|
|
|
|
:- pragma foreign_enum("C", more_modules/0, [
|
|
no_more_modules - "MR_no_more_modules",
|
|
next_module - "MR_next_module"
|
|
]).
|
|
|
|
:- pred is_more_modules(int::in, more_modules::out) is semidet.
|
|
|
|
:- pragma foreign_proc("C",
|
|
is_more_modules(Int::in, MoreModules::out),
|
|
[promise_pure, will_not_call_mercury, thread_safe],
|
|
"
|
|
MoreModules = (MR_MoreModules) Int;
|
|
|
|
switch (MoreModules) {
|
|
case MR_no_more_modules:
|
|
case MR_next_module:
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
break;
|
|
|
|
default:
|
|
SUCCESS_INDICATOR = MR_FALSE;
|
|
break;
|
|
}
|
|
").
|
|
|
|
:- type more_procs
|
|
---> no_more_procs
|
|
; next_proc.
|
|
|
|
:- pragma foreign_enum("C", more_procs/0, [
|
|
no_more_procs - "MR_no_more_procs",
|
|
next_proc - "MR_next_proc"
|
|
]).
|
|
|
|
:- pred is_more_procs(int::in, more_procs::out) is semidet.
|
|
|
|
:- pragma foreign_proc("C",
|
|
is_more_procs(Int::in, MoreProcs::out),
|
|
[promise_pure, will_not_call_mercury, thread_safe],
|
|
"
|
|
MoreProcs = (MR_MoreProcs) Int;
|
|
|
|
switch (MoreProcs) {
|
|
case MR_no_more_procs:
|
|
case MR_next_proc:
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
break;
|
|
|
|
default:
|
|
SUCCESS_INDICATOR = MR_FALSE;
|
|
break;
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_enum("C", proclabel_kind_token/0, [
|
|
proclabel_user_predicate - "MR_proclabel_user_predicate",
|
|
proclabel_user_function - "MR_proclabel_user_function",
|
|
proclabel_special - "MR_proclabel_special"
|
|
]).
|
|
|
|
:- pragma foreign_proc("C",
|
|
is_proclabel_kind(Int::in, ProcLabelKind::out),
|
|
[promise_pure, will_not_call_mercury, thread_safe],
|
|
"
|
|
ProcLabelKind = (MR_ProcLabelToken) Int;
|
|
|
|
switch (ProcLabelKind) {
|
|
case MR_proclabel_user_predicate:
|
|
case MR_proclabel_user_function:
|
|
case MR_proclabel_special:
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
break;
|
|
|
|
default:
|
|
SUCCESS_INDICATOR = MR_FALSE;
|
|
break;
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% An abstraction to read the given number of items using the higher order
|
|
% predicate.
|
|
%
|
|
:- pred read_n_items(pred(T, int, int), int, list(T), int, int).
|
|
:- mode read_n_items(in(pred(out, in, out) is det), in, out, in, out) is det.
|
|
:- mode read_n_items(in(pred(out, in, out) is semidet), in, out, in, out)
|
|
is semidet.
|
|
|
|
read_n_items(Read, N, Items, !Pos) :-
|
|
( if N > 0 then
|
|
Read(Item, !Pos),
|
|
read_n_items(Read, N - 1, TailItems, !Pos),
|
|
Items = [ Item | TailItems ]
|
|
else
|
|
Items = []
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
no_type_info_builtin(ModuleName, PredName, Arity) :-
|
|
% NOTE Any predicates listed here should also be handled by
|
|
% process_no_type_info_builtin in compiler/term_constr_initial.m.
|
|
(
|
|
PredName = "unsafe_promise_unique", Arity = 2,
|
|
ModuleName = mercury_public_builtin_module
|
|
;
|
|
( PredName = "builtin_compound_eq", Arity = 2
|
|
; PredName = "builtin_compound_lt", Arity = 2
|
|
; PredName = "compare_local_int16_bitfields", Arity = 4
|
|
; PredName = "compare_local_int32_bitfields", Arity = 4
|
|
; PredName = "compare_local_int8_bitfields", Arity = 4
|
|
; PredName = "compare_local_uint_bitfields", Arity = 5
|
|
; PredName = "compare_local_uint_words", Arity = 3
|
|
; PredName = "compare_remote_int16_bitfields", Arity = 6
|
|
; PredName = "compare_remote_int32_bitfields", Arity = 6
|
|
; PredName = "compare_remote_int8_bitfields", Arity = 6
|
|
; PredName = "compare_remote_uint_bitfields", Arity = 7
|
|
; PredName = "compare_remote_uint_words", Arity = 5
|
|
; PredName = "instance_constraint_from_typeclass_info", Arity = 3
|
|
; PredName = "partial_inst_copy", Arity = 2
|
|
; PredName = "store_at_ref_impure", Arity = 2
|
|
; PredName = "superclass_from_typeclass_info", Arity = 3
|
|
; PredName = "type_info_from_typeclass_info", Arity = 3
|
|
; PredName = "unconstrained_type_info_from_typeclass_info", Arity = 3
|
|
; PredName = "unify_remote_arg_words", Arity = 4
|
|
; PredName = "unsafe_type_cast", Arity = 2
|
|
),
|
|
ModuleName = mercury_private_builtin_module
|
|
;
|
|
( PredName = "table_lookup_insert_enum", Arity = 4
|
|
; PredName = "table_lookup_insert_typeclassinfo", Arity = 3
|
|
; PredName = "table_lookup_insert_typeinfo", Arity = 3
|
|
; PredName = "table_restore_any_answer", Arity = 3
|
|
),
|
|
ModuleName = mercury_table_builtin_module
|
|
;
|
|
PredName = "increment_size", Arity = 2,
|
|
ModuleName = mercury_term_size_prof_builtin_module
|
|
;
|
|
( PredName = "get_future", Arity = 2
|
|
; PredName = "new_future", Arity = 2
|
|
; PredName = "signal_future", Arity = 2
|
|
; PredName = "wait_future", Arity = 2
|
|
),
|
|
ModuleName = mercury_par_builtin_module
|
|
;
|
|
( PredName = "result_call_4", Arity = 4
|
|
; PredName = "result_call_5", Arity = 5
|
|
; PredName = "result_call_6", Arity = 6
|
|
; PredName = "result_call_7", Arity = 7
|
|
; PredName = "result_call_8", Arity = 8
|
|
; PredName = "result_call_9", Arity = 9
|
|
; PredName = "semidet_call_3", Arity = 3
|
|
; PredName = "semidet_call_4", Arity = 4
|
|
; PredName = "semidet_call_5", Arity = 5
|
|
; PredName = "semidet_call_6", Arity = 6
|
|
; PredName = "semidet_call_7", Arity = 7
|
|
; PredName = "semidet_call_8", Arity = 8
|
|
),
|
|
ModuleName = mercury_rtti_implementation_builtin_module
|
|
).
|
|
|
|
% True iff the given predicate has a `:- pragma external_pred' declaration.
|
|
% Note that the arity includes the hidden type info arguments for
|
|
% polymorphic predicates.
|
|
%
|
|
:- pred pred_is_external(string::in, string::in, int::in) is semidet.
|
|
|
|
pred_is_external("exception", "builtin_catch", 4).
|
|
pred_is_external("exception", "builtin_throw", 1).
|
|
pred_is_external("builtin", "unify", 3).
|
|
pred_is_external("builtin", "compare", 4).
|
|
pred_is_external("builtin", "compare_representation", 4).
|
|
pred_is_external("backjump", "builtin_choice_id", 1).
|
|
pred_is_external("backjump", "builtin_backjump", 1).
|
|
pred_is_external("par_builtin", "lc_finish", 1).
|
|
pred_is_external("par_builtin", "lc_wait_free_slot", 2).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Please keep runtime/mercury_deep_profiling.h updated when modifying this
|
|
% section.
|
|
%
|
|
|
|
:- pragma foreign_enum("C", cp_type/0,
|
|
[
|
|
cp_type_coverage_after - "MR_cp_type_coverage_after",
|
|
cp_type_branch_arm - "MR_cp_type_branch_arm"
|
|
]).
|
|
|
|
coverage_point_type_c_value(cp_type_coverage_after,
|
|
"MR_cp_type_coverage_after").
|
|
coverage_point_type_c_value(cp_type_branch_arm,
|
|
"MR_cp_type_branch_arm").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module mdbcomp.program_representation.
|
|
%---------------------------------------------------------------------------%
|