1 /**
2 Copyright: Copyright (c) 2017-2020 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module vox.fe.passes.ast_to_ir;
7 
8 import std.stdio;
9 import vox.all;
10 
11 
12 void pass_ir_gen(ref CompilationContext ctx, CompilePassPerModule[] subPasses) {
13 	IrGenState state = {
14 		context : &ctx
15 	};
16 	foreach (ref SourceFileInfo file; ctx.files.data) {
17 		ir_gen_module_globals(state, file.mod);
18 	}
19 	foreach (ref SourceFileInfo file; ctx.files.data) {
20 		ir_gen_module_func(state, file.mod);
21 	}
22 }
23 
24 enum MAX_GEP_INDICES = 255;
25 struct IrGenState
26 {
27 	CompilationContext* context;
28 	alias context this;
29 
30 	IrBuilder builder;
31 	IrFunction* ir;
32 	FunctionDeclNode* fun;
33 
34 	IrIndex[MAX_GEP_INDICES+2] gepBuf = void; // 2 is extra parameters to GEP instruction
35 
36 	IrLabel* currentLoopHeader;
37 	IrLabel* currentLoopEnd;
38 }
39 
40 void ir_gen_decl(ref IrGenState gen, AstIndex nodeIndex)
41 {
42 	CompilationContext* c = gen.context;
43 	AstNode* n = c.getAstNode(nodeIndex);
44 	switch(n.astType) with(AstType)
45 	{
46 		case decl_enum, decl_enum_member, decl_function, decl_struct, decl_import, decl_alias, decl_template, decl_static_assert: break;
47 		case decl_var: ir_gen_decl_var(c, cast(VariableDeclNode*)n); break;
48 		default:
49 			c.internal_error(n.loc, "ir_gen_decl %s in %s state", n.astType, n.state);
50 	}
51 }
52 
53 void ir_gen_stmt(ref IrGenState gen, AstIndex astIndex, IrIndex curBlock, ref IrLabel nextStmt)
54 {
55 	CompilationContext* c = gen.context;
56 	AstNode* n = c.getAstNode(astIndex);
57 	switch(n.astType) with(AstType)
58 	{
59 		case stmt_block:       ir_gen_block   (gen, curBlock, nextStmt, cast(BlockStmtNode*)n); break;
60 		case stmt_if:          ir_gen_if      (gen, curBlock, nextStmt, cast(IfStmtNode*)n); break;
61 		case stmt_while:       ir_gen_while   (gen, curBlock, nextStmt, cast(WhileStmtNode*)n); break;
62 		case stmt_do_while:    ir_gen_do      (gen, curBlock, nextStmt, cast(DoWhileStmtNode*)n); break;
63 		case stmt_for:         ir_gen_for     (gen, curBlock, nextStmt, cast(ForStmtNode*)n); break;
64 		case stmt_switch:      ir_gen_switch  (gen, curBlock, nextStmt, cast(SwitchStmtNode*)n); break;
65 		case stmt_return:      ir_gen_return  (gen, curBlock, nextStmt, cast(ReturnStmtNode*)n); break;
66 		case stmt_break:       ir_gen_break   (gen, curBlock, nextStmt, cast(BreakStmtNode*)n); break;
67 		case stmt_continue:    ir_gen_continue(gen, curBlock, nextStmt, cast(ContinueStmtNode*)n); break;
68 
69 		// expression statement, must have side effect
70 		case expr_call:        ir_gen_call(gen, curBlock, nextStmt, cast(CallExprNode*)n); break;
71 		case expr_bin_op:      ir_gen_expr_binary_op(gen, curBlock, nextStmt, cast(BinaryExprNode*)n); break;
72 		case expr_un_op:       ir_gen_expr_unary_op(gen, curBlock, nextStmt, cast(UnaryExprNode*)n); break;
73 		// should be catched in semantic check, since they have no side effect
74 		case expr_member:
75 		case expr_name_use:
76 		case expr_index:
77 		case expr_slice:
78 		case expr_type_conv:
79 		case literal_int:
80 		case literal_float:
81 		case literal_string:
82 		case literal_null:
83 		case literal_bool:
84 		case decl_template_param:
85 			c.internal_error(n.loc, "stmt %s in %s state", n.astType, n.state);
86 
87 		// declaration statement
88 		case decl_alias:
89 		case decl_enum:
90 		case decl_enum_member:
91 		case decl_function:
92 		case decl_struct:
93 		case decl_template:
94 		case decl_static_assert:
95 		case decl_import:      gen.builder.addJumpToLabel(curBlock, nextStmt); break;
96 		case decl_var:         ir_gen_local_var(gen, curBlock, nextStmt, cast(VariableDeclNode*)n); break;
97 
98 		default: c.internal_error("%s", n.astType);
99 	}
100 }
101 
102 ExprValue ir_gen_expr(ref IrGenState gen, AstIndex astIndex, IrIndex curBlock, ref IrLabel nextStmt)
103 {
104 	CompilationContext* c = gen.context;
105 	AstNode* n = gen.getAstNode(astIndex);
106 	switch(n.astType) with(AstType)
107 	{
108 		case expr_name_use:    return ir_gen_name_use(gen, curBlock, nextStmt, cast(NameUseExprNode*)n);
109 		case expr_member:      return ir_gen_member(gen, curBlock, nextStmt, cast(MemberExprNode*)n);
110 		case expr_call:        return ir_gen_call(gen, curBlock, nextStmt, cast(CallExprNode*)n);
111 		case expr_index:       return ir_gen_index(gen, curBlock, nextStmt, cast(IndexExprNode*)n);
112 		case expr_slice:       return ir_gen_expr_slice(gen, curBlock, nextStmt, cast(SliceExprNode*)n);
113 		case expr_bin_op:      return ir_gen_expr_binary_op(gen, curBlock, nextStmt, cast(BinaryExprNode*)n);
114 		case expr_un_op:       return ir_gen_expr_unary_op(gen, curBlock, nextStmt, cast(UnaryExprNode*)n);
115 		case expr_type_conv:   return ir_gen_expr_type_conv(gen, curBlock, nextStmt, cast(TypeConvExprNode*)n);
116 		case literal_int: {
117 			IrIndex irValue = ir_gen_literal_int(gen.context, cast(IntLiteralExprNode*)n);
118 			gen.builder.addJumpToLabel(curBlock, nextStmt);
119 			return ExprValue(irValue);
120 		}
121 		case literal_float: {
122 			IrIndex irValue = ir_gen_literal_float(gen.context, cast(FloatLiteralExprNode*)n);
123 			gen.builder.addJumpToLabel(curBlock, nextStmt);
124 			return ExprValue(irValue);
125 		}
126 		case literal_string: {
127 			IrIndex irValue = ir_gen_literal_string(gen.context, cast(StringLiteralExprNode*)n);
128 			gen.builder.addJumpToLabel(curBlock, nextStmt);
129 			return ExprValue(irValue);
130 		}
131 		case literal_null: {
132 			IrIndex irValue = ir_gen_literal_null(gen.context, cast(NullLiteralExprNode*)n);
133 			gen.builder.addJumpToLabel(curBlock, nextStmt);
134 			return ExprValue(irValue);
135 		}
136 		case literal_bool: {
137 			IrIndex irValue = ir_gen_literal_bool(gen.context, cast(BoolLiteralExprNode*)n);
138 			gen.builder.addJumpToLabel(curBlock, nextStmt);
139 			return ExprValue(irValue);
140 		}
141 		case literal_special: {
142 			IrIndex irValue = ir_gen_literal_special(gen.context, cast(SpecialLiteralExprNode*)n);
143 			gen.builder.addJumpToLabel(curBlock, nextStmt);
144 			return ExprValue(irValue);
145 		}
146 		case decl_struct, type_basic, type_ptr, type_slice, type_static_array: {
147 			IrIndex irValue = gen.context.constants.add(makeIrType(IrBasicType.i32), astIndex.storageIndex);
148 			gen.builder.addJumpToLabel(curBlock, nextStmt);
149 			return ExprValue(irValue);
150 		}
151 		case decl_enum_member: {
152 			gen.builder.addJumpToLabel(curBlock, nextStmt);
153 			return ExprValue(n.as!EnumMemberDecl(c).gen_init_value_enum_member(c));
154 		}
155 		case decl_var: {
156 			auto v = n.as!VariableDeclNode(c);
157 			if (v.isGlobal)
158 			{
159 				ir_gen_decl_var(c, v);
160 			}
161 			c.assertf(v.irValue.irValue.isDefined, "Value is undefined");
162 			ExprValue result = v.irValue;
163 			gen.builder.addJumpToLabel(curBlock, nextStmt);
164 			return result;
165 		}
166 		case decl_function: {
167 			gen.builder.addJumpToLabel(curBlock, nextStmt);
168 			return ExprValue(n.as!FunctionDeclNode(c).getIrIndex(c));
169 		}
170 		default:
171 			c.internal_error(n.loc, "Expected expression, not %s", n.astType);
172 	}
173 }
174 
175 void ir_gen_branch(ref IrGenState gen, AstIndex astIndex, IrIndex curBlock, ref IrLabel trueExit, ref IrLabel falseExit)
176 {
177 	CompilationContext* c = gen.context;
178 	AstNode* n = gen.getAstNode(astIndex);
179 	switch(n.astType) with(AstType)
180 	{
181 		case literal_int, literal_float, literal_string, expr_index, expr_slice: // TODO: expr_index may return bool
182 			gen.internal_error("Trying to branch directly on %s, must be wrapped in convertion to bool", n.astType);
183 		case expr_bin_op:    ir_gen_branch_binary_op   (gen, curBlock, trueExit, falseExit, cast(BinaryExprNode*)n); break;
184 		case expr_type_conv: ir_gen_branch_type_conv   (gen, curBlock, trueExit, falseExit, cast(TypeConvExprNode*)n); break;
185 		case expr_un_op:     ir_gen_branch_unary_op    (gen, curBlock, trueExit, falseExit, cast(UnaryExprNode*)n); break;
186 		case literal_bool:   ir_gen_branch_literal_bool(gen, curBlock, trueExit, falseExit, cast(BoolLiteralExprNode*)n); break;
187 		case expr_name_use, expr_call, expr_member:
188 			IrLabel afterExpr = IrLabel(curBlock);
189 			ExprValue lval = ir_gen_expr(gen, astIndex, curBlock, afterExpr);
190 			curBlock = afterExpr.blockIndex;
191 			IrIndex rval = lval.rvalue(gen, n.loc, curBlock);
192 			addUnaryBranch(gen, rval, curBlock, trueExit, falseExit);
193 			break;
194 
195 		default: gen.internal_error(n.loc, "Expected expression, not %s", n.astType);
196 	}
197 }
198 
199 void genBlock(ref IrGenState gen, AstNode* parent, ref AstNodes statements, IrIndex currentBlock, ref IrLabel nextStmt)
200 {
201 	foreach (i, AstIndex stmt; statements)
202 	{
203 		// if not the last statement of block
204 		if (i < statements.length - 1)
205 		{
206 			// nested statement will jump here at its end
207 			IrLabel afterStmt = IrLabel(currentBlock);
208 
209 			// compile nested statement
210 			ir_gen_stmt(gen, stmt, currentBlock, afterStmt);
211 
212 			if (afterStmt.numPredecessors == 0)
213 			{
214 				// Nested statement never returns here
215 				// Skip the rest of block statements
216 				return;
217 			}
218 
219 			// If statement returned, get the new current block,
220 			// as it could have splitted the CFG and created a new block
221 			currentBlock = afterStmt.blockIndex;
222 			// Also seal it, since no other block can jump here
223 			gen.builder.sealBlock(currentBlock);
224 		}
225 		else // last statement
226 		{
227 			// let last statement exit straight to outer scope
228 			ir_gen_stmt(gen, stmt, currentBlock, nextStmt);
229 
230 			// if statement hasn't returned here, let outer scope handle this
231 			// the body exit is handled by function decl code
232 		}
233 	}
234 
235 	if (statements.length == 0)
236 		gen.builder.addJumpToLabel(currentBlock, nextStmt);
237 }
238 
239 IrIndex makeBoolValue(ref IrGenState gen, ExpressionNode* n, IrIndex currentBlock, ref IrLabel nextStmt)
240 {
241 	CompilationContext* c = gen.context;
242 	IrBuilder* builder = &gen.builder;
243 
244 	IrLabel trueLabel = IrLabel(currentBlock);
245 	IrLabel falseLabel = IrLabel(currentBlock);
246 	IrLabel nextLabel = IrLabel(currentBlock);
247 	IrIndex nextBlock;
248 	ir_gen_branch(gen, c.getAstNodeIndex(n), currentBlock, trueLabel, falseLabel);
249 
250 	IrIndex value;
251 	IrIndex irType = n.type.gen_ir_type(c);
252 
253 	if (trueLabel.numPredecessors != 0)
254 	{
255 		IrIndex trueBlock = trueLabel.blockIndex;
256 		builder.sealBlock(trueBlock);
257 		builder.addJumpToLabel(trueBlock, nextLabel);
258 
259 		if (falseLabel.numPredecessors != 0) // both blocks exist
260 		{
261 			IrIndex falseBlock = falseLabel.blockIndex;
262 			builder.sealBlock(falseBlock);
263 			builder.addJumpToLabel(falseBlock, nextLabel);
264 
265 			nextBlock = nextLabel.blockIndex;
266 			builder.sealBlock(nextBlock);
267 
268 			IrIndex phiIndex = builder.addPhi(nextBlock, irType, IrIndex.init);
269 			IrIndex trueValue = c.constants.add(irType, 1);
270 			builder.addPhiArg(phiIndex, trueValue);
271 			IrIndex falseValue = c.constants.addZeroConstant(irType);
272 			builder.addPhiArg(phiIndex, falseValue);
273 			value = builder.ir.getPhi(phiIndex).result;
274 		}
275 		else // only true block exists
276 		{
277 			nextBlock = trueBlock;
278 			value = c.constants.add(irType, 1);
279 		}
280 	}
281 	else if (falseLabel.numPredecessors != 0) // only false block exists
282 	{
283 		nextBlock = falseLabel.blockIndex;
284 		builder.sealBlock(nextBlock);
285 
286 		value = c.constants.addZeroConstant(irType);
287 	}
288 
289 	builder.addJumpToLabel(nextBlock, nextStmt);
290 
291 	return value;
292 }
293 
294 void addUnaryBranch(ref IrGenState gen, IrIndex value, IrIndex currentBlock, ref IrLabel trueExit, ref IrLabel falseExit)
295 {
296 	CompilationContext* c = gen.context;
297 	if (value.isSimpleConstant)
298 	{
299 		long conValue = c.constants.get(value).i64;
300 		if (conValue != 0)
301 			gen.builder.addJumpToLabel(currentBlock, trueExit);
302 		else
303 			gen.builder.addJumpToLabel(currentBlock, falseExit);
304 		return;
305 	}
306 
307 	IrArgSize argSize = sizeToIrArgSize(c.types.typeSize(gen.ir.getValueType(c, value)), c);
308 	gen.builder.addUnaryBranch(currentBlock, IrUnaryCondition.not_zero, argSize, value, trueExit, falseExit);
309 }
310 
311 
312 enum ExprValueKind : ubyte {
313 	// irValue is variable (isLvalue=true), constant or vreg (isLvalue=false) being used directly
314 	value,
315 	// it is data in source language, but prt to data in IR
316 	// for example 1st parameter of function that is passed as pointer to struct in RCX in win64 CC
317 	// irValue is pointer value (vreg, global, stack slot)
318 	ptr_to_data,
319 	// it is data in source language, but prt to ptr to data in IR
320 	// for example 5th parameter of function that is passed as pointer to struct on stack in win64 CC
321 	ptr_to_ptr_to_data,
322 	// irValue is variable (isLvalue=true), constant or vreg (isLvalue=false)
323 	// variable can contain aggregate value as well as pointer to aggregate
324 	// numIndices indicates number of gepBuf indices being used
325 	struct_sub_index,
326 }
327 
328 enum IsLvalue : bool {
329 	no = false,
330 	yes = true,
331 }
332 
333 /// Lvalue means that value is stored in global, stack slot or variable.
334 ///
335 struct ExprValue
336 {
337 	// IR value
338 	IrIndex irValue;
339 	// Describes irValue
340 	private ExprValueKind kind = ExprValueKind.value;
341 	// true if can be assigned or address taken
342 	private IsLvalue isLvalue = IsLvalue.no;
343 	//
344 	private Array!IrIndex indices;
345 	// used as offset into aggregate values
346 	//IrIndex offset;
347 
348 	ExprValue addrOf(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock)
349 	{
350 		ExprValue res = this;
351 		res.isLvalue = IsLvalue.no;
352 		return res;
353 	}
354 
355 	ExprValue deref(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock)
356 	{
357 		switch (kind) {
358 			case ExprValueKind.value:
359 				return ExprValue(irValue, ExprValueKind.ptr_to_data, IsLvalue.yes);
360 			case ExprValueKind.ptr_to_data:
361 				return ExprValue(irValue, ExprValueKind.ptr_to_ptr_to_data, IsLvalue.yes);
362 			default:
363 				gen.context.internal_error(loc, "%s", kind);
364 		}
365 	}
366 
367 	/// loads value from pointer. pointer must be an rvalue
368 	IrIndex load(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock)
369 	{
370 		CompilationContext* c = gen.context;
371 
372 		IrIndex source = this.irValue;
373 
374 		switch (source.kind) with(IrValueKind)
375 		{
376 			case variable:
377 				// it's variable holding pointer
378 				source = gen.builder.readVariable(currentBlock, source);
379 				goto case;
380 
381 			case stackSlot, global, virtualRegister:
382 				// those are already a pointer
383 				IrIndex resultType = c.types.getPointerBaseType(gen.ir.getValueType(c, source));
384 				ExtraInstrArgs extra = {type : resultType};
385 				if (resultType.isTypeAggregate)
386 					return gen.builder.emitInstr!(IrOpcode.load_aggregate)(currentBlock, extra, source).result;
387 				else
388 				{
389 					extra.argSize = resultType.getTypeArgSize(c);
390 					return gen.builder.emitInstr!(IrOpcode.load)(currentBlock, extra, source).result;
391 				}
392 
393 			default:
394 				c.internal_error(loc, "Cannot load from %s", source.kind);
395 		}
396 	}
397 
398 	/// returns value as intended by frontend
399 	IrIndex rvalue(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock)
400 	{
401 		CompilationContext* c = gen.context;
402 		ExprValue source = this;
403 		//writefln("getRvalue %s", source);
404 		switch (source.kind) with(ExprValueKind)
405 		{
406 			case value:
407 				switch (source.irValue.kind) with(IrValueKind)
408 				{
409 					case variable: return gen.builder.readVariable(currentBlock, source.irValue);
410 					default: return source.irValue;
411 				}
412 			case ptr_to_data:
413 				if (source.isLvalue) {
414 					return source.load(gen, loc, currentBlock);
415 				} else {
416 					if (source.irValue.isVariable) {
417 						return gen.builder.readVariable(currentBlock, source.irValue);
418 					}
419 					return source.irValue;
420 				}
421 			case ptr_to_ptr_to_data:
422 				IrIndex ptr = source.load(gen, loc, currentBlock);
423 				return ExprValue(ptr).load(gen, loc, currentBlock);
424 			case struct_sub_index:
425 				IrIndex aggr = source.irValue;
426 				if (aggr.isVariable) {
427 					aggr = gen.builder.readVariable(currentBlock, aggr);
428 				}
429 
430 				IrIndex aggrType = gen.ir.getValueType(c, aggr);
431 				switch (aggrType.typeKind) {
432 					case IrTypeKind.pointer:
433 						IrIndex ZERO = c.constants.addZeroConstant(makeIrType(IrBasicType.i32));
434 						IrIndex ptr = buildGEP(gen, loc, currentBlock, aggr, ZERO, source.indices[]);
435 						if (source.isLvalue) {
436 							return ExprValue(ptr).load(gen, loc, currentBlock);
437 						} else {
438 							return ptr;
439 						}
440 					case IrTypeKind.array:
441 					case IrTypeKind.struct_t:
442 						IrIndex memberType = c.types.getAggregateMember(aggrType, c, source.indices[]).type;
443 						ExtraInstrArgs extra = { type : memberType };
444 						IrIndex[] args = gen.gepBuf[0..source.indices.length+1];
445 						args[0] = aggr;
446 						args[1..$] = source.indices[];
447 						return gen.builder.emitInstr!(IrOpcode.get_element)(currentBlock, extra, args).result;
448 					default: c.internal_error("%s", aggrType.typeKind);
449 				}
450 			default:
451 				c.internal_error(loc, "Cannot load from %s", source.kind);
452 		}
453 	}
454 
455 	/// writes value to a pointer or variable
456 	void store(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock, IrIndex value)
457 	{
458 		CompilationContext* c = gen.context;
459 		ExprValue destination = this;
460 		//writefln("store %s %s", destination, value);
461 		switch (destination.kind)
462 		{
463 			case ExprValueKind.ptr_to_ptr_to_data:
464 				destination.irValue = destination.load(gen, loc, currentBlock);
465 				goto case;
466 
467 			case ExprValueKind.ptr_to_data:
468 				switch (destination.irValue.kind) with(IrValueKind)
469 				{
470 					case variable:
471 						IrIndex ptr = gen.builder.readVariable(currentBlock, destination.irValue);
472 						gen.builder.emitInstr!(IrOpcode.store)(currentBlock, ExtraInstrArgs(), ptr, value);
473 						return;
474 					case stackSlot, global, virtualRegister:
475 						ExtraInstrArgs extra;
476 						// destination must be a pointer
477 						gen.builder.emitInstr!(IrOpcode.store)(currentBlock, extra, destination.irValue, value);
478 						return;
479 
480 					default: break;
481 				}
482 				break;
483 
484 			case ExprValueKind.struct_sub_index:
485 				IrIndex aggr = gen.builder.readVariable(currentBlock, destination.irValue);
486 				IrIndex aggrType = gen.ir.getValueType(c, aggr);
487 				//writefln("Store %s", IrIndexDump(aggrType, c, gen.ir));
488 				switch (aggrType.typeKind) {
489 					case IrTypeKind.pointer:
490 						IrIndex ZERO = c.constants.addZeroConstant(makeIrType(IrBasicType.i32));
491 						IrIndex ptr = buildGEP(gen, loc, currentBlock, aggr, ZERO, destination.indices[]);
492 						gen.builder.emitInstr!(IrOpcode.store)(currentBlock, ExtraInstrArgs(), ptr, value);
493 						break;
494 					case IrTypeKind.struct_t:
495 					case IrTypeKind.array:
496 						IrIndex[] args = gen.gepBuf[0..destination.indices.length+2];
497 						args[0] = aggr;
498 						args[1] = value;
499 						args[2..$] = destination.indices[];
500 						ExtraInstrArgs extra = { type : aggrType };
501 						IrIndex res = gen.builder.emitInstr!(IrOpcode.insert_element)(currentBlock, extra, args).result;
502 						gen.builder.writeVariable(currentBlock, destination.irValue, res);
503 						break;
504 					default: c.internal_error("%s", aggrType.typeKind);
505 				}
506 				return;
507 
508 			default:
509 				switch (destination.irValue.kind) with(IrValueKind)
510 				{
511 					case stackSlot, global, virtualRegister:
512 						ExtraInstrArgs extra;
513 						// destination must be a pointer
514 						gen.builder.emitInstr!(IrOpcode.store)(currentBlock, extra, destination.irValue, value);
515 						return;
516 					case variable:
517 						gen.builder.writeVariable(currentBlock, destination.irValue, value);
518 						return;
519 					default:
520 						break;
521 				}
522 		}
523 		c.internal_error(loc, "Cannot store into %s", destination.irValue.kind);
524 	}
525 
526 	/// Returns reference to aggregate member
527 	/// Index must be a constant when accessing struct members
528 	ExprValue member(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock, IrIndex index)
529 	{
530 		CompilationContext* c = gen.context;
531 		ExprValue aggr = this;
532 		//writefln("getAggregateMember %s %s", aggr, index);
533 
534 		if (aggr.irValue.isVariable) {
535 			switch (aggr.kind) with(ExprValueKind)
536 			{
537 				case value:
538 					Array!IrIndex resIndices;
539 					resIndices.put(c.arrayArena, index);
540 					return ExprValue(aggr.irValue, ExprValueKind.struct_sub_index, IsLvalue.yes, resIndices);
541 				case struct_sub_index:
542 					aggr.indices.put(c.arrayArena, index);
543 					return aggr;
544 				default:
545 					aggr.irValue = gen.builder.readVariable(currentBlock, aggr.irValue);
546 					//writefln("  -1 %s", IrIndexDump(aggr.irValue, &gen.builder));
547 					break;
548 			}
549 		}
550 
551 		switch (aggr.kind) with(ExprValueKind)
552 		{
553 			case ptr_to_ptr_to_data:
554 				aggr.irValue = aggr.load(gen, loc, currentBlock);
555 				break;
556 			default: break;
557 		}
558 
559 		IrIndex aggrType = gen.ir.getValueType(c, aggr.irValue);
560 		//writefln("  -2 %s", IrIndexDump(aggrType, &gen.builder));
561 
562 		switch (aggrType.typeKind) {
563 			case IrTypeKind.pointer:
564 				IrIndex ZERO = c.constants.addZeroConstant(makeIrType(IrBasicType.i32));
565 				return ExprValue(buildGEP(gen, loc, currentBlock, aggr.irValue, ZERO, index), ExprValueKind.ptr_to_data, IsLvalue.yes);
566 			case IrTypeKind.struct_t: {
567 				IrIndex aggrVal = aggr.irValue;
568 
569 				switch (aggrVal.kind) with(IrValueKind) {
570 					case constantAggregate, constantZero:
571 						aggrVal = c.constants.getAggregateMember(aggrVal, index, c);
572 						assert(aggrVal.isDefined);
573 						return ExprValue(aggrVal);
574 					case virtualRegister:
575 						IrIndex memberType = c.types.getAggregateMember(aggrType, c, index).type;
576 						ExtraInstrArgs extra = { type : memberType };
577 						IrIndex[] args = gen.gepBuf[0..2];
578 						args[0] = aggrVal;
579 						args[1] = index;
580 						aggrVal = gen.builder.emitInstr!(IrOpcode.get_element)(currentBlock, extra, args).result;
581 						return ExprValue(aggrVal);
582 					default:
583 						c.internal_error("Cannot read struct member from %s", aggrVal.kind);
584 				}
585 			}
586 			default: c.internal_error("%s", aggrType.typeKind);
587 		}
588 	}
589 }
590 
591 IrIndex buildGEPEx(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock, ExprValue aggrPtrExpr, IrIndex ptrIndex, IrIndex[] indices...)
592 {
593 	CompilationContext* c = gen.context;
594 	IrIndex aggrPtr = aggrPtrExpr.irValue;
595 	if (aggrPtr.isVariable) {
596 		aggrPtr = gen.builder.readVariable(currentBlock, aggrPtr);
597 	}
598 	switch (aggrPtrExpr.kind) with(ExprValueKind)
599 	{
600 		case ptr_to_data: break;
601 		case struct_sub_index:
602 			IrIndex aggrType = gen.ir.getValueType(c, aggrPtr);
603 			switch (aggrType.typeKind) {
604 				case IrTypeKind.pointer:
605 					IrIndex ZERO = c.constants.addZeroConstant(makeIrType(IrBasicType.i32));
606 					aggrPtr = buildGEP(gen, loc, currentBlock, aggrPtr, ZERO, aggrPtrExpr.indices[]);
607 					break;
608 
609 				default: c.internal_error("aggrType.typeKind == %s", aggrType.typeKind);
610 			}
611 			break;
612 
613 		default: c.internal_error("aggrPtrExpr.kind == %s", aggrPtrExpr.kind);
614 	}
615 	return buildGEP(gen, loc, currentBlock, aggrPtr, ptrIndex, indices);
616 }
617 
618 IrIndex buildGEP(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock, IrIndex aggrPtr, IrIndex ptrIndex, IrIndex[] indices...)
619 {
620 	CompilationContext* c = gen.context;
621 	c.assertf(indices.length < MAX_GEP_INDICES,
622 		"too much indices for GEP instruction (%s) > %s",
623 		indices.length, MAX_GEP_INDICES);
624 
625 	if (aggrPtr.isVariable) {
626 		aggrPtr = gen.builder.readVariable(currentBlock, aggrPtr);
627 	}
628 
629 	IrIndex aggrPtrType = gen.ir.getValueType(c, aggrPtr);
630 	IrIndex aggrType = c.types.getPointerBaseType(aggrPtrType);
631 
632 	foreach (i, IrIndex memberIndex; indices)
633 	{
634 		gen.gepBuf[i+2] = memberIndex;
635 		final switch(aggrType.typeKind)
636 		{
637 			case IrTypeKind.basic:
638 				c.internal_error(loc, "Cannot index basic type %s", aggrType.typeKind);
639 
640 			case IrTypeKind.pointer:
641 				c.internal_error(loc, "Cannot index pointer with GEP instruction, use load first");
642 
643 			case IrTypeKind.array:
644 				aggrType = c.types.getArrayElementType(aggrType);
645 				break;
646 
647 			case IrTypeKind.struct_t:
648 				c.assertf(memberIndex.isSimpleConstant, loc,
649 					"Structs can only be indexed with constants, not with %s", memberIndex);
650 				aggrType = c.types.getAggregateMember(aggrType, c, memberIndex).type;
651 				break;
652 
653 			case IrTypeKind.func_t:
654 				c.internal_error(loc, "Cannot index function type");
655 		}
656 	}
657 
658 	if (indices.length == 0 && ptrIndex.isConstantZero)
659 		return aggrPtr; // skip no op GEP
660 
661 	ExtraInstrArgs extra = { type : c.types.appendPtr(aggrType) };
662 	IrIndex[] args = gen.gepBuf[0..indices.length+2];
663 	args[0] = aggrPtr;
664 	args[1] = ptrIndex;
665 	IrIndex result = gen.builder.emitInstr!(IrOpcode.get_element_ptr)(currentBlock, extra, args).result;
666 	return result;
667 }