c-code37.red 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. on echo;
  2. % This file can be run to turn bits of the REDUCE source code
  3. % into C so that this C can be compiled and linked in to make a
  4. % customised CSL executable that will red REDUCE faster.
  5. %
  6. % Run this using slowr37.img to select code to compile into C.
  7. % The functions to be compiled are extracted from a file "profile.dat"
  8. % that was created by "profile.red".
  9. %
  10. symbolic;
  11. % Three major parameters are available:
  12. %
  13. % fnames a list of files to create. Making the list longer (or
  14. % shorter) changes the amount of C that can be created.
  15. % The CSL source code has to know how many there are, and
  16. % its current default is for 12 files.
  17. %
  18. % size_per_file
  19. % this guides the compiler about how much to put in each
  20. % generated file, where the value 7000 results in each
  21. % file of generated C being in the range 120 to 150 Kbytes.
  22. %
  23. % force_count indicates how many functions from alg.tst statistics should
  24. % be included before anything else.
  25. %
  26. %
  27. % Also if "how_many" is set then this will limit the total number of
  28. % functions that are compiled into C. Since I expect to pass that via a
  29. % command line "-dhow_many=362" etc I allow for it being a string
  30. % not a number to start with. In ordinary circumstances this will not be
  31. % used, however it has proved INVALUABLE when tracking down cases where
  32. % compilation into C causes changes in behaviour... how_many can be used
  33. % with a binary-chop selection process to discover exactly which function
  34. % causes upset when compiled into C. Of course in release quality code I
  35. % hope there are no such cases!
  36. global '(fnames size_per_file force_count how_many);
  37. fnames := '("u01" "u02" "u03" "u04" "u05"
  38. "u06" "u07" "u08" "u09" "u10"
  39. "u11" "u12");
  40. size_per_file := 7000;
  41. force_count := 350;
  42. if not boundp 'how_many then how_many := 1000000
  43. else << how_many := compress explodec how_many;
  44. if not numberp how_many then how_many := 1000000 >>;
  45. on comp;
  46. % First update the patch information (if needbe).
  47. load!-module 'remake;
  48. ensure_patches_are_up_to_date();
  49. % Here I need to consider the issue of patches. First consider patches that
  50. % had been in force when "profile.red" was run. In such cases a patched
  51. % function f1 has an associated replacement f1_123456789 (the numeric suffix
  52. % is a checksum on the new definition) and when the profile job was run
  53. % this replacement will have had its definition copied to f1. The way in
  54. % which CSL's mapstore function extracts counts will mean that the
  55. % thing in profile.dat relate to f1_123456789.
  56. % Usually things in profile.dat are in the form
  57. % (function_name . checksum_of_definition)
  58. % but for these patched things I will instead record
  59. % (original_function_name package_involved)
  60. % This can be distinguished because it has a symbol not a number as
  61. % the second component. To make this possible each patch function
  62. % f1_123456789 would have to have this information attached to it
  63. % when the profiling job was run.
  64. %
  65. % But I suppose have now obtained a newer version of the patches file. So
  66. % now the correct patch for f1 will be f1_abcdef. If f1 was in one of the
  67. % REDUCE core packages (eg "alg") then both the functions f1_123456789 and
  68. % f1_abcdef will be in memory now, but it will be the latter that will
  69. % have been copied to plain old f1. In other cases f1_123456789 will now
  70. % have been totally lost and the definition of f1_abcdef will be in the
  71. % patches module. Furthermore the new patches file may patch another
  72. % function f2 that had not previously been subject to patching, but
  73. % that had been selected for compilation into C. And in a truly bad
  74. % case the complete REDUCE sources will contain several functions named
  75. % f2 and of course the patches file identifies which one it is interested
  76. % in by the name of the package it is in.
  77. %
  78. % The response to all this I will use here is intended to make life
  79. % reasonably SIMPLE for me in a complicated situation. So I first
  80. % collect the set of names that I think need compiling into C. Then I
  81. % grab a list of the names of things defined in the current patches file.
  82. % If a function in the paches file has a name similar enough (!) to one that
  83. % I have already decided to compile into C then I will schedule it for
  84. % compilation into C too. Because of the hash suffix added to names in the
  85. % patches file defining a C version having those things present in the Lisp
  86. % kernel should never be a problem - after all the patches file itself is
  87. % intended to be loaded all the time. So the main down-side of this is
  88. % that I will sometimes find that I have compiled into C either patch
  89. % versions of a function when it was another version of that code that was
  90. % time-critical or that I have compiled into C two generations of
  91. % patch function. These waste opportunity and space by having some
  92. % things compiled into C that might not really justify that, but this
  93. % seems a modest cost.
  94. fluid '(w_reduce requests);
  95. w_reduce := requests := nil;
  96. % I make a list of all the functions that profile data suggests that
  97. % I should compile into C. The master copy of the profile data is
  98. % usually expected to be in "../csl-c", but I allow a copy in the
  99. % current directory (which is where the profiling process leaves it).
  100. symbolic procedure read_profile_data file;
  101. begin
  102. scalar w0, w1;
  103. if not errorp(w0 := errorset(list('open, file, ''input), nil, nil)) then <<
  104. w0 := rds car w0;
  105. while not errorp (w1 := errorset('(read), nil, nil)) and
  106. not eqcar(w1, !$eof!$) do
  107. requests := car w1 . requests;
  108. % The data structure read in here will be of the form
  109. % ((module-name f-name1 f_name2 ...) (module-name ...) ...)
  110. % where within each module the requested functions have been listed in
  111. % order of priority.
  112. close rds w0 >>
  113. end;
  114. % I read from the current directory only if I do not find anything
  115. % in the csl-c one.
  116. off echo;
  117. read_profile_data "../csl-c/profile.dat";
  118. if null requests then read_profile_data "profile.dat";
  119. on echo;
  120. % As a fairly shameless hack I am going to insist on compiling ALL the
  121. % things that the "alg" test uses. That is because this one test
  122. % fiel has been used for many years to give a single performance
  123. % figure for REDUCE. In fact it is not too bad to pay lots of
  124. % attention to it since it exercises the basic core algebra and so what is
  125. % good for it is good for quite a lot of everybody else. However by
  126. % tuning this selection process you can adjust the way REDUCE balances
  127. % its speed in different application areas.
  128. w_reduce := assoc('alg, requests)$
  129. requests := for each x in delete(w_reduce, requests) collect cdr x$
  130. w_reduce := reverse cdr w_reduce$
  131. d := length w_reduce - force_count;
  132. if d > 0 then for i := 1:d do w_reduce := cdr w_reduce;
  133. length w_reduce;
  134. % Now I will merge in suggestions from all other modules in
  135. % breadth-first order of priority
  136. % Ie if I have modules A, B, C and D (with A=alg) and each has in it
  137. % functions a1, a2, a3 ... (in priority odder) then I will make up a list
  138. % here that goes
  139. %
  140. % a1 a2 a3 ... an b1 c1 d2 b2 c2 d2 b3 c3 d3 b4 c4 d4 ...
  141. %
  142. % so that the first n items from A get priority and after that B, C and D
  143. % will get about balanced treatment if I have to truncate the list at
  144. % some stage.
  145. symbolic procedure membercar(a, l);
  146. if null l then nil
  147. else if a = caar l then t
  148. else membercar(a, cdr l);
  149. fg := t;
  150. while fg do <<
  151. fg := nil;
  152. for each x on requests do
  153. if car x then <<
  154. if k := assoc(caaar x, w_reduce) then <<
  155. if not (cadr k = cadaar x) then <<
  156. prin caaar x; printc " has multiple definition";
  157. princ " keep version with checksum: "; print cadr k;
  158. princ " ignore: "; print cadaar x;
  159. terpri() >> >>
  160. % ORDP is a special case because I have put a version of it into the
  161. % CSL kernel by hand, and any redefinition here would be unfriendly and
  162. % might clash with that.
  163. else if caaar x = 'ordp then printc "Ignoring ORDP (!)"
  164. else w_reduce := caar x . w_reduce;
  165. fg := t;
  166. rplaca(x, cdar x) >> >>;
  167. % Now I scan all pre-compiled modules to recover source versions of the
  168. % selected REDUCE functions. The values put as load!-source properties
  169. % are checksums of the recovered definitions that I would be prepared
  170. % to accept.
  171. for each n in w_reduce do put(car n, 'load!-source, cdr n);
  172. w_reduce := for each n in w_reduce collect car n$
  173. for each m in library!-members() do load!-source m;
  174. % Now deal with patches...
  175. load!-source := t;
  176. patch!-functions := load!-source 'patches;
  177. % Some of the functions just collected are not patches for bits of REDUCE
  178. % but are the code that installs the patches. I do not worry too much
  179. % about that here.
  180. % Now I will scan down w_reduce (the list of all things to be compiled into C)
  181. % and if that contains an entry either f1 or f1_123456789 and there is
  182. % an entry f2_abcdef in the list of patch-functions then I will
  183. % insert f2_abcdef into the list of things to be compiled into C just
  184. % next to plain f2 or f2_123456789.
  185. %
  186. % The way I do this will often set up a few false-matches but the cost of
  187. % them is just that some unimportant things will get compiled into C.
  188. global '(tag!-chars);
  189. tag!-chars := explodec "0123456789abcdefghijklmnopqrstuvwxyz";
  190. symbolic procedure trim!-suffix name;
  191. begin
  192. scalar w;
  193. w := reverse explode name;
  194. if eqcar(w, '!_) then w := cdr w;
  195. if null w or not member(car w, tag!-chars) then return nil;
  196. w := cdr w;
  197. while w and member(car w, tag!-chars) do w := cdr w;
  198. if not eqcar(w, '!_) then return nil;
  199. w := cdr w;
  200. if null w then return nil
  201. else return compress reverse w
  202. end;
  203. w := w_reduce$
  204. w_reduce := nil;
  205. while w do <<
  206. w_reduce := car w . w_reduce;
  207. p := trim!-suffix car w;
  208. for each n in patch!-functions do
  209. if not (n = car w) and
  210. p and
  211. not (n member w_reduce) and
  212. p = trim!-suffix n then <<
  213. w_reduce := n . w_reduce;
  214. princ "+++ Also C-compile "; prin n; princ " as match for ";
  215. prin car w; princ ": root is "; print p >>;
  216. w := cdr w >>;
  217. verbos nil;
  218. global '(rprifn!*);
  219. load_package ccomp;
  220. on fastfor, fastvector, unsafecar;
  221. symbolic procedure listsize(x, n);
  222. if null x then n
  223. else if atom x then n+1
  224. else listsize(cdr x, listsize(car x, n+1));
  225. <<
  226. count := 0;
  227. while fnames do begin
  228. scalar name, bulk;
  229. name := car fnames;
  230. princ "About to create "; printc name;
  231. c!:ccompilestart(name, "../csl-c");
  232. bulk := 0;
  233. while bulk < size_per_file and w_reduce and how_many > 0 do begin
  234. scalar name, defn;
  235. name := car w_reduce;
  236. if null (defn := get(name, '!*savedef)) then <<
  237. princ "+++ "; prin name; printc ": no saved definition found";
  238. w_reduce := cdr w_reduce >>
  239. else <<
  240. bulk := listsize(defn, bulk);
  241. if bulk < size_per_file then <<
  242. c!:ccmpout1 ('de . name . cdr defn);
  243. how_many := how_many - 1;
  244. count := count + 1;
  245. w_reduce := cdr w_reduce >> >> end;
  246. eval '(c!-end);
  247. fnames := cdr fnames
  248. end;
  249. terpri();
  250. printc "*** End of compilation from REDUCE into C ***";
  251. terpri();
  252. bulk := 0;
  253. % I list the next 50 functions that WOULD get selected - just for interest.
  254. if null w_reduce then printc "No more functions need compiling into C"
  255. else while bulk < 50 and w_reduce do
  256. begin
  257. name := car w_reduce;
  258. if null (defn := get(name, '!*savedef)) then <<
  259. princ "+++ "; prin name; printc ": no saved definition found";
  260. w_reduce := cdr w_reduce >>
  261. else <<
  262. bulk := bulk+1;
  263. print name;
  264. w_reduce := cdr w_reduce >> end;
  265. terpri();
  266. prin count; printc " functions compiled into C";
  267. nil >>;
  268. quit;