1 module d.llvm.backend;
2 
3 import d.llvm.codegen;
4 import d.llvm.evaluator;
5 import d.llvm.datalayout;
6 
7 import d.ir.symbol;
8 
9 import llvm.c.core;
10 import llvm.c.target;
11 import llvm.c.targetMachine;
12 
13 final class LLVMBackend {
14 private:
15 	CodeGen pass;
16 	
17 	uint optLevel;
18 	string[] linkerPaths;
19 	
20 	LLVMEvaluator evaluator;
21 	LLVMDataLayout dataLayout;
22 	
23 	LLVMTargetMachineRef targetMachine;
24 	
25 public:
26 	import d.semantic.semantic;
27 	this(SemanticPass sema, string name, uint optLevel, string[] linkerPaths) {
28 		this.optLevel = optLevel;
29 		this.linkerPaths = linkerPaths;
30 		
31 		LLVMInitializeX86TargetInfo();
32 		LLVMInitializeX86Target();
33 		LLVMInitializeX86TargetMC();
34 		
35 		import llvm.c.executionEngine;
36 		LLVMLinkInMCJIT();
37 		LLVMInitializeX86AsmPrinter();
38 		
39 		version(OSX) {
40 			auto triple = "x86_64-apple-darwin9".ptr;
41 		} else version (FreeBSD) {
42 			auto triple = "x86_64-unknown-freebsd".ptr;
43 		} else {
44 			auto triple = "x86_64-pc-linux-gnu".ptr;
45 		}
46 		
47 		version(linux) {
48 			enum Reloc = LLVMRelocMode.PIC;
49 		} else {
50 			enum Reloc = LLVMRelocMode.Default;
51 		}
52 		
53 		targetMachine = LLVMCreateTargetMachine(
54 			LLVMGetFirstTarget(),
55 			triple,
56 			"x86-64".ptr,
57 			"".ptr,
58 			LLVMCodeGenOptLevel.Default,
59 			Reloc,
60 			LLVMCodeModel.Default,
61 		);
62 		
63 		auto td = LLVMCreateTargetDataLayout(targetMachine);
64 		scope(exit) LLVMDisposeTargetData(td);
65 		
66 		pass = new CodeGen(sema, name, this, td);
67 		dataLayout = new LLVMDataLayout(pass, pass.targetData);
68 	}
69 	
70 	~this() {
71 		LLVMDisposeTargetMachine(targetMachine);
72 	}
73 	
74 	auto getPass() {
75 		return pass;
76 	}
77 	
78 	auto getEvaluator() {
79 		if (evaluator is null) {
80 			evaluator = new LLVMEvaluator(pass);
81 		}
82 		
83 		return evaluator;
84 	}
85 	
86 	auto getDataLayout() {
87 		return dataLayout;
88 	}
89 	
90 	void visit(Module mod) {
91 		pass.visit(mod);
92 	}
93 	
94 	void visit(Function f) {
95 		import d.llvm.global;
96 		GlobalGen(pass).define(f);
97 	}
98 	
99 	private void runLLVMPasses(Module[] modules) {
100 		foreach(m; modules) {
101 			pass.visit(m);
102 		}
103 		
104 		import llvm.c.transforms.passManagerBuilder;
105 		auto pmb = LLVMPassManagerBuilderCreate();
106 		scope(exit) LLVMPassManagerBuilderDispose(pmb);
107 		
108 		uint optLevel = optLevel;
109 		if (optLevel == 0) {
110 			LLVMPassManagerBuilderUseInlinerWithThreshold(pmb, 0);
111 			LLVMPassManagerBuilderSetOptLevel(pmb, 0);
112 		} else {
113 			LLVMPassManagerBuilderUseInlinerWithThreshold(pmb, 100);
114 			LLVMPassManagerBuilderSetOptLevel(pmb, optLevel);
115 		}
116 		
117 		auto pm = LLVMCreatePassManager();
118 		scope(exit) LLVMDisposePassManager(pm);
119 		
120 		LLVMPassManagerBuilderPopulateModulePassManager(pmb, pm);
121 		LLVMRunPassManager(pm, pass.dmodule);
122 	}
123 	
124 	void emitObject(Module[] modules, string objFile) {
125 		runLLVMPasses(modules);
126 		
127 		import std.string;
128 		char* errorPtr;
129 		auto emitError = LLVMTargetMachineEmitToFile(
130 			targetMachine,
131 			pass.dmodule,
132 			objFile.toStringz(),
133 			LLVMCodeGenFileType.Object,
134 			&errorPtr,
135 		);
136 		
137 		if (emitError) {
138 			scope(exit) LLVMDisposeMessage(errorPtr);
139 			
140 			import core.stdc.string, std.stdio;
141 			writeln(errorPtr[0 .. strlen(errorPtr)]);
142 			
143 			assert(0, "Fail to emit object file ! Exiting...");
144 		}
145 	}
146 	
147 	void emitAsm(Module[] modules, string filename) {
148 		runLLVMPasses(modules);
149 		
150 		import std.string;
151 		char* errorPtr;
152 		auto printError = LLVMTargetMachineEmitToFile(
153 			targetMachine,
154 			pass.dmodule,
155 			filename.toStringz(),
156 			LLVMCodeGenFileType.Assembly,
157 			&errorPtr,
158 		);
159 		
160 		if (printError) {
161 			scope(exit) LLVMDisposeMessage(errorPtr);
162 			
163 			import core.stdc.string, std.stdio;
164 			writeln(errorPtr[0 .. strlen(errorPtr)]);
165 			
166 			assert(0, "Failed to output assembly file! Exiting...");
167 		}
168 	}
169 	
170 	void emitLLVMAsm(Module[] modules, string filename) {
171 		runLLVMPasses(modules);
172 		
173 		import std.string;
174 		char* errorPtr;
175 		auto printError = LLVMPrintModuleToFile(
176 			pass.dmodule,
177 			filename.toStringz(),
178 			&errorPtr,
179 		);
180 		
181 		if (printError) {
182 			scope(exit) LLVMDisposeMessage(errorPtr);
183 			
184 			import core.stdc.string, std.stdio;
185 			writeln(errorPtr[0 .. strlen(errorPtr)]);
186 			
187 			assert(0, "Failed to output LLVM assembly file! Exiting...");
188 		}
189 	}
190 	
191 	void emitLLVMBitcode(Module[] modules, string filename) {
192 		runLLVMPasses(modules);
193 		
194 		import llvm.c.bitWriter;
195 		import std.string;
196 		auto error = LLVMWriteBitcodeToFile(pass.dmodule, filename.toStringz());
197 		if (error) {
198 			assert(0, "Failed to output LLVM bitcode file! Exiting...");
199 		}
200 	}
201 	
202 	void link(string objFile, string executable) {
203 		import std.algorithm, std.array;
204 		auto params = linkerPaths
205 			.map!(path => " -L" ~ (cast(string) path))
206 			.join();
207 		
208 		import std.process;
209 		auto linkCommand = "gcc -o "
210 			~ escapeShellFileName(executable) ~ " "
211 			~ escapeShellFileName(objFile)
212 			~ params ~ " -lsdrt -lphobos -lpthread";
213 		
214 		wait(spawnShell(linkCommand));
215 	}
216 	
217 	auto runUnittests(Module[] modules) {
218 		// In a first step, we do all the codegen.
219 		// We need to do it in a first step so that we can reuse
220 		// one instance of MCJIT.
221 		auto e = getEvaluator();
222 		
223 		struct Test {
224 			Function unit;
225 			LLVMValueRef stub;
226 			
227 			this(LLVMEvaluator e, Function t) {
228 				unit = t;
229 				stub = e.createTestStub(t);
230 			}
231 		}
232 		
233 		Test[] tests;
234 		foreach (m; modules) {
235 			foreach (t; m.tests) {
236 				import source.name;
237 				tests ~= Test(e, t);
238 			}
239 		}
240 		
241 		// Now that we generated the IR, we run the unittests.
242 		import d.llvm.evaluator;
243 		auto ee = createExecutionEngine(pass.dmodule);
244 		scope(exit) destroyExecutionEngine(ee, pass.dmodule);
245 		
246 		struct Result {
247 			import std.bitmanip;
248 			mixin(taggedClassRef!(
249 				Function, "test",
250 				bool, "pass", 1,
251 			));
252 			
253 			this(Function test, bool pass) {
254 				this.test = test;
255 				this.pass = pass;
256 			}
257 		}
258 		
259 		Result[] results;
260 		
261 		foreach (t; tests) {
262 			import llvm.c.executionEngine;
263 			auto result = LLVMRunFunction(ee, t.stub, 0, null);
264 			scope(exit) LLVMDisposeGenericValue(result);
265 			
266 			// Check the return value and report.
267 			// TODO: We need to make a specific report of the failure
268 			// if indeed there is failure.
269 			bool pass = !LLVMGenericValueToInt(result, false);
270 			results ~= Result(t.unit, pass);
271 		}
272 		
273 		return results;
274 	}
275 }