Home My Page Projects Code Snippets Project Openings diderot
Summary Activity Tracker Tasks SCM

SCM Repository

[diderot] Annotation of /branches/pure-cfg/src/compiler/c-target/c-target.sml
ViewVC logotype

Annotation of /branches/pure-cfg/src/compiler/c-target/c-target.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 549 - (view) (download)

1 : jhr 519 (* c-target.sml
2 :     *
3 :     * COPYRIGHT (c) 2011 The Diderot Project (http://diderot-language.cs.uchicago.edu)
4 :     * All rights reserved.
5 :     *
6 :     * Generate C code with SSE 4.2 intrinsics.
7 :     *)
8 :    
9 :     structure CTarget : TARGET =
10 :     struct
11 :    
12 : jhr 522 structure CL = CLang
13 :    
14 : jhr 525 datatype ty
15 :     = T_Bool
16 : jhr 534 | T_String
17 : jhr 525 | T_Int (* default float type *)
18 :     | T_Real (* default real type *)
19 :     | T_Vec of int
20 :     | T_IVec of int
21 : jhr 548 | T_Image of int * RawTypes.ty (* n-dimensional image data *)
22 :     | T_Ptr of RawTypes.ty (* pointer type *)
23 : jhr 519
24 : jhr 537 (* string representation of types (for debugging *)
25 :     fun tyToString ty = (case ty
26 :     of T_Bool => "T_Bool"
27 :     | T_String => "T_String"
28 :     | T_Int => "T_Int"
29 :     | T_Real => "T_Real"
30 :     | T_Vec n => concat["T_Vec(", Int.toString n, ")"]
31 :     | T_IVec n => concat["T_IVec(", Int.toString n, ")"]
32 : jhr 548 | T_Image(n, ty) => concat[
33 :     "T_Image", Int.toString n, "D(", RawTypes.toString ty, ")"
34 :     ]
35 :     | T_Ptr ty => concat["T_Ptr(", RawTypes.toString ty, ")"]
36 : jhr 537 (* end case *))
37 :    
38 : jhr 544 datatype strand = Strand of {
39 :     name : string,
40 :     tyName : string,
41 :     state : (ty * string) list ref,
42 :     code : CL.decl list ref
43 :     }
44 : jhr 525
45 :     type var = (ty * string) (* FIXME *)
46 :    
47 :     type exp = CLang.exp * ty
48 :    
49 :     type stm = CL.stm
50 :    
51 :     type method = unit (* FIXME *)
52 :    
53 : jhr 527 datatype program = Prog of {
54 :     globals : CL.decl list ref,
55 : jhr 533 topDecls : CL.decl list ref,
56 : jhr 527 strands : strand list ref
57 :     }
58 :    
59 : jhr 525 (* globals that specify the target characteristics. These should be initialized
60 :     * when the program object is created.
61 :     *)
62 :     val gVectorWid = ref 4
63 :     val gIntTy = ref CL.int32
64 :     val gRealTy = ref CL.float
65 : jhr 544 val gRealSuffix = ref "f"
66 : jhr 525
67 :     (* initialize globals based on target precision *)
68 : jhr 533 fun initTargetSpec () = if !Controls.doublePrecision
69 : jhr 525 then (
70 :     gVectorWid := 2;
71 :     gIntTy := CL.int64;
72 : jhr 544 gRealTy := CL.double;
73 :     gRealSuffix := "d")
74 : jhr 525 else (
75 :     gVectorWid := 4;
76 :     gIntTy := CL.int32;
77 : jhr 544 gRealTy := CL.float;
78 :     gRealSuffix := "f")
79 : jhr 525
80 : jhr 519 (* for SSE, we have 128-bit vectors *)
81 : jhr 525 fun vectorWidth () = !gVectorWid
82 : jhr 519
83 : jhr 544 fun vectorSuffix n = Int.toString n ^ !gRealSuffix
84 :    
85 : jhr 519 (* target types *)
86 : jhr 525 val boolTy = T_Bool
87 :     val intTy = T_Int
88 :     val realTy = T_Real
89 :     fun vecTy 1 = T_Real
90 :     | vecTy n = if (n < 1) orelse (!gVectorWid < n)
91 :     then raise Size
92 :     else T_Vec n
93 :     fun ivecTy 1 = T_Int
94 :     | ivecTy n = if (n < 1) orelse (!gVectorWid < n)
95 :     then raise Size
96 :     else T_IVec n
97 : jhr 548 fun imageTy (ImageInfo.ImgInfo{ty=([], rTy), dim, ...}) = T_Image(dim, rTy)
98 :     fun imageDataTy (ImageInfo.ImgInfo{ty=([], rTy), ...}) = T_Ptr rTy
99 : jhr 534 val stringTy = T_String
100 : jhr 519
101 : jhr 534 val statusTy = CL.T_Named "Status_t"
102 :    
103 : jhr 528 (* convert target types to CLang types *)
104 :     fun cvtTy T_Bool = CLang.T_Named "bool"
105 : jhr 534 | cvtTy T_String = CL.charPtr
106 : jhr 528 | cvtTy T_Int = !gIntTy
107 :     | cvtTy T_Real = !gRealTy
108 :     | cvtTy (T_Vec n) = CLang.T_Named(concat["Diderot_vec", Int.toString n, "D_t"])
109 :     | cvtTy (T_IVec n) = raise Fail "FIXME: T_IVec"
110 : jhr 548 | cvtTy (T_Image(n, _)) = CLang.T_Named(concat["Diderot_image", Int.toString n, "D_t"])
111 :     | cvtTy (T_Ptr ty) = CL.T_Ptr(CL.T_Num ty)
112 : jhr 528
113 : jhr 548 (* report invalid arguments *)
114 :     fun invalid (name, []) = raise Fail("invaild "^name)
115 :     | invalid (name, args) = let
116 :     fun arg2s (e, ty) = concat["(", CL.expToString e, " : ", tyToString ty, ")"]
117 :     val args = String.concatWith ", " (List.map arg2s args)
118 :     in
119 :     raise Fail(concat["invalid arguments to ", name, ": ", args])
120 :     end
121 :    
122 : jhr 525 (* helper functions for checking the types of arguments *)
123 :     fun scalarTy T_Int = true
124 :     | scalarTy T_Real = true
125 :     | scalarTy _ = false
126 : jhr 548 fun numTy T_Int = true
127 :     | numTy T_Real = true
128 :     | numTy (T_Vec _) = true
129 :     | numTy (T_IVec _) = true
130 :     | numTy _ = false
131 : jhr 519
132 : jhr 528 fun newProgram () = (
133 : jhr 533 initTargetSpec();
134 : jhr 528 Prog{
135 :     globals = ref [],
136 : jhr 533 topDecls = ref [],
137 : jhr 528 strands = ref []
138 :     })
139 :    
140 : jhr 533 fun globalInit (Prog{topDecls, ...}, init) = let
141 :     val initFn = CL.D_Func([], CL.voidTy, "Diderot_InitGlobals", [], init)
142 :     in
143 :     topDecls := initFn :: !topDecls
144 :     end
145 :    
146 : jhr 525 structure Var =
147 :     struct
148 : jhr 528 fun global (Prog{globals, ...}, ty, name) = (
149 :     globals := CL.D_Var([], cvtTy ty, name) :: !globals;
150 :     (ty, name))
151 : jhr 544 fun param (ty, name) = (ty, name)
152 :     fun state (Strand{state, ...}, ty, name) = (
153 :     state := (ty, name) :: !state;
154 :     (ty, name))
155 :     fun var (ty, name) = (ty, name)
156 : jhr 525 fun tmp ty = raise Fail "FIXME: Var.tmp"
157 : jhr 519 end
158 :    
159 :     (* expression construction *)
160 : jhr 525 structure Expr =
161 :     struct
162 : jhr 549 (* return true if the given expression from is allowed as a subexpression *)
163 :     fun allowedInline _ = true (* FIXME *)
164 :    
165 : jhr 519 (* variable references *)
166 : jhr 525 fun global (ty, x) = (CL.mkVar x, ty)
167 : jhr 547 fun getState (ty, x) = (CL.mkIndirect(CL.mkVar "selfIn", x), ty)
168 : jhr 525 fun param (ty, x) = (CL.mkVar x, ty)
169 :     fun var (ty, x) = (CL.mkVar x, ty)
170 :    
171 : jhr 519 (* literals *)
172 : jhr 525 fun intLit n = (CL.mkInt(n, !gIntTy), intTy)
173 :     fun floatLit f = (CL.mkFlt(f, !gRealTy), realTy)
174 : jhr 533 fun stringLit s = (CL.mkStr s, stringTy)
175 : jhr 525 fun boolLit b = (CL.mkBool b, boolTy)
176 :    
177 : jhr 519 (* select from a vector *)
178 : jhr 525 fun select (i, (e, T_Vec n)) =
179 :     if (i < 0) orelse (n <= i)
180 :     then raise Subscript
181 :     else (CL.mkSubscript(e, CL.mkInt(IntInf.fromInt i, CL.int32)), T_Real)
182 :     | select (i, (e, T_IVec n)) =
183 :     if (i < 0) orelse (n <= i)
184 :     then raise Subscript
185 :     else (CL.mkSubscript(e, CL.mkInt(IntInf.fromInt i, CL.int32)), T_Int)
186 : jhr 548 | select (_, x) = invalid("select", [x])
187 : jhr 525
188 : jhr 519 (* vector (and scalar) arithmetic *)
189 : jhr 525 local
190 :     fun checkTys (ty1, ty2) = (ty1 = ty2) andalso numTy ty1
191 :     fun binop rator ((e1, ty1), (e2, ty2)) =
192 :     if checkTys (ty1, ty2)
193 :     then (CL.mkBinOp(e1, rator, e2), ty1)
194 : jhr 548 else invalid (
195 :     concat["binary operator \"", CL.binopToString rator, "\""],
196 :     [(e1, ty1), (e2, ty2)])
197 : jhr 525 in
198 : jhr 548 fun add ((e1, ty as T_Ptr _), (e2, T_Int)) = (CL.mkBinOp(e1, CL.#+, e2), ty)
199 :     | add args = binop CL.#+ args
200 :     fun sub ((e1, ty as T_Ptr _), (e2, T_Int)) = (CL.mkBinOp(e1, CL.#-, e2), ty)
201 :     | sub args = binop CL.#- args
202 : jhr 544 (* NOTE: multiplication and division are also used for scaling *)
203 :     fun mul ((e1, T_Real), (e2, T_Vec n)) =
204 :     (CL.E_Apply("Diderot_scale"^vectorSuffix n, [e1, e2]), T_Vec n)
205 :     | mul args = binop CL.#* args
206 :     fun divide ((e1, T_Vec n), (e2, T_Real)) =
207 :     (CL.E_Apply("Diderot_scale"^vectorSuffix n,
208 :     [CL.mkBinOp(CL.mkFlt(FloatLit.one, !gRealTy), CL.#/, e2), e1]), T_Vec n)
209 :     | divide args = binop CL.#/ args
210 : jhr 525 end (* local *)
211 :     fun neg (e, T_Bool) = raise Fail "invalid argument to neg"
212 :     | neg (e, ty) = (CL.mkUnOp(CL.%-, e), ty)
213 :    
214 :     fun abs (e, T_Int) = (CL.mkApply("abs", [e]), T_Int) (* FIXME: not the right type for 64-bit ints *)
215 : jhr 544 | abs (e, T_Real) = (CL.mkApply("fabs" ^ !gRealSuffix, [e]), T_Real)
216 : jhr 525 | abs (e, T_Vec n) = raise Fail "FIXME: Expr.abs"
217 :     | abs (e, T_IVec n) = raise Fail "FIXME: Expr.abs"
218 :     | abs _ = raise Fail "invalid argument to abs"
219 :    
220 : jhr 544 fun dot ((e1, T_Vec n1), (e2, T_Vec n2)) =
221 :     (CL.E_Apply("Diderot_dot"^vectorSuffix n1, [e1, e2]), T_Real)
222 : jhr 525 | dot _ = raise Fail "invalid argument to dot"
223 :    
224 :     fun cross ((e1, T_Vec 3), (e2, T_Vec 3)) = raise Fail "FIXME: Expr.cross"
225 :     | cross _ = raise Fail "invalid argument to cross"
226 :    
227 : jhr 544 fun length (e, T_Vec n) =
228 :     (CL.E_Apply("Diderot_length"^vectorSuffix n, [e]), T_Real)
229 : jhr 525 | length _ = raise Fail "invalid argument to length"
230 :    
231 : jhr 544 fun normalize (e, T_Vec n) =
232 :     (CL.E_Apply("Diderot_normalize"^vectorSuffix n, [e]), T_Vec n)
233 : jhr 525 | normalize _ = raise Fail "invalid argument to length"
234 :    
235 : jhr 519 (* comparisons *)
236 : jhr 525 local
237 :     fun checkTys (ty1, ty2) =
238 :     (ty1 = ty2) andalso scalarTy ty1
239 :     fun cmpop rator ((e1, ty1), (e2, ty2)) =
240 :     if checkTys (ty1, ty2)
241 :     then (CL.mkBinOp(e1, rator, e2), T_Bool)
242 : jhr 548 else invalid (
243 :     concat["compare operator \"", CL.binopToString rator, "\""],
244 :     [(e1, ty1), (e2, ty2)])
245 : jhr 525 in
246 :     val lt = cmpop CL.#<
247 :     val lte = cmpop CL.#<=
248 :     val equ = cmpop CL.#==
249 :     val neq = cmpop CL.#!=
250 :     val gte = cmpop CL.#>=
251 :     val gt = cmpop CL.#>
252 :     end (* local *)
253 :    
254 : jhr 519 (* logical connectives *)
255 : jhr 525 fun not (e, T_Bool) = (CL.mkUnOp(CL.%!, e), T_Bool)
256 :     | not _ = raise Fail "invalid argument to not"
257 :     fun && ((e1, T_Bool), (e2, T_Bool)) = (CL.mkBinOp(e1, CL.#&&, e2), T_Bool)
258 :     | && _ = raise Fail "invalid arguments to &&"
259 :     fun || ((e1, T_Bool), (e2, T_Bool)) = (CL.mkBinOp(e1, CL.#||, e2), T_Bool)
260 :     | || _ = raise Fail "invalid arguments to ||"
261 :    
262 :     local
263 :     fun checkTys (ty1, ty2) = (ty1 = ty2) andalso scalarTy ty1
264 :     fun binFn f ((e1, ty1), (e2, ty2)) =
265 :     if checkTys (ty1, ty2)
266 :     then (CL.mkApply(f, [e1, e2]), ty1)
267 :     else raise Fail "invalid arguments to binary function"
268 :     in
269 : jhr 519 (* misc functions *)
270 : jhr 525 val min = binFn "Diderot_min"
271 :     val max = binFn "Diderot_max"
272 :     end (* local *)
273 :    
274 : jhr 519 (* math functions *)
275 : jhr 525 fun pow ((e1, T_Real), (e2, T_Real)) =
276 :     if !Controls.doublePrecision
277 :     then (CL.mkApply("pow", [e1, e2]), T_Real)
278 :     else (CL.mkApply("powf", [e1, e2]), T_Real)
279 :     | pow _ = raise Fail "invalid arguments to pow"
280 :    
281 :     local
282 :     fun r2r (ff, fd) (e, T_Real) = if !Controls.doublePrecision
283 :     then (CL.mkApply(fd, [e]), T_Real)
284 :     else (CL.mkApply(ff, [e]), T_Real)
285 :     | r2r (_, fd) _ = raise Fail("invalid argument for "^fd)
286 :     in
287 :     val sin = r2r ("sinf", "sin")
288 :     val cos = r2r ("cosf", "cos")
289 :     val sqrt = r2r ("sqrtf", "sqrt")
290 : jhr 519 (* rounding *)
291 : jhr 525 val trunc = r2r ("truncf", "trunc")
292 :     val round = r2r ("roundf", "round")
293 :     val floor = r2r ("floorf", "floor")
294 :     val ceil = r2r ("ceilf", "ceil")
295 :     end (* local *)
296 :    
297 : jhr 519 (* conversions *)
298 : jhr 525 fun toReal (e, T_Int) = (CL.mkCast(!gRealTy, e), T_Real)
299 : jhr 548 | toReal e = invalid ("toReal", [e])
300 : jhr 525
301 :     fun truncToInt (e as (_, T_Real)) = (CL.mkCast(!gIntTy, #1(trunc e)), T_Int)
302 : jhr 548 | truncToInt e = invalid ("truncToInt", [e])
303 : jhr 525 fun roundToInt (e as (_, T_Real)) = (CL.mkCast(!gIntTy, #1(round e)), T_Int)
304 : jhr 548 | roundToInt e = invalid ("roundToInt", [e])
305 : jhr 525 fun ceilToInt (e as (_, T_Real)) = (CL.mkCast(!gIntTy, #1(floor e)), T_Int)
306 : jhr 548 | ceilToInt e = invalid ("ceilToInt", [e])
307 : jhr 525 fun floorToInt (e as (_, T_Real)) = (CL.mkCast(!gIntTy, #1(ceil e)), T_Int)
308 : jhr 548 | floorToInt e = invalid ("floorToInt", [e])
309 : jhr 525
310 : jhr 519 (* runtime system hooks *)
311 : jhr 548 fun imageAddr (e, T_Image(_, rTy)) = let
312 : jhr 528 val cTy = CL.T_Ptr(!gRealTy)
313 :     in
314 : jhr 548 (CL.mkCast(cTy, CL.mkIndirect(e, "data")), T_Ptr rTy)
315 : jhr 528 end
316 : jhr 548 | imageAddr a = invalid("imageAddr", [a])
317 :     fun getImgData (e, T_Ptr rTy) = let
318 :     val realTy as CL.T_Num rTy' = !gRealTy
319 :     val e = CL.E_UnOp(CL.%*, e)
320 :     in
321 :     if (rTy' = rTy)
322 :     then (e, T_Real)
323 :     else (CL.E_Cast(realTy, e), T_Real)
324 :     end
325 :     | getImgData a = invalid("getImgData", [a])
326 :     fun posToImgSpace ((img, T_Image(d, _)), (pos, T_Vec n)) = let
327 :     val fnName = concat["Diderot_ToImageSpace", Int.toString d, "D"]
328 :     val e = CL.mkApply(fnName, [img, pos])
329 :     in
330 :     (e, T_Vec n)
331 :     end
332 :     | posToImgSpace (a, b) = invalid("posToImgSpace", [a, b])
333 :     fun inside ((pos, T_Vec n), (img, T_Image(d, _)), s) = let
334 : jhr 547 val fnName = concat["Diderot_Inside", Int.toString d, "D"]
335 :     val e = CL.mkApply(fnName,
336 :     [pos, img, CL.mkInt(IntInf.fromInt n, CL.int32)])
337 :     in
338 :     (e, T_Bool)
339 :     end
340 : jhr 548 | inside (a, b, _) = invalid("inside", [a, b])
341 : jhr 519
342 : jhr 547 end (* Expr *)
343 :    
344 : jhr 519 (* statement construction *)
345 : jhr 525 structure Stmt =
346 :     struct
347 :     val comment = CL.S_Comment
348 : jhr 547 fun assignState ((_, x), (e, _)) =
349 :     CL.mkAssign(CL.mkIndirect(CL.mkVar "selfOut", x), e)
350 : jhr 525 fun assign ((_, x), (e, _)) = CL.mkAssign(CL.mkVar x, e)
351 : jhr 528 fun decl ((ty, x), SOME(e, _)) = CL.mkDecl(cvtTy ty, x, SOME e)
352 :     | decl ((ty, x), NONE) = CL.mkDecl(cvtTy ty, x, NONE)
353 : jhr 525 val block = CL.mkBlock
354 : jhr 532 fun ifthen ((e, T_Bool), s1) = CL.mkIfThen(e, s1)
355 : jhr 525 fun ifthenelse ((e, T_Bool), s1, s2) = CL.mkIfThenElse(e, s1, s2)
356 : jhr 534 (* special Diderot forms *)
357 :     fun cons (lhs, args) = comment ["**** cons ****"] (* FIXME *)
358 : jhr 548 fun getImgData (lhs, n, e) = comment ["**** getImgData ****"] (* FIXME *)
359 : jhr 534 fun loadImage (lhs : var, dim, name : exp) = let
360 :     val sts = "sts"
361 :     val imgTy = CL.T_Named(concat["Diderot_image", Int.toString dim, "D_t"])
362 :     val loadFn = concat["Diderot_LoadImage", Int.toString dim, "D"]
363 :     in [
364 :     CL.S_Decl(
365 :     statusTy, sts,
366 :     SOME(CL.E_Apply(loadFn, [#1 name, CL.mkUnOp(CL.%&, CL.E_Var(#2 lhs))])))
367 :     ] end
368 :     fun input (lhs : var, name, optDflt) = let
369 :     val sts = "sts"
370 :     val inputFn = (case #1 lhs
371 :     of T_String => "Diderot_InputString"
372 : jhr 537 | T_Real => "Diderot_InputReal"
373 : jhr 534 | T_Vec 3 => "Diderot_InputVec3"
374 : jhr 537 | ty => raise Fail("unsupported input type " ^ tyToString ty)
375 : jhr 534 (* end case *))
376 :     val lhs = CL.E_Var(#2 lhs)
377 :     val (initCode, hasDflt) = (case optDflt
378 :     of SOME(e, _) => ([CL.S_Assign(lhs, e)], true)
379 :     | NONE => ([], false)
380 :     (* end case *))
381 :     val code = [
382 :     CL.S_Decl(
383 :     statusTy, sts,
384 :     SOME(CL.E_Apply(inputFn, [
385 :     CL.E_Str name, CL.mkUnOp(CL.%&, lhs), CL.mkBool hasDflt
386 :     ])))
387 :     ]
388 :     in
389 :     initCode @ code
390 :     end
391 : jhr 528 fun die () = comment ["**** die ****"] (* FIXME *)
392 :     fun stabilize () = comment ["**** stabilize ****"] (* FIXME *)
393 : jhr 519 end
394 :    
395 : jhr 544 structure Strand =
396 :     struct
397 :     fun define (Prog{strands, ...}, strandId) = let
398 :     val strand = Strand{
399 :     name = strandId,
400 :     tyName = strandId ^ "_t",
401 :     state = ref [],
402 :     code = ref []
403 :     }
404 :     in
405 :     strands := strand :: !strands;
406 :     strand
407 :     end
408 :    
409 :     (* register the strand-state initialization code. The variables are the strand
410 :     * parameters.
411 :     *)
412 :     fun init (Strand{name, tyName, code, ...}, params, init) = let
413 :     val fName = name ^ "_InitState"
414 :     val params =
415 : jhr 547 CL.PARAM([], CL.T_Ptr(CL.T_Named tyName), "selfOut") ::
416 : jhr 544 List.map (fn (ty, x) => CL.PARAM([], cvtTy ty, x)) params
417 :     val initFn = CL.D_Func([], CL.voidTy, fName, params, init)
418 :     in
419 :     code := initFn :: !code
420 :     end
421 : jhr 547
422 :     (* register a strand method *)
423 :     fun method (Strand{name, tyName, code, ...}, methName, body) = let
424 :     val fName = concat[name, "_", methName]
425 :     val params = [
426 :     CL.PARAM([], CL.T_Ptr(CL.T_Named tyName), "selfIn"),
427 :     CL.PARAM([], CL.T_Ptr(CL.T_Named tyName), "selfOut")
428 :     ]
429 :     val methFn = CL.D_Func([], CL.int32, fName, params, body)
430 :     in
431 :     code := methFn :: !code
432 :     end
433 : jhr 544 end (* Strand *)
434 :    
435 :     fun genStrand (Strand{name, tyName, state, code}) = let
436 :     val selfTyDef = CL.D_StructDef(
437 :     List.rev (List.map (fn (ty, x) => (cvtTy ty, x)) (!state)),
438 :     tyName)
439 :     in
440 :     selfTyDef :: List.rev (!code)
441 :     end
442 :    
443 : jhr 533 fun generate (baseName, Prog{globals, topDecls, strands}) = let
444 : jhr 527 val fileName = OS.Path.joinBaseExt{base=baseName, ext=SOME "c"}
445 :     val outS = TextIO.openOut fileName
446 :     val ppStrm = PrintAsC.new outS
447 : jhr 533 fun ppDecl dcl = PrintAsC.output(ppStrm, dcl)
448 : jhr 527 in
449 : jhr 533 List.app ppDecl (List.rev (!globals));
450 :     List.app ppDecl (List.rev (!topDecls));
451 : jhr 527 (* what about the strands, etc? *)
452 : jhr 544 List.app (fn strand => List.app ppDecl (genStrand strand)) (!strands);
453 : jhr 527 PrintAsC.close ppStrm;
454 :     TextIO.closeOut outS
455 :     end
456 :    
457 : jhr 519 end
458 :    
459 :     structure CBackEnd = CodeGenFn(CTarget)

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0