1 /// Copyright: Copyright (c) 2017-2020 Andrey Penechko.
2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
3 /// Authors: Andrey Penechko.
4 module vox.passes;
5 
6 import vox.all;
7 
8 void pass_write_binary(ref CompilationContext context, CompilePassPerModule[] subPasses)
9 {
10 	import std.file : write;
11 	write(context.outputFilename, context.binaryBuffer.data);
12 
13 	version(Posix) {
14 		import std.file : setAttributes, getAttributes;
15 		import std.conv : octal;
16 		setAttributes(context.outputFilename, getAttributes(context.outputFilename) | octal!111);
17 	}
18 }
19 
20 void write_bundle(ref CompilationContext context)
21 {
22 	import std.file : write;
23 	write(context.outputFilename, context.bundleBuffer.data);
24 }
25 
26 immutable CompilePassGlobal[] frontendPasses = [
27 	global_pass("Read source", &pass_read_source),
28 	global_pass("Lex", &pass_lexer),
29 	global_pass("Parse", &pass_parser),
30 	global_pass("Register names", &pass_names_register),
31 	global_pass("Lookup names", &pass_names_resolve),
32 	global_pass("Check types", &pass_type_check),
33 ];
34 
35 immutable CompilePassGlobal[] backendPasses = [
36 	global_pass("IR gen", &pass_ir_gen),
37 
38 	global_pass("Optimize", &pass_optimize_ir),
39 
40 	global_pass(null, &run_ir_to_lir_liveness_and_reg_alloc, [
41 		CompilePassPerFunction("IR lower", null),
42 		CompilePassPerFunction("IR to LIR AMD64", null),
43 		// IR liveness
44 		CompilePassPerFunction("Live intervals", null),
45 		// IR regalloc
46 		CompilePassPerFunction("Linear scan", null),
47 		// Stack layout
48 		CompilePassPerFunction("Stack layout", null),
49 	]),
50 
51 	// LIR -> machine code
52 	global_pass("Code gen", &pass_emit_mc_amd64),
53 ];
54 
55 immutable CompilePassGlobal[] commonPasses = frontendPasses ~ backendPasses;
56 
57 
58 
59 immutable CompilePassGlobal[] extraJitPasses = [
60 	CompilePassGlobal("Link JIT", &pass_link_jit),
61 ];
62 
63 immutable CompilePassGlobal[] extraExePasses = [
64 	CompilePassGlobal("Link executable", &pass_create_executable),
65 	CompilePassGlobal("Write binary", &pass_write_binary),
66 ];
67 
68 CompilePassGlobal[] frontendOnlyPasses = frontendPasses;
69 CompilePassGlobal[] jitPasses = commonPasses ~ extraJitPasses;
70 CompilePassGlobal[] exePasses = commonPasses ~ extraExePasses;
71 CompilePassGlobal[] bundlePasses = commonPasses ~ extraExePasses[0..$-1];
72 
73 void run_global_pass(ref CompilationContext context, CompilePassPerModule[] subPasses)
74 {
75 	//context.printMemSize;
76 	foreach (ref SourceFileInfo file; context.files.data)
77 	{
78 		foreach(ref CompilePassPerModule subPass; subPasses)
79 		{
80 			auto time1 = currTime;
81 
82 			// throws immediately on unrecoverable error or ICE
83 			subPass.run(context, *file.mod, subPass.subPasses);
84 
85 			auto time2 = currTime;
86 			subPass.duration += time2-time1;
87 
88 			// throws if there were recoverable error in the pass
89 			context.throwOnErrors;
90 		}
91 	}
92 }
93 
94 void run_module_pass(ref CompilationContext context, ref ModuleDeclNode mod, CompilePassPerFunction[] subPasses)
95 {
96 	foreach (AstIndex funcIndex; mod.functions)
97 	{
98 		FunctionDeclNode* func = context.getAst!FunctionDeclNode(funcIndex);
99 
100 		foreach(ref CompilePassPerFunction subPass; subPasses)
101 		{
102 			auto time1 = currTime;
103 
104 			// throws immediately on unrecoverable error or ICE
105 			subPass.run(context, mod, *func);
106 
107 			auto time2 = currTime;
108 			subPass.duration += time2-time1;
109 
110 			// throws if there were recoverable error in the pass
111 			context.throwOnErrors;
112 		}
113 	}
114 }
115 
116 void run_ir_to_lir_liveness_and_reg_alloc(ref CompilationContext context, CompilePassPerModule[] subPasses)
117 {
118 	CompilePassPerFunction* ir_lower_pass = &subPasses[0].subPasses[0];
119 	CompilePassPerFunction* ir_to_lir_pass = &subPasses[0].subPasses[1];
120 	CompilePassPerFunction* liveness_pass = &subPasses[0].subPasses[2];
121 	CompilePassPerFunction* ra_pass = &subPasses[0].subPasses[3];
122 	CompilePassPerFunction* stack_layout_pass = &subPasses[0].subPasses[4];
123 
124 	// gets reused for all functions
125 	LivenessInfo liveness;
126 	LinearScan linearScan;
127 	linearScan.context = &context;
128 	linearScan.livePtr = &liveness;
129 	scope(exit) linearScan.freeMem;
130 
131 	scope(exit) context.tempBuffer.clear;
132 	scope(exit) context.currentFunction = null;
133 
134 	foreach (ref SourceFileInfo file; context.files.data)
135 	{
136 		foreach (AstIndex funcIndex; file.mod.functions)
137 		{
138 			FunctionDeclNode* func = context.getAst!FunctionDeclNode(funcIndex);
139 
140 			if (func.isExternal) continue;
141 
142 			context.currentFunction = func;
143 			context.tempBuffer.clear;
144 
145 
146 			{
147 				auto time1 = currTime;
148 				pass_ir_lower(&context, file.mod, func); // throws immediately on unrecoverable error or ICE
149 				auto time2 = currTime;
150 				ir_lower_pass.duration += time2-time1;
151 				context.throwOnErrors; // throws if there were recoverable error in the pass
152 			}
153 
154 			IrBuilder lirBuilder;
155 			{
156 				auto time1 = currTime;
157 				pass_ir_to_lir_amd64(&context, &lirBuilder, file.mod, func); // throws immediately on unrecoverable error or ICE
158 				auto time2 = currTime;
159 				ir_to_lir_pass.duration += time2-time1;
160 				context.throwOnErrors; // throws if there were recoverable error in the pass
161 			}
162 
163 			{
164 				auto time1 = currTime;
165 				pass_live_intervals(&context, file.mod, func, &liveness); // throws immediately on unrecoverable error or ICE
166 				auto time2 = currTime;
167 				liveness_pass.duration += time2-time1;
168 				context.throwOnErrors; // throws if there were recoverable error in the pass
169 			}
170 
171 			{
172 				auto time1 = currTime;
173 
174 				linearScan.builder = &lirBuilder;
175 				linearScan.fun = func;
176 				pass_linear_scan(&linearScan); // throws immediately on unrecoverable error or ICE
177 
178 				auto time2 = currTime;
179 				ra_pass.duration += time2-time1;
180 				context.throwOnErrors; // throws if there were recoverable error in the pass
181 			}
182 
183 			{
184 				auto time1 = currTime;
185 				pass_stack_layout(&context, func); // throws immediately on unrecoverable error or ICE
186 				auto time2 = currTime;
187 				stack_layout_pass.duration += time2-time1;
188 				context.throwOnErrors; // throws if there were recoverable error in the pass
189 			}
190 		}
191 	}
192 }
193 
194 CompilePassGlobal global_pass(string name, GlobalPassFun run, CompilePassPerModule[] subPasses = null)
195 {
196 	return CompilePassGlobal(name, run, subPasses);
197 }
198 
199 CompilePassGlobal global_pass(string name, ModulePassFun run, CompilePassPerFunction[] subPasses = null)
200 {
201 	return CompilePassGlobal(null, &run_global_pass, [CompilePassPerModule(null, run, subPasses)]);
202 }
203 
204 CompilePassGlobal global_pass(string name, GlobalPassFun run, CompilePassPerFunction[] subPasses)
205 {
206 	return CompilePassGlobal(name, run, [CompilePassPerModule(null, &run_module_pass, subPasses)]);
207 }
208 
209 CompilePassGlobal global_pass(string name, FunctionPassFun run)
210 {
211 	return CompilePassGlobal(null, &run_global_pass, [
212 		CompilePassPerModule(null, &run_module_pass, [
213 			CompilePassPerFunction(name, run)])]);
214 }
215 
216 alias GlobalPassFun = void function(ref CompilationContext context, CompilePassPerModule[] subPasses);
217 alias ModulePassFun = void function(ref CompilationContext context, ref ModuleDeclNode mod, CompilePassPerFunction[] subPasses);
218 alias FunctionPassFun = void function(ref CompilationContext context, ref ModuleDeclNode mod, ref FunctionDeclNode func);
219 
220 /// Must have either `run` or subPasses
221 struct CompilePassGlobal
222 {
223 	string name;
224 	GlobalPassFun run;
225 	CompilePassPerModule[] subPasses;
226 	Duration duration;
227 	void clear() {
228 		duration = Duration.init;
229 		foreach(ref subPass; subPasses) subPass.clear;
230 	}
231 }
232 
233 struct CompilePassPerModule
234 {
235 	string name;
236 	void function(ref CompilationContext context, ref ModuleDeclNode mod, CompilePassPerFunction[] subPasses) run;
237 	CompilePassPerFunction[] subPasses;
238 	Duration duration;
239 	void clear() {
240 		duration = Duration.init;
241 		foreach(ref subPass; subPasses) subPass.clear;
242 	}
243 }
244 
245 struct CompilePassPerFunction
246 {
247 	string name;
248 	void function(ref CompilationContext context, ref ModuleDeclNode mod, ref FunctionDeclNode func) run;
249 	Duration duration;
250 	void clear() {
251 		duration = Duration.init;
252 	}
253 }
254 
255 struct PassMetaIterator
256 {
257 	CompilePassGlobal[] passes;
258 	int opApply(scope int delegate(size_t, string name, Duration duration) dg)
259 	{
260 		size_t i = 0;
261 		foreach(ref pass; passes)
262 		{
263 			if (auto res = dg(i, pass.name, pass.duration)) return res;
264 			++i;
265 			foreach(ref subPass; pass.subPasses)
266 			{
267 				if (auto res = dg(i, subPass.name, subPass.duration)) return res;
268 				++i;
269 				foreach(ref subPass2; subPass.subPasses)
270 				{
271 					if (auto res = dg(i, subPass2.name, subPass2.duration)) return res;
272 					++i;
273 				}
274 			}
275 		}
276 		return 0;
277 	}
278 }