%---------------------------------------------------------------------------e
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------e
% Copyright (C) 1994-2012 The University of Melbourne.
% Copyright (C) 2013-2018 The Mercury team.
% 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_goal.
:- import_module ll_backend.code_info.
:- import_module ll_backend.code_loc_dep.
:- import_module ll_backend.llds.

%---------------------------------------------------------------------------%

    % Generate LLDS code for a unification.
    %
:- 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.

%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- implementation.

:- import_module backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module check_hlds.
:- import_module check_hlds.type_util.
:- import_module hlds.hlds_module.
:- import_module ll_backend.unify_gen_construct.
:- import_module ll_backend.unify_gen_deconstruct.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.

:- import_module cord.
:- import_module list.
:- import_module maybe.
:- import_module require.
:- import_module term.

%---------------------------------------------------------------------------%

generate_unification(CodeModel, Unification, GoalInfo, Code, !CI, !CLD) :-
    (
        Unification = assign(LHSVar, RHSVar),
        expect(unify(CodeModel, model_det), $pred, "assign not det"),
        generate_assignment_unification(LHSVar, RHSVar, Code, !CLD)
    ;
        Unification = simple_test(VarA, VarB),
        expect(unify(CodeModel, model_semi), $pred, "simple_test not semidet"),
        generate_simple_test_unification(VarA, VarB, Code, !CI, !CLD)
    ;
        Unification = construct(LHSVar, ConsId, RHSVars, ArgModes,
            HowToConstruct, _CellIsUnique, SubInfo),
        expect(unify(CodeModel, model_det), $pred, "construct not det"),
        (
            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 = []
            ),
            generate_construction_unification(LHSVar, ConsId,
                RHSVars, ArgModes, HowToConstruct, TakeAddr, MaybeSize,
                GoalInfo, Code, !CI, !CLD)
        else
            Code = empty
        )
    ;
        Unification = deconstruct(LHSVar, ConsId, RHSVars, ArgModes,
            CanFail, CanCGC),
        generate_deconstruction_unification(LHSVar, ConsId, RHSVars, ArgModes,
            CanFail, CanCGC, Code, !CI, !CLD)
    ;
        Unification = complicated_unify(_, _, _),
        % These should have been converted into calls to unification predicates
        % by the simplification pass.
        unexpected($pred, "complicated unify")
    ).

%---------------------------------------------------------------------------%

    % Generate code for the assignment unification LHSVar := RHSVar.
    %
:- pred generate_assignment_unification(prog_var::in, prog_var::in,
    llds_code::out, code_loc_dep::in, code_loc_dep::out) is det.

generate_assignment_unification(LHSVar, RHSVar, Code, !CLD) :-
    ( if variable_is_forward_live(!.CLD, LHSVar) then
        assign_var_to_var(LHSVar, RHSVar, !CLD),
        % The assignment is cached; we do not generate any code for it *here*
        % (though we *will* generate code for it later, when the value
        % of LHSVar is materialized).
        Code = empty
    else
        % Mode analysis reports free-free unifications as assignments
        % to a dead variable. For such unifications, we of course
        % do not generate any code.
        Code = empty
    ).

%---------------------------------------------------------------------------%

    % generate_simple_test_unification(VarA, VarB, Code, !CI, !CLD):
    %
    % We generate code for a simple test unification by flushing both variables
    % from the cache, and producing code that branches to the location that is
    % appropriate for a failure in the current environment if the two values
    % are not the same. Simple tests are in-in unifications on enumerations,
    % integers, strings and floats.
    %
:- pred generate_simple_test_unification(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_simple_test_unification(VarA, VarB, Code, !CI, !CLD) :-
    Type = variable_type(!.CI, VarA),
    get_module_info(!.CI, ModuleInfo),
    IsDummyType = is_type_a_dummy(ModuleInfo, Type),
    (
        IsDummyType = is_dummy_type,
        Code = empty
    ;
        IsDummyType = is_not_dummy_type,
        ( if Type = builtin_type(BuiltinType) then
            (
                BuiltinType = builtin_type_string,
                EqOp = str_eq
            ;
                BuiltinType = builtin_type_float,
                EqOp = float_eq
            ;
                BuiltinType = builtin_type_char,
                EqOp = eq(int_type_int)
            ;
                BuiltinType = builtin_type_int(IntType),
                EqOp = eq(IntType)
            )
        else
            % The else branch handles enumerations.
            EqOp = eq(int_type_int)
        ),
        produce_variable(VarA, VarCodeA, VarRvalA, !CLD),
        produce_variable(VarB, VarCodeB, VarRvalB, !CLD),
        fail_if_rval_is_false(binop(EqOp, VarRvalA, VarRvalB), FailCode,
            !CI, !CLD),
        Code = VarCodeA ++ VarCodeB ++ FailCode
    ).

%---------------------------------------------------------------------------%
:- end_module ll_backend.unify_gen.
%---------------------------------------------------------------------------%
