1 /// Copyright: Copyright (c) 2017-2019 Andrey Penechko.
2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
3 /// Authors: Andrey Penechko.
4 module vox.fe.ast.decl.var;
5 
6 import vox.all;
7 
8 enum VariableFlags : ushort {
9 	forceMemoryStorage = AstFlags.userFlag << 0,
10 	isParameter        = AstFlags.userFlag << 1,
11 	isVariadicParam    = AstFlags.userFlag << 2,
12 	isAddressTaken     = AstFlags.userFlag << 3,
13 	isAnonymous        = AstFlags.userFlag << 4,
14 }
15 
16 @(AstType.decl_var)
17 struct VariableDeclNode
18 {
19 	mixin AstNodeData!(AstType.decl_var);
20 	ScopeIndex parentScope;
21 	AstIndex type;
22 	AstIndex initializer; // may be null, stores initializer for variables, default argument for parameters
23 	Identifier id;
24 	ushort scopeIndex; // stores index of parameter or index of member (for struct fields)
25 	// for local vars kind is variable or stackSlot, unique id of variable within a function
26 	// for global it is IrValueKind.global
27 	// nothing is generated for members
28 	ExprValue irValue;
29 	IrIndex defaultVal;
30 	bool forceMemoryStorage() { return cast(bool)(flags & VariableFlags.forceMemoryStorage); }
31 	bool isParameter() { return cast(bool)(flags & VariableFlags.isParameter); }
32 	bool isVariadicParam() { return cast(bool)(flags & VariableFlags.isVariadicParam); }
33 	bool isAddressTaken() { return cast(bool)(flags & VariableFlags.isAddressTaken); }
34 	bool isAnonymous() { return cast(bool)(flags & VariableFlags.isAnonymous); }
35 
36 	IrIndex getIrIndex(CompilationContext* c)
37 	{
38 		c.assertf(isGlobal, "Must be used for globals only");
39 		c.assertf(irValue.irValue.isDefined, "Value is undefined");
40 		return irValue.irValue;
41 	}
42 }
43 
44 void print_var(VariableDeclNode* node, ref AstPrintState state)
45 {
46 	state.print(
47 		node.isParameter ? "PARAM " : "VAR ",
48 		node.type.printer(state.context), " ", state.context.idString(node.id));
49 	if (node.initializer) print_ast(node.initializer, state);
50 }
51 
52 void post_clone_var(VariableDeclNode* node, ref CloneState state)
53 {
54 	state.fixScope(node.parentScope);
55 	state.fixAstIndex(node.type);
56 	state.fixAstIndex(node.initializer);
57 }
58 
59 void name_register_self_var(AstIndex nodeIndex, VariableDeclNode* node, ref NameRegisterState state) {
60 	node.state = AstNodeState.name_register_self;
61 	if (!node.isAnonymous) {
62 		// registered during expansion in function signature
63 		node.parentScope.insert_scope(node.id, nodeIndex, state.context);
64 	}
65 	node.state = AstNodeState.name_register_self_done;
66 }
67 
68 void name_register_nested_var(AstIndex nodeIndex, VariableDeclNode* node, ref NameRegisterState state) {
69 	node.state = AstNodeState.name_register_nested;
70 	if (node.initializer) require_name_register(node.initializer, state);
71 	node.state = AstNodeState.name_register_nested_done;
72 }
73 
74 void name_resolve_var(VariableDeclNode* node, ref NameResolveState state) {
75 	node.state = AstNodeState.name_resolve;
76 	require_name_resolve(node.type, state);
77 	if (node.initializer) require_name_resolve(node.initializer, state);
78 	node.state = AstNodeState.name_resolve_done;
79 }
80 
81 void type_check_var(VariableDeclNode* node, ref TypeCheckState state)
82 {
83 	CompilationContext* c = state.context;
84 
85 	node.state = AstNodeState.type_check;
86 	scope (exit) node.state = AstNodeState.type_check_done;
87 
88 	require_type_check(node.type, state);
89 	check_is_type(node.type, c);
90 
91 	TypeNode* type = node.type.get_type(c);
92 
93 	if (type.isOpaqueStruct(c)) {
94 		if (node.isParameter) {
95 			c.error(node.loc,
96 				"cannot declare parameter of opaque type `%s`",
97 				type.printer(c));
98 		} else {
99 			c.error(node.loc,
100 				"cannot declare variable `%s` of opaque type `%s`",
101 				c.idString(node.id),
102 				type.printer(c));
103 		}
104 	}
105 
106 	if (node.initializer) {
107 		require_type_check(node.initializer, state);
108 		if (type.isAuto)
109 		{
110 			node.type = node.initializer.get_expr_type(c);
111 		}
112 		else
113 		{
114 			bool res = autoconvTo(node.initializer, node.type, c);
115 			if (!res) {
116 				c.error(node.loc,
117 					"cannot convert initializer of type `%s` to `%s`",
118 					node.initializer.get_expr_type(c).printer(c), type.printer(c));
119 			}
120 		}
121 	}
122 
123 	if (!node.isParameter)
124 	{
125 		switch (type.astType) with(AstType)
126 		{
127 			case type_static_array, decl_struct, type_slice:
128 				node.flags |= VariableFlags.forceMemoryStorage;
129 				break;
130 
131 			default: break;
132 		}
133 	}
134 
135 	if (!node.isLocal)
136 		gen_init_value_var(node, c);
137 }
138 
139 // only called for members, parameters and globals
140 IrIndex gen_init_value_var(VariableDeclNode* node, CompilationContext* c)
141 {
142 	if (node.defaultVal.isDefined) return node.defaultVal;
143 	c.assertf(node.isParameter || node.isMember || node.isGlobal, node.loc, "gen_init_value_var");
144 	if (node.initializer)
145 	{
146 		node.defaultVal = eval_static_expr(node.initializer, c);
147 	}
148 	else
149 	{
150 		node.defaultVal = node.type.get_type(c).gen_init_value(c);
151 	}
152 	return node.defaultVal;
153 }
154 
155 void ir_gen_local_var(ref IrGenState gen, IrIndex curBlock, ref IrLabel nextStmt, VariableDeclNode* v)
156 {
157 	CompilationContext* c = gen.context;
158 
159 	if (v.isGlobal)
160 	{
161 		ir_gen_decl_var(c, v);
162 		gen.builder.addJumpToLabel(curBlock, nextStmt);
163 		return;
164 	}
165 
166 	TypeNode* varType = c.getAstType(v.type).foldAliases(c);
167 
168 	IrIndex irType = varType.gen_ir_type(c);
169 
170 	if (c.buildDebug)
171 		v.flags |= VariableFlags.forceMemoryStorage;
172 
173 	bool needsStackSlot = v.forceMemoryStorage || v.isAddressTaken;
174 
175 	if (needsStackSlot)
176 	{
177 		// allocate stack slot
178 		IrIndex slot = gen.builder.appendStackSlot(irType, c.types.typeSizeAndAlignment(irType), StackSlotKind.local);
179 		v.irValue = ExprValue(slot, ExprValueKind.ptr_to_data, IsLvalue.yes);
180 	}
181 	else
182 	{
183 		// allocate new variable
184 		v.irValue = ExprValue(gen.builder.newIrVarIndex(irType), ExprValueKind.value, IsLvalue.yes);
185 	}
186 
187 	if (v.isParameter)
188 	{
189 		ExtraInstrArgs extra = {type : irType};
190 		InstrWithResult param = gen.builder.emitInstr!(IrOpcode.parameter)(gen.ir.entryBasicBlock, extra);
191 		gen.ir.get!IrInstr_parameter(param.instruction).index(gen.ir) = v.scopeIndex;
192 		v.irValue.store(gen, v.loc, curBlock, param.result);
193 	}
194 	else
195 	{
196 		// initialize variable by default or with user-specified value
197 		if (v.initializer)
198 		{
199 			IrLabel afterExpr = IrLabel(curBlock);
200 			ExprValue initValue = ir_gen_expr(gen, v.initializer, curBlock, afterExpr);
201 			curBlock = afterExpr.blockIndex;
202 			IrIndex val = initValue.rvalue(gen, v.loc, curBlock);
203 			v.irValue.store(gen, v.loc, curBlock, val);
204 		}
205 		else
206 		{
207 			IrIndex value = varType.gen_init_value(c);
208 			v.irValue.store(gen, v.loc, curBlock, value);
209 		}
210 	}
211 
212 	gen.builder.addJumpToLabel(curBlock, nextStmt);
213 }
214 
215 void ir_gen_decl_var(CompilationContext* c, VariableDeclNode* node)
216 {
217 	if (node.isGlobal)
218 	{
219 		if (node.irValue.irValue.isDefined) return;
220 
221 		// register global variable, type is not set yet
222 		IrIndex globalIndex = c.globals.add();
223 		IrGlobal* global = c.globals.get(globalIndex);
224 		node.irValue = ExprValue(globalIndex, ExprValueKind.ptr_to_data, IsLvalue.yes);
225 
226 		AstIndex moduleIndex = find_innermost_owner(node.parentScope, AstType.decl_module, c);
227 
228 		ObjectSymbol sym = {
229 			kind : ObjectSymbolKind.isLocal,
230 			sectionIndex : c.builtinSections[ObjectSectionType.rw_data],
231 			moduleIndex : moduleIndex.get!ModuleDeclNode(c).objectSymIndex,
232 			flags : ObjectSymbolFlags.isMutable,
233 			id : node.id,
234 		};
235 
236 		global.objectSymIndex = c.objSymTab.addSymbol(sym);
237 
238 		TypeNode* varType = c.getAstType(node.type).foldAliases(c);
239 		IrIndex irType = varType.gen_ir_type(c);
240 		global.type = c.types.appendPtr(irType);
241 
242 		SizeAndAlignment valueSizealign = c.types.typeSizeAndAlignment(irType);
243 
244 		// symbol is created in parser
245 		ObjectSymbol* globalSym = c.objSymTab.getSymbol(global.objectSymIndex);
246 		globalSym.length = valueSizealign.size;
247 		globalSym.alignmentPower = valueSizealign.alignmentPower;
248 
249 		IrIndex initializer = node.defaultVal;
250 
251 		if (initializer.isConstantZero)
252 		{
253 			globalSym.flags |= ObjectSymbolFlags.isAllZero;
254 		}
255 		else
256 		{
257 			ubyte[] buffer = c.globals.allocateInitializer(valueSizealign.size);
258 			void onGlobal(ubyte[] subbuffer, IrIndex index, CompilationContext* c)
259 			{
260 				c.assertf(index.isGlobal, "%s is not a constant", index);
261 
262 				// initialize with 0
263 				// generate ObjectSymbolReference for linker to fix
264 				subbuffer[] = 0;
265 
266 				assert(subbuffer.ptr >= buffer.ptr);
267 				size_t offset = subbuffer.ptr - buffer.ptr;
268 				assert(offset <= uint.max);
269 
270 				IrGlobal* toGlobal = c.globals.get(index);
271 				assert(toGlobal.objectSymIndex.isDefined);
272 
273 				ObjectSymbolReference r = {
274 					fromSymbol : global.objectSymIndex,
275 					referencedSymbol : toGlobal.objectSymIndex,
276 					refOffset : cast(uint)offset,
277 					extraOffset : 0,
278 					refKind : ObjectSymbolRefKind.absolute64,
279 				};
280 				c.objSymTab.addReference(r);
281 			}
282 			constantToMem(buffer, initializer, c, &onGlobal);
283 			globalSym.setInitializer(buffer);
284 		}
285 
286 		return;
287 	}
288 }