1 module d.llvm.statement;
2 
3 import d.llvm.local;
4 
5 import d.ir.expression;
6 import d.ir.instruction;
7 
8 import source.location;
9 
10 import util.visitor;
11 
12 import llvm.c.core;
13 
14 struct StatementGenData {
15 private:
16 	LLVMValueRef llvmEhTypeIdFor;
17 }
18 
19 struct StatementGen {
20 	private LocalPass pass;
21 	alias pass this;
22 	
23 	LLVMValueRef fun;
24 	
25 	LLVMBasicBlockRef[] basicBlocks;
26 	LLVMBasicBlockRef[] landingPads;
27 	
28 	Body fbody;
29 	
30 	this(LocalPass pass) {
31 		this.pass = pass;
32 	}
33 	
34 	void visit(Body fbody) in {
35 		assert(fbody, "Empty body");
36 	} do {
37 		basicBlocks.length = fbody.length;
38 		landingPads.length = fbody.length;
39 		
40 		auto allocaBB = LLVMGetInsertBlock(builder);
41 		fun = LLVMGetBasicBlockParent(allocaBB);
42 		scope(success) {
43 			// Branch from alloca block to function body.
44 			LLVMPositionBuilderAtEnd(builder, allocaBB);
45 			LLVMBuildBr(builder, basicBlocks[0]);
46 		}
47 		
48 		this.fbody = fbody;
49 		foreach (b; range(fbody)) {
50 			visit(b);
51 		}
52 	}
53 	
54 	void visit(BasicBlockRef b) {
55 		auto llvmBB = genBasicBlock(b);
56 		LLVMMoveBasicBlockAfter(llvmBB, LLVMGetInsertBlock(builder));
57 		LLVMPositionBuilderAtEnd(builder, llvmBB);
58 		
59 		lpBB = genLandingPad(b);
60 		
61 		foreach(i; range(fbody, b)) {
62 			final switch(i.op) with(OpCode) {
63 				case Alloca:
64 					define(i.var);
65 					break;
66 				
67 				case Destroy:
68 					auto v = i.var;
69 					auto s = v.type.getCanonical().dstruct;
70 					assert(!s.isPod, "struct is not a pod");
71 					
72 					import source.name;
73 					auto dsym = s.resolve(i.location, BuiltinName!"__dtor");
74 					
75 					import d.ir.symbol;
76 					auto dtor = cast(Function) dsym;
77 					if (dtor is null) {
78 						auto os = cast(OverloadSet) dsym;
79 						assert(os, "__dtor must be an overload set");
80 						dtor = cast(Function) os.set[0];
81 					}
82 					
83 					assert(dtor, "Cannot find dtor");
84 					genCall(declare(dtor), [declare(v)]);
85 					break;
86 				
87 				case Evaluate:
88 					genExpression(i.expr);
89 					break;
90 				
91 				// FIXME: Delete this, we can generate these upon use.
92 				case Declare:
93 					define(i.sym);
94 					break;
95 			}
96 		}
97 		
98 		auto bb = &fbody[b];
99 		final switch(bb.terminator) with(Terminator) {
100 			case None:
101 				assert(0, "Unterminated block");
102 			
103 			case Branch:
104 				if (bb.value) {
105 					LLVMBuildCondBr(
106 						builder,
107 						genExpression(bb.value),
108 						genBasicBlock(fbody[b].successors[0]),
109 						genBasicBlock(fbody[b].successors[1]),
110 					);
111 				} else {
112 					LLVMBuildBr(
113 						builder,
114 						genBasicBlock(fbody[b].successors[0]),
115 					);
116 				}
117 				
118 				break;
119 			
120 			case Switch:
121 				auto switchTable = bb.switchTable;
122 				auto e = genExpression(bb.value);
123 				auto switchInstr = LLVMBuildSwitch(
124 					builder,
125 					e,
126 					genBasicBlock(switchTable.defaultBlock),
127 					switchTable.entryCount,
128 				);
129 				
130 				auto t = LLVMTypeOf(e);
131 				foreach(c; switchTable.cases) {
132 					LLVMAddCase(
133 						switchInstr,
134 						LLVMConstInt(t, c.value, false),
135 						genBasicBlock(c.block),
136 					);
137 				}
138 				
139 				break;
140 			
141 			case Return:
142 				if (bb.value) {
143 					auto ret = genExpression(bb.value);
144 					LLVMBuildRet(builder, ret);
145 				} else {
146 					LLVMBuildRetVoid(builder);
147 				}
148 				
149 				break;
150 			
151 			case Throw:
152 				if (bb.value) {
153 					genCall(
154 						declare(pass.object.getThrow()),
155 						[genExpression(bb.value)],
156 					);
157 					LLVMBuildUnreachable(builder);
158 					break;
159 				}
160 				
161 				// Create an alloca for the landing pad results.
162 				if (!lpContext) {
163 					auto currentBB = LLVMGetInsertBlock(builder);
164 					LLVMPositionBuilderAtEnd(builder, LLVMGetFirstBasicBlock(fun));
165 					lpContext = LLVMBuildAlloca(builder, getLpType(), "lpContext");
166 					LLVMPositionBuilderAtEnd(builder, currentBB);
167 					LLVMSetPersonalityFn(fun, declare(pass.object.getPersonality()));
168 				}
169 				
170 				auto catchTable = fbody[b].catchTable;
171 				if (catchTable is null) {
172 					Resume:
173 					if (auto lpBlock = fbody[b].landingpad) {
174 						LLVMBuildBr(builder, genBasicBlock(lpBlock));
175 					} else {
176 						auto lp = LLVMBuildLoad(builder, lpContext, "");
177 						LLVMBuildResume(builder, lp);
178 					}
179 					
180 					break;
181 				}
182 				
183 				auto i32 = LLVMInt32TypeInContext(llvmCtx);
184 				LLVMValueRef[2] gepIdx = [
185 					LLVMConstInt(i32, 0, false),
186 					LLVMConstInt(i32, 1, false),
187 				];
188 				
189 				auto ptr = LLVMBuildInBoundsGEP(
190 					builder,
191 					lpContext,
192 					gepIdx.ptr,
193 					gepIdx.length,
194 					"",
195 				);
196 				
197 				auto actionid = LLVMBuildLoad(builder, ptr, "actionid");
198 				auto i8 = LLVMInt8TypeInContext(llvmCtx);
199 				auto voidstar = LLVMPointerType(i8, 0);
200 				foreach(c; catchTable.catches) {
201 					auto nextUnwindBB =
202 						LLVMAppendBasicBlockInContext(llvmCtx, fun, "");
203 					
204 					import d.llvm.type;
205 					auto typeinfo = LLVMBuildBitCast(
206 						builder,
207 						TypeGen(pass.pass).getTypeInfo(c.type),
208 						voidstar,
209 						"",
210 					);
211 					
212 					LLVMBuildCondBr(
213 						builder,
214 						LLVMBuildICmp(
215 							builder,
216 							LLVMIntPredicate.EQ,
217 							genCall(getEhTypeidFor(), [typeinfo]),
218 							actionid,
219 							"",
220 						),
221 						genBasicBlock(c.block),
222 						nextUnwindBB,
223 					);
224 					
225 					LLVMPositionBuilderAtEnd(builder, nextUnwindBB);
226 				}
227 				
228 				auto lpBlock = fbody[b].landingpad;
229 				if (lpBlock) {
230 					LLVMBuildBr(builder, genBasicBlock(lpBlock));
231 					break;
232 				}
233 				
234 				goto Resume;
235 			
236 			case Halt:
237 				genHalt(bb.location, bb.value);
238 				break;
239 		}
240 	}
241 	
242 	private auto getEhTypeidFor() {
243 		if (pass.statementGenData.llvmEhTypeIdFor !is null) {
244 			return pass.statementGenData.llvmEhTypeIdFor;
245 		}
246 		
247 		auto i32 = LLVMInt32TypeInContext(llvmCtx);
248 		auto arg = LLVMPointerType(LLVMInt8TypeInContext(llvmCtx), 0);
249 		
250 		pass.statementGenData.llvmEhTypeIdFor = LLVMAddFunction(
251 			dmodule,
252 			"llvm.eh.typeid.for".ptr,
253 			LLVMFunctionType(i32, &arg, 1, false),
254 		);
255 		
256 		return pass.statementGenData.llvmEhTypeIdFor;
257 	}
258 	
259 	private auto genExpression(Expression e) {
260 		import d.llvm.expression;
261 		return ExpressionGen(pass).visit(e);
262 	}
263 	
264 	private auto getLpType() {
265 		LLVMTypeRef[2] lpTypes = [
266 			LLVMPointerType(LLVMInt8TypeInContext(llvmCtx), 0),
267 			LLVMInt32TypeInContext(llvmCtx),
268 		];
269 		
270 		return LLVMStructTypeInContext(
271 			llvmCtx,
272 			lpTypes.ptr,
273 			lpTypes.length,
274 			false,
275 		);
276 	}
277 	
278 	private LLVMBasicBlockRef genLandingPad(BasicBlockRef srcBlock) {
279 		auto b = fbody[srcBlock].landingpad;
280 		if (!b) {
281 			return null;
282 		}
283 		
284 		auto i = *(cast(uint*) &b) - 1;
285 		if (landingPads[i] !is null) {
286 			return landingPads[i];
287 		}
288 		
289 		// We have a failure case.
290 		auto currentBB = LLVMGetInsertBlock(builder);
291 		scope(exit) LLVMPositionBuilderAtEnd(builder, currentBB);
292 		
293 		auto lpType = getLpType();
294 		
295 		// Create an alloca for the landing pad results.
296 		if (!lpContext) {
297 			LLVMPositionBuilderAtEnd(builder, LLVMGetFirstBasicBlock(fun));
298 			lpContext = LLVMBuildAlloca(builder, lpType, "lpContext");
299 			LLVMSetPersonalityFn(fun, declare(pass.object.getPersonality()));
300 		}
301 		
302 		auto lpBB = landingPads[i] = LLVMAppendBasicBlockInContext(
303 			llvmCtx,
304 			fun,
305 			"landingPad",
306 		);
307 		
308 		auto instrs = range(fbody, b);
309 		auto terminator = fbody[b].terminator;
310 		bool cleanup = instrs.length > 0 || terminator != Terminator.Throw;
311 		
312 		LLVMValueRef[] clauses;
313 		if (terminator == Terminator.Throw && !fbody[b].value) {
314 			if (auto catchTable = fbody[b].catchTable) {
315 				foreach(c; catchTable.catches) {
316 					import d.llvm.type;
317 					clauses ~= TypeGen(pass.pass).getTypeInfo(c.type);
318 				}
319 			}
320 		}
321 		
322 		if (auto nextLpBB = genLandingPad(b)) {
323 			auto nextLp = LLVMGetFirstInstruction(nextLpBB);
324 			cleanup = cleanup || LLVMIsCleanup(nextLp);
325 			
326 			clauses.length = LLVMGetNumClauses(nextLp);
327 			foreach (n, ref c; clauses) {
328 				c = LLVMGetClause(nextLp, cast(uint) n);
329 			}
330 		}
331 		
332 		LLVMPositionBuilderAtEnd(builder, lpBB);
333 		auto landingPad = LLVMBuildLandingPad(
334 			builder,
335 			lpType,
336 			null,
337 			cast(uint) clauses.length,
338 			"",
339 		);
340 		
341 		LLVMSetCleanup(landingPad, cleanup);
342 		foreach (c; clauses) {
343 			LLVMAddClause(landingPad, c);
344 		}
345 		
346 		LLVMBuildStore(builder, landingPad, lpContext);
347 		LLVMBuildBr(builder, genBasicBlock(b));
348 		
349 		return lpBB;
350 	}
351 	
352 	private LLVMBasicBlockRef genBasicBlock(BasicBlockRef b) {
353 		auto i = *(cast(uint*) &b) - 1;
354 		if (basicBlocks[i] !is null) {
355 			return basicBlocks[i];
356 		}
357 		
358 		// Make sure we have the landign pad ready.
359 		genLandingPad(b);
360 		
361 		return basicBlocks[i] = LLVMAppendBasicBlockInContext(
362 			llvmCtx,
363 			fun,
364 			fbody[b].name.toStringz(context),
365 		);
366 	}
367 	
368 	private auto genCall(LLVMValueRef callee, LLVMValueRef[] args) {
369 		import d.llvm.expression;
370 		return ExpressionGen(pass).buildCall(callee, args);
371 	}
372 	
373 	void genHalt(Location location, Expression msg) {
374 		auto floc = location.getFullLocation(context);
375 		
376 		LLVMValueRef[3] args;
377 		args[1] = buildDString(floc.getSource().getFileName().toString());
378 		args[2] = LLVMConstInt(
379 			LLVMInt32TypeInContext(llvmCtx),
380 			floc.getStartLineNumber(),
381 			false,
382 		);
383 		
384 		if (msg) {
385 			args[0] = genExpression(msg);
386 			genCall(declare(pass.object.getAssertFailMsg()), args[]);
387 		} else {
388 			genCall(declare(pass.object.getAssertFail()), args[1 .. $]);
389 		}
390 		
391 		// Conclude that block.
392 		LLVMBuildUnreachable(builder);
393 	}
394 }