1 module d.llvm.global;
2 
3 import d.llvm.codegen;
4 
5 import d.ir.symbol;
6 import d.ir.type;
7 
8 import util.visitor;
9 
10 import llvm.c.analysis;
11 import llvm.c.core;
12 
13 // Conflict with Interface in object.di
14 alias Interface = d.ir.symbol.Interface;
15 
16 struct GlobalGen {
17 	private CodeGen pass;
18 	alias pass this;
19 	
20 	import d.llvm.local : Mode;
21 	Mode mode;
22 	
23 	this(CodeGen pass, Mode mode = Mode.Lazy) {
24 		this.pass = pass;
25 		this.mode = mode;
26 	}
27 	
28 	void define(Symbol s) in {
29 		assert(s.step == Step.Processed);
30 	} do {
31 		if (auto f = cast(Function) s) {
32 			define(f);
33 		} else if (auto t = cast(Template) s) {
34 			define(t);
35 		} else if (auto a = cast(Aggregate) s) {
36 			define(a);
37 		} else if (auto v = cast(Variable) s) {
38 			define(v);
39 		}
40 	}
41 	
42 	LLVMValueRef declare(Function f) in {
43 		assert(!f.hasContext, "function must not have context");
44 	} do {
45 		import d.llvm.local;
46 		return LocalGen(pass).declare(f);
47 	}
48 	
49 	LLVMValueRef define(Function f) in {
50 		assert(!f.hasContext, "function must not have context");
51 	} do {
52 		import d.llvm.local;
53 		return LocalGen(pass).define(f);
54 	}
55 	
56 	LLVMValueRef declare(Variable v) in {
57 		assert(v.storage.isGlobal, "locals not supported");
58 		assert(!v.isFinal);
59 		assert(!v.isRef);
60 	} do {
61 		auto var = globals.get(v, {
62 			if (v.storage == Storage.Enum) {
63 				import d.llvm.constant;
64 				return ConstantGen(pass).visit(v.value);
65 			}
66 			
67 			return createVariableStorage(v);
68 		}());
69 		
70 		// Register the variable.
71 		globals[v] = var;
72 		if (!v.value || v.storage == Storage.Enum) {
73 			return var;
74 		}
75 		
76 		if (v.inTemplate || mode == Mode.Eager) {
77 			if (maybeDefine(v, var)) {
78 				LLVMSetLinkage(var, LLVMLinkage.LinkOnceODR);
79 			}
80 		}
81 		
82 		return var;
83 	}
84 	
85 	LLVMValueRef define(Variable v) in {
86 		assert(v.storage.isGlobal, "locals not supported");
87 		assert(!v.isFinal);
88 		assert(!v.isRef);
89 	} do {
90 		auto var = declare(v);
91 		if (!v.value || v.storage == Storage.Enum) {
92 			return var;
93 		}
94 		
95 		if (!maybeDefine(v, var)) {
96 			auto linkage = LLVMGetLinkage(var);
97 			assert(linkage == LLVMLinkage.LinkOnceODR, "variable " ~ v.mangle.toString(context) ~ " already defined");
98 			LLVMSetLinkage(var, LLVMLinkage.External);
99 		}
100 		
101 		return var;
102 	}
103 	
104 	bool maybeDefine(Variable v, LLVMValueRef var) in {
105 		assert(v.storage.isGlobal, "locals not supported");
106 		assert(v.storage != Storage.Enum, "enum do not have a storage");
107 		assert(!v.isFinal);
108 		assert(!v.isRef);
109 	} do {
110 		if (LLVMGetInitializer(var)) {
111 			return false;
112 		}
113 		
114 		import d.llvm.constant;
115 		auto value = ConstantGen(pass).visit(v.value);
116 		
117 		// Store the initial value into the global variable.
118 		LLVMSetInitializer(var, value);
119 		return true;
120 	}
121 	
122 	private LLVMValueRef createVariableStorage(Variable v) in {
123 		assert(v.storage.isGlobal, "locals not supported");
124 		assert(v.storage != Storage.Enum, "enum do not have a storage");
125 	} do {
126 		auto qualifier = v.type.qualifier;
127 		
128 		import d.llvm.type;
129 		auto type = TypeGen(pass).visit(v.type);
130 		
131 		// If it is not enum, it must be static.
132 		assert(v.storage == Storage.Static);
133 		auto var = LLVMAddGlobal(dmodule, type, v.mangle.toStringz(context));
134 		
135 		// Depending on the type qualifier,
136 		// make it thread local/ constant or nothing.
137 		final switch(qualifier) with(TypeQualifier) {
138 			case Mutable, Inout, Const:
139 				LLVMSetThreadLocal(var, true);
140 				break;
141 			
142 			case Shared, ConstShared:
143 				break;
144 			
145 			case Immutable:
146 				LLVMSetGlobalConstant(var, true);
147 				break;
148 		}
149 		
150 		return var;
151 	}
152 	
153 	LLVMTypeRef define(Aggregate a) in {
154 		assert(a.step == Step.Processed);
155 	} do {
156 		return this.dispatch(a);
157 	}
158 	
159 	LLVMTypeRef visit(Struct s) in {
160 		assert(s.step == Step.Processed);
161 	} do {
162 		import d.llvm.type;
163 		auto ret = TypeGen(pass).visit(s);
164 		
165 		foreach(m; s.members) {
166 			if (typeid(m) !is typeid(Field)) {
167 				define(m);
168 			}
169 		}
170 		
171 		return ret;
172 	}
173 	
174 	LLVMTypeRef visit(Class c) in {
175 		assert(c.step == Step.Processed);
176 	} do {
177 		import d.llvm.type;
178 		auto ret = TypeGen(pass).visit(c);
179 		
180 		foreach(m; c.members) {
181 			if (auto f = cast(Method) m) {
182 				// We don't want to define inherited methods in childs.
183 				if (!f.hasThis || f.type.parameters[0].getType().dclass is c) {
184 					define(f);
185 				}
186 				
187 				continue;
188 			}
189 			
190 			if (typeid(m) !is typeid(Field)) {
191 				define(m);
192 			}
193 		}
194 		
195 		return ret;
196 	}
197 	
198 	LLVMTypeRef visit(Union u) in {
199 		assert(u.step == Step.Processed);
200 	} do {
201 		import d.llvm.type;
202 		auto ret = TypeGen(pass).visit(u);
203 		
204 		foreach(m; u.members) {
205 			if (typeid(m) !is typeid(Field)) {
206 				define(m);
207 			}
208 		}
209 		
210 		return ret;
211 	}
212 	
213 	LLVMTypeRef visit(Interface i) in {
214 		assert(i.step == Step.Processed);
215 	} do {
216 		import d.llvm.type;
217 		return TypeGen(pass).visit(i);
218 	}
219 	
220 	void define(Template t) {
221 		foreach(i; t.instances) {
222 			if (i.hasThis || i.hasContext) {
223 				continue;
224 			}
225 			
226 			foreach(m; i.members) {
227 				import d.llvm.local;
228 				LocalGen(pass).define(m);
229 			}
230 		}
231 	}
232 }