1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
|
<html>
<head>
<title>
Notes On The Design Of The Mercury Compiler
</title>
</head>
<body bgcolor="#ffffff" text="#000000">
<hr>
<!---------------------------------------------------------------------------->
This file contains an overview of the design of the compiler.
See also <a href="overall_design.html">overall_design.html</a>
for an overview of how the different sub-systems (compiler,
library, runtime, etc.) fit together.
<hr>
<!---------------------------------------------------------------------------->
<h2> OUTLINE </h2>
<p>
The main job of the compiler is to translate Mercury into C, although it
can also translate (subsets of) Mercury to some other languages: Goedel,
Mercury bytecode (for a planned bytecode interpreter), MSIL (for the
Microsoft .NET platform) and RL (the Aditi Relational Language).
<p>
The top-level of the compiler is in the file mercury_compile.m.
The basic design is that compilation is broken into the following
stages:
<ul>
<li> 1. parsing (source files -> HLDS)
<li> 2. semantic analysis and error checking (HLDS -> annotated HLDS)
<li> 3. high-level transformations (annotated HLDS -> annotated HLDS)
<li> 4. code generation (annotated HLDS -> target representation)
<li> 5. low-level optimizations
(target representation -> target representation)
<li> 6. output code (target representation -> target code)
</ul>
Note that in reality the separation is not quite as simple as that.
Although parsing is listed as step 1 and semantic analysis is listed
as step 2, the last stage of parsing actually includes some semantic checks.
And although optimization is listed as steps 3 and 5, it also occurs in
steps 2, 4, and 6. For example, elimination of assignments to dead
variables is done in mode analysis; middle-recursion optimization and
the use of static constants for ground terms is done in code
generation; and a few low-level optimizations are done in llds_out.m
as we are spitting out the C code.
<p>
In addition, the compiler is actually a multi-targeted compiler
with several different back-ends. When you take the different
back-ends into account, the structure looks like this:
<ul type=disc>
<li> front-end
<ul type=disc>
<li> 1. parsing (source files -> HLDS)
<li> 2. semantic analysis and error checking (HLDS -> annotated HLDS)
<li> 3. high-level transformations (annotated HLDS -> annotated HLDS)
</ul>
<li> back-ends
<ul type=disc>
<li> a. LLDS back-end
<ul type=disc>
<li> 4a. code generation (annotated HLDS -> LLDS)
<li> 5a. low-level optimizations (LLDS -> LLDS)
<li> 6a. output code (LLDS -> C)
</ul>
<li> b. MLDS back-end
<ul type=disc>
<li> 4b. code generation (annotated HLDS -> MLDS)
<li> 5b. MLDS transformations (MLDS -> MLDS)
<li> 6b. output code
(MLDS -> C or MLDS -> MSIL
or eventually MLDS -> Java, etc.)
</ul>
<li> c. RL back-end
<ul type=disc>
<li> 4c. code generation (annotated HLDS -> RL)
<li> 5c. low-level optimizations (RL -> RL)
<li> 6c. output code (RL -> RL-bytecode)
</ul>
<li> d. bytecode back-end
<ul type=disc>
<li> 4d. code generation (annotated HLDS -> bytecode)
</ul>
</ul>
</ul>
<p>
<hr>
<!---------------------------------------------------------------------------->
<h2> DETAILED DESIGN </h2>
This section describes the role of each module in the compiler.
For more information about the design of a particular module,
see the documentation at the start of that module's source code.
<p>
<hr>
<!---------------------------------------------------------------------------->
<p>
The action is co-ordinated from mercury_compile.m.
<p>
<h3> Option handling </h3>
<p>
The command-line options are defined in the module options.m.
mercury_compile.m calls library/getopt.m, passing the predicates
defined in options.m as arguments, to parse them. It then invokes
handle_options.m to postprocess the option set. The results are
stored in the io__state, using the type globals defined in globals.m.
<p>
<hr>
<!---------------------------------------------------------------------------->
<h3> FRONT END </h3>
<h4> 1. Parsing </h4>
<p>
<ul>
<li> lexical analysis (library/lexer.m)
<li> stage 1 parsing - convert strings to terms. <br>
library/parser.m contains the code to do this, while
library/term.m and library/varset.m contain the term and varset
data structures that result, and predicates for manipulating them.
<li> stage 2 parsing - convert terms to `items' (declarations, clauses, etc.)
<br>
The result of this stage is a parse tree that has a one-to-one
correspondence with the source code. The parse tree data structure
definition is in prog_data.m, while the code to create it is in
prog_io.m and its submodules prog_io_dcg.m (which handles clauses
using Definite Clause Grammar notation), prog_io_goal.m (which handles
goals), prog_io_pragma.m (which handles pragma declarations),
prog_io_typeclass.m (which handles typeclass and instance declarations)
and prog_io_util.m (which defines predicates and types needed by the
other prog_io*.m modules. The data structure for insts is stored in
its own module, inst.m.
<p>
The modules prog_out.m and mercury_to_mercury.m contain predicates
for printing the parse tree. prog_util.m contains some utility
predicates for manipulating the parse tree.
<li> imports and exports are handled at this point (modules.m) <br>
modules.m has the code to write out `.int', `.int2', `.int3',
`.d' and `.dep' files.
<li> module qualification of types, insts and modes <br>
module_qual.m - <br>
Adds module qualifiers to all types insts and modes,
checking that a given type, inst or mode exists and that
there is only possible match. This is done here because
it must be done before the `.int' and `.int2' interface files
are written. This also checks whether imports are really needed
in the interface.
<br>
Notes on module qualification:
<ul>
<li> all types, typeclasses, insts and modes occuring in pred, func,
type, typeclass and mode declarations are module qualified by
module_qual.m.
<li> all types, insts and modes occuring in lambda expressions and
explicit type qualifications are module qualified in
make_hlds.m.
<li> constructors occuring in predicate and function mode declarations
are module qualified during type checking.
<li> predicate and function calls and constructors within goals
are module qualified during mode analysis.
</ul>
<li> reading and writing of optimization interfaces
(intermod.m and trans_opt.m). <br>
<module>.opt contains clauses for exported preds suitable for
inlining or higher-order specialization. The `.opt' file for the
current module is written after type-checking. `.opt' files
for imported modules are read here.
<module>.opt contains termination analysis information
for exported preds (eventually it ought to contain other
"transitive" information too, e.g. for optimization, but
currently it is only used for termination analysis).
`.trans_opt' files for imported modules are read here.
The `.trans_opt' file for the current module is written
after the end of semantic analysis.
<li> expansion of equivalence types (equiv_type.m) <br>
This is really part of type-checking, but is done
on the item_list rather than on the HLDS because it
turned out to be much easier to implement that way.
<li> conversion to superhomogeneous form and into HLDS <br>
make_hlds.m transforms the code into superhomogeneous form,
and at the same time converts the parse tree into the HLDS.
It expands away universal quantification
(using `all [Vs] G' ===> `not (some [Vs] (not G))')
and implication (using `A => B' ===> `not(A, not B)').
It converts `pragma import', `pragma c_code' and `pragma fact_table'
declarations into clauses with HLDS `pragma_c_code'
instructions for bodies.
The `pragma fact_table' conversion is done by calling fact_table.m
which also reads the facts from the declared file and compiles them
into a separate C file for which the `pragma_c_code' contains lookup
code.
make_hlds.m also calls make_tags.m which chooses the data
representation for each discriminated union type by
assigning tags to each functor.
</ul>
<p>
The result at this stage is the High Level Data Structure,
which is defined in four files:
<ol>
<li> hlds_data.m defines the parts of the HLDS concerned with
function symbols, types, insts, modes and determinisms;
<li> hlds_goal.m defines the part of the HLDS concerned with the
structure of goals, including the annotations on goals;
<li> hlds_pred.m defines the part of the HLDS concerning
predicates and procedures;
<li> hlds_module.m defines the top-level parts of the HLDS,
including the type module_info.
</ol>
The module hlds_out.m contains predicates to dump the HLDS to a file.
The module goal_util.m contains predicates for renaming variables
in an HLDS goal.
<p>
<h4> 2. Semantic analysis and error checking </h4>
<p>
Any pass which can report errors or warnings must be part of this stage,
so that the compiler does the right thing for options such as
`--halt-at-warn' (which turns warnings into errors) and
`--error-check-only' (which makes the compiler only compile up to this stage).
<p>
<dl>
<dt> implicit quantification
<dd>
quantification.m handles implicit quantification and computes
the set of non-local variables for each sub-goal.
It also expands away bi-implication (unlike the expansion
of implication and universal quantification, this expansion
cannot be done until after quantification).
This pass is called from the `transform' predicate in make_hlds.m.
<dt> checking typeclass instances (check_typeclass.m)
<dd>
check_typeclass.m both checks that instance declarations satisfy all
the appropriate superclass constraints and
performs a source-to-source transformation on the
methods methods from the instance declarations.
The transformed code is checked for type, mode, uniqueness, purity
and determinism correctness by the later passes, which has the effect
of checking the correctness of the instance methods themselves
(ie. that the instance methods match those expected by the typeclass
declaration).
During the transformation,
pred_ids and proc_ids are assigned to the methods for each instance.
In
addition, while checking that the superclasses of a class are satisfied
by the instance declaration, a set of constraint_proofs are built up
for the superclass constraints. These are used by polymorphism.m when
generating the base_typeclass_info for the instance.
<dt> type checking
<dd>
<ul>
<li> typecheck.m handles type checking, overloading resolution &
module name resolution, and almost fully qualifies all predicate
and functor names. It sets the map(var, type) field in the
pred_info. However, typecheck.m doesn't figure out the pred_id
for function calls or calls to overloaded predicates; that can't
be done in a single pass of typechecking, and so it is done
later on (in post_typecheck.m, for both preds and function calls)
Typeclass constraints are checked here, and
any redundant constraints that are eliminated are recorded (as
constraint_proofs) in the pred_info for future reference. When it has
finished, typecheck.m calls clause_to_proc.m to make duplicate copies
of the clauses for each different mode of a predicate; all later
stages work on procedures, not predicates.
<li> type_util.m contains utility predicates dealing with types
that are used in a variety of different places within the compiler
<li> post_typecheck.m may also be considered to logically be a part
of typechecking, but it is actually called from purity
analysis (see below). It contains the stuff related to
type checking that can't be done in the main type checking pass.
It also removes assertions from further processing.
</ul>
<dt> assertions
<dd>
assertion.m is the abstract interface to the assertion table.
Currently all the compiler does is type check the assertions and
record for each predicate that is used in an assertion, which
assertion it is used in. The set up of the assertion table occurs
in post_typecheck__finish_assertion.
<dt> purity analysis
<dd>
purity.m is responsible for purity checking, as well as
defining the <CODE>purity</CODE> type and a few public
operations on it. It also calls post_typecheck.m to
complete the handling of predicate
overloading for cases which typecheck.m is unable to handle,
and to check for unbound type variables.
Elimination of double negation is also done here; that needs to
be done after quantification analysis and before mode analysis.
<dt> polymorphism transformation
<dd>
polymorphism.m handles introduction of type_info arguments for
polymorphic predicates and introduction of typeclass_info arguments
for typeclass-constrained predicates.
This phase needs to come before mode analysis so that mode analysis
can properly reorder code involving existential types.
(It also needs to come before simplification so that simplify.m's
optimization of goals with no output variables doesn't do the
wrong thing for goals whose only output is the type_info for
an existentially quantified type parameter.)
<p>
This phase also
converts higher-order predicate terms into lambda expressions,
and copies the clauses to the proc_infos in preparation for
mode analysis.
<p>
The polymorphism.m module also exports some utility routines that
are used by other modules. These include some routines for generating
code to create type_infos, which are used by simplify.m and magic.m
when those modules introduce new calls to polymorphic procedures.
<dt> mode analysis
<dd>
<ul>
<li> modes.m is the main mode analysis module.
It checks that the code is mode-correct, reordering it
if necessary, and annotates each goal with a delta-instmap
that specifies the changes in instantiatedness of each
variable over that goal.
<li> modecheck_unify.m is the sub-module which analyses
unification goals.
It also module qualifies data constructors.
<li> modecheck_call.m is the sub-module which analyses calls.
<p>
The following sub-modules are used:
<dl>
<dt> mode_info.m
<dd>
(the main data structure for mode analysis)
<dt> delay_info.m
<dd>
(a sub-component of the mode_info data
structure used for storing the information
for scheduling: which goals are currently
delayed, what variables they are delayed on, etc.)
<dt> instmap.m
<dd>
Defines the instmap and instmap_delta ADTs
which store information on what instantiations
a set of variables may be bound to.
<dt> inst_match.m
<dd>
This contains the code for examining insts and
checking whether they match.
<dt> inst_util.m
<dd>
This contains the code for creating new insts from
old ones: unifying them, merging them and so on.
<dt> mode_errors.m
<dd>
This module contains all the code to
print error messages for mode errors
</dl>
<li> mode_util.m contains miscellaneous useful predicates dealing
with modes (many of these are used by lots of later stages
of the compiler)
<li> mode_debug.m contains utility code for tracing the actions
of the mode checker.
</ul>
<dt> indexing and determinism analysis
<dd>
<ul>
<li> switch_detection.m transforms into switches those disjunctions
in which several disjuncts test the same variable against different
function symbols.
<li> cse_detection.m looks for disjunctions in which each disjunct tests
the same variable against the same function symbols, and hoists any
such unifications out of the disjunction.
If cse_detection.m modifies the code,
it will re-run mode analysis and switch detection.
<li> det_analysis.m annotates each goal with its determinism;
it inserts cuts in the form of "some" goals wherever the determinisms
and delta instantiations of the goals involved make it necessary.
Any errors found during determinism analysis are reported by
det_report.m.
Det_util.m contains utility predicates used in several modules.
</ul>
<dt> checking of unique modes (unique_modes.m)
<dd>
unique_modes.m checks that non-backtrackable unique modes were
not used in a context which might require backtracking.
Note that what unique_modes.m does is quite similar to
what modes.m does, and unique_modes calls lots of predicates
defined in modes.m to do it.
<dt> stratification checking
<dd>
The module stratify.m implements the `--warn-non-stratification'
warning, which is an optional warning that checks for loops
through negation.
<dt> simplification (simplify.m)
<dd>
simplify.m finds and exploits opportunities for simplifying the
internal form of the program, both to optimize the code and to
massage the code into a form the code generator will accept.
It also warns the programmer about any constructs that are so simple
that they should not have been included in the program in the first
place. (That's why this pass needs to be part of semantic analysis:
because it can report warnings.)
simplify.m converts complicated unifications into procedure calls.
simplify.m calls common.m which looks for (a) construction unifications
that construct a term that is the same as one that already exists,
or (b) repeated calls to a predicate with the same inputs, and replaces
them with assignment unifications.
simplify.m also attempts to partially evaluate calls to builtin
procedures if the inputs are all constants (see const_prop.m),
</dl>
<h4> 3. High-level transformations </h4>
<p>
The first pass of this stage does tabling transformations (table_gen.m).
This involves the insertion of several calls to tabling predicates
defined in mercury_builtin.m and the addition of some scaffolding structure.
Note that this pass can change the evaluation methods of some procedures to
eval_table_io, so it should come before any passes that require definitive
evaluation methods (e.g. inlining).
<p>
The next pass of this stage is a code simplification, namely
removal of lambda expressions (lambda.m):
<ul>
<li>
lambda.m converts lambda expressions into higher-order predicate
terms referring to freshly introduced separate predicates.
This pass needs to come after unique_modes.m to ensure that
the modes we give to the introduced predicates are correct.
It also needs to come after polymorphism.m since polymorphism.m
doesn't handle higher-order predicate constants.
</ul>
(Is there any good reason why lambda.m comes after table_gen.m?)
<p>
The next pass is termination analysis. The various modules involved are:
<ul>
<li>
termination.m is the control module. It sets the argument size and
termination properties of builtin and compiler generated procedures,
invokes term_pass1.m and term_pass2.m
and writes .trans_opt files and error messages as appropriate.
<li>
term_pass1.m analyzes the argument size properties of user-defined procedures,
<li>
term_pass2.m analyzes the termination properties of user-defined procedures.
<li>
term_traversal.m contains code common to the two passes.
<li>
term_errors.m defines the various kinds of termination errors
and prints the messages appropriate for each.
<li>
term_util.m defines the main types used in termination analysis
and contains utility predicates.
<li>
lp.m is used by term_pass1.m. It implements the Linear Programming
algorithm for optimizing a set of linear constraints with respect to
a linear cost function.
</ul>
<p>
Most of the remaining HLDS-to-HLDS transformations are optimizations:
<ul>
<li> specialization of higher-order and polymorphic predicates where the
value of the higher-order/type_info/typeclass_info arguments are known
(higher_order.m)
<li> attempt to introduce accumulators (accumulator.m). This optimizes
procedures whose tail consists of independent associative computations
or independant chains of commutative computations into a tail
recursive form by the introduction of accumulators. If lco is turned
on it can also transform some procedures so that only construction
unifications are after the recursive call. This pass must come before
lco, unused_args (eliminating arguments makes it hard to relate the
code back to the assertion) and inlining (can make the associative
call disappear).
<li> inlining (i.e. unfolding) of simple procedures (inlining.m)
<li> pushing constraints as far left as possible (constraint.m);
this does not yet work.
<li> deforestation and partial evaluation (deforest.m). This optimizes
multiple traversals of data structures within a conjunction, and
avoids creating intermediate data structures. It also performs
loop unrolling where the clause used is known at compile time.
deforest.m makes use of the following sub-modules
(`pd_' stands for "partial deduction"):
<ul>
<li>
pd_cost.m contains some predicates to estimate the improvement
caused by deforest.m.
<li>
pd_debug.m produces debugging output.
<li>
pd_info.m contains a state type for deforestation.
<li>
pd_term.m contains predicates to check that the deforestation algorithm
terminates.
<li>
pd_util.m contains various utility predicates.
</ul>
<li> issue warnings about unused arguments from predicates, and create
specialized versions without them (unused_args.m); type_infos are
often unused.
<li> unneeded_code.m looks for goals whose results are either not needed
at all, or needed in some branches of computation but not others. Provided
that the goal in question satisfies some requirements (e.g. it is pure,
it cannot fail etc), it either deletes the goal or moves it to the
computation branches where its output is needed.
<li> elimination of dead procedures (dead_proc_elim.m). Inlining, higher-order
specialization and the elimination of unused args can make procedures dead
even the user doesn't, and automatically constructed unification and
comparison predicates are often dead as well.
<li> conversion of Aditi procedures into disjunctive normal form (dnf.m).
The supplementary magic sets and context transformations are only defined
for predicates in DNF.
<li> supplementary magic sets or supplementary context transformation of
Aditi procedures (magic.m, magic_util.m, context.m).
The magic sets or context transformations must be applied to convert the
program to a form for which Aditi-RL bytecode can be generated.
<li> reducing the number of variables that have to be saved across
procedure calls (saved_vars.m). We do this by putting the code that
generates the value of a variable just before the use of that variable,
duplicating the variable and the code that produces it if necessary,
provided the cost of doing so is smaller than the cost of saving and
restoring the variable would be.
</ul>
<p>
The module transform.m contains stuff that is supposed to be useful
for high-level optimizations (but which is not yet used).
<p>
<hr>
<!---------------------------------------------------------------------------->
<h3> a. LLDS BACK-END </h3>
<h4> 4a. Code generation. </h4>
<p>
<dl>
<dt> pre-passes to annotate the HLDS
<dd>
Before code generation there are a few more passes which
annotate the HLDS with information used for code generation:
<dl>
<dt> choosing registers for procedure arguments (arg_info.m)
<dd>
Currently uses one of two simple algorithms, but
we may add other algorithms later.
<dt> annotation of goals with liveness information (liveness.m)
<dd>
This records the birth and death of each variable
in the HLDS goal_info.
<dt> allocation of stack slots
<dd>
This is done by live_vars.m, which works
out which variables need to be saved on the
stack when (trace.m determines what variables
are needed for debugging purposes).
It then uses graph_colour.m to determine
a good allocation of variables to stack slots.
<dt> migration of builtins following branched structures
<dd>
This transformation, which is performed by
follow_code.m, improves the results of follow_vars.
<dt> allocating the follow vars (follow_vars.m)
<dd>
Traverses backwards over the HLDS, annotating some
goals with information about what locations variables
will be needed in next. This allows us to generate
more efficient code by putting variables in the right
spot directly. This module is not called from
mercury_compile.m; it is called from store_alloc.m.
<dt> allocating the store map (store_alloc.m)
<dd>
Annotates each branched goal with variable location
information so that we can generate correct code
by putting variables in the same spot at the end
of each branch.
<dt> computing goal paths (goal_path.m)
<dd>
The goal path of a goal defines its position in
the procedure body. This transformation attaches
its goal path to every goal, for use by the debugger.
</dl>
<dt> code generation
<dd>
Code generation converts HLDS into LLDS.
For the LLDS back-end, this is also the point at which we
insert code to handle debugging and trailing, and to do
heap reclamation on failure.
The main code generation module is code_gen.m.
It handles conjunctions and negations, but calls sub-modules
to do most of the other work:
<ul>
<li> ite_gen.m (if-then-elses)
<li> call_gen.m (predicate calls and also calls to
out-of-line unification procedures)
<li> disj_gen.m (disjunctions)
<li> par_conj.m (parallel conjunctions)
<li> unify_gen.m (unifications)
<li> switch_gen.m (switches), which has sub-modules
<ul>
<li> dense_switch.m
<li> lookup_switch.m
<li> string_switch.m
<li> tag_switch.m
<li> switch_util.m (also used by MLDS back-end)
</ul>
<li> commit_gen.m (commits)
<li> pragma_c_gen.m (embedded C code)
</ul>
<p>
It also calls middle_rec.m to do middle recursion optimization.
<p>
The code generation modules make use of
<dl>
<dt> code_info.m
<dd>
The main data structure for the code generator.
<dt> code_exprn.m
<dd>
This defines the exprn_info type, which is
a sub-component of the code_info data structure
which holds the information about
the contents of registers and
the values/locations of variables.
<dt> exprn_aux.m
<dd>
Various preds which use exprn_info.
<dt> code_util.m
<dd>
Some miscellaneous preds used for code generation.
<dt> code_aux.m
<dd>
Some miscellaneous preds which, unlike those in
code_util, use code_info.
<dt> continuation_info.m
<dd>
For accurate garbage collection, collects
information about each live value after calls,
and saves information about procedures.
<dt> trace.m
<dd>
Inserts calls to the runtime debugger.
</dl>
<dt> code generation for `pragma export' declarations (export.m)
<dd> This is handled seperately from the other parts of code generation.
mercury_compile.m calls the procedures `export__produce_header_file'
and `export__get_pragma_exported_procs' to produce C code fragments
which declare/define the C functions which are the interface stubs
for procedures exported to C.
<dt> generation of constants for RTTI data structures
<dd> This could also be considered a part of code generation,
but for the LLDS back-end this is currently done as part
of the output phase (see below).
</dl>
<p>
The result of code generation is the Low Level Data Structure (llds.m),
which may also contains some data structures whose types are defined in rtti.m.
The code for each procedure is generated as a tree of code fragments
which is then flattened (tree.m).
<p>
<h4> 5a. Low-level optimization (LLDS). </h4>
<p>
The various LLDS-to-LLDS optimizations are invoked from optimize.m.
They are:
<ul>
<li> optimization of jumps to jumps (jumpopt.m)
<li> elimination of duplicate code sequences (dupelim.m)
<li> optimization of stack frame allocation/deallocation (frameopt.m)
<li> filling branch delay slots (delay_slot.m)
<li> dead code and dead label removal (labelopt.m)
<li> peephole optimization (peephole.m)
<li> value numbering <br>
This is done by value_number.m, which has the following sub-modules:
<dl>
<dt> vn_block.m
<dd>
Traverse an extended basic block, building up tables showing
the actions that must be taken, and the current and desired
contents of locations.
<dt> vn_cost.m
<dd>
Computes the cost of instruction sequences.
Value numbering should never replace an instruction
sequence with a more expensive sequence. Unfortunately,
computing costs accurately is very difficult.
<dt> vn_debug.m
<dd>
Predicates to dump data structures used in value
numbering.
<dt> vn_filter.m
<dd>
Module to eliminate useless temporaries introduced by
value numbering. Not generating them in the first place
would be better, but would be quite difficult.
<dt> vn_flush.m
<dd>
Given the tables built up by vn_block and a list of nodes
computed by vn_order, generate code to assign the required
values to each temporary and live location in what is
hopefully the fastest and most compact way.
<dt> vn_order.m
<dd>
Given tables built up by vn_block showing the actions that
must be taken, and the current and desired contents of
locations, find out which shared subexpressions should
have temporaries allocated to them and in what order these
temporaries and the live locations should be assigned to.
This module uses the module atsort.m to perform an approximate
topological sort on the nodes of the location dependency
graph it operations on (since the graph may have cycles,
a precise topological sort may not exist).
<dt> vn_table.m
<dd>
Abstract data type showing the current and desired
contents of locations.
<dt> vn_temploc.m
<dd>
Abstract data type to keep track of the availability
of registers and temporaries.
<dt> vn_type.m
<dd>
This module defines the types used by the other
modules of the value numbering optimization.
<dt> vn_util.m
<dd>
Utility predicates.
<dt> vn_verify.m
<dd>
Sanity checks to make sure that (a) the optimized code
computes the same values as the original code, and (b)
the optimized code does not dereference tagged pointers
until the tag is known. (Violations of (b) usually cause
unaligned accesses, which cause bus errors on many machines.)
</dl>
Several of these modules (and also frameopt, above) use livemap.m,
which finds the set of locations live at each label.
</ul>
<p>
Depending on which optimization flags are enabled,
optimize.m may invoke many of these passes multiple times.
<p>
Some of the low-level optimization passes use basic_block.m,
which defines predicates for converting sequences of instructions to
basic block format and back, as well as opt_util.m, which contains
miscellaneous predicates for LLDS-to-LLDS optimization.
<p>
<h4> 6a. Output C code </h4>
<ul>
<li> type_ctor_info.m generates the type_ctor_gen_info structures that list
items of information (including unification, index and compare predicates)
associated with each declared type constructor that go into the static
type_ctor_info data structure. If the type_ctor_gen_info structure is not
eliminated as inaccessible, this module adds the corresponding type_ctor_info
structure to the RTTI data structures defined in rtti.m,
which are part of the LLDS.
<li> base_typeclass_info.m generates the base_typeclass_info structures that
list the methods of a class for each instance declaration. These are added to
the RTTI data structures, which are part of the LLDS.
<li> stack_layout.m generates the stack_layout structures for
accurate garbage collection. Tables are created from the data
collected in continuation_info.m.
Stack_layout.m uses prog_rep.m and static_term.m to generate representations
of procedure bodies for use by the declarative debugger.
<li> Type_ctor_info structures and stack_layout structures both contain
pseudo_type_infos, which are type_infos with holes for type variables;
these are generated by pseudo_type_info.m.
<li> llds_common.m extracts static terms from the main body of the LLDS, and
puts them at the front. If a static term originally appeared several times,
it will now appear as a single static term with multiple references to it.
<li> transform_llds.m is responsible for doing any source to source
transformations on the llds which are required to make the C output
acceptable to various C compilers. Currently computed gotos can have
their maximum size limited to avoid a fixed limit in lcc.
<li> Final generation of C code is done in llds_out.m, which subcontracts the
output of RTTI structures to rtti_out.m.
</ul>
<p>
<hr>
<!---------------------------------------------------------------------------->
<h3> b. MLDS BACK-END </h3>
The original LLDS code generator generates very low-level code,
since the LLDS was designed to map easily to RISC architectures.
We're currently developing a new back-end that generates much higher-level
code, suitable for generating Java, high-level C, etc.
This back-end uses the Medium Level Data Structure (mlds.m) as its
intermediate representation.
<h4> 3b. pre-passes to annotate/transform the HLDS </h4>
Before code generation there is a pass which annotates the HLDS with
information used for code generation:
<ul>
<li> mark_static_terms.m marks construction unifications
which can be implemented using static constants rather
than heap allocation.
</ul>
For the MLDS back-end, we've tried to keep the code generator simple.
So we prefer to do things as HLDS to HLDS transformations where possible,
rather than complicating the HLDS to MLDS code generator.
So we have a pass which transforms the HLDS to handle trailing:
<ul>
<li> add_trail_ops.m inserts code to manipulate the trail,
in particular ensuring that we apply the appropriate
trail operations before each choice point, when execution
resumes after backtracking, and whenever we do a commit.
The trail operations are represented as (and implemented as)
calls to impure procedures defined in library/private_builtin.m.
</ul>
<h4> 4b. MLDS code generation </h4>
<ul>
<li> ml_code_gen.m converts HLDS code to MLDS.
The following sub-modules are used to handle different constructs:
<dl>
<dt> ml_unify_gen.m
<dt> ml_call_gen.m
<dt> ml_switch_gen.m, which in turn has sub-modules
<ul>
<li> ml_dense_switch.m
<li> ml_string_switch.m
<li> ml_tag_switch.m
<li> switch_util.m (also used by MLDS back-end)
</ul>
<dl>
The module ml_code_util.m provides utility routines for
MLDS code generation. The module ml_util.m provides some
general utility routines for the MLDS.
<li> ml_type_gen.m converts HLDS types to MLDS.
<li> type_ctor_info.m and base_typeclass_info.m generate
the RTTI data structures defined in rtti.m and pseudo_type_info.m
(those four modules are shared with the LLDS back-end)
and then mlds_to_rtti.m converts these to MLDS.
</ul>
<h4> 5b. MLDS transformations </h4>
<ul>
<li> ml_tailcall.m annotates the MLDS with information about tailcalls.
<li> ml_optimize.m does MLDS->MLDS optimizations
<li> ml_elim_nested.m transforms the MLDS to eliminate nested functions.
</ul>
<h4> 6b. MLDS output </h4>
There are currently two backends that generate code from MLDS, one
generates C/C++ code, the other generates Microsoft's Intermediate
Language (MSIL or IL).
<p>
<ul>
<li>mlds_to_c.m converts MLDS to C/C++ code.
</ul>
<p>
The MLDS->IL backend is broken into several submodules.
<ul>
<li> mlds_to_ilasm.m converts MLDS to IL assembler and writes it to a .il file.
<li> mlds_to_il.m converts MLDS to IL
<li> ilds.m contains representations of IL
<li> ilasm.m contains output routines for writing IL to assembler.
<li> il_peephole.m performs peephole optimization on IL instructions.
</ul>
After IL assembler has been emitted, ILASM in invoked to turn the .il
file into a .dll or .exe.
<p>
<hr>
<!---------------------------------------------------------------------------->
<h3> c. Aditi-RL BACK-END </h3>
<h4> 4c. Aditi-RL generation </h4>
<ul>
<li> rl_gen.m converts HLDS to RL.
<li> rl_relops.m generates RL for relational operations such as joins.
<li> rl_info.m defines a state type.
<li> rl.m defines the the representation of Aditi-RL used within the
Mercury compiler. There are some slight differences between rl.m
and Aditi-RL to make optimization easier.
<li> rl_dump.m contains predicates to write the types defined in rl.m.
</ul>
<h4> 5c. Aditi-RL optimization </h4>
<ul>
<li> rl_opt.m invokes the RL optimization passes.
<li> rl_block.m converts an RL procedure into basic blocks, and performs
other tasks such as detecting the loops in those basic blocks.
<li> rl_analyse.m contains a generic data-flow analysis procedure for
RL procedures.
<li> rl_liveness.m uses rl_analyse.m to insert code to initialise relations
and clear references to them when they are no longer needed.
<li> rl_loop.m moves invariants out of loops.
<li> rl_block_opt.m performs common subexpression elimination and instruction
merging on basic blocks.
<li> rl_key.m computes upper and lower bounds on the non-locals of a goal
for which the goal could succeed, which is useful for determining when
an index could be used for a relational operation.
<li> rl_sort.m introduces sort-merge and indexed versions of relational
operations where possible, and optimizes away unnecessary sorting and
indexing.
<li> rl_stream.m detects relations which do not need to be materialised.
</ul>
<h4> 6c. Output Aditi-RL code </h4>
<ul>
<li> rl_out.m converts from the instructions defined in rl.m
to bytecode either as data in the <module>.c file or
to <module>.rlo and outputs a text representation to
<module>.rla.
<li> rl_exprn.m is called from rl_out.m to convert top down Mercury
code (in HLDS form) to Aditi bytecode.
<li> rl_code.m contains the definition of the bytecodes interpreted
by Aditi. This file is automatically generated from the Aditi sources.
<li> rl_file.m contains routines to output the bytecodes defined in rl_code.m.
</ul>
<p>
<hr>
<!---------------------------------------------------------------------------->
<h3> d. BYTECODE BACK-END </h3>
<p>
The Mercury compiler can translate Mercury programs into bytecode for
interpretation by a bytecode interpreter. The intent of this is to
achieve faster turn-around time during development. However, the
bytecode interpreter has not yet been written.
<ul>
<li> bytecode.m defines the internal representation of bytecodes, and contains
the predicates to emit them in two forms. The raw bytecode form is emitted
into <filename>.bytecode for interpretation, while a human-readable
form is emitted into <filename>.bytedebug for visual inspection.
<li> bytecode_gen.m contains the predicates that translate HLDS into bytecode.
<li> bytecode_data.m contains the predicates that translate ints, strings
and floats into bytecode. This is also used by rl_code.m.
</ul>
<p>
<hr>
<!---------------------------------------------------------------------------->
<h3> MISCELLANEOUS </h3>
<dl>
<dt> builtin_ops:
<dd>
This module defines the types unary_op and binary_op
which are used by several of the different back-ends:
bytecode.m, llds.m, and mlds.m.
<dt> c_util:
<dd>
This module defines utility routines useful for generating
C code. It is used by both llds_out.m and mlds_to_c.m.
<dt> det_util:
<dd>
This module contains utility predicates needed by the parts
of the semantic analyzer and optimizer concerned with
determinism.
<dt> special_pred.m, unify_proc.m:
<dd>
These modules contain stuff for handling the special
compiler-generated predicates which are generated for
each type: unify/2, compare/3, and index/1 (used in the
implementation of compare/3).
<dt> dependency_graph.m:
<dd>
This contains predicates to compute the call graph for a
module, and to print it out to a file.
(The call graph file is used by the profiler.)
The call graph may eventually also be used by det_analysis.m,
inlining.m, and other parts of the compiler which could benefit
from traversing the predicates in a module in a bottom-up or
top-down fashion with respect to the call graph.
<dt> passes_aux.m
<dd>
Contains code to write progress messages, and higher-order
code to traverse all the predicates defined in the current
module and do something with each one.
<dt> opt_debug.m:
<dd>
Utility routines for debugging the LLDS-to-LLDS optimizations.
<dt> error_util.m:
<dd>
Utility routines for printing nicely formatted error messages.
</dl>
<p>
<hr>
<!---------------------------------------------------------------------------->
<h3> CURRENTLY USELESS </h3>
<p>
The following modules do not serve any function at the moment.
Some of them are obsolete; other are work-in-progress.
(For some of them its hard to say which!)
<dl>
<dt> excess.m:
<dd>
This eliminates assignments that merely introduce another
name for an already existing variable. The functionality of
this module has been included in simplify.m, however sometime
in the future it may be necessary to provide a version which
maintains superhomogeneous form.
<dt> lco.m:
<dd>
This finds predicates whose implementations would benefit
from last call optimization modulo constructor application.
It does not apply the optimization and will not until the
mode system is capable of expressing definite aliasing.
<dt> mercury_to_goedel.m:
<dd>
This converts from item_list to Goedel source code.
It works for simple programs, but doesn't handle
various Mercury constructs such as lambda expressions,
higher-order predicates, and functor overloading.
</dl>
<p>
<hr>
<!---------------------------------------------------------------------------->
Last update was $Date: 2001/01/19 01:50:32 $ by $Author: zs $@cs.mu.oz.au. <br>
</body>
</html>
|