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.driver;
5 
6 import std.stdio : writeln, write, writef, writefln, stdout;
7 import std.path : baseName, stripExtension;
8 
9 import vox.all;
10 
11 /// To compile a set of modules do following steps:
12 /// 1. initialize(passes)
13 /// 2. beginCompilation()
14 /// 3. addHostSymbols(hostSymbols)  --+
15 /// foreach(module; modules)          | In any order
16 /// 4. addModule(module)  ------------+
17 /// 5. compile()
18 /// 6. markCodeAsExecutable() if in JIT mode
19 /// 7. releaseMemory()
20 struct Driver
21 {
22 	CompilationContext context;
23 	CompilePassGlobal[] passes;
24 
25 	ArenaPool arenaPool;
26 
27 	static void funWithAddress(){}
28 	void initialize(CompilePassGlobal[] passes_)
29 	{
30 		passes = passes_;
31 
32 		// IrIndex can address 2^28 * 4 bytes = 1GB
33 		size_t BYTES_TO_RESERVE = GiB*186;
34 		arenaPool.reserve(BYTES_TO_RESERVE);
35 		//writefln("arenaPool %X .. %X", arenaPool.buffer.ptr, arenaPool.buffer.ptr+arenaPool.buffer.length);
36 
37 		/// Those 3 must be allocated in this order (or in inverse order)
38 		/// Code must be able to use RIP-relative addressing into static data (and into import data when in JIT mode)
39 		context.importBuffer.setBuffer(arenaPool.take(GiB), 0);
40 		context.codeBuffer.setBuffer(arenaPool.take(GiB), 0);
41 		context.staticDataBuffer.setBuffer(arenaPool.take(GiB/2), 0);
42 		context.roStaticDataBuffer.setBuffer(arenaPool.take(GiB/2), 0);
43 
44 		context.sourceBuffer.setBuffer(arenaPool.take(GiB), 0);
45 		context.files.setBuffer(arenaPool.take(GiB), 0);
46 		context.tokenBuffer.setBuffer(arenaPool.take(GiB), 0);
47 		context.tokenLocationBuffer.setBuffer(arenaPool.take(GiB), 0);
48 		context.binaryBuffer.setBuffer(arenaPool.take(GiB), 0);
49 		context.bundleBuffer.setBuffer(arenaPool.take(GiB), 0);
50 
51 		context.irStorage.instrHeaderBuffer.setBuffer(arenaPool.take(8*GiB), 0);
52 		context.irStorage.instrPayloadBuffer.setBuffer(arenaPool.take(8*GiB), 0);
53 		context.irStorage.instrNextBuffer.setBuffer(arenaPool.take(8*GiB), 0);
54 		context.irStorage.instrPrevBuffer.setBuffer(arenaPool.take(8*GiB), 0);
55 		context.irStorage.vregBuffer.setBuffer(arenaPool.take(8*GiB), 0);
56 		context.irStorage.phiBuffer.setBuffer(arenaPool.take(8*GiB), 0);
57 		context.irStorage.basicBlockBuffer.setBuffer(arenaPool.take(8*GiB), 0);
58 		context.irStorage.arrayBuffer.setBuffer(arenaPool.take(8*GiB), 0);
59 		context.irStorage.stackSlotBuffer.setBuffer(arenaPool.take(8*GiB), 0);
60 
61 		context.types.buffer.setBuffer(arenaPool.take(GiB), 0);
62 		context.tempBuffer.setBuffer(arenaPool.take(8*GiB), 0);
63 		context.vmBuffer.setBuffer(arenaPool.take(4*GiB), 0);
64 		context.objSymTab.buffer.setBuffer(arenaPool.take(GiB), 0);
65 		context.globals.buffer.setBuffer(arenaPool.take(GiB), 0);
66 		context.globals.initializerBuffer.setBuffer(arenaPool.take(GiB), 0);
67 		context.constants.buffer.setBuffer(arenaPool.take(GiB), 0);
68 		context.constants.aggregateBuffer.setBuffer(arenaPool.take(4*GiB), 0);
69 		context.astBuffer.setBuffer(arenaPool.take(16*GiB), 0);
70 		context.arrayArena.setBuffers(
71 			arenaPool.take(12*4*GiB),
72 			arenaPool.take(16*GiB));
73 
74 		context.idMap.entries.setBuffer(arenaPool.take(2*GiB), 0);
75 		context.idMap.stringDataBuffer.setBuffer(arenaPool.take(2*GiB), 0);
76 
77 		// all arena sizes must sum up to predefined constant
78 		context.assertf(arenaPool.takenBytes == BYTES_TO_RESERVE,
79 			"%s bytes taken, %s bytes to take", arenaPool.takenBytes, BYTES_TO_RESERVE);
80 
81 		context.initialize();
82 	}
83 
84 	void releaseMemory()
85 	{
86 		arenaPool.decommitAll;
87 	}
88 
89 	void beginCompilation()
90 	{
91 		markAsRW(context.codeBuffer.bufPtr, divCeil(context.codeBuffer.length, PAGE_SIZE));
92 		markAsRW(context.roStaticDataBuffer.bufPtr, divCeil(context.roStaticDataBuffer.length, PAGE_SIZE));
93 		context.beginCompilation;
94 		foreach(ref pass; passes) pass.clear;
95 	}
96 
97 	void addModule(SourceFileInfo moduleFile)
98 	{
99 		uint fileIndex = cast(uint)context.files.length;
100 		context.files.put(moduleFile);
101 		SourceFileInfo* file = &context.files.back();
102 
103 		Identifier id = context.idMap.getOrReg(&context, file.name.baseName.stripExtension);
104 		ObjectModule localModule = {
105 			kind : ObjectModuleKind.isLocal,
106 			id : id
107 		};
108 		auto mod = context.appendAst!ModuleDeclNode();
109 		file.mod = context.getAst!ModuleDeclNode(mod);
110 		file.mod.moduleIndex = ModuleIndex(fileIndex);
111 		file.mod.fqn = id;
112 		file.mod.objectSymIndex = context.objSymTab.addModule(localModule);
113 	}
114 
115 	void addHar(string harFilename, const(char)[] harData)
116 	{
117 		void onHarFile(SourceFileInfo fileInfo) {
118 			addModule(fileInfo);
119 		}
120 		parseHar(context, harFilename, harData, &onHarFile);
121 	}
122 
123 	void compile()
124 	{
125 		foreach (ref pass; passes)
126 		{
127 			auto time1 = currTime;
128 
129 			// throws immediately on unrecoverable error or ICE
130 			pass.run(context, pass.subPasses);
131 
132 			auto time2 = currTime;
133 			pass.duration = time2-time1;
134 
135 			// throws if there were recoverable error in the pass
136 			context.throwOnErrors;
137 		}
138 	}
139 
140 	/// Must be called after compilation is finished and before execution
141 	/// Effect is reverted with the call to beginCompilation
142 	/// Marks code pages as read-execute, and readonly data pages as read-only
143 	/// Clears zero-initialized data
144 	/// Only needed in JIT mode, not needed in AOT mode
145 	void markCodeAsExecutable()
146 	{
147 		markAsExecutable(context.codeBuffer.bufPtr, divCeil(context.codeBuffer.length, PAGE_SIZE));
148 		markAsRO(context.roStaticDataBuffer.bufPtr, divCeil(context.roStaticDataBuffer.length, PAGE_SIZE));
149 		// we cannot have a separate section for zeroinitialized data (would require 2 smaller arenas)
150 		// because it needs to occupy the same GiB as initialized data
151 		// to be RIP addressable in JIT mode
152 		context.staticDataBuffer.voidPut(context.zeroDataLength)[] = 0; // zero initialize
153 	}
154 
155 	void addHostSymbols(HostSymbol[] hostSymbols)
156 	{
157 		if (hostSymbols.length > 0)
158 			context.assertf(context.buildType == BuildType.jit, "Can only add host symbols in JIT mode");
159 
160 		LinkIndex hostModuleIndex = context.getOrCreateExternalModule(CommonIds.id_host, ObjectModuleKind.isHost);
161 
162 		foreach (HostSymbol hostSym; hostSymbols)
163 		{
164 			Identifier symId = context.idMap.getOrReg(&context, hostSym.symName);
165 			context.addHostSymbol(hostModuleIndex, symId, hostSym.ptr);
166 		}
167 	}
168 }