%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2023 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: make.util.m.
% Authors: stayl, wangp.
%
% Hashing predicates used to implement `mmc --make'.
%
%---------------------------------------------------------------------------%

:- module make.hash.
:- interface.

:- import_module make.make_info.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.

%---------------------------------------------------------------------------%
%
% Hash functions.
%

:- pred module_name_hash(module_name::in, int::out) is det.

:- pred dependency_file_hash(dependency_file::in, int::out) is det.

:- pred dependency_file_with_module_index_hash(
    dependency_file_with_module_index::in, int::out) is det.

:- pred target_file_hash(target_file::in, int::out) is det.

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

:- implementation.

:- import_module backend_libs.
:- import_module backend_libs.compile_target_code.

:- import_module enum.
:- import_module int.
:- import_module string.
:- import_module uint.

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

module_name_hash(SymName, Hash) :-
    (
        SymName = unqualified(Name),
        Hash = string.hash(Name)
    ;
        SymName = qualified(_ModuleName, Name),
        % Hashing the module name seems to be not worthwhile.
        Hash = string.hash(Name)
    ).

dependency_file_hash(DepFile, Hash) :-
    (
        DepFile = dep_target(TargetFile),
        target_file_hash(TargetFile, Hash)
    ;
        DepFile = dep_file(FileName),
        Hash = string.hash(FileName)
    ).

dependency_file_with_module_index_hash(DepFile, Hash) :-
    (
        DepFile = dfmi_target(ModuleIndex, Type),
        Hash0 = cast_to_int(to_uint(ModuleIndex)),
        Hash1 = module_target_type_to_nonce(Type),
        Hash = mix(Hash0, Hash1)
    ;
        DepFile = dfmi_file(FileName),
        Hash = string.hash(FileName)
    ).

target_file_hash(TargetFile, Hash) :-
    TargetFile = target_file(ModuleName, Type),
    module_name_hash(ModuleName, Hash0),
    Hash1 = module_target_type_to_nonce(Type),
    Hash = mix(Hash0, Hash1).

:- func module_target_type_to_nonce(module_target_type) = int.

module_target_type_to_nonce(Type) = X :-
    (
        Type = module_target_source,
        X = 1
    ;
        Type = module_target_errors,
        X = 2
    ;
        Type = module_target_int0,
        X = 3
    ;
        Type = module_target_int1,
        X = 4
    ;
        Type = module_target_int2,
        X = 5
    ;
        Type = module_target_int3,
        X = 6
    ;
        Type = module_target_opt,
        X = 7
    ;
        Type = module_target_analysis_registry,
        X = 8
    ;
        Type = module_target_c_header(header_mh),
        X = 9
    ;
        Type = module_target_c_header(header_mih),
        X = 10
    ;
        Type = module_target_c_code,
        X = 11
    ;
        Type = module_target_java_code,
        X = 12
%   ;
%       Type = module_target_erlang_header,
%       X = 13
%   ;
%       Type = module_target_erlang_code,
%       X = 14
%   ;
%       Type = module_target_erlang_beam_code,
%       X = 15
    ;
        Type = module_target_object_code(PIC),
        X = 16 `mix` pic_to_nonce(PIC)
%   ;
%       Type = module_target_foreign_object(_PIC, _ForeignLang),
%       X = 17
    ;
        Type = module_target_fact_table_object(_PIC, _FileName),
        X = 18
    ;
        Type = module_target_xml_doc,
        X = 19
    ;
        Type = module_target_track_flags,
        X = 20
    ;
        Type = module_target_java_class_code,
        X = 21
    ;
        Type = module_target_csharp_code,
        X = 22
    ).

:- func pic_to_nonce(pic) = int.

pic_to_nonce(pic) = 1.
pic_to_nonce(non_pic) = 3.
% For compatibility; we used to have pic_to_nonce(link_with_pic) = 2.

:- func mix(int, int) = int.

mix(H0, X) = H :-
    H1 = H0 `xor` (H0 `unchecked_left_shift` 5),
    H = H1 `xor` X.

%---------------------------------------------------------------------------%
:- end_module make.hash.
%---------------------------------------------------------------------------%
