mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 23:05:21 +00:00
switch_gen.nl: Improve code generation for deterministic switches by omitting the test for the last case. (I haven't tested this change.)
217 lines
8.1 KiB
Mathematica
217 lines
8.1 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module switch_gen.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds, llds, code_gen, code_info, code_util.
|
|
|
|
:- pred switch_gen__generate_det_switch(var, list(case),
|
|
code_tree, code_info, code_info).
|
|
:- mode switch_gen__generate_det_switch(in, in, out, in, out) is det.
|
|
|
|
:- pred switch_gen__generate_semi_switch(var, list(case),
|
|
code_tree, code_info, code_info).
|
|
:- mode switch_gen__generate_semi_switch(in, in, out, in, out) is det.
|
|
|
|
:- pred switch_gen__generate_non_switch(var, list(case),
|
|
code_tree, code_info, code_info).
|
|
:- mode switch_gen__generate_non_switch(in, in, out, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- implementation.
|
|
|
|
:- import_module unify_gen.
|
|
|
|
:- import_module tree, list, map, std_util, require.
|
|
|
|
% To generate a deterministic switch, first we flush the
|
|
% variable on whose tag we are going to switch, then we
|
|
% generate the cases for the switch.
|
|
|
|
switch_gen__generate_det_switch(CaseVar, Cases, Instr) -->
|
|
code_info__flush_variable(CaseVar, VarCode),
|
|
code_info__get_variable_register(CaseVar, Lval),
|
|
code_info__get_next_label(EndLabel),
|
|
switch_gen__generate_det_cases(Cases, CaseVar,
|
|
Lval, EndLabel, CasesCode),
|
|
{ Instr = tree(VarCode, CasesCode) },
|
|
code_info__remake_with_store_map.
|
|
|
|
% To generate a case for a deterministic switch we generate
|
|
% code to do a tag-test and fall through to the next case in
|
|
% the event of failure. The bodies of the cases are deterministic
|
|
% so we generate them as such.
|
|
%
|
|
% Each case except the last consists of a tag test, followed by
|
|
% the goal for that case, followed by a branch to the end of
|
|
% the switch. The goal is generated as a "forced" goal which
|
|
% ensures that all variables which are live at the end of the
|
|
% case get stored in their stack slot. For the last case, we
|
|
% don't need to generate the tag test or the branch to the end
|
|
% of the switch. After the last case, we put the end-of-switch
|
|
% label which other cases branch to after their case goals.
|
|
|
|
:- pred switch_gen__generate_det_cases(list(case), var, lval, label,
|
|
code_tree, code_info, code_info).
|
|
:- mode switch_gen__generate_det_cases(in, in, in, in, out, in, out) is det.
|
|
|
|
switch_gen__generate_det_cases([], _Var, _Lval, _EndLabel, _Code) -->
|
|
% should never be reached
|
|
{ error("switch_gen__generate_det_cases: empty switch") }.
|
|
|
|
switch_gen__generate_det_cases([case(Cons, Goal)|Cases], Var, Lval, EndLabel,
|
|
CasesCode) -->
|
|
( { Cases = [] } ->
|
|
code_gen__generate_forced_det_goal(Goal, ThisCode),
|
|
{ EndCode = node([label(EndLabel) - "End of det switch"]) },
|
|
{ CasesCode = tree(ThisCode, EndCode) }
|
|
;
|
|
code_info__get_next_label(ThisLab),
|
|
code_info__get_next_label(ElseLab),
|
|
code_info__grab_code_info(CodeInfo),
|
|
unify_gen__generate_tag_rval(Var, Cons, Rval, FlushCode),
|
|
% generate the case as a deterministic goal
|
|
code_info__generate_icond_branch(Rval,ThisLab, ElseLab,
|
|
BranchCode),
|
|
{ TestCode = tree(
|
|
tree(BranchCode, FlushCode),
|
|
node([label(ThisLab) - ""])
|
|
) },
|
|
code_gen__generate_forced_det_goal(Goal, ThisCode),
|
|
{ ElseLabel = node([
|
|
goto(EndLabel) - "skip to the end of the det switch",
|
|
label(ElseLab) - "next case"
|
|
]) },
|
|
% generate the rest of the cases.
|
|
code_info__slap_code_info(CodeInfo),
|
|
switch_gen__generate_det_cases(Cases, Var, Lval, EndLabel,
|
|
CasesCode0),
|
|
{ CasesCode = tree(tree(TestCode, ThisCode),
|
|
tree(ElseLabel, CasesCode0)) }
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% A semideterministic switch contains semideterministic goals.
|
|
|
|
switch_gen__generate_semi_switch(CaseVar, Cases, Instr) -->
|
|
code_info__flush_variable(CaseVar, VarCode),
|
|
code_info__get_variable_register(CaseVar, Lval),
|
|
code_info__get_next_label(EndLabel),
|
|
switch_gen__generate_semi_cases(Cases, CaseVar, Lval,
|
|
EndLabel, CasesCode),
|
|
{ Instr = tree(VarCode, CasesCode) },
|
|
code_info__remake_with_store_map.
|
|
|
|
:- pred switch_gen__generate_semi_cases(list(case), var, lval, label,
|
|
code_tree, code_info, code_info).
|
|
:- mode switch_gen__generate_semi_cases(in, in, in, in, out, in, out) is det.
|
|
|
|
% At the end of the switch, we fail because we came across a
|
|
% tag which was not covered by one of the cases. It is followed
|
|
% by the end of switch label to which the cases branch.
|
|
switch_gen__generate_semi_cases([], _Var, _Lval, EndLabel, Code) -->
|
|
code_info__generate_failure(FailCode),
|
|
{ Code = tree(FailCode, node([ label(EndLabel) -
|
|
"End of semidet switch" ])) }.
|
|
|
|
% A semidet cases consists of a tag-test followed by a semidet
|
|
% goal and a label for the start of the next case.
|
|
switch_gen__generate_semi_cases([case(Cons, Goal)|Cases], Var, Lval, EndLabel,
|
|
CasesCode) -->
|
|
code_info__grab_code_info(CodeInfo),
|
|
code_info__get_next_label(ThisLab),
|
|
code_info__get_next_label(ElseLab),
|
|
unify_gen__generate_tag_rval(Var, Cons, Rval, FlushCode),
|
|
% generate the case as a deterministic goal
|
|
code_info__generate_icond_branch(Rval,ThisLab, ElseLab, BranchCode),
|
|
{ TestCode = tree(
|
|
tree(FlushCode, BranchCode),
|
|
node([label(ThisLab) - ""])
|
|
) },
|
|
% generate the case as a semi-deterministc goal
|
|
code_gen__generate_forced_semi_goal(Goal, ThisCode),
|
|
{ ElseLabel = node([
|
|
goto(EndLabel) - "skip to the end of the semidet switch",
|
|
label(ElseLab) - "next case"
|
|
]) },
|
|
% If there are more cases, the we need to restore
|
|
% the expression cache, etc.
|
|
(
|
|
{ Cases = [_|_] }
|
|
->
|
|
code_info__slap_code_info(CodeInfo)
|
|
;
|
|
{ true }
|
|
),
|
|
% generate the rest of the cases.
|
|
switch_gen__generate_semi_cases(Cases, Var, Lval, EndLabel, CasesCode0),
|
|
{ CasesCode = tree(tree(TestCode, ThisCode),
|
|
tree(ElseLabel, CasesCode0)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
switch_gen__generate_non_switch(CaseVar, Cases, Instr) -->
|
|
code_info__flush_variable(CaseVar, VarCode),
|
|
code_info__get_variable_register(CaseVar, Lval),
|
|
code_info__get_next_label(EndLabel),
|
|
switch_gen__generate_non_cases(Cases, CaseVar, Lval,
|
|
EndLabel, CasesCode),
|
|
{ Instr = tree(VarCode, CasesCode) },
|
|
code_info__remake_with_store_map.
|
|
|
|
:- pred switch_gen__generate_non_cases(list(case), var, lval, label,
|
|
code_tree, code_info, code_info).
|
|
:- mode switch_gen__generate_non_cases(in, in, in, in, out, in, out) is det.
|
|
|
|
% At the end of the switch, we fail because we came across a
|
|
% tag which was not covered by one of the cases. It is followed
|
|
% by the end of switch label to which the cases branch.
|
|
switch_gen__generate_non_cases([], _Var, _Lval, EndLabel, Code) -->
|
|
{ Code = node([
|
|
redo - "",
|
|
label(EndLabel) - "End of nondet switch"
|
|
]) }.
|
|
|
|
% A nondet cases consists of a tag-test followed by a semidet
|
|
% goal and a label for the start of the next case.
|
|
switch_gen__generate_non_cases([case(Cons, Goal)|Cases], Var, Lval, EndLabel,
|
|
CasesCode) -->
|
|
code_info__grab_code_info(CodeInfo),
|
|
code_info__get_next_label(ThisLab),
|
|
code_info__get_next_label(ElseLab),
|
|
unify_gen__generate_tag_rval(Var, Cons, Rval, FlushCode),
|
|
% generate the case as a deterministic goal
|
|
code_info__generate_icond_branch(Rval,ThisLab, ElseLab, BranchCode),
|
|
{ TestCode = tree(
|
|
tree(FlushCode, BranchCode),
|
|
node([label(ThisLab) - ""])
|
|
) },
|
|
% generate the case as a non-deterministc goal
|
|
code_gen__generate_forced_non_goal(Goal, ThisCode),
|
|
{ ElseLabel = node([
|
|
goto(EndLabel) - "skip to the end of the nondet switch",
|
|
label(ElseLab) - "next case"
|
|
]) },
|
|
% If there are more cases, the we need to restore
|
|
% the expression cache, etc.
|
|
(
|
|
{ Cases = [_|_] }
|
|
->
|
|
code_info__slap_code_info(CodeInfo)
|
|
;
|
|
{ true }
|
|
),
|
|
% generate the rest of the cases.
|
|
switch_gen__generate_non_cases(Cases, Var, Lval, EndLabel, CasesCode0),
|
|
{ CasesCode = tree(tree(TestCode, ThisCode),
|
|
tree(ElseLabel, CasesCode0)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|