1 (*===----------------------------------------------------------------------===
3 *===----------------------------------------------------------------------===*)
7 exception Error
of string
9 let context = global_context
()
10 let the_module = create_module
context "my cool jit"
11 let builder = builder context
12 let named_values:(string, llvalue
) Hashtbl.t
= Hashtbl.create
10
13 let double_type = double_type context
15 let rec codegen_expr = function
16 | Ast.Number n
-> const_float
double_type n
17 | Ast.Variable name
->
18 (try Hashtbl.find
named_values name
with
19 | Not_found
-> raise
(Error
"unknown variable name"))
20 | Ast.Unary
(op
, operand
) ->
21 let operand = codegen_expr operand in
22 let callee = "unary" ^
(String.make
1 op
) in
24 match lookup_function
callee the_module with
25 | Some
callee -> callee
26 | None
-> raise
(Error
"unknown unary operator")
28 build_call
callee [|operand|] "unop" builder
29 | Ast.Binary
(op
, lhs
, rhs
) ->
30 let lhs_val = codegen_expr lhs
in
31 let rhs_val = codegen_expr rhs
in
34 | '
+'
-> build_fadd
lhs_val rhs_val "addtmp" builder
35 | '
-'
-> build_fsub
lhs_val rhs_val "subtmp" builder
36 | '
*'
-> build_fmul
lhs_val rhs_val "multmp" builder
38 (* Convert bool 0/1 to double 0.0 or 1.0 *)
39 let i = build_fcmp
Fcmp.Ult
lhs_val rhs_val "cmptmp" builder in
40 build_uitofp
i double_type "booltmp" builder
42 (* If it wasn't a builtin binary operator, it must be a user defined
43 * one. Emit a call to it. *)
44 let callee = "binary" ^
(String.make
1 op
) in
46 match lookup_function
callee the_module with
47 | Some
callee -> callee
48 | None
-> raise
(Error
"binary operator not found!")
50 build_call
callee [|lhs_val; rhs_val|] "binop" builder
52 | Ast.Call
(callee, args
) ->
53 (* Look up the name in the module table. *)
55 match lookup_function
callee the_module with
56 | Some
callee -> callee
57 | None
-> raise
(Error
"unknown function referenced")
59 let params = params callee in
61 (* If argument mismatch error. *)
62 if Array.length
params == Array.length args
then () else
63 raise
(Error
"incorrect # arguments passed");
64 let args = Array.map
codegen_expr args in
65 build_call
callee args "calltmp" builder
66 | Ast.If
(cond
, then_
, else_
) ->
67 let cond = codegen_expr cond in
69 (* Convert condition to a bool by comparing equal to 0.0 *)
70 let zero = const_float
double_type 0.0 in
71 let cond_val = build_fcmp
Fcmp.One
cond zero "ifcond" builder in
73 (* Grab the first block so that we might later add the conditional branch
74 * to it at the end of the function. *)
75 let start_bb = insertion_block
builder in
76 let the_function = block_parent
start_bb in
78 let then_bb = append_block
context "then" the_function in
80 (* Emit 'then' value. *)
81 position_at_end
then_bb builder;
82 let then_val = codegen_expr then_
in
84 (* Codegen of 'then' can change the current block, update then_bb for the
85 * phi. We create a new name because one is used for the phi node, and the
86 * other is used for the conditional branch. *)
87 let new_then_bb = insertion_block
builder in
89 (* Emit 'else' value. *)
90 let else_bb = append_block
context "else" the_function in
91 position_at_end
else_bb builder;
92 let else_val = codegen_expr else_
in
94 (* Codegen of 'else' can change the current block, update else_bb for the
96 let new_else_bb = insertion_block
builder in
98 (* Emit merge block. *)
99 let merge_bb = append_block
context "ifcont" the_function in
100 position_at_end
merge_bb builder;
101 let incoming = [(then_val, new_then_bb); (else_val, new_else_bb)] in
102 let phi = build_phi
incoming "iftmp" builder in
104 (* Return to the start block to add the conditional branch. *)
105 position_at_end
start_bb builder;
106 ignore
(build_cond_br
cond_val then_bb else_bb builder);
108 (* Set a unconditional branch at the end of the 'then' block and the
109 * 'else' block to the 'merge' block. *)
110 position_at_end
new_then_bb builder; ignore
(build_br
merge_bb builder);
111 position_at_end
new_else_bb builder; ignore
(build_br
merge_bb builder);
113 (* Finally, set the builder to the end of the merge block. *)
114 position_at_end
merge_bb builder;
117 | Ast.For
(var_name
, start
, end_
, step
, body
) ->
118 (* Emit the start code first, without 'variable' in scope. *)
119 let start_val = codegen_expr start
in
121 (* Make the new basic block for the loop header, inserting after current
123 let preheader_bb = insertion_block
builder in
124 let the_function = block_parent
preheader_bb in
125 let loop_bb = append_block
context "loop" the_function in
127 (* Insert an explicit fall through from the current block to the
129 ignore
(build_br
loop_bb builder);
131 (* Start insertion in loop_bb. *)
132 position_at_end
loop_bb builder;
134 (* Start the PHI node with an entry for start. *)
135 let variable = build_phi
[(start_val, preheader_bb)] var_name
builder in
137 (* Within the loop, the variable is defined equal to the PHI node. If it
138 * shadows an existing variable, we have to restore it, so save it
141 try Some
(Hashtbl.find
named_values var_name
) with Not_found
-> None
143 Hashtbl.add
named_values var_name
variable;
145 (* Emit the body of the loop. This, like any other expr, can change the
146 * current BB. Note that we ignore the value computed by the body, but
147 * don't allow an error *)
148 ignore
(codegen_expr body
);
150 (* Emit the step value. *)
153 | Some step
-> codegen_expr step
154 (* If not specified, use 1.0. *)
155 | None
-> const_float
double_type 1.0
158 let next_var = build_add
variable step_val "nextvar" builder in
160 (* Compute the end condition. *)
161 let end_cond = codegen_expr end_
in
163 (* Convert condition to a bool by comparing equal to 0.0. *)
164 let zero = const_float
double_type 0.0 in
165 let end_cond = build_fcmp
Fcmp.One
end_cond zero "loopcond" builder in
167 (* Create the "after loop" block and insert it. *)
168 let loop_end_bb = insertion_block
builder in
169 let after_bb = append_block
context "afterloop" the_function in
171 (* Insert the conditional branch into the end of loop_end_bb. *)
172 ignore
(build_cond_br
end_cond loop_bb after_bb builder);
174 (* Any new code will be inserted in after_bb. *)
175 position_at_end
after_bb builder;
177 (* Add a new entry to the PHI node for the backedge. *)
178 add_incoming
(next_var, loop_end_bb) variable;
180 (* Restore the unshadowed variable. *)
181 begin match old_val with
182 | Some
old_val -> Hashtbl.add
named_values var_name
old_val
186 (* for expr always returns 0.0. *)
187 const_null
double_type
189 let codegen_proto = function
190 | Ast.Prototype
(name
, args) | Ast.BinOpPrototype
(name
, args, _
) ->
191 (* Make the function type: double(double,double) etc. *)
192 let doubles = Array.make
(Array.length
args) double_type in
193 let ft = function_type
double_type doubles in
195 match lookup_function name
the_module with
196 | None
-> declare_function name
ft the_module
198 (* If 'f' conflicted, there was already something named 'name'. If it
199 * has a body, don't allow redefinition or reextern. *)
201 (* If 'f' already has a body, reject this. *)
202 if block_begin
f <> At_end
f then
203 raise
(Error
"redefinition of function");
205 (* If 'f' took a different number of arguments, reject. *)
206 if element_type
(type_of
f) <> ft then
207 raise
(Error
"redefinition of function with different # args");
211 (* Set names for all arguments. *)
212 Array.iteri
(fun i a
->
215 Hashtbl.add
named_values n a
;
219 let codegen_func the_fpm
= function
220 | Ast.Function
(proto
, body
) ->
221 Hashtbl.clear
named_values;
222 let the_function = codegen_proto proto
in
224 (* If this is an operator, install it. *)
225 begin match proto
with
226 | Ast.BinOpPrototype
(name
, args, prec
) ->
227 let op = name
.[String.length name
- 1] in
228 Hashtbl.add
Parser.binop_precedence
op prec
;
232 (* Create a new basic block to start insertion into. *)
233 let bb = append_block
context "entry" the_function in
234 position_at_end
bb builder;
237 let ret_val = codegen_expr body
in
239 (* Finish off the function. *)
240 let _ = build_ret
ret_val builder in
242 (* Validate the generated code, checking for consistency. *)
243 Llvm_analysis.assert_valid_function
the_function;
245 (* Optimize the function. *)
246 let _ = PassManager.run_function
the_function the_fpm
in
250 delete_function
the_function;