mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 13:55:07 +00:00
Add the new builtin types: int64 and uint64.
Support for these new types will need to be bootstrapped over several changes.
This is the first such change and does the following:
- Extends the compiler to recognise 'int64' and 'uint64' as builtin types.
- Extends the set of builtin arithmetic, bitwise and relational operators
to cover the new types.
- Adds the new internal option '--unboxed-int64s' to the compiler; this will be
used to control whether 64-bit integer types are boxed or not.
- Extends all of the code generators to handle the new types.
- Extends the runtimes to support the new types.
- Adds new modules to the standard library intend to contain basic operations
on the new types. (These are currently empty and not documented.)
There are bunch of limitations marks with "XXX INT64"; these will be lifted in
part 2 of this change. Also, 64-bit integer types are currently always boxed,
again this limitation will be lifted in later changes.
compiler/options.m:
Add the new option --unboxed-int64s.
compiler/prog_type.m:
compiler/prog_data.m:
compiler/builtin_lib_types.m:
Recognise int64 and uint64 as builtin types.
compiler/builtin_ops.m:
Add builtin operations for the new types.
compiler/hlds_data.m:
Add new tag types for the new types.
compiler/ctgc.selector.m:
compiler/dead_proc_elim.m:
compiler/export.m:
compiler/foreign.m:
compiler/goal_util.m:
compiler/higher_order.m:
compiler/hlds_code_util.m:
compiler/hlds_dependency_graph.m:
compiler/hlds_out_pred.m:
compiler/hlds_out_util.m:
compiler/implementation_defined_literals.m:
compiler/inst_check.m:
compiler/mercury_to_mercury.m:
compiler/mode_util.m:
compiler/module_qual.qualify_items.m:
compiler/opt_debug.m:
compiler/opt_util.m:
compiler/parse_tree_to_term.m:
compiler/parse_type_name.m:
compiler/polymorphism.m:
compiler/prog_out.m:
compiler/prog_util.m:
compiler/rbmm.execution_path.m:
compiler/rtti.m:
compiler/table_gen.m:
compiler/type_util.m:
compiler/typecheck.m:
compiler/unify_gen.m:
compiler/unify_proc.m:
compiler/unused_imports.m:
compiler/xml_documentation.m:
Conform to the above changes to the parse tree and HLDS.
compiler/c_util.m:
Support writing out constants of the new types.
compiler/llds.m:
Add a representation for constants of the new types to the LLDS.
compiler/stack_layout.m:
Add a new field to the stack layout params that records whether
64-bit integers are boxed or not.
compiler/call_gen.:m
compiler/code_info.m:
compiler/disj_gen.m:
compiler/dupproc.m:
compiler/exprn_aux.m:
compiler/global_data.m:
compiler/jumpopt.m:
compiler/llds_out_data.m:
compiler/llds_out_instr.m:
compiler/lookup_switch.m:
compiler/mercury_compile_llds_back_end.m:
compiler/prog_rep.m:
compiler/prog_rep_tables.m:
compiler/var_locn.m b/compiler/var_locn.m:
Support the new types in the LLDS code generator.
compiler/mlds.m:
Support constants of the new types in the MLDS.
compiler/ml_call_gen.m:
compiler/ml_code_util.m:
compiler/ml_global_data.m:
compiler/ml_rename_classes.m:
compiler/ml_top_gen.m:
compiler/ml_type_gen.m:
compiler/ml_unify_gen.m:
compiler/ml_util.m:
compiler/mlds_to_target_util.m:
compiler/rtti_to_mlds.m:
Conform to the above changes to the MLDS.
compiler/mlds_to_c.m:
compiler/mlds_to_cs.m:
compiler/mlds_to_java.m:
Generate the appropriate target code for constants of the new types
and operations involving them.
compiler/bytecode.m:
compiler/bytecode_gen.m:
Handle the new types in the bytecode generator; we just abort if we
encounter them for now.
compiler/elds.m:
compiler/elds_to_erlang.m:
compiler/erl_call_gen.m:
compiler/erl_code_util.m:
compiler/erl_unify_gen.m:
Handle the new types in the Erlang code generator.
library/private_builtin.m:
Add placeholders for the builtin unify and compare operations for
the new types. Since the bootstrapping compiler will not recognise
the new types we give them polymorphic arguments. These can be
replaced after this change has bootstrapped.
Update the Java list of TypeCtorRep constants here.
library/int64.m:
library/uint64.m:
New modules that will eventually contain builtin operations on the new
types.
library/library.m:
library/MODULES_UNDOC:
Do not include the above modules in the library documentation for now.
library/construct.m:
library/erlang_rtti_implementation.m:
library/rtti_implementation.m:
library/table_statistics.m:
deep_profiler/program_representation_utils.m:
mdbcomp/program_representation.m:
Handle the new types.
configure.ac:
runtime/mercury_conf.h.in:
Define the macro MR_BOXED_INT64S. For now it is always defined, support for
unboxed 64-bit integers will be enabled in a later change.
runtime/mercury_dotnet.cs.in:
java/runtime/TypeCtorRep.java:
runtime/mercury_type_info.h:
Update the list of type_ctor reps.
runtime/mercury.h:
runtime/mercury_int.[ch]:
Add macros for int64 / uint64 -> MR_Word conversion, boxing and
unboxing.
Add functions for hashing 64-bit integer types suitable for use
with the tabling mechanism.
runtime/mercury_tabling.[ch]:
Add additional HashTableSlot structs for 64-bit integer types.
Omit the '%' character from the conversion specifiers we pass via
the 'key_format' argument to the macros that generate the table lookup
function. This is so we can use the C99 exact size integer conversion
specifiers (e.g. PRIu64 etc.) directly here.
runtime/mercury_hash_lookup_or_add_body.h:
Add the '%' character that was omitted above to the call to debug_key_msg.
runtime/mercury_memory.h:
Add new builtin allocation sites for boxed 64-bit integer types.
runtime/mercury_builtin_types.[ch]:
runtime/mercury_builitn_types_proc_layouts.h:
runtime/mercury_construct.c:
runtime/mercury_deconstruct.c:
runtime/mercury_deep_copy_body.h:
runtime/mercury_ml_expand_body.h:
runtime/mercury_table_type_body.h:
runtime/mercury_tabling_macros.h:
runtime/mercury_tabling_preds.h:
runtime/mercury_term_size.c:
runtime/mercury_unify_compare_body.h:
Add the new builtin types and handle them throughout the runtime.
runtime/Mmakefile:
Add mercury_int.c to the list of .c files.
doc/reference_manual.texi:
Add the new types to the list of reserved type names.
Add the mapping from the new types to their target language types.
These are commented out for now.
2402 lines
94 KiB
Mathematica
2402 lines
94 KiB
Mathematica
%---------------------------------------------------------------------------e
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------e
|
|
% Copyright (C) 1994-2012 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: unify_gen.m.
|
|
%
|
|
% This module handles code generation for "simple" unifications,
|
|
% i.e. those unifications which are simple enough for us to generate
|
|
% inline code.
|
|
%
|
|
% For "complicated" unifications, we generate a call to an out-of-line
|
|
% unification predicate (the call is handled in call_gen.m) - and then
|
|
% eventually generate the out-of-line code (unify_proc.m).
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.unify_gen.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module ll_backend.code_info.
|
|
:- import_module ll_backend.code_loc_dep.
|
|
:- import_module ll_backend.global_data.
|
|
:- import_module ll_backend.llds.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type test_sense
|
|
---> branch_on_success
|
|
; branch_on_failure.
|
|
|
|
:- pred generate_unification(code_model::in, unification::in,
|
|
hlds_goal_info::in, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
:- pred generate_tag_test(prog_var::in, cons_id::in,
|
|
maybe_cheaper_tag_test::in, test_sense::in, label::out, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
:- pred generate_raw_tag_test_case(rval::in, mer_type::in, string::in,
|
|
tagged_cons_id::in, list(tagged_cons_id)::in, maybe_cheaper_tag_test::in,
|
|
test_sense::in, label::out, llds_code::out, code_info::in, code_info::out)
|
|
is det.
|
|
|
|
:- pred generate_ground_term(prog_var::in, hlds_goal::in,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
:- pred generate_const_structs(module_info::in, const_struct_map::out,
|
|
global_data::in, global_data::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.arg_pack.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module backend_libs.proc_label.
|
|
:- import_module backend_libs.rtti.
|
|
:- import_module backend_libs.type_class_info.
|
|
:- import_module check_hlds.
|
|
:- import_module check_hlds.mode_util.
|
|
:- import_module check_hlds.type_util.
|
|
:- import_module hlds.const_struct.
|
|
:- import_module hlds.hlds_code_util.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_out.hlds_out_goal.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.hlds_rtti.
|
|
:- import_module hlds.vartypes.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module ll_backend.code_util.
|
|
:- import_module ll_backend.continuation_info.
|
|
:- import_module ll_backend.layout.
|
|
:- import_module ll_backend.stack_layout.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.prog_out.
|
|
:- import_module parse_tree.prog_type.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type uni_val
|
|
---> ref(prog_var)
|
|
; lval(lval, arg_width).
|
|
% The argument may occupy a word, two words, or only part of a
|
|
% word.
|
|
|
|
:- type field_addr
|
|
---> field_addr(
|
|
fa_offset :: int,
|
|
fa_var :: prog_var
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_unification(CodeModel, Uni, GoalInfo, Code, !CI, !CLD) :-
|
|
(
|
|
CodeModel = model_det
|
|
;
|
|
CodeModel = model_semi
|
|
;
|
|
CodeModel = model_non,
|
|
unexpected($module, $pred, "nondet unification")
|
|
),
|
|
(
|
|
Uni = assign(LHSVar, RHSVar),
|
|
( if variable_is_forward_live(!.CLD, LHSVar) then
|
|
generate_assignment(LHSVar, RHSVar, Code, !CLD)
|
|
else
|
|
Code = empty
|
|
)
|
|
;
|
|
Uni = construct(LHSVar, ConsId, RHSVars, ArgModes, HowToConstruct, _,
|
|
SubInfo),
|
|
(
|
|
SubInfo = no_construct_sub_info,
|
|
MaybeTakeAddr = no,
|
|
MaybeSize = no
|
|
;
|
|
SubInfo = construct_sub_info(MaybeTakeAddr, MaybeSize)
|
|
),
|
|
( if
|
|
( variable_is_forward_live(!.CLD, LHSVar)
|
|
; MaybeTakeAddr = yes(_)
|
|
)
|
|
then
|
|
(
|
|
MaybeTakeAddr = yes(TakeAddr)
|
|
;
|
|
MaybeTakeAddr = no,
|
|
TakeAddr = []
|
|
),
|
|
get_module_info(!.CI, ModuleInfo),
|
|
get_cons_arg_widths(ModuleInfo, ConsId, RHSVars, ConsArgWidths),
|
|
generate_construction(LHSVar, ConsId, RHSVars, ArgModes,
|
|
ConsArgWidths, HowToConstruct, TakeAddr, MaybeSize, GoalInfo,
|
|
Code, !CI, !CLD)
|
|
else
|
|
Code = empty
|
|
)
|
|
;
|
|
Uni = deconstruct(LHSVar, ConsId, RHSVars, ArgModes, _CanFail, CanCGC),
|
|
get_module_info(!.CI, ModuleInfo),
|
|
get_cons_arg_widths(ModuleInfo, ConsId, RHSVars, ConsArgWidths),
|
|
(
|
|
CodeModel = model_det,
|
|
generate_det_deconstruction(LHSVar, ConsId, RHSVars, ArgModes,
|
|
ConsArgWidths, Code0, !.CI, !CLD)
|
|
;
|
|
CodeModel = model_semi,
|
|
generate_semi_deconstruction(LHSVar, ConsId, RHSVars, ArgModes,
|
|
ConsArgWidths, Code0, !CI, !CLD)
|
|
),
|
|
(
|
|
CanCGC = can_cgc,
|
|
LHSVarName = variable_name(!.CI, LHSVar),
|
|
produce_variable(LHSVar, ProduceVar, VarRval, !.CI, !CLD),
|
|
( if VarRval = lval(VarLval) then
|
|
save_reused_cell_fields(LHSVar, VarLval, SaveArgs, Regs,
|
|
!.CI, !CLD),
|
|
% This seems to be fine.
|
|
list.foldl(release_reg, Regs, !CLD),
|
|
% XXX avoid strip_tag when we know what tag it will have
|
|
FreeVar = singleton(
|
|
llds_instr(free_heap(unop(strip_tag, VarRval)),
|
|
"Free " ++ LHSVarName)
|
|
),
|
|
Code = Code0 ++ ProduceVar ++ SaveArgs ++ FreeVar
|
|
else
|
|
Code = Code0
|
|
)
|
|
;
|
|
CanCGC = cannot_cgc,
|
|
Code = Code0
|
|
)
|
|
;
|
|
Uni = simple_test(VarA, VarB),
|
|
(
|
|
CodeModel = model_det,
|
|
unexpected($module, $pred, "det simple_test")
|
|
;
|
|
CodeModel = model_semi,
|
|
generate_test(VarA, VarB, Code, !CI, !CLD)
|
|
)
|
|
;
|
|
% These should have been transformed into calls to unification
|
|
% procedures by polymorphism.m.
|
|
Uni = complicated_unify(_Mode, _CanFail, _TypeInfoVars),
|
|
unexpected($module, $pred, "complicated unify")
|
|
).
|
|
|
|
:- pred get_cons_arg_widths(module_info::in, cons_id::in,
|
|
list(T)::in, list(arg_width)::out) is det.
|
|
|
|
get_cons_arg_widths(ModuleInfo, ConsId, Args, AllArgWidths) :-
|
|
( if
|
|
ConsId = cons(_, _, TypeCtor),
|
|
get_cons_defn(ModuleInfo, TypeCtor, ConsId, ConsDefn)
|
|
then
|
|
ConsArgs = ConsDefn ^ cons_args,
|
|
ArgWidths = list.map((func(C) = C ^ arg_width), ConsArgs),
|
|
list.length(Args, NumArgs),
|
|
list.length(ConsArgs, NumConsArgs),
|
|
NumExtraArgs = NumArgs - NumConsArgs,
|
|
( if NumExtraArgs = 0 then
|
|
AllArgWidths = ArgWidths
|
|
else if NumExtraArgs > 0 then
|
|
ExtraArgWidths = list.duplicate(NumExtraArgs, full_word),
|
|
AllArgWidths = ExtraArgWidths ++ ArgWidths
|
|
else
|
|
unexpected($module, $pred, "too few arguments")
|
|
)
|
|
else
|
|
AllArgWidths = list.duplicate(length(Args), full_word)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Assignment unifications are generated by simply caching the bound
|
|
% variable as the expression that generates the free variable.
|
|
% No immediate code is generated.
|
|
%
|
|
:- pred generate_assignment(prog_var::in, prog_var::in, llds_code::out,
|
|
code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_assignment(VarA, VarB, empty, !CLD) :-
|
|
( if variable_is_forward_live(!.CLD, VarA) then
|
|
assign_var_to_var(VarA, VarB, !CLD)
|
|
else
|
|
% For free-free unifications, the mode analysis reports them as
|
|
% assignment to the dead variable. For such unifications we of course
|
|
% do not generate any code.
|
|
true
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% A [simple] test unification is generated by flushing both variables
|
|
% from the cache, and producing code that branches to the fall-through
|
|
% point if the two values are not the same. Simple tests are in-in
|
|
% unifications on enumerations, integers, strings and floats.
|
|
%
|
|
:- pred generate_test(prog_var::in, prog_var::in, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_test(VarA, VarB, Code, !CI, !CLD) :-
|
|
IsDummy = variable_is_of_dummy_type(!.CI, VarA),
|
|
(
|
|
IsDummy = is_dummy_type,
|
|
Code = empty
|
|
;
|
|
IsDummy = is_not_dummy_type,
|
|
produce_variable(VarA, CodeA, ValA, !.CI, !CLD),
|
|
produce_variable(VarB, CodeB, ValB, !.CI, !CLD),
|
|
Type = variable_type(!.CI, VarA),
|
|
( if Type = builtin_type(BuiltinType) then
|
|
(
|
|
BuiltinType = builtin_type_string,
|
|
Op = str_eq
|
|
;
|
|
BuiltinType = builtin_type_float,
|
|
Op = float_eq
|
|
;
|
|
BuiltinType = builtin_type_char,
|
|
Op = eq(int_type_int)
|
|
;
|
|
BuiltinType = builtin_type_int(IntType),
|
|
Op = eq(IntType)
|
|
)
|
|
else
|
|
% The else branch handles enumerations.
|
|
Op = eq(int_type_int)
|
|
),
|
|
fail_if_rval_is_false(binop(Op, ValA, ValB), FailCode, !CI, !CLD),
|
|
Code = CodeA ++ CodeB ++ FailCode
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_raw_tag_test_case(VarRval, VarType, VarName,
|
|
MainTaggedConsId, OtherTaggedConsIds, CheaperTagTest,
|
|
Sense, ElseLabel, Code, !CI) :-
|
|
(
|
|
OtherTaggedConsIds = [],
|
|
MainTaggedConsId = tagged_cons_id(MainConsId, MainConsTag),
|
|
generate_raw_tag_test(VarRval, VarType, VarName,
|
|
MainConsId, yes(MainConsTag), CheaperTagTest, Sense, ElseLabel,
|
|
Code, !CI)
|
|
;
|
|
OtherTaggedConsIds = [_ | _],
|
|
% The cheaper tag test optimization doesn't apply.
|
|
project_cons_name_and_tag(MainTaggedConsId, MainConsName, MainConsTag),
|
|
list.map2(project_cons_name_and_tag, OtherTaggedConsIds,
|
|
OtherConsNames, OtherConsTags),
|
|
Comment = branch_sense_comment(Sense) ++
|
|
case_comment(VarName, MainConsName, OtherConsNames),
|
|
raw_tag_test(VarRval, MainConsTag, MainTagTestRval),
|
|
list.map(raw_tag_test(VarRval), OtherConsTags, OtherTagTestRvals),
|
|
disjoin_tag_tests(MainTagTestRval, OtherTagTestRvals, TestRval),
|
|
get_next_label(ElseLabel, !CI),
|
|
(
|
|
Sense = branch_on_success,
|
|
TheRval = TestRval
|
|
;
|
|
Sense = branch_on_failure,
|
|
code_util.neg_rval(TestRval, TheRval)
|
|
),
|
|
Code = singleton(
|
|
llds_instr(if_val(TheRval, code_label(ElseLabel)), Comment)
|
|
)
|
|
).
|
|
|
|
:- pred disjoin_tag_tests(rval::in, list(rval)::in, rval::out) is det.
|
|
|
|
disjoin_tag_tests(CurTestRval, OtherTestRvals, TestRval) :-
|
|
(
|
|
OtherTestRvals = [],
|
|
TestRval = CurTestRval
|
|
;
|
|
OtherTestRvals = [HeadTestRval | TailTestRvals],
|
|
NextTestRval = binop(logical_or, CurTestRval, HeadTestRval),
|
|
disjoin_tag_tests(NextTestRval, TailTestRvals, TestRval)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_tag_test(Var, ConsId, CheaperTagTest, Sense, ElseLabel, Code,
|
|
!CI, !CLD) :-
|
|
produce_variable(Var, VarCode, VarRval, !.CI, !CLD),
|
|
VarType = variable_type(!.CI, Var),
|
|
VarName = variable_name(!.CI, Var),
|
|
generate_raw_tag_test(VarRval, VarType, VarName, ConsId, no,
|
|
CheaperTagTest, Sense, ElseLabel, TestCode, !CI),
|
|
Code = VarCode ++ TestCode.
|
|
|
|
:- pred generate_raw_tag_test(rval::in, mer_type::in, string::in,
|
|
cons_id::in, maybe(cons_tag)::in,
|
|
maybe_cheaper_tag_test::in, test_sense::in, label::out, llds_code::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
generate_raw_tag_test(VarRval, _VarType, VarName, ConsId, MaybeConsTag,
|
|
CheaperTagTest, Sense, ElseLabel, Code, !CI) :-
|
|
ConsIdName = cons_id_and_arity_to_string(ConsId),
|
|
% As an optimization, for data types with exactly two alternatives,
|
|
% one of which is a constant, we make sure that we test against the
|
|
% constant (negating the result of the test, if needed),
|
|
% since a test against a constant is cheaper than a tag test.
|
|
( if
|
|
CheaperTagTest = cheaper_tag_test(ExpensiveConsId, _ExpensiveConsTag,
|
|
_CheapConsId, CheapConsTag),
|
|
ConsId = ExpensiveConsId
|
|
then
|
|
Comment = branch_sense_comment(Sense) ++ VarName ++
|
|
" has functor " ++ ConsIdName ++ " (inverted test)",
|
|
raw_tag_test(VarRval, CheapConsTag, NegTestRval),
|
|
code_util.neg_rval(NegTestRval, TestRval)
|
|
else
|
|
Comment = branch_sense_comment(Sense) ++ VarName ++
|
|
" has functor " ++ ConsIdName,
|
|
(
|
|
MaybeConsTag = yes(ConsTag)
|
|
% Our caller has already computed ConsTag.
|
|
;
|
|
MaybeConsTag = no,
|
|
get_module_info(!.CI, ModuleInfo),
|
|
ConsTag = cons_id_to_tag(ModuleInfo, ConsId)
|
|
),
|
|
raw_tag_test(VarRval, ConsTag, TestRval)
|
|
),
|
|
get_next_label(ElseLabel, !CI),
|
|
(
|
|
Sense = branch_on_success,
|
|
TheRval = TestRval
|
|
;
|
|
Sense = branch_on_failure,
|
|
code_util.neg_rval(TestRval, TheRval)
|
|
),
|
|
Code = singleton(
|
|
llds_instr(if_val(TheRval, code_label(ElseLabel)), Comment)
|
|
).
|
|
|
|
:- func branch_sense_comment(test_sense) = string.
|
|
|
|
branch_sense_comment(branch_on_success) =
|
|
"branch away if ".
|
|
branch_sense_comment(branch_on_failure) =
|
|
"branch away unless ".
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred raw_tag_test(rval::in, cons_tag::in, rval::out) is det.
|
|
|
|
raw_tag_test(Rval, ConsTag, TestRval) :-
|
|
(
|
|
ConsTag = string_tag(String),
|
|
TestRval = binop(str_eq, Rval, const(llconst_string(String)))
|
|
;
|
|
ConsTag = float_tag(Float),
|
|
TestRval = binop(float_eq, Rval, const(llconst_float(Float)))
|
|
;
|
|
ConsTag = int_tag(IntTag),
|
|
int_tag_to_const_and_int_type(IntTag, Const, IntType),
|
|
TestRval = binop(eq(IntType), Rval, const(Const))
|
|
;
|
|
ConsTag = foreign_tag(ForeignLang, ForeignVal),
|
|
expect(unify(ForeignLang, lang_c), $module, $pred,
|
|
"foreign tag for language other than C"),
|
|
TestRval = binop(eq(int_type_int), Rval,
|
|
const(llconst_foreign(ForeignVal, lt_int(int_type_int))))
|
|
;
|
|
ConsTag = closure_tag(_, _, _),
|
|
% This should never happen, since the error will be detected
|
|
% during mode checking.
|
|
unexpected($module, $pred, "Attempted higher-order unification")
|
|
;
|
|
ConsTag = type_ctor_info_tag(_, _, _),
|
|
unexpected($module, $pred, "Attempted type_ctor_info unification")
|
|
;
|
|
ConsTag = base_typeclass_info_tag(_, _, _),
|
|
unexpected($module, $pred, "Attempted base_typeclass_info unification")
|
|
;
|
|
ConsTag = type_info_const_tag(_),
|
|
unexpected($module, $pred, "Attempted type_info_const_tag unification")
|
|
;
|
|
ConsTag = typeclass_info_const_tag(_),
|
|
unexpected($module, $pred,
|
|
"Attempted typeclass_info_const_tag unification")
|
|
;
|
|
ConsTag = ground_term_const_tag(_, _),
|
|
unexpected($module, $pred,
|
|
"Attempted ground_term_const_tag unification")
|
|
;
|
|
ConsTag = tabling_info_tag(_, _),
|
|
unexpected($module, $pred, "Attempted tabling_info unification")
|
|
;
|
|
ConsTag = deep_profiling_proc_layout_tag(_, _),
|
|
unexpected($module, $pred,
|
|
"Attempted deep_profiling_proc_layout_tag unification")
|
|
;
|
|
ConsTag = table_io_entry_tag(_, _),
|
|
unexpected($module, $pred, "Attempted table_io_entry_tag unification")
|
|
;
|
|
ConsTag = no_tag,
|
|
TestRval = const(llconst_true)
|
|
;
|
|
ConsTag = single_functor_tag,
|
|
TestRval = const(llconst_true)
|
|
;
|
|
( ConsTag = unshared_tag(UnsharedTag)
|
|
; ConsTag = direct_arg_tag(UnsharedTag)
|
|
),
|
|
VarPtag = unop(tag, Rval),
|
|
ConstPtag = unop(mktag, const(llconst_int(UnsharedTag))),
|
|
TestRval = binop(eq(int_type_int), VarPtag, ConstPtag)
|
|
;
|
|
ConsTag = shared_remote_tag(Bits, Num),
|
|
VarPtag = unop(tag, Rval),
|
|
ConstPtag = unop(mktag, const(llconst_int(Bits))),
|
|
PtagTestRval = binop(eq(int_type_int), VarPtag, ConstPtag),
|
|
VarStag = lval(field(yes(Bits), Rval, const(llconst_int(0)))),
|
|
ConstStag = const(llconst_int(Num)),
|
|
StagTestRval = binop(eq(int_type_int), VarStag, ConstStag),
|
|
TestRval = binop(logical_and, PtagTestRval, StagTestRval)
|
|
;
|
|
ConsTag = shared_local_tag(Bits, Num),
|
|
ConstStag = mkword(Bits, unop(mkbody, const(llconst_int(Num)))),
|
|
TestRval = binop(eq(int_type_int), Rval, ConstStag)
|
|
;
|
|
ConsTag = reserved_address_tag(RA),
|
|
TestRval = binop(eq(int_type_int), Rval, generate_reserved_address(RA))
|
|
;
|
|
ConsTag = shared_with_reserved_addresses_tag(ReservedAddrs, ThisTag),
|
|
% We first check that the Rval doesn't match any of the ReservedAddrs,
|
|
% and then check that it matches ThisTag.
|
|
CheckReservedAddrs = (func(RA, InnerTestRval0) = InnerTestRval :-
|
|
raw_tag_test(Rval, reserved_address_tag(RA), EqualRA),
|
|
InnerTestRval = binop(logical_and,
|
|
unop(logical_not, EqualRA), InnerTestRval0)
|
|
),
|
|
raw_tag_test(Rval, ThisTag, MatchesThisTag),
|
|
TestRval = list.foldr(CheckReservedAddrs, ReservedAddrs,
|
|
MatchesThisTag)
|
|
).
|
|
|
|
:- func generate_reserved_address(reserved_address) = rval.
|
|
|
|
generate_reserved_address(null_pointer) = const(llconst_int(0)).
|
|
generate_reserved_address(small_pointer(N)) = const(llconst_int(N)).
|
|
generate_reserved_address(reserved_object(_, _, _)) = _ :-
|
|
% These should only be used for the MLDS back-end.
|
|
unexpected($module, $pred, "reserved_object").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% A construction unification is implemented as a simple assignment
|
|
% of a function symbol if the function symbol has arity zero.
|
|
% If the function symbol's arity is greater than zero, and all its
|
|
% arguments are constants, the construction is implemented by
|
|
% constructing the new term statically. If not all the arguments are
|
|
% constants, the construction is implemented as a heap-increment
|
|
% to create a term, and a series of [optional] assignments to
|
|
% instantiate the arguments of that term.
|
|
%
|
|
:- pred generate_construction(prog_var::in, cons_id::in,
|
|
list(prog_var)::in, list(unify_mode)::in, list(arg_width)::in,
|
|
how_to_construct::in,
|
|
list(int)::in, maybe(term_size_value)::in, hlds_goal_info::in,
|
|
llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_construction(LHSVar, ConsId, RHSVars, ArgModes, ArgWidths,
|
|
HowToConstruct, TakeAddr, MaybeSize, GoalInfo, Code, !CI, !CLD) :-
|
|
get_module_info(!.CI, ModuleInfo),
|
|
Tag = cons_id_to_tag(ModuleInfo, ConsId),
|
|
generate_construction_2(Tag, LHSVar, RHSVars, ArgModes, ArgWidths,
|
|
HowToConstruct, TakeAddr, MaybeSize, GoalInfo, Code, !CI, !CLD).
|
|
|
|
:- pred generate_construction_2(cons_tag::in, prog_var::in,
|
|
list(prog_var)::in, list(unify_mode)::in, list(arg_width)::in,
|
|
how_to_construct::in,
|
|
list(int)::in, maybe(term_size_value)::in, hlds_goal_info::in,
|
|
llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_construction_2(ConsTag, LHSVar, RHSVars, ArgModes, ArgWidths,
|
|
HowToConstruct0, TakeAddr, MaybeSize, GoalInfo, Code, !CI, !CLD) :-
|
|
(
|
|
ConsTag = string_tag(String),
|
|
assign_const_to_var(LHSVar, const(llconst_string(String)), !.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = int_tag(IntTag),
|
|
int_tag_to_const_and_int_type(IntTag, Const, _),
|
|
assign_const_to_var(LHSVar, const(Const), !.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = foreign_tag(Lang, Val),
|
|
expect(unify(Lang, lang_c), $module, $pred,
|
|
"foreign_tag for language other than C"),
|
|
ForeignConst = const(llconst_foreign(Val, lt_int(int_type_int))),
|
|
assign_const_to_var(LHSVar, ForeignConst, !.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = float_tag(Float),
|
|
assign_const_to_var(LHSVar, const(llconst_float(Float)), !.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = no_tag,
|
|
( if
|
|
RHSVars = [RHSVar],
|
|
ArgModes = [ArgMode]
|
|
then
|
|
(
|
|
TakeAddr = [],
|
|
Type = variable_type(!.CI, RHSVar),
|
|
generate_sub_unify(ref(LHSVar), ref(RHSVar), ArgMode, Type,
|
|
Code, !.CI, !CLD)
|
|
;
|
|
TakeAddr = [_ | _],
|
|
unexpected($module, $pred, "notag: take_addr")
|
|
)
|
|
else
|
|
unexpected($module, $pred, "no_tag: arity != 1")
|
|
)
|
|
;
|
|
(
|
|
ConsTag = single_functor_tag,
|
|
% Treat single_functor the same as unshared_tag(0).
|
|
Ptag = 0
|
|
;
|
|
ConsTag = unshared_tag(Ptag)
|
|
),
|
|
var_types(!.CI, RHSVars, ArgTypes),
|
|
generate_cons_args(RHSVars, ArgTypes, ArgModes, ArgWidths, TakeAddr,
|
|
!.CI, CellArgs0, MayUseAtomic),
|
|
pack_cell_rvals(ArgWidths, CellArgs0, CellArgs, PackCode, !.CI, !CLD),
|
|
pack_how_to_construct(ArgWidths, HowToConstruct0, HowToConstruct),
|
|
Context = goal_info_get_context(GoalInfo),
|
|
construct_cell(LHSVar, Ptag, CellArgs, HowToConstruct, MaybeSize,
|
|
Context, MayUseAtomic, ConstructCode, !CI, !CLD),
|
|
Code = PackCode ++ ConstructCode
|
|
;
|
|
ConsTag = direct_arg_tag(Ptag),
|
|
( if
|
|
RHSVars = [RHSVar],
|
|
ArgModes = [ArgMode]
|
|
then
|
|
(
|
|
TakeAddr = [],
|
|
Type = variable_type(!.CI, RHSVar),
|
|
generate_direct_arg_construct(LHSVar, RHSVar, Ptag,
|
|
ArgMode, Type, Code, !.CI, !CLD)
|
|
;
|
|
TakeAddr = [_ | _],
|
|
unexpected($module, $pred, "direct_arg_tag: take_addr")
|
|
)
|
|
else
|
|
unexpected($module, $pred, "direct_arg_tag: arity != 1")
|
|
)
|
|
;
|
|
ConsTag = shared_remote_tag(Ptag, Sectag),
|
|
var_types(!.CI, RHSVars, ArgTypes),
|
|
generate_cons_args(RHSVars, ArgTypes, ArgModes, ArgWidths, TakeAddr,
|
|
!.CI, CellArgs0, MayUseAtomic),
|
|
pack_cell_rvals(ArgWidths, CellArgs0, CellArgs1, PackCode, !.CI, !CLD),
|
|
pack_how_to_construct(ArgWidths, HowToConstruct0, HowToConstruct),
|
|
CellArgs = [cell_arg_full_word(const(llconst_int(Sectag)), complete)
|
|
| CellArgs1],
|
|
Context = goal_info_get_context(GoalInfo),
|
|
construct_cell(LHSVar, Ptag, CellArgs, HowToConstruct, MaybeSize,
|
|
Context, MayUseAtomic, ConstructCode, !CI, !CLD),
|
|
Code = PackCode ++ ConstructCode
|
|
;
|
|
ConsTag = shared_local_tag(Ptag, Sectag),
|
|
assign_const_to_var(LHSVar,
|
|
mkword(Ptag, unop(mkbody, const(llconst_int(Sectag)))),
|
|
!.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = type_ctor_info_tag(ModuleName, TypeName, TypeArity),
|
|
expect(unify(RHSVars, []), $module, $pred,
|
|
"type_ctor_info constant has args"),
|
|
RttiTypeCtor = rtti_type_ctor(ModuleName, TypeName, TypeArity),
|
|
DataId = rtti_data_id(ctor_rtti_id(RttiTypeCtor,
|
|
type_ctor_type_ctor_info)),
|
|
assign_const_to_var(LHSVar, const(llconst_data_addr(DataId, no)),
|
|
!.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = base_typeclass_info_tag(ModuleName, ClassId, Instance),
|
|
expect(unify(RHSVars, []), $module, $pred,
|
|
"base_typeclass_info constant has args"),
|
|
TCName = generate_class_name(ClassId),
|
|
DataId = rtti_data_id(tc_rtti_id(TCName,
|
|
type_class_base_typeclass_info(ModuleName, Instance))),
|
|
assign_const_to_var(LHSVar, const(llconst_data_addr(DataId, no)),
|
|
!.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
( ConsTag = type_info_const_tag(ConstNum)
|
|
; ConsTag = typeclass_info_const_tag(ConstNum)
|
|
; ConsTag = ground_term_const_tag(ConstNum, _)
|
|
),
|
|
get_const_struct_map(!.CI, ConstStructMap),
|
|
map.lookup(ConstStructMap, ConstNum, typed_rval(Rval, _Type)),
|
|
assign_expr_to_var(LHSVar, Rval, Code, !CLD)
|
|
;
|
|
ConsTag = tabling_info_tag(PredId, ProcId),
|
|
expect(unify(RHSVars, []), $module, $pred,
|
|
"tabling_info constant has args"),
|
|
get_module_info(!.CI, ModuleInfo),
|
|
ProcLabel = make_proc_label(ModuleInfo, PredId, ProcId),
|
|
DataId = proc_tabling_data_id(ProcLabel, tabling_info),
|
|
assign_const_to_var(LHSVar, const(llconst_data_addr(DataId, no)),
|
|
!.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = deep_profiling_proc_layout_tag(PredId, ProcId),
|
|
expect(unify(RHSVars, []), $module, $pred,
|
|
"deep_profiling_proc_static has args"),
|
|
get_module_info(!.CI, ModuleInfo),
|
|
RttiProcLabel = make_rtti_proc_label(ModuleInfo, PredId, ProcId),
|
|
Origin = RttiProcLabel ^ rpl_pred_info_origin,
|
|
( if Origin = origin_special_pred(_, _) then
|
|
UserOrUCI = uci
|
|
else
|
|
UserOrUCI = user
|
|
),
|
|
ProcKind = proc_layout_proc_id(UserOrUCI),
|
|
DataId = layout_id(proc_layout(RttiProcLabel, ProcKind)),
|
|
assign_const_to_var(LHSVar, const(llconst_data_addr(DataId, no)),
|
|
!.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = table_io_entry_tag(PredId, ProcId),
|
|
expect(unify(RHSVars, []), $module, $pred, "table_io_entry has args"),
|
|
PredProcId = proc(PredId, ProcId),
|
|
DataId = layout_slot_id(table_io_entry_id, PredProcId),
|
|
assign_const_to_var(LHSVar, const(llconst_data_addr(DataId, no)),
|
|
!.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = reserved_address_tag(RA),
|
|
expect(unify(RHSVars, []), $module, $pred,
|
|
"reserved_address constant has args"),
|
|
assign_const_to_var(LHSVar, generate_reserved_address(RA), !.CI, !CLD),
|
|
Code = empty
|
|
;
|
|
ConsTag = shared_with_reserved_addresses_tag(_RAs, ThisTag),
|
|
% For shared_with_reserved_address, the sharing is only important
|
|
% for tag tests, not for constructions, so here we just recurse
|
|
% on the real representation.
|
|
generate_construction_2(ThisTag, LHSVar, RHSVars, ArgModes, ArgWidths,
|
|
HowToConstruct0, TakeAddr, MaybeSize, GoalInfo, Code, !CI, !CLD)
|
|
;
|
|
ConsTag = closure_tag(PredId, ProcId, EvalMethod),
|
|
expect(unify(TakeAddr, []), $module, $pred,
|
|
"closure_tag has take_addr"),
|
|
expect(unify(MaybeSize, no), $module, $pred,
|
|
"closure_tag has size"),
|
|
generate_closure(PredId, ProcId, EvalMethod, LHSVar, RHSVars, GoalInfo,
|
|
Code, !CI, !CLD)
|
|
).
|
|
|
|
% This predicate constructs or extends a closure.
|
|
% The structure of closures is defined in runtime/mercury_ho_call.h.
|
|
%
|
|
:- pred generate_closure(pred_id::in, proc_id::in, lambda_eval_method::in,
|
|
prog_var::in, list(prog_var)::in, hlds_goal_info::in, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_closure(PredId, ProcId, EvalMethod, Var, Args, GoalInfo, Code,
|
|
!CI, !CLD) :-
|
|
get_module_info(!.CI, ModuleInfo),
|
|
module_info_get_preds(ModuleInfo, Preds),
|
|
map.lookup(Preds, PredId, PredInfo),
|
|
pred_info_get_proc_table(PredInfo, Procs),
|
|
map.lookup(Procs, ProcId, ProcInfo),
|
|
|
|
% We handle currying of a higher-order pred variable as a special case.
|
|
% We recognize
|
|
%
|
|
% P = l(P0, X, Y, Z)
|
|
%
|
|
% where
|
|
%
|
|
% l(P0, A, B, C, ...) :- P0(A, B, C, ...). % higher-order call
|
|
%
|
|
% as a special case, and generate special code to construct the
|
|
% new closure P from the old closure P0 by appending the args X, Y, Z.
|
|
% The advantage of this optimization is that when P is called, we
|
|
% will only need to do one indirect call rather than two.
|
|
% Its disadvantage is that the cost of creating the closure P is greater.
|
|
% Whether this is a net win depend on the number of times P is called.
|
|
%
|
|
% The pattern that this optimization looks for happens rarely at the
|
|
% moment. The reason is that although we allow the creation of closures
|
|
% with a simple syntax (e.g. P0 = append4([1])), we don't allow their
|
|
% extension with a similarly simple syntax (e.g. P = call(P0, [2])).
|
|
% In fact, typecheck.m contains code to detect such constructs, because
|
|
% it does not have code to typecheck them (you get a message about call/2
|
|
% should be used as a goal, not an expression).
|
|
|
|
proc_info_get_goal(ProcInfo, ProcInfoGoal),
|
|
CodeModel = proc_info_interface_code_model(ProcInfo),
|
|
proc_info_get_headvars(ProcInfo, ProcHeadVars),
|
|
( if
|
|
EvalMethod = lambda_normal,
|
|
Args = [CallPred | CallArgs],
|
|
ProcHeadVars = [ProcPred | ProcArgs],
|
|
ProcInfoGoal = hlds_goal(generic_call(higher_order(ProcPred, _, _, _),
|
|
ProcArgs, _, _, CallDeterminism), _GoalInfo),
|
|
determinism_to_code_model(CallDeterminism, CallCodeModel),
|
|
% Check that the code models are compatible. Note that det is not
|
|
% compatible with semidet, and semidet is not compatible with nondet,
|
|
% since the arguments go in different registers.
|
|
% But det is compatible with nondet.
|
|
(
|
|
CodeModel = CallCodeModel
|
|
;
|
|
CodeModel = model_non,
|
|
CallCodeModel = model_det
|
|
),
|
|
% This optimization distorts deep profiles, so don't perform it
|
|
% in deep profiling grades.
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, profile_deep, Deep),
|
|
Deep = no,
|
|
% XXX If float registers are used, float register arguments are placed
|
|
% after regular register arguments in the hidden arguments vector.
|
|
% The code below does not handle that layout.
|
|
globals.lookup_bool_option(Globals, use_float_registers, UseFloatRegs),
|
|
UseFloatRegs = no
|
|
then
|
|
(
|
|
CallArgs = [],
|
|
% If there are no new arguments, we can just use the old closure.
|
|
assign_var_to_var(Var, CallPred, !CLD),
|
|
Code = empty
|
|
;
|
|
CallArgs = [_ | _],
|
|
get_next_label(LoopStart, !CI),
|
|
get_next_label(LoopTest, !CI),
|
|
acquire_reg(reg_r, LoopCounter, !CLD),
|
|
acquire_reg(reg_r, NumOldArgs, !CLD),
|
|
acquire_reg(reg_r, NewClosure, !CLD),
|
|
Zero = const(llconst_int(0)),
|
|
One = const(llconst_int(1)),
|
|
Two = const(llconst_int(2)),
|
|
Three = const(llconst_int(3)),
|
|
list.length(CallArgs, NumNewArgs),
|
|
NumNewArgs_Rval = const(llconst_int(NumNewArgs)),
|
|
NumNewArgsPlusThree = NumNewArgs + 3,
|
|
NumNewArgsPlusThree_Rval = const(llconst_int(NumNewArgsPlusThree)),
|
|
produce_variable(CallPred, OldClosureCode, OldClosure, !.CI, !CLD),
|
|
Context = goal_info_get_context(GoalInfo),
|
|
maybe_add_alloc_site_info(Context, "closure", NumNewArgsPlusThree,
|
|
MaybeAllocId, !CI),
|
|
% The new closure contains a pointer to the old closure.
|
|
NewClosureMayUseAtomic = may_not_use_atomic_alloc,
|
|
NewClosureCode = from_list([
|
|
llds_instr(comment("build new closure from old closure"), ""),
|
|
llds_instr(
|
|
assign(NumOldArgs, lval(field(yes(0), OldClosure, Two))),
|
|
"get number of arguments"),
|
|
llds_instr(incr_hp(NewClosure, no, no,
|
|
binop(int_add(int_type_int), lval(NumOldArgs),
|
|
NumNewArgsPlusThree_Rval),
|
|
MaybeAllocId, NewClosureMayUseAtomic, no, no_llds_reuse),
|
|
"allocate new closure"),
|
|
llds_instr(assign(field(yes(0), lval(NewClosure), Zero),
|
|
lval(field(yes(0), OldClosure, Zero))),
|
|
"set closure layout structure"),
|
|
llds_instr(assign(field(yes(0), lval(NewClosure), One),
|
|
lval(field(yes(0), OldClosure, One))),
|
|
"set closure code pointer"),
|
|
llds_instr(assign(field(yes(0), lval(NewClosure), Two),
|
|
binop(int_add(int_type_int), lval(NumOldArgs),
|
|
NumNewArgs_Rval)),
|
|
"set new number of arguments"),
|
|
llds_instr(
|
|
assign(NumOldArgs,
|
|
binop(int_add(int_type_int), lval(NumOldArgs), Three)),
|
|
"set up loop limit"),
|
|
llds_instr(assign(LoopCounter, Three),
|
|
"initialize loop counter"),
|
|
% It is possible for the number of hidden arguments to be zero,
|
|
% in which case the body of this loop should not be executed
|
|
% at all. This is why we jump to the loop condition test.
|
|
llds_instr(goto(code_label(LoopTest)),
|
|
"enter the copy loop at the conceptual top"),
|
|
llds_instr(label(LoopStart),
|
|
"start of loop, nofulljump"),
|
|
llds_instr(
|
|
assign(field(yes(0), lval(NewClosure), lval(LoopCounter)),
|
|
lval(field(yes(0), OldClosure, lval(LoopCounter)))),
|
|
"copy old hidden argument"),
|
|
llds_instr(
|
|
assign(LoopCounter,
|
|
binop(int_add(int_type_int), lval(LoopCounter), One)),
|
|
"increment loop counter"),
|
|
llds_instr(label(LoopTest),
|
|
"do we have more old arguments to copy? nofulljump"),
|
|
llds_instr(
|
|
if_val(binop(int_lt(int_type_int),
|
|
lval(LoopCounter), lval(NumOldArgs)),
|
|
code_label(LoopStart)),
|
|
"repeat the loop?")
|
|
]),
|
|
generate_extra_closure_args(CallArgs, LoopCounter, NewClosure,
|
|
ExtraArgsCode, !.CI, !CLD),
|
|
release_reg(LoopCounter, !CLD),
|
|
release_reg(NumOldArgs, !CLD),
|
|
release_reg(NewClosure, !CLD),
|
|
assign_lval_to_var(Var, NewClosure, AssignCode, !.CI, !CLD),
|
|
Code = OldClosureCode ++ NewClosureCode ++ ExtraArgsCode ++
|
|
AssignCode
|
|
)
|
|
else
|
|
CodeAddr = make_proc_entry_label(!.CI, ModuleInfo, PredId, ProcId, no),
|
|
ProcLabel = extract_proc_label_from_code_addr(CodeAddr),
|
|
CodeAddrRval = const(llconst_code_addr(CodeAddr)),
|
|
continuation_info.generate_closure_layout( ModuleInfo, PredId, ProcId,
|
|
ClosureInfo),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
Context = goal_info_get_context(GoalInfo),
|
|
term.context_file(Context, FileName),
|
|
term.context_line(Context, LineNumber),
|
|
GoalId = goal_info_get_goal_id(GoalInfo),
|
|
GoalId = goal_id(GoalIdNum),
|
|
GoalIdStr = string.int_to_string(GoalIdNum),
|
|
get_proc_label(!.CI, CallerProcLabel),
|
|
get_next_closure_seq_no(SeqNo, !CI),
|
|
get_static_cell_info(!.CI, StaticCellInfo0),
|
|
hlds.hlds_pred.pred_info_get_origin(PredInfo, PredOrigin),
|
|
stack_layout.construct_closure_layout(CallerProcLabel,
|
|
SeqNo, ClosureInfo, ProcLabel, ModuleName, FileName, LineNumber,
|
|
PredOrigin, GoalIdStr, StaticCellInfo0, StaticCellInfo,
|
|
ClosureLayoutTypedRvals, Data),
|
|
set_static_cell_info(StaticCellInfo, !CI),
|
|
add_closure_layout(Data, !CI),
|
|
% For now, closures always have zero size, and the size slot
|
|
% is never looked at.
|
|
add_scalar_static_cell(ClosureLayoutTypedRvals, ClosureDataAddr, !CI),
|
|
ClosureLayoutRval = const(llconst_data_addr(ClosureDataAddr, no)),
|
|
proc_info_arg_info(ProcInfo, ArgInfo),
|
|
get_vartypes(!.CI, VarTypes),
|
|
MayUseAtomic0 = initial_may_use_atomic(ModuleInfo),
|
|
generate_pred_args(!.CI, VarTypes, Args, ArgInfo, ArgsR, ArgsF,
|
|
MayUseAtomic0, MayUseAtomic),
|
|
list.length(ArgsR, NumArgsR),
|
|
list.length(ArgsF, NumArgsF),
|
|
NumArgsRF = encode_num_generic_call_vars(NumArgsR, NumArgsF),
|
|
list.append(ArgsR, ArgsF, ArgsRF),
|
|
Vector = [
|
|
cell_arg_full_word(ClosureLayoutRval, complete),
|
|
cell_arg_full_word(CodeAddrRval, complete),
|
|
cell_arg_full_word(const(llconst_int(NumArgsRF)), complete)
|
|
| ArgsRF
|
|
],
|
|
% XXX construct_dynamically is just a dummy value. We just want
|
|
% something which is not construct_in_region(_).
|
|
HowToConstruct = construct_dynamically,
|
|
MaybeSize = no,
|
|
maybe_add_alloc_site_info(Context, "closure", length(Vector),
|
|
MaybeAllocId, !CI),
|
|
assign_cell_to_var(Var, no, 0, Vector, HowToConstruct,
|
|
MaybeSize, MaybeAllocId, MayUseAtomic, Code, !CI, !CLD)
|
|
).
|
|
|
|
:- pred generate_extra_closure_args(list(prog_var)::in, lval::in,
|
|
lval::in, llds_code::out,
|
|
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_extra_closure_args([], _, _, empty, _CI, !CLD).
|
|
generate_extra_closure_args([Var | Vars], LoopCounter, NewClosure, Code,
|
|
CI, !CLD) :-
|
|
FieldLval = field(yes(0), lval(NewClosure), lval(LoopCounter)),
|
|
IsDummy = variable_is_of_dummy_type(CI, Var),
|
|
(
|
|
IsDummy = is_dummy_type,
|
|
ProduceCode = empty,
|
|
AssignCode = singleton(
|
|
llds_instr(assign(FieldLval, const(llconst_int(0))),
|
|
"set new argument field (dummy type)")
|
|
)
|
|
;
|
|
IsDummy = is_not_dummy_type,
|
|
produce_variable(Var, ProduceCode, Value, CI, !CLD),
|
|
AssignCode = singleton(
|
|
llds_instr(assign(FieldLval, Value),
|
|
"set new argument field")
|
|
)
|
|
),
|
|
IncrCode = singleton(
|
|
llds_instr(assign(LoopCounter,
|
|
binop(int_add(int_type_int), lval(LoopCounter),
|
|
const(llconst_int(1)))),
|
|
"increment argument counter")
|
|
),
|
|
generate_extra_closure_args(Vars, LoopCounter, NewClosure, VarsCode,
|
|
CI, !CLD),
|
|
Code = ProduceCode ++ AssignCode ++ IncrCode ++ VarsCode.
|
|
|
|
:- pred generate_pred_args(code_info::in, vartypes::in, list(prog_var)::in,
|
|
list(arg_info)::in, list(cell_arg)::out, list(cell_arg)::out,
|
|
may_use_atomic_alloc::in, may_use_atomic_alloc::out) is det.
|
|
|
|
generate_pred_args(_, _, [], _, [], [], !MayUseAtomic).
|
|
generate_pred_args(_, _, [_ | _], [], _, _, !MayUseAtomic) :-
|
|
unexpected($module, $pred, "insufficient args").
|
|
generate_pred_args(CI, VarTypes, [Var | Vars], [ArgInfo | ArgInfos],
|
|
ArgsR, ArgsF, !MayUseAtomic) :-
|
|
ArgInfo = arg_info(reg(RegType, _), ArgMode),
|
|
(
|
|
ArgMode = top_in,
|
|
IsDummy = variable_is_of_dummy_type(CI, Var),
|
|
(
|
|
IsDummy = is_dummy_type,
|
|
Rval = const(llconst_int(0))
|
|
;
|
|
IsDummy = is_not_dummy_type,
|
|
Rval = var(Var)
|
|
),
|
|
CellArg = cell_arg_full_word(Rval, complete)
|
|
;
|
|
( ArgMode = top_out
|
|
; ArgMode = top_unused
|
|
),
|
|
CellArg = cell_arg_skip
|
|
),
|
|
lookup_var_type(VarTypes, Var, Type),
|
|
get_module_info(CI, ModuleInfo),
|
|
update_type_may_use_atomic_alloc(ModuleInfo, Type, !MayUseAtomic),
|
|
generate_pred_args(CI, VarTypes, Vars, ArgInfos, ArgsR0, ArgsF0,
|
|
!MayUseAtomic),
|
|
(
|
|
RegType = reg_r,
|
|
ArgsR = [CellArg | ArgsR0],
|
|
ArgsF = ArgsF0
|
|
;
|
|
RegType = reg_f,
|
|
ArgsR = ArgsR0,
|
|
ArgsF = [CellArg | ArgsF0]
|
|
).
|
|
|
|
:- pred generate_cons_args(list(prog_var)::in, list(mer_type)::in,
|
|
list(unify_mode)::in, list(arg_width)::in, list(int)::in,
|
|
code_info::in, list(cell_arg)::out, may_use_atomic_alloc::out) is det.
|
|
|
|
generate_cons_args(Vars, Types, Modes, Widths, TakeAddr, CI, !:Args,
|
|
!:MayUseAtomic) :-
|
|
get_module_info(CI, ModuleInfo),
|
|
!:MayUseAtomic = initial_may_use_atomic(ModuleInfo),
|
|
( if
|
|
FirstArgNum = 1,
|
|
generate_cons_args_2(Vars, Types, Modes, Widths, FirstArgNum, TakeAddr,
|
|
CI, !:Args, !MayUseAtomic)
|
|
then
|
|
true
|
|
else
|
|
unexpected($module, $pred, "length mismatch")
|
|
).
|
|
|
|
% Create a list of maybe(rval) for the arguments for a construction
|
|
% unification. For each argument which is input to the construction
|
|
% unification, we produce `yes(var(Var))', but if the argument is free,
|
|
% we just produce `no', meaning don't generate an assignment to that field.
|
|
%
|
|
:- pred generate_cons_args_2(list(prog_var)::in, list(mer_type)::in,
|
|
list(unify_mode)::in, list(arg_width)::in, int::in, list(int)::in,
|
|
code_info::in, list(cell_arg)::out,
|
|
may_use_atomic_alloc::in, may_use_atomic_alloc::out) is semidet.
|
|
|
|
generate_cons_args_2([], [], [], [], _CurArgNum, _TakeAddr, _CI, [],
|
|
!MayUseAtomic).
|
|
generate_cons_args_2([Var | Vars], [Type | Types], [ArgMode | ArgModes],
|
|
[Width | Widths], CurArgNum, !.TakeAddr, CI, [CellArg | CellArgs],
|
|
!MayUseAtomic) :-
|
|
get_module_info(CI, ModuleInfo),
|
|
update_type_may_use_atomic_alloc(ModuleInfo, Type, !MayUseAtomic),
|
|
( if !.TakeAddr = [CurArgNum | !:TakeAddr] then
|
|
get_lcmc_null(CI, LCMCNull),
|
|
(
|
|
LCMCNull = no,
|
|
MaybeNull = no
|
|
;
|
|
LCMCNull = yes,
|
|
MaybeNull = yes(const(llconst_int(0)))
|
|
),
|
|
CellArg = cell_arg_take_addr(Var, MaybeNull),
|
|
!:MayUseAtomic = may_not_use_atomic_alloc,
|
|
generate_cons_args_2(Vars, Types, ArgModes, Widths, CurArgNum + 1,
|
|
!.TakeAddr, CI, CellArgs, !MayUseAtomic)
|
|
else
|
|
ArgMode = unify_modes_lhs_rhs(_LHSMode, RHSInsts),
|
|
from_to_insts_to_top_functor_mode(ModuleInfo, RHSInsts, Type,
|
|
RHSTopFunctorMode),
|
|
(
|
|
RHSTopFunctorMode = top_in,
|
|
(
|
|
( Width = full_word
|
|
; Width = partial_word_first(_)
|
|
; Width = partial_word_shifted(_, _)
|
|
),
|
|
CellArg = cell_arg_full_word(var(Var), complete)
|
|
;
|
|
Width = double_word,
|
|
CellArg = cell_arg_double_word(var(Var))
|
|
)
|
|
;
|
|
( RHSTopFunctorMode = top_out
|
|
; RHSTopFunctorMode = top_unused
|
|
),
|
|
CellArg = cell_arg_skip
|
|
),
|
|
generate_cons_args_2(Vars, Types, ArgModes, Widths, CurArgNum + 1,
|
|
!.TakeAddr, CI, CellArgs, !MayUseAtomic)
|
|
).
|
|
|
|
:- func initial_may_use_atomic(module_info) = may_use_atomic_alloc.
|
|
|
|
initial_may_use_atomic(ModuleInfo) = InitMayUseAtomic :-
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, use_atomic_cells, UseAtomicCells),
|
|
(
|
|
UseAtomicCells = no,
|
|
InitMayUseAtomic = may_not_use_atomic_alloc
|
|
;
|
|
UseAtomicCells = yes,
|
|
InitMayUseAtomic = may_use_atomic_alloc
|
|
).
|
|
|
|
:- pred pack_cell_rvals(list(arg_width)::in,
|
|
list(cell_arg)::in, list(cell_arg)::out,
|
|
llds_code::out, code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
pack_cell_rvals(ArgWidths, CellArgs0, CellArgs, Code, CI, !CLD) :-
|
|
pack_args(shift_combine_arg(CI), ArgWidths, CellArgs0, CellArgs,
|
|
empty, Code, !CLD).
|
|
|
|
:- pred pack_how_to_construct(list(arg_width)::in,
|
|
how_to_construct::in, how_to_construct::out) is det.
|
|
|
|
pack_how_to_construct(ArgWidths, !HowToConstruct) :-
|
|
(
|
|
!.HowToConstruct = construct_statically
|
|
;
|
|
!.HowToConstruct = construct_dynamically
|
|
;
|
|
!.HowToConstruct = construct_in_region(_)
|
|
;
|
|
!.HowToConstruct = reuse_cell(CellToReuse0),
|
|
% If an argument within a packed field needs updating,
|
|
% the field needs updating.
|
|
CellToReuse0 = cell_to_reuse(Var, ConsIds, NeedsUpdates0),
|
|
group_same_word_elements(ArgWidths, NeedsUpdates0, NeedsUpdates1),
|
|
list.map(condense_needs_updates, NeedsUpdates1) = NeedsUpdates,
|
|
CellToReuse = cell_to_reuse(Var, ConsIds, NeedsUpdates),
|
|
!:HowToConstruct = reuse_cell(CellToReuse)
|
|
).
|
|
|
|
:- func condense_needs_updates(list(needs_update)) = needs_update.
|
|
|
|
condense_needs_updates(NeedsUpdatess) =
|
|
( if list.member(needs_update, NeedsUpdatess) then
|
|
needs_update
|
|
else
|
|
does_not_need_update
|
|
).
|
|
|
|
:- pred construct_cell(prog_var::in, tag::in, list(cell_arg)::in,
|
|
how_to_construct::in, maybe(term_size_value)::in, prog_context::in,
|
|
may_use_atomic_alloc::in, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
construct_cell(Var, Ptag, CellArgs, HowToConstruct, MaybeSize, Context,
|
|
MayUseAtomic, Code, !CI, !CLD) :-
|
|
VarType = variable_type(!.CI, Var),
|
|
var_type_msg(VarType, VarTypeMsg),
|
|
% If we're doing accurate GC, then for types which hold RTTI that
|
|
% will be traversed by the collector at GC-time, we need to allocate
|
|
% an extra word at the start, to hold the forwarding pointer.
|
|
% Normally we would just overwrite the first word of the object
|
|
% in the "from" space, but this can't be done for objects which will be
|
|
% referenced during the garbage collection process.
|
|
( if
|
|
get_globals(!.CI, Globals),
|
|
globals.get_gc_method(Globals, GCMethod),
|
|
GCMethod = gc_accurate,
|
|
is_introduced_type_info_type(VarType)
|
|
then
|
|
ReserveWordAtStart = yes
|
|
else
|
|
ReserveWordAtStart = no
|
|
),
|
|
Size = size_of_cell_args(CellArgs),
|
|
maybe_add_alloc_site_info(Context, VarTypeMsg, Size, MaybeAllocId, !CI),
|
|
assign_cell_to_var(Var, ReserveWordAtStart, Ptag, CellArgs, HowToConstruct,
|
|
MaybeSize, MaybeAllocId, MayUseAtomic, CellCode, !CI, !CLD),
|
|
generate_field_addrs(CellArgs, FieldAddrs),
|
|
(
|
|
FieldAddrs = [],
|
|
% Optimize common case.
|
|
Code = CellCode
|
|
;
|
|
FieldAddrs = [_ | _],
|
|
% Any field whose address we take will be represented by a
|
|
% `cell_arg_take_addr' which should prevent the cell from being made
|
|
% into static data.
|
|
generate_field_take_address_assigns(FieldAddrs, Var, Ptag,
|
|
FieldCode, !CLD),
|
|
Code = CellCode ++ FieldCode
|
|
).
|
|
|
|
:- pred maybe_add_alloc_site_info(prog_context::in, string::in, int::in,
|
|
maybe(alloc_site_id)::out, code_info::in, code_info::out) is det.
|
|
|
|
maybe_add_alloc_site_info(Context, VarTypeMsg, Size, MaybeAllocId, !CI) :-
|
|
get_globals(!.CI, Globals),
|
|
globals.lookup_bool_option(Globals, profile_memory, ProfileMemory),
|
|
(
|
|
ProfileMemory = yes,
|
|
add_alloc_site_info(Context, VarTypeMsg, Size, AllocId, !CI),
|
|
MaybeAllocId = yes(AllocId)
|
|
;
|
|
ProfileMemory = no,
|
|
MaybeAllocId = no
|
|
).
|
|
|
|
:- pred generate_field_addrs(list(cell_arg)::in, list(field_addr)::out) is det.
|
|
|
|
generate_field_addrs(CellArgs, FieldAddrs) :-
|
|
list.foldl2(generate_field_addr, CellArgs, 0, _, [], RevFieldAddrs),
|
|
list.reverse(RevFieldAddrs, FieldAddrs).
|
|
|
|
:- pred generate_field_addr(cell_arg::in, int::in, int::out,
|
|
list(field_addr)::in, list(field_addr)::out) is det.
|
|
|
|
generate_field_addr(CellArg, ArgOffset, NextOffset, !RevFieldAddrs) :-
|
|
(
|
|
( CellArg = cell_arg_full_word(_, _)
|
|
; CellArg = cell_arg_skip
|
|
),
|
|
NextOffset = ArgOffset + 1
|
|
;
|
|
CellArg = cell_arg_double_word(_),
|
|
NextOffset = ArgOffset + 2
|
|
;
|
|
CellArg = cell_arg_take_addr(Var, _),
|
|
NextOffset = ArgOffset + 1,
|
|
FieldAddr = field_addr(ArgOffset, Var),
|
|
!:RevFieldAddrs = [FieldAddr | !.RevFieldAddrs]
|
|
).
|
|
|
|
:- pred generate_field_take_address_assigns(list(field_addr)::in,
|
|
prog_var::in, int::in, llds_code::out,
|
|
code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_field_take_address_assigns([], _, _, empty, !CLD).
|
|
generate_field_take_address_assigns([FieldAddr | FieldAddrs],
|
|
CellVar, CellPtag, ThisCode ++ RestCode, !CLD) :-
|
|
FieldAddr = field_addr(FieldNum, Var),
|
|
FieldNumRval = const(llconst_int(FieldNum)),
|
|
Addr = mem_addr(heap_ref(var(CellVar), yes(CellPtag), FieldNumRval)),
|
|
assign_expr_to_var(Var, Addr, ThisCode, !CLD),
|
|
generate_field_take_address_assigns(FieldAddrs, CellVar, CellPtag,
|
|
RestCode, !CLD).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred var_types(code_info::in, list(prog_var)::in, list(mer_type)::out)
|
|
is det.
|
|
|
|
var_types(CI, Vars, Types) :-
|
|
get_proc_info(CI, ProcInfo),
|
|
proc_info_get_vartypes(ProcInfo, VarTypes),
|
|
lookup_var_types(VarTypes, Vars, Types).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Construct a pair of lists that associates the fields of a term
|
|
% with variables.
|
|
%
|
|
:- pred make_fields_and_argvars(list(prog_var)::in, list(arg_width)::in,
|
|
rval::in, int::in, int::in, list(uni_val)::out, list(uni_val)::out) is det.
|
|
|
|
make_fields_and_argvars([], [], _, _, _, [], []).
|
|
make_fields_and_argvars([Var | Vars], [Width | Widths], Rval, PrevOffset0,
|
|
TagNum, [F | Fs], [A | As]) :-
|
|
(
|
|
( Width = full_word
|
|
; Width = partial_word_first(_Mask)
|
|
),
|
|
Offset = PrevOffset0 + 1,
|
|
PrevOffset = Offset
|
|
;
|
|
Width = partial_word_shifted(_Shift, _Mask),
|
|
Offset = PrevOffset0,
|
|
PrevOffset = Offset
|
|
;
|
|
Width = double_word,
|
|
Offset = PrevOffset0 + 1,
|
|
PrevOffset = Offset + 1
|
|
),
|
|
F = lval(field(yes(TagNum), Rval, const(llconst_int(Offset))), Width),
|
|
A = ref(Var),
|
|
make_fields_and_argvars(Vars, Widths, Rval, PrevOffset, TagNum, Fs, As).
|
|
make_fields_and_argvars([], [_ | _], _, _, _, _, _) :-
|
|
unexpected($module, $pred, "mismatched lists").
|
|
make_fields_and_argvars([_ | _], [], _, _, _, _, _) :-
|
|
unexpected($module, $pred, "mismatched lists").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate a deterministic deconstruction. In a deterministic
|
|
% deconstruction, we know the value of the tag, so we don't
|
|
% need to generate a test.
|
|
|
|
% Deconstructions are generated semi-eagerly. Any test sub-unifications
|
|
% are generated eagerly (they _must_ be), but assignment unifications
|
|
% are cached.
|
|
%
|
|
:- pred generate_det_deconstruction(prog_var::in, cons_id::in,
|
|
list(prog_var)::in, list(unify_mode)::in, list(arg_width)::in,
|
|
llds_code::out, code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_det_deconstruction(Var, Cons, Args, Modes, ArgWidths, Code,
|
|
CI, !CLD) :-
|
|
get_module_info(CI, ModuleInfo),
|
|
Tag = cons_id_to_tag(ModuleInfo, Cons),
|
|
generate_det_deconstruction_2(Var, Cons, Args, Modes, ArgWidths, Tag,
|
|
Code, CI, !CLD).
|
|
|
|
:- pred generate_det_deconstruction_2(prog_var::in, cons_id::in,
|
|
list(prog_var)::in, list(unify_mode)::in, list(arg_width)::in,
|
|
cons_tag::in, llds_code::out,
|
|
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_det_deconstruction_2(Var, Cons, Args, Modes, ArgWidths, Tag, Code,
|
|
CI, !CLD) :-
|
|
% For constants, if the deconstruction is det, then we already know
|
|
% the value of the constant, so Code = empty.
|
|
(
|
|
( Tag = string_tag(_String)
|
|
; Tag = int_tag(_)
|
|
; Tag = foreign_tag(_, _)
|
|
; Tag = float_tag(_Float)
|
|
; Tag = closure_tag(_, _, _)
|
|
; Tag = type_ctor_info_tag(_, _, _)
|
|
; Tag = base_typeclass_info_tag(_, _, _)
|
|
; Tag = tabling_info_tag(_, _)
|
|
; Tag = deep_profiling_proc_layout_tag(_, _)
|
|
; Tag = shared_local_tag(_Ptag, _Sectag2)
|
|
; Tag = reserved_address_tag(_RA)
|
|
),
|
|
Code = empty
|
|
;
|
|
Tag = type_info_const_tag(_),
|
|
unexpected($module, $pred, "type_info_const_tag")
|
|
;
|
|
Tag = typeclass_info_const_tag(_),
|
|
unexpected($module, $pred, "typeclass_info_const_tag")
|
|
;
|
|
Tag = ground_term_const_tag(_, _),
|
|
unexpected($module, $pred, "ground_term_const_tag")
|
|
;
|
|
Tag = table_io_entry_tag(_, _),
|
|
unexpected($module, $pred, "table_io_entry_tag")
|
|
;
|
|
Tag = no_tag,
|
|
( if
|
|
Args = [Arg],
|
|
Modes = [Mode],
|
|
ArgWidths = [_ArgWidth]
|
|
then
|
|
VarType = variable_type(CI, Var),
|
|
get_module_info(CI, ModuleInfo),
|
|
IsDummy = check_dummy_type(ModuleInfo, VarType),
|
|
(
|
|
IsDummy = is_dummy_type,
|
|
% We must handle this case specially. If we didn't, the
|
|
% generated code would copy the reference to the Var's
|
|
% current location, which may be stackvar(N) or framevar(N)
|
|
% for negative N, to be the location of Arg, and since Arg
|
|
% may not be a dummy type, it would actually use that location.
|
|
% This can happen in the unify/compare routines for e.g.
|
|
% io.state.
|
|
( if variable_is_forward_live(!.CLD, Arg) then
|
|
assign_const_to_var(Arg, const(llconst_int(0)), CI, !CLD)
|
|
else
|
|
true
|
|
),
|
|
Code = empty
|
|
;
|
|
IsDummy = is_not_dummy_type,
|
|
ArgType = variable_type(CI, Arg),
|
|
generate_sub_unify(ref(Var), ref(Arg), Mode, ArgType,
|
|
Code, CI, !CLD)
|
|
)
|
|
else
|
|
unexpected($module, $pred, "no_tag: arity != 1")
|
|
)
|
|
;
|
|
Tag = single_functor_tag,
|
|
% Treat single_functor the same as unshared_tag(0).
|
|
generate_det_deconstruction_2(Var, Cons, Args, Modes, ArgWidths,
|
|
unshared_tag(0), Code, CI, !CLD)
|
|
;
|
|
Tag = unshared_tag(Ptag),
|
|
Rval = var(Var),
|
|
make_fields_and_argvars(Args, ArgWidths, Rval, -1, Ptag,
|
|
Fields, ArgVars),
|
|
var_types(CI, Args, ArgTypes),
|
|
generate_unify_args(Fields, ArgVars, Modes, ArgTypes, Code, CI, !CLD)
|
|
;
|
|
Tag = direct_arg_tag(Ptag),
|
|
( if
|
|
Args = [Arg],
|
|
Modes = [Mode],
|
|
ArgWidths = [_]
|
|
then
|
|
Type = variable_type(CI, Arg),
|
|
generate_direct_arg_deconstruct(Var, Arg, Ptag, Mode, Type, Code,
|
|
CI, !CLD)
|
|
else
|
|
unexpected($module, $pred, "direct_arg_tag: arity != 1")
|
|
)
|
|
;
|
|
Tag = shared_remote_tag(Ptag, _Sectag1),
|
|
Rval = var(Var),
|
|
make_fields_and_argvars(Args, ArgWidths, Rval, 0, Ptag,
|
|
Fields, ArgVars),
|
|
var_types(CI, Args, ArgTypes),
|
|
generate_unify_args(Fields, ArgVars, Modes, ArgTypes, Code, CI, !CLD)
|
|
;
|
|
% For shared_with_reserved_address, the sharing is only important
|
|
% for tag tests, not for det deconstructions, so here we just recurse
|
|
% on the real representation.
|
|
Tag = shared_with_reserved_addresses_tag(_RAs, ThisTag),
|
|
generate_det_deconstruction_2(Var, Cons, Args, Modes, ArgWidths,
|
|
ThisTag, Code, CI, !CLD)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate a semideterministic deconstruction.
|
|
% A semideterministic deconstruction unification is tag-test
|
|
% followed by a deterministic deconstruction.
|
|
%
|
|
:- pred generate_semi_deconstruction(prog_var::in, cons_id::in,
|
|
list(prog_var)::in, list(unify_mode)::in, list(arg_width)::in,
|
|
llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_semi_deconstruction(Var, Tag, Args, Modes, ArgWidths, Code,
|
|
!CI, !CLD) :-
|
|
VarType = variable_type(!.CI, Var),
|
|
CheaperTagTest = lookup_cheaper_tag_test(!.CI, VarType),
|
|
generate_tag_test(Var, Tag, CheaperTagTest, branch_on_success, SuccLabel,
|
|
TagTestCode, !CI, !CLD),
|
|
remember_position(!.CLD, AfterUnify),
|
|
generate_failure(FailCode, !CI, !.CLD),
|
|
reset_to_position(AfterUnify, !.CI, !:CLD),
|
|
generate_det_deconstruction(Var, Tag, Args, Modes, ArgWidths, DeconsCode,
|
|
!.CI, !CLD),
|
|
SuccessLabelCode = singleton(llds_instr(label(SuccLabel), "")),
|
|
Code = TagTestCode ++ FailCode ++ SuccessLabelCode ++ DeconsCode.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate code to perform a list of deterministic subunifications
|
|
% for the arguments of a construction.
|
|
%
|
|
:- pred generate_unify_args(list(uni_val)::in, list(uni_val)::in,
|
|
list(unify_mode)::in, list(mer_type)::in, llds_code::out,
|
|
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_unify_args(Ls, Rs, Ms, Ts, Code, CI, !CLD) :-
|
|
( if generate_unify_args_2(Ls, Rs, Ms, Ts, Code0, CI, !CLD) then
|
|
Code = Code0
|
|
else
|
|
unexpected($module, $pred, "length mismatch")
|
|
).
|
|
|
|
:- pred generate_unify_args_2(list(uni_val)::in, list(uni_val)::in,
|
|
list(unify_mode)::in, list(mer_type)::in, llds_code::out,
|
|
code_info::in, code_loc_dep::in, code_loc_dep::out) is semidet.
|
|
|
|
generate_unify_args_2([], [], [], [], empty, _CI, !CLD).
|
|
generate_unify_args_2([L | Ls], [R | Rs], [M | Ms], [T | Ts], Code,
|
|
CI, !CLD) :-
|
|
generate_sub_unify(L, R, M, T, CodeA, CI, !CLD),
|
|
generate_unify_args_2(Ls, Rs, Ms, Ts, CodeB, CI, !CLD),
|
|
Code = CodeA ++ CodeB.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate a subunification between two [field | variable].
|
|
%
|
|
:- pred generate_sub_unify(uni_val::in, uni_val::in, unify_mode::in,
|
|
mer_type::in, llds_code::out,
|
|
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_sub_unify(L, R, ArgMode, Type, Code, CI, !CLD) :-
|
|
get_module_info(CI, ModuleInfo),
|
|
ArgMode = unify_modes_lhs_rhs(LeftFromToInsts, RightFromToInsts),
|
|
from_to_insts_to_top_functor_mode(ModuleInfo, LeftFromToInsts, Type,
|
|
LeftTopFunctorMode),
|
|
from_to_insts_to_top_functor_mode(ModuleInfo, RightFromToInsts, Type,
|
|
RightTopFunctorMode),
|
|
( if
|
|
% Input - input == test unification
|
|
LeftTopFunctorMode = top_in,
|
|
RightTopFunctorMode = top_in
|
|
then
|
|
% This shouldn't happen, since mode analysis should avoid creating
|
|
% any tests in the arguments of a construction or deconstruction
|
|
% unification.
|
|
unexpected($module, $pred, "test in arg of [de]construction")
|
|
else if
|
|
% Input - Output== assignment ->
|
|
LeftTopFunctorMode = top_in,
|
|
RightTopFunctorMode = top_out
|
|
then
|
|
generate_sub_assign(R, L, Code, CI, !CLD)
|
|
else if
|
|
% Output - Input== assignment <-
|
|
LeftTopFunctorMode = top_out,
|
|
RightTopFunctorMode = top_in
|
|
then
|
|
generate_sub_assign(L, R, Code, CI, !CLD)
|
|
else if
|
|
LeftTopFunctorMode = top_unused,
|
|
RightTopFunctorMode = top_unused
|
|
then
|
|
Code = empty
|
|
% free-free - ignore
|
|
% XXX I think this will have to change if we start to support aliasing.
|
|
else
|
|
unexpected($module, $pred, "some strange unify")
|
|
).
|
|
|
|
:- pred generate_sub_assign(uni_val::in, uni_val::in, llds_code::out,
|
|
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_sub_assign(Left, Right, Code, CI, !CLD) :-
|
|
(
|
|
Left = lval(_Lval, _),
|
|
Right = lval(_Rval, _),
|
|
% Assignment between two lvalues - cannot happen.
|
|
unexpected($module, $pred, "lval/lval")
|
|
;
|
|
Left = lval(Lval0, LeftWidth),
|
|
Right = ref(Var),
|
|
% Assignment from a variable to an lvalue - cannot cache
|
|
% so generate immediately.
|
|
produce_variable(Var, SourceCode, Source, CI, !CLD),
|
|
materialize_vars_in_lval(Lval0, Lval, MaterializeCode, CI, !CLD),
|
|
(
|
|
LeftWidth = full_word,
|
|
AssignCode = singleton(llds_instr(assign(Lval, Source),
|
|
"Copy value"))
|
|
;
|
|
(
|
|
LeftWidth = partial_word_first(Mask),
|
|
Shift = 0
|
|
;
|
|
LeftWidth = partial_word_shifted(Shift, Mask)
|
|
),
|
|
ComplementMask = const(llconst_int(\(Mask << Shift))),
|
|
MaskOld = binop(bitwise_and(int_type_int), lval(Lval),
|
|
ComplementMask),
|
|
ShiftNew = maybe_left_shift_rval(Source, Shift),
|
|
Combined = binop(bitwise_or(int_type_int), MaskOld, ShiftNew),
|
|
AssignCode = singleton(llds_instr(assign(Lval, Combined),
|
|
"Update part of word"))
|
|
;
|
|
LeftWidth = double_word,
|
|
( if field_offset_pair(Lval, LvalA, LvalB) then
|
|
SrcA = binop(float_word_bits, Source, const(llconst_int(0))),
|
|
SrcB = binop(float_word_bits, Source, const(llconst_int(1))),
|
|
Comment = "Update double word",
|
|
AssignCode = from_list([
|
|
llds_instr(assign(LvalA, SrcA), Comment),
|
|
llds_instr(assign(LvalB, SrcB), Comment)
|
|
])
|
|
else
|
|
sorry($module, $pred, "double_word: non-field lval")
|
|
)
|
|
),
|
|
Code = SourceCode ++ MaterializeCode ++ AssignCode
|
|
;
|
|
Left = ref(Lvar),
|
|
( if variable_is_forward_live(!.CLD, Lvar) then
|
|
(
|
|
Right = lval(Lval, RightWidth),
|
|
% Assignment of a value to a variable, generate now.
|
|
(
|
|
RightWidth = full_word,
|
|
assign_lval_to_var(Lvar, Lval, Code, CI, !CLD)
|
|
;
|
|
(
|
|
RightWidth = partial_word_first(Mask),
|
|
Rval0 = lval(Lval)
|
|
;
|
|
RightWidth = partial_word_shifted(Shift, Mask),
|
|
Rval0 = right_shift_rval(lval(Lval), Shift)
|
|
),
|
|
Rval = binop(bitwise_and(int_type_int), Rval0,
|
|
const(llconst_int(Mask))),
|
|
assign_field_lval_expr_to_var(Lvar, [Lval], Rval, Code,
|
|
!CLD)
|
|
;
|
|
RightWidth = double_word,
|
|
( if field_offset_pair(Lval, LvalA, LvalB) then
|
|
Rval = binop(float_from_dword,
|
|
lval(LvalA), lval(LvalB)),
|
|
assign_field_lval_expr_to_var(Lvar, [LvalA, LvalB],
|
|
Rval, Code, !CLD)
|
|
else
|
|
sorry($module, $pred, "double_word: non-field lval")
|
|
)
|
|
)
|
|
;
|
|
Right = ref(Rvar),
|
|
% Assignment of a variable to a variable, so cache it.
|
|
assign_var_to_var(Lvar, Rvar, !CLD),
|
|
Code = empty
|
|
)
|
|
else
|
|
Code = empty
|
|
)
|
|
).
|
|
|
|
:- pred field_offset_pair(lval::in, lval::out, lval::out) is semidet.
|
|
|
|
field_offset_pair(LvalA, LvalA, LvalB) :-
|
|
LvalA = field(Ptag, Address, const(llconst_int(Offset))),
|
|
LvalB = field(Ptag, Address, const(llconst_int(Offset + 1))).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate a direct arg unification between
|
|
% - the left-hand-side (the whole term), and
|
|
% - the right-hand-side (the one argument).
|
|
%
|
|
:- pred generate_direct_arg_construct(prog_var::in, prog_var::in, tag_bits::in,
|
|
unify_mode::in, mer_type::in, llds_code::out,
|
|
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_direct_arg_construct(Var, Arg, Ptag, ArgMode, Type, Code, CI, !CLD) :-
|
|
get_module_info(CI, ModuleInfo),
|
|
ArgMode = unify_modes_lhs_rhs(LeftFromToInsts, RightFromToInsts),
|
|
from_to_insts_to_top_functor_mode(ModuleInfo, LeftFromToInsts, Type,
|
|
LeftTopFunctorMode),
|
|
from_to_insts_to_top_functor_mode(ModuleInfo, RightFromToInsts, Type,
|
|
RightTopFunctorMode),
|
|
( if
|
|
% Input - input == test unification
|
|
LeftTopFunctorMode = top_in,
|
|
RightTopFunctorMode = top_in
|
|
then
|
|
% This shouldn't happen, since mode analysis should avoid creating
|
|
% any tests in the arguments of a construction or deconstruction
|
|
% unification.
|
|
unexpected($module, $pred, "test in arg of [de]construction")
|
|
else if
|
|
% Input - Output == assignment ->
|
|
LeftTopFunctorMode = top_in,
|
|
RightTopFunctorMode = top_out
|
|
then
|
|
unexpected($module, $pred, "left-to-right data flow in construction")
|
|
else if
|
|
% Output - Input == assignment <-
|
|
LeftTopFunctorMode = top_out,
|
|
RightTopFunctorMode = top_in
|
|
then
|
|
assign_expr_to_var(Var, mkword(Ptag, var(Arg)), Code, !CLD)
|
|
else if
|
|
LeftTopFunctorMode = top_unused,
|
|
RightTopFunctorMode = top_unused
|
|
then
|
|
% XXX I think this will have to change if we start to support aliasing.
|
|
% Construct a tagged pointer to a pointer value which is unknown yet.
|
|
assign_const_to_var(Var, mkword_hole(Ptag), CI, !CLD),
|
|
Code = empty
|
|
else
|
|
unexpected($module, $pred, "some strange unify")
|
|
).
|
|
|
|
% Generate a direct arg unification between
|
|
% - the left-hand-side (the whole term), and
|
|
% - the right-hand-side (the one argument).
|
|
%
|
|
:- pred generate_direct_arg_deconstruct(prog_var::in, prog_var::in,
|
|
tag_bits::in, unify_mode::in, mer_type::in, llds_code::out,
|
|
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
generate_direct_arg_deconstruct(Var, ArgVar, Ptag, ArgMode, Type, Code,
|
|
CI, !CLD) :-
|
|
get_module_info(CI, ModuleInfo),
|
|
ArgMode = unify_modes_lhs_rhs(LeftFromToInsts, RightFromToInsts),
|
|
from_to_insts_to_top_functor_mode(ModuleInfo, LeftFromToInsts, Type,
|
|
LeftTopFunctorMode),
|
|
from_to_insts_to_top_functor_mode(ModuleInfo, RightFromToInsts, Type,
|
|
RightTopFunctorMode),
|
|
( if
|
|
% Input - Output == assignment ->
|
|
LeftTopFunctorMode = top_in,
|
|
RightTopFunctorMode = top_out
|
|
then
|
|
( if variable_is_forward_live(!.CLD, ArgVar) then
|
|
BodyRval = binop(body, var(Var), const(llconst_int(Ptag))),
|
|
assign_expr_to_var(ArgVar, BodyRval, Code, !CLD)
|
|
else
|
|
Code = empty
|
|
)
|
|
else if
|
|
% Output - Input == assignment <-
|
|
LeftTopFunctorMode = top_out,
|
|
RightTopFunctorMode = top_in
|
|
then
|
|
reassign_mkword_hole_var(Var, Ptag, var(ArgVar), Code, !CLD)
|
|
else if
|
|
LeftTopFunctorMode = top_unused,
|
|
RightTopFunctorMode = top_unused
|
|
then
|
|
Code = empty
|
|
% free-free - ignore
|
|
% XXX I think this will have to change if we start to support aliasing.
|
|
else if
|
|
% Input - input == test unification
|
|
LeftTopFunctorMode = top_in,
|
|
RightTopFunctorMode = top_in
|
|
then
|
|
% This shouldn't happen, since mode analysis should avoid creating
|
|
% any tests in the arguments of a construction or deconstruction
|
|
% unification.
|
|
unexpected($module, $pred, "test in arg of [de]construction")
|
|
else
|
|
unexpected($module, $pred, "some strange unify")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_const_structs(ModuleInfo, ConstStructMap, !GlobalData) :-
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, unboxed_float, UB),
|
|
(
|
|
UB = yes,
|
|
UnboxedFloats = have_unboxed_floats
|
|
;
|
|
UB = no,
|
|
UnboxedFloats = do_not_have_unboxed_floats
|
|
),
|
|
module_info_get_const_struct_db(ModuleInfo, ConstStructDb),
|
|
const_struct_db_get_structs(ConstStructDb, ConstStructs),
|
|
global_data_get_static_cell_info(!.GlobalData, StaticCellInfo0),
|
|
list.foldl2(generate_const_struct(ModuleInfo, UnboxedFloats), ConstStructs,
|
|
map.init, ConstStructMap, StaticCellInfo0, StaticCellInfo),
|
|
global_data_set_static_cell_info(StaticCellInfo, !GlobalData).
|
|
|
|
:- pred generate_const_struct(module_info::in, have_unboxed_floats::in,
|
|
pair(int, const_struct)::in,
|
|
const_struct_map::in, const_struct_map::out,
|
|
static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
generate_const_struct(ModuleInfo, UnboxedFloats, ConstNum - ConstStruct,
|
|
!ConstStructMap, !StaticCellInfo) :-
|
|
ConstStruct = const_struct(ConsId, ConstArgs, _, _),
|
|
ConsTag = cons_id_to_tag(ModuleInfo, ConsId),
|
|
get_cons_arg_widths(ModuleInfo, ConsId, ConstArgs, ConsArgWidths),
|
|
generate_const_struct_rval(ModuleInfo, UnboxedFloats, !.ConstStructMap,
|
|
ConsId, ConsTag, ConstArgs, ConsArgWidths, Rval, !StaticCellInfo),
|
|
map.det_insert(ConstNum, Rval, !ConstStructMap).
|
|
|
|
:- pred generate_const_struct_rval(module_info::in, have_unboxed_floats::in,
|
|
const_struct_map::in, cons_id::in, cons_tag::in,
|
|
list(const_struct_arg)::in, list(arg_width)::in, typed_rval::out,
|
|
static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
generate_const_struct_rval(ModuleInfo, UnboxedFloats, ConstStructMap,
|
|
ConsId, ConsTag, ConstArgs, ConsArgWidths, TypedRval,
|
|
!StaticCellInfo) :-
|
|
(
|
|
ConsTag = shared_with_reserved_addresses_tag(_, ActualConsTag),
|
|
generate_const_struct_rval(ModuleInfo, UnboxedFloats, ConstStructMap,
|
|
ConsId, ActualConsTag, ConstArgs, ConsArgWidths,
|
|
TypedRval, !StaticCellInfo)
|
|
;
|
|
ConsTag = no_tag,
|
|
(
|
|
ConstArgs = [ConstArg],
|
|
det_single_arg_width(ConsArgWidths, ConsArgWidth),
|
|
generate_const_struct_arg(ModuleInfo, UnboxedFloats,
|
|
ConstStructMap, ConstArg, ConsArgWidth, ArgTypedRval),
|
|
TypedRval = ArgTypedRval
|
|
;
|
|
( ConstArgs = []
|
|
; ConstArgs = [_, _ | _]
|
|
),
|
|
unexpected($module, $pred, "no_tag arity != 1")
|
|
)
|
|
;
|
|
ConsTag = direct_arg_tag(Ptag),
|
|
(
|
|
ConstArgs = [ConstArg],
|
|
det_single_arg_width(ConsArgWidths, ConsArgWidth),
|
|
generate_const_struct_arg(ModuleInfo, UnboxedFloats,
|
|
ConstStructMap, ConstArg, ConsArgWidth, ArgTypedRval),
|
|
ArgTypedRval = typed_rval(ArgRval, _RvalType),
|
|
Rval = mkword(Ptag, ArgRval),
|
|
TypedRval = typed_rval(Rval, lt_data_ptr)
|
|
;
|
|
( ConstArgs = []
|
|
; ConstArgs = [_, _ | _]
|
|
),
|
|
unexpected($module, $pred, "direct_arg_tag: arity != 1")
|
|
)
|
|
;
|
|
(
|
|
ConsTag = single_functor_tag,
|
|
Ptag = 0
|
|
;
|
|
ConsTag = unshared_tag(Ptag)
|
|
),
|
|
generate_const_struct_args(ModuleInfo, UnboxedFloats, ConstStructMap,
|
|
ConstArgs, ConsArgWidths, ArgTypedRvals),
|
|
pack_ground_term_args(ConsArgWidths, ArgTypedRvals, PackArgTypedRvals),
|
|
add_scalar_static_cell(PackArgTypedRvals, DataAddr, !StaticCellInfo),
|
|
MaybeOffset = no,
|
|
CellPtrConst = const(llconst_data_addr(DataAddr, MaybeOffset)),
|
|
Rval = mkword(Ptag, CellPtrConst),
|
|
TypedRval = typed_rval(Rval, lt_data_ptr)
|
|
;
|
|
ConsTag = shared_remote_tag(Ptag, Stag),
|
|
generate_const_struct_args(ModuleInfo, UnboxedFloats, ConstStructMap,
|
|
ConstArgs, ConsArgWidths, ArgTypedRvals),
|
|
pack_ground_term_args(ConsArgWidths, ArgTypedRvals, PackArgTypedRvals),
|
|
StagTypedRval = typed_rval(const(llconst_int(Stag)),
|
|
lt_int(int_type_int)),
|
|
AllTypedRvals = [StagTypedRval | PackArgTypedRvals],
|
|
add_scalar_static_cell(AllTypedRvals, DataAddr, !StaticCellInfo),
|
|
MaybeOffset = no,
|
|
CellPtrConst = const(llconst_data_addr(DataAddr, MaybeOffset)),
|
|
Rval = mkword(Ptag, CellPtrConst),
|
|
TypedRval = typed_rval(Rval, lt_data_ptr)
|
|
;
|
|
( ConsTag = string_tag(_)
|
|
; ConsTag = int_tag(_)
|
|
; ConsTag = foreign_tag(_, _)
|
|
; ConsTag = float_tag(_)
|
|
; ConsTag = shared_local_tag(_, _)
|
|
; ConsTag = reserved_address_tag(_)
|
|
; ConsTag = closure_tag(_, _, _)
|
|
; ConsTag = type_ctor_info_tag(_, _, _)
|
|
; ConsTag = base_typeclass_info_tag(_, _, _)
|
|
; ConsTag = type_info_const_tag(_)
|
|
; ConsTag = typeclass_info_const_tag(_)
|
|
; ConsTag = ground_term_const_tag(_, _)
|
|
; ConsTag = tabling_info_tag(_, _)
|
|
; ConsTag = table_io_entry_tag(_, _)
|
|
; ConsTag = deep_profiling_proc_layout_tag(_, _)
|
|
),
|
|
unexpected($module, $pred, "unexpected tag")
|
|
).
|
|
|
|
:- pred generate_const_struct_args(module_info::in, have_unboxed_floats::in,
|
|
const_struct_map::in, list(const_struct_arg)::in, list(arg_width)::in,
|
|
list(typed_rval)::out) is det.
|
|
|
|
generate_const_struct_args(ModuleInfo, UnboxedFloats, ConstStructMap,
|
|
ConstArgs, ArgWidths, TypedRvals) :-
|
|
list.map_corresponding(
|
|
generate_const_struct_arg(ModuleInfo, UnboxedFloats, ConstStructMap),
|
|
ConstArgs, ArgWidths, TypedRvals).
|
|
|
|
:- pred generate_const_struct_arg(module_info::in, have_unboxed_floats::in,
|
|
const_struct_map::in, const_struct_arg::in, arg_width::in, typed_rval::out)
|
|
is det.
|
|
|
|
generate_const_struct_arg(ModuleInfo, UnboxedFloats, ConstStructMap,
|
|
ConstArg, ArgWidth, TypedRval) :-
|
|
(
|
|
ConstArg = csa_const_struct(ConstNum),
|
|
map.lookup(ConstStructMap, ConstNum, TypedRval)
|
|
;
|
|
ConstArg = csa_constant(ConsId, _),
|
|
ConsTag = cons_id_to_tag(ModuleInfo, ConsId),
|
|
generate_const_struct_arg_tag(ModuleInfo, UnboxedFloats,
|
|
ConstStructMap, ConsTag, ArgWidth, TypedRval)
|
|
).
|
|
|
|
:- pred generate_const_struct_arg_tag(module_info::in, have_unboxed_floats::in,
|
|
const_struct_map::in, cons_tag::in, arg_width::in, typed_rval::out) is det.
|
|
|
|
generate_const_struct_arg_tag(ModuleInfo, UnboxedFloats, ConstStructMap,
|
|
ConsTag, ArgWidth, TypedRval) :-
|
|
(
|
|
(
|
|
ConsTag = string_tag(String),
|
|
Const = llconst_string(String),
|
|
Type = lt_string
|
|
;
|
|
ConsTag = int_tag(IntTag),
|
|
int_tag_to_const_and_int_type(IntTag, Const, IntType),
|
|
Type = lt_int(IntType)
|
|
;
|
|
ConsTag = foreign_tag(Lang, Val),
|
|
expect(unify(Lang, lang_c), $module, $pred,
|
|
"foreign_tag for language other than C"),
|
|
Const = llconst_foreign(Val, lt_int(int_type_int)),
|
|
Type = lt_int(int_type_int)
|
|
;
|
|
ConsTag = float_tag(Float),
|
|
Const = llconst_float(Float),
|
|
(
|
|
UnboxedFloats = have_unboxed_floats,
|
|
Type = lt_float
|
|
;
|
|
UnboxedFloats = do_not_have_unboxed_floats,
|
|
% Though a standalone float might have needed to boxed, it may
|
|
% be stored in unboxed form as a constructor argument.
|
|
( if ArgWidth = double_word then
|
|
Type = lt_float
|
|
else
|
|
Type = lt_data_ptr
|
|
)
|
|
)
|
|
),
|
|
TypedRval = typed_rval(const(Const), Type)
|
|
;
|
|
ConsTag = shared_local_tag(Ptag, Stag),
|
|
Rval = mkword(Ptag, unop(mkbody, const(llconst_int(Stag)))),
|
|
TypedRval = typed_rval(Rval, lt_data_ptr)
|
|
;
|
|
ConsTag = reserved_address_tag(RA),
|
|
Rval = generate_reserved_address(RA),
|
|
rval_type(Rval, Type),
|
|
TypedRval = typed_rval(Rval, Type)
|
|
;
|
|
ConsTag = shared_with_reserved_addresses_tag(_, ActualConsTag),
|
|
generate_const_struct_arg_tag(ModuleInfo, UnboxedFloats,
|
|
ConstStructMap, ActualConsTag, ArgWidth, TypedRval)
|
|
;
|
|
ConsTag = type_ctor_info_tag(ModuleName, TypeName, TypeArity),
|
|
RttiTypeCtor = rtti_type_ctor(ModuleName, TypeName, TypeArity),
|
|
DataId = rtti_data_id(ctor_rtti_id(RttiTypeCtor,
|
|
type_ctor_type_ctor_info)),
|
|
Rval = const(llconst_data_addr(DataId, no)),
|
|
Type = lt_data_ptr,
|
|
TypedRval = typed_rval(Rval, Type)
|
|
;
|
|
ConsTag = base_typeclass_info_tag(ModuleName, ClassId, Instance),
|
|
TCName = generate_class_name(ClassId),
|
|
DataId = rtti_data_id(tc_rtti_id(TCName,
|
|
type_class_base_typeclass_info(ModuleName, Instance))),
|
|
Rval = const(llconst_data_addr(DataId, no)),
|
|
Type = lt_data_ptr,
|
|
TypedRval = typed_rval(Rval, Type)
|
|
;
|
|
( ConsTag = no_tag
|
|
; ConsTag = direct_arg_tag(_)
|
|
; ConsTag = single_functor_tag
|
|
; ConsTag = unshared_tag(_)
|
|
; ConsTag = shared_remote_tag(_, _)
|
|
; ConsTag = type_info_const_tag(_)
|
|
; ConsTag = typeclass_info_const_tag(_)
|
|
; ConsTag = ground_term_const_tag(_, _)
|
|
; ConsTag = closure_tag(_, _, _)
|
|
; ConsTag = tabling_info_tag(_, _)
|
|
; ConsTag = table_io_entry_tag(_, _)
|
|
; ConsTag = deep_profiling_proc_layout_tag(_, _)
|
|
),
|
|
unexpected($module, $pred, "unexpected tag")
|
|
).
|
|
|
|
:- pred det_single_arg_width(list(arg_width)::in, arg_width::out) is det.
|
|
|
|
det_single_arg_width(ArgWidths, ArgWidth) :-
|
|
(
|
|
ArgWidths = [ArgWidth]
|
|
;
|
|
( ArgWidths = []
|
|
; ArgWidths = [_, _ | _]
|
|
),
|
|
unexpected($module, $pred, "unexpected arg_width list")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_ground_term(TermVar, Goal, !CI, !CLD) :-
|
|
Goal = hlds_goal(GoalExpr, GoalInfo),
|
|
NonLocals = goal_info_get_nonlocals(GoalInfo),
|
|
set_of_var.to_sorted_list(NonLocals, NonLocalList),
|
|
(
|
|
NonLocalList = []
|
|
% The term being constructed by the scope is not needed, so there is
|
|
% nothing to do.
|
|
;
|
|
NonLocalList = [NonLocal],
|
|
( if NonLocal = TermVar then
|
|
( if GoalExpr = conj(plain_conj, Conjuncts) then
|
|
get_module_info(!.CI, ModuleInfo),
|
|
get_exprn_opts(!.CI, ExprnOpts),
|
|
UnboxedFloats = get_unboxed_floats(ExprnOpts),
|
|
get_static_cell_info(!.CI, StaticCellInfo0),
|
|
map.init(ActiveMap0),
|
|
generate_ground_term_conjuncts(ModuleInfo, Conjuncts,
|
|
UnboxedFloats, StaticCellInfo0, StaticCellInfo,
|
|
ActiveMap0, ActiveMap),
|
|
map.to_assoc_list(ActiveMap, ActivePairs),
|
|
( if ActivePairs = [TermVar - GroundTerm] then
|
|
add_forward_live_vars(NonLocals, !CLD),
|
|
set_static_cell_info(StaticCellInfo, !CI),
|
|
GroundTerm = typed_rval(Rval, _),
|
|
assign_const_to_var(TermVar, Rval, !.CI, !CLD)
|
|
else
|
|
unexpected($module, $pred, "no active pairs")
|
|
)
|
|
else
|
|
unexpected($module, $pred, "malformed goal")
|
|
)
|
|
else
|
|
unexpected($module, $pred, "unexpected nonlocal")
|
|
)
|
|
;
|
|
NonLocalList = [_, _ | _],
|
|
unexpected($module, $pred, "unexpected nonlocals")
|
|
).
|
|
|
|
:- type active_ground_term_map == map(prog_var, typed_rval).
|
|
|
|
:- pred generate_ground_term_conjuncts(module_info::in,
|
|
list(hlds_goal)::in, have_unboxed_floats::in,
|
|
static_cell_info::in, static_cell_info::out,
|
|
active_ground_term_map::in, active_ground_term_map::out) is det.
|
|
|
|
generate_ground_term_conjuncts(_ModuleInfo, [],
|
|
_UnboxedFloats, !StaticCellInfo, !ActiveMap).
|
|
generate_ground_term_conjuncts(ModuleInfo, [Goal | Goals],
|
|
UnboxedFloats, !StaticCellInfo, !ActiveMap) :-
|
|
generate_ground_term_conjunct(ModuleInfo, Goal, UnboxedFloats,
|
|
!StaticCellInfo, !ActiveMap),
|
|
generate_ground_term_conjuncts(ModuleInfo, Goals, UnboxedFloats,
|
|
!StaticCellInfo, !ActiveMap).
|
|
|
|
:- pred generate_ground_term_conjunct(module_info::in,
|
|
hlds_goal::in, have_unboxed_floats::in,
|
|
static_cell_info::in, static_cell_info::out,
|
|
active_ground_term_map::in, active_ground_term_map::out) is det.
|
|
|
|
generate_ground_term_conjunct(ModuleInfo, Goal, UnboxedFloats,
|
|
!StaticCellInfo, !ActiveMap) :-
|
|
Goal = hlds_goal(GoalExpr, _GoalInfo),
|
|
( if
|
|
GoalExpr = unify(_, _, _, Unify, _),
|
|
Unify = construct(Var, ConsId, Args, _, _, _, SubInfo),
|
|
SubInfo = no_construct_sub_info
|
|
then
|
|
ConsTag = cons_id_to_tag(ModuleInfo, ConsId),
|
|
get_cons_arg_widths(ModuleInfo, ConsId, Args, ConsArgWidths),
|
|
generate_ground_term_conjunct_tag(Var, ConsTag, Args, ConsArgWidths,
|
|
UnboxedFloats, !StaticCellInfo, !ActiveMap)
|
|
else
|
|
unexpected($module, $pred, "malformed goal")
|
|
).
|
|
|
|
:- pred generate_ground_term_conjunct_tag(prog_var::in, cons_tag::in,
|
|
list(prog_var)::in, list(arg_width)::in, have_unboxed_floats::in,
|
|
static_cell_info::in, static_cell_info::out,
|
|
active_ground_term_map::in, active_ground_term_map::out) is det.
|
|
|
|
generate_ground_term_conjunct_tag(Var, ConsTag, Args, ConsArgWidths,
|
|
UnboxedFloats, !StaticCellInfo, !ActiveMap) :-
|
|
(
|
|
(
|
|
ConsTag = string_tag(String),
|
|
Const = llconst_string(String),
|
|
Type = lt_string
|
|
;
|
|
ConsTag = int_tag(IntTag),
|
|
int_tag_to_const_and_int_type(IntTag, Const, IntType),
|
|
Type = lt_int(IntType)
|
|
;
|
|
ConsTag = foreign_tag(Lang, Val),
|
|
expect(unify(Lang, lang_c), $module, $pred,
|
|
"foreign_tag for language other than C"),
|
|
Const = llconst_foreign(Val, lt_int(int_type_int)),
|
|
Type = lt_int(int_type_int)
|
|
;
|
|
ConsTag = float_tag(Float),
|
|
Const = llconst_float(Float),
|
|
(
|
|
UnboxedFloats = have_unboxed_floats,
|
|
Type = lt_float
|
|
;
|
|
UnboxedFloats = do_not_have_unboxed_floats,
|
|
Type = lt_data_ptr
|
|
)
|
|
),
|
|
ActiveGroundTerm = typed_rval(const(Const), Type),
|
|
map.det_insert(Var, ActiveGroundTerm, !ActiveMap)
|
|
;
|
|
ConsTag = shared_local_tag(Ptag, Stag),
|
|
Rval = mkword(Ptag, unop(mkbody, const(llconst_int(Stag)))),
|
|
ActiveGroundTerm = typed_rval(Rval, lt_data_ptr),
|
|
map.det_insert(Var, ActiveGroundTerm, !ActiveMap)
|
|
;
|
|
ConsTag = reserved_address_tag(RA),
|
|
Rval = generate_reserved_address(RA),
|
|
rval_type(Rval, RvalType),
|
|
ActiveGroundTerm = typed_rval(Rval, RvalType),
|
|
map.det_insert(Var, ActiveGroundTerm, !ActiveMap)
|
|
;
|
|
ConsTag = shared_with_reserved_addresses_tag(_, ActualConsTag),
|
|
generate_ground_term_conjunct_tag(Var, ActualConsTag, Args,
|
|
ConsArgWidths, UnboxedFloats, !StaticCellInfo, !ActiveMap)
|
|
;
|
|
ConsTag = no_tag,
|
|
(
|
|
Args = [],
|
|
unexpected($module, $pred, "no_tag arity != 1")
|
|
;
|
|
Args = [Arg],
|
|
map.det_remove(Arg, RvalType, !ActiveMap),
|
|
map.det_insert(Var, RvalType, !ActiveMap)
|
|
;
|
|
Args = [_, _ | _],
|
|
unexpected($module, $pred, "no_tag arity != 1")
|
|
)
|
|
;
|
|
(
|
|
ConsTag = single_functor_tag,
|
|
Ptag = 0
|
|
;
|
|
ConsTag = unshared_tag(Ptag)
|
|
),
|
|
generate_ground_term_args(Args, ConsArgWidths, ArgTypedRvals,
|
|
!ActiveMap),
|
|
pack_ground_term_args(ConsArgWidths, ArgTypedRvals, PackArgTypedRvals),
|
|
add_scalar_static_cell(PackArgTypedRvals, DataAddr, !StaticCellInfo),
|
|
MaybeOffset = no,
|
|
CellPtrConst = const(llconst_data_addr(DataAddr, MaybeOffset)),
|
|
Rval = mkword(Ptag, CellPtrConst),
|
|
ActiveGroundTerm = typed_rval(Rval, lt_data_ptr),
|
|
map.det_insert(Var, ActiveGroundTerm, !ActiveMap)
|
|
;
|
|
ConsTag = direct_arg_tag(Ptag),
|
|
(
|
|
Args = [Arg],
|
|
map.det_remove(Arg, typed_rval(ArgRval, _RvalType), !ActiveMap),
|
|
Rval = mkword(Ptag, ArgRval),
|
|
ActiveGroundTerm = typed_rval(Rval, lt_data_ptr),
|
|
map.det_insert(Var, ActiveGroundTerm, !ActiveMap)
|
|
;
|
|
( Args = []
|
|
; Args = [_, _ | _]
|
|
),
|
|
unexpected($module, $pred, "direct_arg_tag: arity != 1")
|
|
)
|
|
;
|
|
ConsTag = shared_remote_tag(Ptag, Stag),
|
|
generate_ground_term_args(Args, ConsArgWidths, ArgTypedRvals,
|
|
!ActiveMap),
|
|
pack_ground_term_args(ConsArgWidths, ArgTypedRvals, PackArgTypedRvals),
|
|
StagTypedRval = typed_rval(const(llconst_int(Stag)),
|
|
lt_int(int_type_int)),
|
|
AllTypedRvals = [StagTypedRval | PackArgTypedRvals],
|
|
add_scalar_static_cell(AllTypedRvals, DataAddr, !StaticCellInfo),
|
|
MaybeOffset = no,
|
|
CellPtrConst = const(llconst_data_addr(DataAddr, MaybeOffset)),
|
|
Rval = mkword(Ptag, CellPtrConst),
|
|
ActiveGroundTerm = typed_rval(Rval, lt_data_ptr),
|
|
map.det_insert(Var, ActiveGroundTerm, !ActiveMap)
|
|
;
|
|
( ConsTag = closure_tag(_, _, _)
|
|
; ConsTag = type_ctor_info_tag(_, _, _)
|
|
; ConsTag = base_typeclass_info_tag(_, _, _)
|
|
; ConsTag = type_info_const_tag(_)
|
|
; ConsTag = typeclass_info_const_tag(_)
|
|
; ConsTag = ground_term_const_tag(_, _)
|
|
; ConsTag = tabling_info_tag(_, _)
|
|
; ConsTag = table_io_entry_tag(_, _)
|
|
; ConsTag = deep_profiling_proc_layout_tag(_, _)
|
|
),
|
|
unexpected($module, $pred, "unexpected tag")
|
|
).
|
|
|
|
:- pred int_tag_to_const_and_int_type(int_tag::in, rval_const::out,
|
|
int_type::out) is det.
|
|
|
|
int_tag_to_const_and_int_type(IntTag, Const, Type) :-
|
|
(
|
|
IntTag = int_tag_int(Int),
|
|
Const = llconst_int(Int),
|
|
Type = int_type_int
|
|
;
|
|
IntTag = int_tag_uint(UInt),
|
|
Const = llconst_uint(UInt),
|
|
Type = int_type_uint
|
|
;
|
|
IntTag = int_tag_int8(Int8),
|
|
Const = llconst_int8(Int8),
|
|
Type = int_type_int8
|
|
;
|
|
IntTag = int_tag_uint8(UInt8),
|
|
Const = llconst_uint8(UInt8),
|
|
Type = int_type_uint8
|
|
;
|
|
IntTag = int_tag_int16(Int16),
|
|
Const = llconst_int16(Int16),
|
|
Type = int_type_int16
|
|
;
|
|
IntTag = int_tag_uint16(UInt16),
|
|
Const = llconst_uint16(UInt16),
|
|
Type = int_type_uint16
|
|
;
|
|
IntTag = int_tag_int32(Int32),
|
|
Const = llconst_int32(Int32),
|
|
Type = int_type_int32
|
|
;
|
|
IntTag = int_tag_uint32(UInt32),
|
|
Const = llconst_uint32(UInt32),
|
|
Type = int_type_uint32
|
|
;
|
|
IntTag = int_tag_int64(Int64),
|
|
Const = llconst_int64(Int64),
|
|
Type = int_type_int64
|
|
;
|
|
IntTag = int_tag_uint64(UInt64),
|
|
Const = llconst_uint64(UInt64),
|
|
Type = int_type_uint64
|
|
).
|
|
|
|
:- pred generate_ground_term_args(list(prog_var)::in, list(arg_width)::in,
|
|
list(typed_rval)::out,
|
|
active_ground_term_map::in, active_ground_term_map::out) is det.
|
|
|
|
generate_ground_term_args(Vars, ConsArgWidths, TypedRvals, !ActiveMap) :-
|
|
list.map_corresponding_foldl(generate_ground_term_arg, Vars, ConsArgWidths,
|
|
TypedRvals, !ActiveMap).
|
|
|
|
:- pred generate_ground_term_arg(prog_var::in, arg_width::in,
|
|
typed_rval::out,
|
|
active_ground_term_map::in, active_ground_term_map::out) is det.
|
|
|
|
generate_ground_term_arg(Var, ConsArgWidth, TypedRval, !ActiveMap) :-
|
|
map.det_remove(Var, TypedRval0, !ActiveMap),
|
|
% Though a standalone float might have needed to boxed, it may be stored in
|
|
% unboxed form as a constructor argument.
|
|
( if
|
|
ConsArgWidth = double_word,
|
|
TypedRval0 = typed_rval(Rval, lt_data_ptr)
|
|
then
|
|
TypedRval = typed_rval(Rval, lt_float)
|
|
else
|
|
TypedRval = TypedRval0
|
|
).
|
|
|
|
:- pred pack_ground_term_args(list(arg_width)::in,
|
|
list(typed_rval)::in, list(typed_rval)::out) is det.
|
|
|
|
pack_ground_term_args(Widths, !TypedRvals) :-
|
|
pack_args(shift_combine_rval_type, Widths, !TypedRvals, unit, _, unit, _).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred shift_combine_arg(code_info::in, cell_arg::in, int::in,
|
|
maybe(cell_arg)::in, cell_arg::out, llds_code::in, llds_code::out,
|
|
code_loc_dep::in, code_loc_dep::out) is det.
|
|
|
|
shift_combine_arg(CI, CellArgA, Shift, MaybeCellArgB, FinalCellArg, !Code,
|
|
!CLD) :-
|
|
( if
|
|
Shift = 0,
|
|
MaybeCellArgB = no
|
|
then
|
|
FinalCellArg = CellArgA
|
|
else
|
|
(
|
|
CellArgA = cell_arg_full_word(RvalA, Completeness),
|
|
( if RvalA = var(Var) then
|
|
IsDummy = variable_is_of_dummy_type(CI, Var),
|
|
(
|
|
IsDummy = is_dummy_type,
|
|
ShiftCellArgA = cell_arg_skip
|
|
;
|
|
IsDummy = is_not_dummy_type,
|
|
produce_variable(Var, VarCode, VarRval, CI, !CLD),
|
|
ShiftCellArgA = cell_arg_full_word(
|
|
maybe_left_shift_rval(VarRval, Shift),
|
|
Completeness),
|
|
!:Code = !.Code ++ VarCode
|
|
)
|
|
else if RvalA = const(llconst_int(Int)) then
|
|
NewInt = maybe_left_shift_int(Int, Shift),
|
|
ShiftCellArgA = cell_arg_full_word(const(llconst_int(NewInt)),
|
|
Completeness)
|
|
else
|
|
unexpected($module, $pred, "non-var or int argument")
|
|
)
|
|
;
|
|
CellArgA = cell_arg_double_word(RvalA),
|
|
expect(unify(Shift, 0), $module, $pred,
|
|
"double word rval cannot be shifted"),
|
|
( if RvalA = var(Var) then
|
|
produce_variable(Var, VarCode, VarRval, CI, !CLD),
|
|
ShiftCellArgA = cell_arg_double_word(VarRval),
|
|
!:Code = !.Code ++ VarCode
|
|
else if RvalA = const(llconst_float(_)) then
|
|
ShiftCellArgA = CellArgA
|
|
else
|
|
unexpected($module, $pred, "non-var or float argument")
|
|
)
|
|
;
|
|
CellArgA = cell_arg_skip,
|
|
ShiftCellArgA = cell_arg_skip
|
|
;
|
|
CellArgA = cell_arg_take_addr(_, _),
|
|
unexpected($module, $pred, "cell_arg_take_addr")
|
|
),
|
|
(
|
|
MaybeCellArgB = yes(CellArgB),
|
|
FinalCellArg = bitwise_or_cell_arg(ShiftCellArgA, CellArgB)
|
|
;
|
|
MaybeCellArgB = no,
|
|
FinalCellArg = ShiftCellArgA
|
|
)
|
|
).
|
|
|
|
:- pred shift_combine_rval_type(typed_rval::in, int::in,
|
|
maybe(typed_rval)::in, typed_rval::out,
|
|
unit::in, unit::out, unit::in, unit::out) is det.
|
|
|
|
shift_combine_rval_type(ArgA, Shift, MaybeArgB, FinalArg, !Acc1, !Acc2) :-
|
|
ArgA = typed_rval(RvalA, TypeA),
|
|
ShiftRvalA = maybe_left_shift_rval(RvalA, Shift),
|
|
(
|
|
MaybeArgB = yes(typed_rval(RvalB, TypeB)),
|
|
( if TypeA = TypeB then
|
|
FinalRval = binop(bitwise_or(int_type_int), ShiftRvalA, RvalB)
|
|
else
|
|
unexpected($module, $pred, "mismatched llds_types")
|
|
)
|
|
;
|
|
MaybeArgB = no,
|
|
FinalRval = ShiftRvalA
|
|
),
|
|
FinalArg = typed_rval(FinalRval, TypeA).
|
|
|
|
:- func maybe_left_shift_rval(rval, int) = rval.
|
|
|
|
maybe_left_shift_rval(Rval, Shift) =
|
|
( if Shift = 0 then
|
|
Rval
|
|
else
|
|
binop(unchecked_left_shift(int_type_int), Rval,
|
|
const(llconst_int(Shift)))
|
|
).
|
|
|
|
:- func maybe_left_shift_int(int, int) = int.
|
|
|
|
maybe_left_shift_int(X, Shift) =
|
|
( if Shift = 0 then
|
|
X
|
|
else
|
|
X << Shift
|
|
).
|
|
|
|
:- func right_shift_rval(rval, int) = rval.
|
|
|
|
right_shift_rval(Rval, Shift) =
|
|
binop(unchecked_right_shift(int_type_int), Rval,
|
|
const(llconst_int(Shift))).
|
|
|
|
:- func bitwise_or_cell_arg(cell_arg, cell_arg) = cell_arg.
|
|
|
|
bitwise_or_cell_arg(CellArgA, CellArgB) = CellArg :-
|
|
( if bitwise_or_cell_arg(CellArgA, CellArgB, CellArgPrime) then
|
|
CellArg = CellArgPrime
|
|
else
|
|
unexpected($module, $pred, "invalid combination")
|
|
).
|
|
|
|
:- pred bitwise_or_cell_arg(cell_arg::in, cell_arg::in, cell_arg::out)
|
|
is semidet.
|
|
|
|
bitwise_or_cell_arg(CellArgA, CellArgB, CellArg) :-
|
|
(
|
|
CellArgA = cell_arg_full_word(RvalA, CompletenessA),
|
|
CellArgB = cell_arg_full_word(RvalB, CompletenessB),
|
|
Expr = binop(bitwise_or(int_type_int), RvalA, RvalB),
|
|
Completeness = combine_completeness(CompletenessA, CompletenessB),
|
|
CellArg = cell_arg_full_word(Expr, Completeness)
|
|
;
|
|
CellArgA = cell_arg_full_word(Rval, _),
|
|
CellArgB = cell_arg_skip,
|
|
CellArg = cell_arg_full_word(Rval, incomplete)
|
|
;
|
|
CellArgA = cell_arg_skip,
|
|
CellArgB = cell_arg_full_word(Rval, _),
|
|
CellArg = cell_arg_full_word(Rval, incomplete)
|
|
;
|
|
CellArgA = cell_arg_skip,
|
|
CellArgB = cell_arg_skip,
|
|
CellArg = cell_arg_skip
|
|
).
|
|
|
|
:- func combine_completeness(completeness, completeness) = completeness.
|
|
|
|
combine_completeness(complete, complete) = complete.
|
|
combine_completeness(incomplete, complete) = incomplete.
|
|
combine_completeness(complete, incomplete) = incomplete.
|
|
combine_completeness(incomplete, incomplete) = incomplete.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred var_type_msg(mer_type::in, string::out) is det.
|
|
|
|
var_type_msg(Type, Msg) :-
|
|
type_to_ctor_det(Type, TypeCtor),
|
|
TypeCtor = type_ctor(TypeSym, TypeArity),
|
|
TypeSymStr = sym_name_to_string(TypeSym),
|
|
string.int_to_string(TypeArity, TypeArityStr),
|
|
string.append_list([TypeSymStr, "/", TypeArityStr], Msg).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ll_backend.unify_gen.
|
|
%---------------------------------------------------------------------------%
|