1 /**
2 Copyright: Copyright (c) 2017-2019 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module vox.be.ir_to_lir_amd64;
7 
8 import std.stdio;
9 import vox.all;
10 
11 
12 void pass_ir_to_lir_amd64(CompilationContext* context, IrBuilder* builder, ModuleDeclNode* mod, FunctionDeclNode* func)
13 {
14 	assert(!func.isExternal);
15 
16 	func.backendData.lirData = context.appendAst!IrFunction;
17 	IrFunction* lirData = context.getAst!IrFunction(func.backendData.lirData);
18 
19 	mod.lirModule.addFunction(context, lirData);
20 	IrFunction* irData = context.getAst!IrFunction(func.backendData.loweredIrData);
21 
22 	lirData.name = irData.name;
23 
24 	builder.beginLir(lirData, irData, context);
25 	processFunc(context, builder, mod, irData, lirData);
26 	builder.finalizeIr;
27 
28 	if (context.validateIr) validateIrFunction(context, lirData, "IR -> LIR");
29 	if (context.printLir && context.printDumpOf(func)) dumpFunction(context, lirData, "IR -> LIR");
30 }
31 
32 void processFunc(CompilationContext* context, IrBuilder* builder, ModuleDeclNode* mod, IrFunction* ir, IrFunction* lir)
33 {
34 	string funName = context.idString(ir.name);
35 	//writefln("IR to LIR %s", funName);
36 	lir.instructionSet = IrInstructionSet.lir_amd64;
37 
38 	// Mirror of original IR, we will put the new IrIndex of copied entities there
39 	// and later use this info to rewire all connections between basic blocks
40 	IrMirror!IrIndex mirror;
41 	mirror.createVirtRegMirror(context, ir);
42 
43 	enum MAX_ARGS = 16; // only used for physical registers in calls
44 	IrIndex[MAX_ARGS] argBuffer = void;
45 
46 	lir.type = ir.type;
47 
48 	// copy stack slots
49 	lir.stackSlotPtr = ir.stackSlotPtr;
50 	lir.numStackSlots = ir.numStackSlots;
51 	dupSingleIrStorage(context.irStorage.stackSlotBuffer, lir.stackSlotPtr, lir.numStackSlots);
52 
53 	// save map from old index to new index
54 	void recordIndex(IrIndex oldIndex, IrIndex newIndex)
55 	{
56 		//writefln("rec %s -> %s", oldIndex, newIndex);
57 		assert(oldIndex.isDefined);
58 		assert(newIndex.isDefined);
59 		assert(oldIndex.isVirtReg);
60 		mirror[oldIndex] = newIndex;
61 	}
62 
63 	IrIndex getFixedIndex(IrIndex index)
64 	{
65 		assert(index.isDefined);
66 		assert(!(index.isBasicBlock || index.isPhi || index.isInstruction), format("%s", index));
67 		if (index.isVirtReg) {
68 			return mirror[index];
69 		}
70 		return index;
71 	}
72 
73 	void fixIndex(ref IrIndex index)
74 	{
75 		assert(index.isDefined);
76 		assert(!(index.isBasicBlock || index.isPhi || index.isInstruction));
77 		if (index.isVirtReg) {
78 			//writefln("%s -> %s", index, mirror[index]);
79 			assert(mirror[index].isDefined);
80 			index = mirror[index];
81 		}
82 	}
83 
84 	// Fixes virtual registers
85 	// Legalizes complex arguments into a separate instruction
86 	IrIndex getFixedLegalIndex(IrIndex index, IrIndex lirBlockIndex)
87 	{
88 		final switch (index.kind) with(IrValueKind) {
89 			case none, array, instruction, basicBlock, phi, type, variable: context.internal_error("getFixedLegalIndex %s", index.kind);
90 			case physicalRegister:
91 			case constantAggregate:
92 			case constantZero:
93 				return index;
94 			case constant:
95 				final switch(index.constantKind) with(IrConstantKind) {
96 					case smallZx, smallSx: return index;
97 					case big:
98 						auto con = context.constants.get(index);
99 						if (con.type.isTypeFloat) break;
100 						if (con.type.isTypeInteger && !con.intFitsIn32Bits) break;
101 						return index;
102 				}
103 				goto case; // big constant
104 			case global:
105 			case stackSlot:
106 			case func:
107 				// copy to temp register
108 				IrIndex type = ir.getValueType(context, index);
109 				ExtraInstrArgs extra = { type : type };
110 				return builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra, index).result;
111 
112 			case virtualRegister:
113 				return mirror[index];
114 		}
115 	}
116 
117 	void makeMov(IrIndex to, IrIndex from, IrArgSize argSize, IrIndex block) {
118 		ExtraInstrArgs extra = { addUsers : false, result : to, argSize : argSize };
119 		builder.emitInstr!(Amd64Opcode.mov)(block, extra, from);
120 	}
121 
122 	IrIndex genAddressOffset(IrIndex lirPtr, uint offset, IrIndex ptrType, IrIndex lirBlockIndex) {
123 		IrIndex ptr;
124 		if (offset == 0) {
125 			ExtraInstrArgs extra = { addUsers : false, type : ptrType };
126 			InstrWithResult movInstr = builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra, lirPtr);
127 			ptr = movInstr.result;
128 		} else {
129 			IrIndex offsetIndex = context.constants.add(makeIrType(IrBasicType.i32), offset);
130 			ExtraInstrArgs extra = { addUsers : false, type : ptrType };
131 			InstrWithResult addressInstr = builder.emitInstr!(Amd64Opcode.add)(lirBlockIndex, extra, lirPtr, offsetIndex);
132 			ptr = addressInstr.result;
133 		}
134 		return ptr;
135 	}
136 
137 	IrIndex genLoad(IrIndex lirPtr, uint offset, IrIndex ptrType, IrIndex lirBlockIndex) {
138 		IrIndex ptr = genAddressOffset(lirPtr, offset, ptrType, lirBlockIndex);
139 		IrIndex valType = context.types.getPointerBaseType(ptrType);
140 		IrArgSize argSize = typeToIrArgSize(valType, context);
141 		ExtraInstrArgs extra = { addUsers : false, type : valType, argSize : argSize };
142 		InstrWithResult instr = builder.emitInstr!(Amd64Opcode.load)(lirBlockIndex, extra, ptr);
143 		return instr.result;
144 	}
145 
146 	void genMemZero(IrIndex lirPtr, ulong numBytes, IrIndex lirBlockIndex) {
147 		// generate rep stos rax, rcx, rdi
148 		IrIndex dataReg = IrIndex(amd64_reg.ax, IrArgSize.size64);
149 		IrIndex sizeReg = IrIndex(amd64_reg.cx, IrArgSize.size64);
150 		IrIndex ptrReg = IrIndex(amd64_reg.di, IrArgSize.size64);
151 		// data
152 		makeMov(dataReg, context.constants.addZeroConstant(makeIrType(IrBasicType.i64)), IrArgSize.size64, lirBlockIndex);
153 		// size
154 		makeMov(sizeReg, context.constants.add(makeIrType(IrBasicType.i64), numBytes), IrArgSize.size64, lirBlockIndex);
155 		// ptr
156 		makeMov(ptrReg, lirPtr, IrArgSize.size64, lirBlockIndex);
157 
158 		ExtraInstrArgs extra = { addUsers : false };
159 		builder.emitInstr!(Amd64Opcode.rep_stos)(lirBlockIndex, extra, dataReg, sizeReg, ptrReg);
160 	}
161 
162 	// fromOffset is used when irValue is pointer that needs deferencing
163 	void genStore(IrIndex lirPtr, uint offset, IrIndex irValue, uint fromOffset, IrIndex valueType, IrIndex lirBlockIndex, IrFunction* ir)
164 	{
165 		//writefln("genStore %s %s %s %s %s", lirPtr, offset, irValue, fromOffset, IrIndexDump(valueType, context, ir));
166 		IrIndex dstType = getValueType(lirPtr, lir, context);
167 		IrIndex srcType = getValueType(irValue, ir, context);
168 		//writefln("  %s %s <- %s %s", IrTypeDump(dstType, *context), dstType, IrTypeDump(srcType, *context), srcType);
169 		context.assertf(dstType.isTypePointer, "%s", IrIndexDump(dstType, context, lir));
170 		IrIndex ptrType = context.types.appendPtr(valueType);
171 
172 		switch(valueType.typeKind) with(IrTypeKind) {
173 			case basic, pointer:
174 				IrArgSize argSize = typeToIrArgSize(valueType, context);
175 
176 				// check if irValue is l-value
177 				if (context.types.isSameType(dstType, srcType)) {
178 					// load from irValue ptr. loadedVal is already fixed
179 					IrIndex loadedVal = genLoad(getFixedIndex(irValue), fromOffset, ptrType, lirBlockIndex);
180 
181 					IrIndex ptr = genAddressOffset(lirPtr, offset, ptrType, lirBlockIndex);
182 					ExtraInstrArgs extra = { addUsers : false, argSize : argSize };
183 					builder.emitInstr!(Amd64Opcode.store)(lirBlockIndex, extra, ptr, loadedVal);
184 					break;
185 				}
186 
187 				IrIndex rvalue = getFixedLegalIndex(irValue, lirBlockIndex);
188 				ExtraInstrArgs extra = { addUsers : false, argSize : argSize };
189 				IrIndex ptr = genAddressOffset(lirPtr, offset, ptrType, lirBlockIndex);
190 				builder.emitInstr!(Amd64Opcode.store)(lirBlockIndex, extra, ptr, rvalue);
191 				break;
192 
193 			case struct_t:
194 				IrTypeStruct* structType = &context.types.get!IrTypeStruct(valueType);
195 				IrIndex[] members;
196 
197 				switch(irValue.kind) with(IrValueKind)
198 				{
199 					case virtualRegister:
200 						IrIndex origin = ir.getVirtReg(irValue).definition;
201 						IrInstrHeader* instr = ir.getInstr(origin);
202 
203 						switch (instr.op)
204 						{
205 							case IrOpcode.load_aggregate, IrOpcode.load:
206 								foreach (i, IrTypeStructMember member; structType.members)
207 								{
208 									IrIndex ptr = genAddressOffset(lirPtr, offset + member.offset, ptrType, lirBlockIndex);
209 									genStore(ptr, 0, instr.arg(ir, 0), fromOffset + member.offset, member.type, lirBlockIndex, ir);
210 								}
211 								return;
212 
213 							case IrOpcode.create_aggregate:
214 								members = instr.args(ir);
215 								break;
216 
217 							case IrOpcode.call:
218 								// register fixup for return stack slot
219 								IrIndex resultSlot = getFixedIndex(irValue);
220 								//context.internal_error("call . todo %s", resultSlot);
221 								foreach (i, IrTypeStructMember member; structType.members)
222 								{
223 									IrIndex ptr = genAddressOffset(lirPtr, offset + member.offset, ptrType, lirBlockIndex);
224 									genStore(ptr, 0, resultSlot, fromOffset + member.offset, member.type, lirBlockIndex, ir);
225 								}
226 								return;
227 
228 							case IrOpcode.move, IrOpcode.or:
229 								// value fits into register, store as is
230 								IrIndex rvalue = getFixedIndex(irValue);
231 								IrArgSize argSize = typeToIrArgSize(valueType, context);
232 								ExtraInstrArgs extra = { addUsers : false, argSize : argSize };
233 								IrIndex ptr = genAddressOffset(lirPtr, offset, ptrType, lirBlockIndex);
234 								builder.emitInstr!(Amd64Opcode.store)(lirBlockIndex, extra, ptr, rvalue);
235 								return;
236 
237 							default:
238 								context.internal_error("%s", cast(IrOpcode)instr.op);
239 						}
240 						break;
241 
242 					case constantZero:
243 						IrIndex ptr = genAddressOffset(lirPtr, offset, context.i8PtrType, lirBlockIndex);
244 						genMemZero(ptr, structType.sizealign.size, lirBlockIndex);
245 						return;
246 
247 					case constantAggregate:
248 						members = context.constants.getAggregate(irValue).members;
249 						break;
250 
251 					default: context.internal_error("%s", irValue.kind);
252 				}
253 
254 				context.assertf(members.length == structType.numMembers, "%s != %s", members.length, structType.numMembers);
255 
256 				foreach (i, IrTypeStructMember member; structType.members)
257 				{
258 					genStore(lirPtr, offset + member.offset, members[i], fromOffset, member.type, lirBlockIndex, ir);
259 				}
260 				break;
261 
262 			case array:
263 				IrTypeArray* arrayType = &context.types.get!IrTypeArray(valueType);
264 				uint elemSize = context.types.typeSize(arrayType.elemType);
265 				IrIndex[] members;
266 
267 				switch(irValue.kind) with(IrValueKind)
268 				{
269 					case virtualRegister:
270 						IrIndex origin = ir.getVirtReg(irValue).definition;
271 						IrInstrHeader* instr = ir.getInstr(origin);
272 
273 						switch (instr.op)
274 						{
275 							case IrOpcode.load_aggregate, IrOpcode.load:
276 								foreach (i; 0..arrayType.numElements)
277 								{
278 									IrIndex ptr = genAddressOffset(lirPtr, offset + elemSize * i, ptrType, lirBlockIndex);
279 									genStore(ptr, 0, instr.arg(ir, 0), fromOffset + elemSize * i, arrayType.elemType, lirBlockIndex, ir);
280 								}
281 								return;
282 
283 							case IrOpcode.create_aggregate:
284 								members = instr.args(ir);
285 								break;
286 
287 							case IrOpcode.call:
288 								// register fixup for return stack slot
289 								IrIndex resultSlot = getFixedIndex(irValue);
290 								//context.internal_error("call . todo %s", resultSlot);
291 								foreach (i; 0..arrayType.numElements)
292 								{
293 									IrIndex ptr = genAddressOffset(lirPtr, offset + elemSize * i, ptrType, lirBlockIndex);
294 									genStore(ptr, 0, resultSlot, fromOffset + elemSize * i, arrayType.elemType, lirBlockIndex, ir);
295 								}
296 								return;
297 
298 							default:
299 								context.internal_error("%s", cast(IrOpcode)instr.op);
300 						}
301 						break;
302 
303 					case constantZero:
304 						IrIndex ptr = genAddressOffset(lirPtr, offset, context.i8PtrType, lirBlockIndex);
305 						genMemZero(ptr, elemSize * arrayType.numElements, lirBlockIndex);
306 						return;
307 
308 					case constantAggregate:
309 						members = context.constants.getAggregate(irValue).members;
310 						break;
311 
312 					default: context.internal_error("%s", irValue.kind);
313 				}
314 
315 				context.assertf(members.length == arrayType.numElements, "%s != %s", members.length, arrayType.numElements);
316 
317 				foreach (i; 0..arrayType.numElements)
318 				{
319 					genStore(lirPtr, offset + elemSize * i, members[i], fromOffset, arrayType.elemType, lirBlockIndex, ir);
320 				}
321 				break;
322 
323 			default:
324 				context.internal_error("%s", valueType.typeKind);
325 		}
326 	}
327 
328 	// copy single integer
329 	void genCopyInt(IrIndex dst, IrIndex src, IrIndex valType, IrArgSize argSize, IrIndex lirBlockIndex)
330 	{
331 		ExtraInstrArgs loadExtra = { addUsers : false, type : valType, argSize : argSize };
332 		InstrWithResult loadInstr = builder.emitInstr!(Amd64Opcode.load)(lirBlockIndex, loadExtra, src);
333 		ExtraInstrArgs storeExtra = { addUsers : false, argSize : argSize };
334 		IrIndex storeInstr = builder.emitInstr!(Amd64Opcode.store)(lirBlockIndex, storeExtra, dst, loadInstr.result);
335 	}
336 
337 	// copy single item of any type
338 	void genCopy(IrIndex dst, IrIndex src, IrIndex lirBlockIndex)
339 	{
340 		//writefln("genCopy %s %s", dst, src);
341 		IrIndex ptrType = getValueType(dst, lir, context);
342 		//writefln("ptrType %s %s", ptrType, getValueType(src, lir, context));
343 		IrIndex valType = context.types.getPointerBaseType(ptrType);
344 		uint typeSize = context.types.typeSize(valType);
345 
346 		uint offset = 0;
347 		IrIndex elemType = makeIrType(IrBasicType.i64); // i64, i32, i16, i8
348 		IrArgSize elemArgSize = IrArgSize.size64; // size64, size32, size16, size8
349 		uint elemSize = 8; // 8, 4, 2, 1
350 		while (elemSize > 0)
351 		{
352 			IrIndex elemPtrType = context.types.appendPtr(elemType);
353 			while (typeSize >= elemSize)
354 			{
355 				IrIndex ptrFrom = genAddressOffset(src, offset, elemPtrType, lirBlockIndex);
356 				IrIndex ptrTo = genAddressOffset(dst, offset, elemPtrType, lirBlockIndex);
357 				//writefln("from %s to %s", ptrFrom, ptrTo);
358 				genCopyInt(ptrTo, ptrFrom, elemType, elemArgSize, lirBlockIndex);
359 
360 				offset += elemSize;
361 				typeSize -= elemSize;
362 			}
363 			elemSize /= 2;
364 			elemType.typeIndex = elemType.typeIndex - 1;
365 			--elemArgSize;
366 		}
367 	}
368 
369 	// dup basic blocks
370 	// old and new blocks have the same indices
371 	foreach (size_t i, ref IrBasicBlock irBlock; ir.blocksArray)
372 	{
373 		IrIndex blockIndex = IrIndex(cast(uint)i, IrValueKind.basicBlock);
374 		IrIndex lirBlock = builder.appendBasicBlockSlot;
375 
376 		lir.getBlock(lirBlock).isLoopHeader = irBlock.isLoopHeader;
377 
378 		foreach(IrIndex pred; irBlock.predecessors.range(ir)) {
379 			lir.getBlock(lirBlock).predecessors.append(builder, pred);
380 		}
381 		foreach(IrIndex succ; irBlock.successors.range(ir)) {
382 			lir.getBlock(lirBlock).successors.append(builder, succ);
383 		}
384 
385 		lir.getBlock(lirBlock).prevBlock = irBlock.prevBlock;
386 		lir.getBlock(lirBlock).nextBlock = irBlock.nextBlock;
387 
388 		// Add phis with old args
389 		foreach(IrIndex phiIndex, ref IrPhi phi; irBlock.phis(ir))
390 		{
391 			IrVirtualRegister* oldReg = ir.getVirtReg(phi.result);
392 			IrIndex newPhi = builder.addPhi(lirBlock, oldReg.type, phi.var);
393 			IrIndex newResult = lir.getPhi(newPhi).result;
394 			IrVirtualRegister* newReg = lir.getVirtReg(newResult);
395 			newReg.type = oldReg.type;
396 
397 			recordIndex(phi.result, lir.getPhi(newPhi).result);
398 			foreach(size_t arg_i, ref IrIndex phiArg; phi.args(ir))
399 			{
400 				builder.addPhiArg(newPhi, phiArg);
401 			}
402 		}
403 	}
404 
405 	//writefln("%s", IrIndexDump(lir.type, context, lir));
406 
407 	// fix successors predecessors links
408 	foreach (IrIndex lirBlockIndex, ref IrBasicBlock lirBlock; lir.blocks)
409 	{
410 		lirBlock.isSealed = true;
411 		lirBlock.isFinished = true;
412 
413 		// old and new blocks have the same indices
414 		IrIndex irBlockIndex = lirBlockIndex;
415 
416 		// Add instructions with old args
417 		foreach(IrIndex instrIndex, ref IrInstrHeader instrHeader; ir.getBlock(irBlockIndex).instructions(ir))
418 		{
419 			void emitLirInstr(alias I)()
420 			{
421 				static assert(!getInstrInfo!I.hasVariadicArgs);
422 				static assert(!getInstrInfo!I.hasVariadicResult);
423 
424 				IrIndex[getInstrInfo!I.numArgs] fixedArgs = instrHeader.args(ir);
425 				foreach(ref arg; fixedArgs) arg = getFixedLegalIndex(arg, lirBlockIndex);
426 
427 				static if (getInstrInfo!I.hasResult)
428 				{
429 					ExtraInstrArgs extra = { addUsers : false, argSize : instrHeader.argSize };
430 					IrIndex result = instrHeader.result(ir);
431 					if (result.isVirtReg)
432 						extra.type = ir.getVirtReg(result).type;
433 					InstrWithResult res = builder.emitInstr!I(lirBlockIndex, extra, fixedArgs);
434 					recordIndex(result, res.result);
435 				}
436 				else
437 				{
438 					ExtraInstrArgs extra = { addUsers : false, argSize : instrHeader.argSize };
439 					IrIndex res = builder.emitInstr!I(lirBlockIndex, extra, fixedArgs);
440 				}
441 			}
442 
443 			// for movsx/movzx
444 			// arg is not fixed
445 			IrIndex emit_nonfinal(alias I)(IrIndex resType, IrIndex arg) {
446 				arg = getFixedLegalIndex(arg, lirBlockIndex);
447 				ExtraInstrArgs extra = { addUsers : false, type : resType };
448 				return builder.emitInstr!I(lirBlockIndex, extra, arg).result;
449 			}
450 
451 			IrIndex legalizeFloatArg(IrIndex arg, IrIndex type) {
452 				switch (arg.kind) {
453 					case IrValueKind.constant:
454 						IrIndex globalIndex = context.globals.add();
455 						IrGlobal* global = context.globals.get(globalIndex);
456 						global.type = context.types.appendPtr(type);
457 
458 						ObjectSymbol sym = {
459 							kind : ObjectSymbolKind.isLocal,
460 							sectionIndex : context.builtinSections[ObjectSectionType.ro_data],
461 							moduleIndex : mod.objectSymIndex,
462 							flags : ObjectSymbolFlags.isFloat,
463 							id : context.idMap.getOrReg(context, ":float"),
464 						};
465 						global.objectSymIndex = context.objSymTab.addSymbol(sym);
466 
467 						ObjectSymbol* globalSym = context.objSymTab.getSymbol(global.objectSymIndex);
468 
469 						SizeAndAlignment valueSizealign = context.types.typeSizeAndAlignment(type);
470 						ubyte[] buffer = context.globals.allocateInitializer(valueSizealign.size);
471 						constantToMem(buffer, arg, context);
472 						//writefln("%s %s", arg, buffer);
473 						globalSym.setInitializer(buffer);
474 
475 						ExtraInstrArgs extra = { addUsers : false, type : type, argSize : instrHeader.argSize };
476 						return builder.emitInstr!(Amd64Opcode.load)(lirBlockIndex, extra, globalIndex).result;
477 
478 					case IrValueKind.constantZero:
479 						ExtraInstrArgs extra = { addUsers : false, type : type, argSize : instrHeader.argSize };
480 						return builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra, arg).result;
481 
482 					default: return getFixedLegalIndex(arg, lirBlockIndex);
483 				}
484 			}
485 
486 			// args are fixed
487 			void emit_final(alias I)(IrIndex resType, IrIndex[] args...) {
488 				ExtraInstrArgs extra = { addUsers : false, type : resType, argSize : instrHeader.argSize };
489 				InstrWithResult res = builder.emitInstr!I(lirBlockIndex, extra, args);
490 				IrIndex result = instrHeader.result(ir);
491 				recordIndex(result, res.result);
492 			}
493 
494 			switch_instr:
495 			switch(instrHeader.op)
496 			{
497 				case IrOpcode.call:
498 					lir.numCalls += 1;
499 					foreach(i; 0..instrHeader.numArgs) {
500 						argBuffer[i] = getFixedIndex(instrHeader.arg(ir, i));
501 					}
502 					IrIndex returnReg;
503 					if (instrHeader.hasResult) returnReg = instrHeader.result(ir);
504 					ExtraInstrArgs callExtra = {
505 						addUsers : false,
506 						hasResult : instrHeader.hasResult,
507 						result : returnReg // will be used if function has result
508 					};
509 					InstrWithResult callInstr = builder.emitInstr!(Amd64Opcode.call)(
510 						lirBlockIndex, callExtra, argBuffer[0..instrHeader.numArgs]);
511 					lir.getInstr(callInstr.instruction).extendFixedArgRange = instrHeader.extendFixedArgRange;
512 					lir.getInstr(callInstr.instruction).extendFixedResultRange = instrHeader.extendFixedResultRange;
513 					break;
514 
515 				case IrOpcode.syscall:
516 					lir.numCalls += 1;
517 					foreach(i; 0..instrHeader.numArgs) {
518 						argBuffer[i] = getFixedIndex(instrHeader.arg(ir, i));
519 					}
520 					IrIndex returnReg;
521 					if (instrHeader.hasResult) returnReg = instrHeader.result(ir);
522 					ExtraInstrArgs callExtra = {
523 						addUsers : false,
524 						hasResult : instrHeader.hasResult,
525 						result : returnReg // will be used if function has result
526 					};
527 					InstrWithResult callInstr = builder.emitInstr!(Amd64Opcode.syscall)(
528 						lirBlockIndex, callExtra, argBuffer[0..instrHeader.numArgs]);
529 					lir.getInstr(callInstr.instruction).extendFixedArgRange = instrHeader.extendFixedArgRange;
530 					lir.getInstr(callInstr.instruction).extendFixedResultRange = instrHeader.extendFixedResultRange;
531 					break;
532 
533 				case IrOpcode.add: emitLirInstr!(Amd64Opcode.add); break;
534 				case IrOpcode.sub: emitLirInstr!(Amd64Opcode.sub); break;
535 				case IrOpcode.umul, IrOpcode.smul: emitLirInstr!(Amd64Opcode.imul); break;
536 				case IrOpcode.not: emitLirInstr!(Amd64Opcode.not); break;
537 				case IrOpcode.neg: emitLirInstr!(Amd64Opcode.neg); break;
538 				case IrOpcode.fneg: emitLirInstr!(Amd64Opcode.fneg); break;
539 				case IrOpcode.and: emitLirInstr!(Amd64Opcode.and); break;
540 				case IrOpcode.or: emitLirInstr!(Amd64Opcode.or); break;
541 				case IrOpcode.xor: emitLirInstr!(Amd64Opcode.xor); break;
542 
543 				case IrOpcode.fadd:
544 					IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type;
545 					emit_final!(Amd64Opcode.fadd)(type, legalizeFloatArg(instrHeader.arg(ir, 0), type), legalizeFloatArg(instrHeader.arg(ir, 1), type));
546 					break;
547 				case IrOpcode.fsub:
548 					IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type;
549 					emit_final!(Amd64Opcode.fsub)(type, legalizeFloatArg(instrHeader.arg(ir, 0), type), legalizeFloatArg(instrHeader.arg(ir, 1), type));
550 					break;
551 				case IrOpcode.fmul:
552 					IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type;
553 					emit_final!(Amd64Opcode.fmul)(type, legalizeFloatArg(instrHeader.arg(ir, 0), type), legalizeFloatArg(instrHeader.arg(ir, 1), type));
554 					break;
555 				case IrOpcode.fdiv:
556 					IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type;
557 					emit_final!(Amd64Opcode.fdiv)(type, legalizeFloatArg(instrHeader.arg(ir, 0), type), legalizeFloatArg(instrHeader.arg(ir, 1), type));
558 					break;
559 
560 				case IrOpcode.udiv, IrOpcode.sdiv, IrOpcode.urem, IrOpcode.srem:
561 					//   v1 = div v2, v3
562 					// is converted into:
563 					//   mov ax, v2
564 					//   zx/sx dx:ax
565 					//   ax = div ax, dx, v3
566 					//   mov v1, ax
567 					// since 8 bit division doesn't follow dx:ax pattern and uses ax instead, we use bigger division there
568 
569 					bool isSigned = instrHeader.op == IrOpcode.sdiv || instrHeader.op == IrOpcode.srem;
570 					bool isDivision = instrHeader.op == IrOpcode.udiv || instrHeader.op == IrOpcode.sdiv;
571 
572 					IrIndex dividendBottom;
573 
574 					IrArgSize argSize = instrHeader.argSize;
575 					switch(argSize) with(IrArgSize)
576 					{
577 						case size8:
578 							// we transform 8 bit div/rem into 16 bit, so we don't need to deal with ah register
579 							argSize = size16;
580 							dividendBottom = IrIndex(amd64_reg.ax, argSize);
581 							ExtraInstrArgs extra = { addUsers : false, result : dividendBottom };
582 							if (isSigned) builder.emitInstr!(Amd64Opcode.movsx_btod)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)));
583 							else builder.emitInstr!(Amd64Opcode.movzx_btod)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)));
584 							break;
585 						case size16:
586 						case size32:
587 						case size64:
588 							// copy bottom half of dividend
589 							dividendBottom = IrIndex(amd64_reg.ax, argSize);
590 							makeMov(dividendBottom, getFixedIndex(instrHeader.arg(ir, 0)), argSize, lirBlockIndex);
591 							break;
592 						default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, argSize);
593 					}
594 
595 
596 					// divisor must be in register
597 					IrIndex divisor = instrHeader.arg(ir, 1);
598 					if (divisor.isSimpleConstant) {
599 						auto con = context.constants.get(divisor);
600 						if (instrHeader.argSize == IrArgSize.size8) {
601 							divisor = context.constants.add(makeIrType(IrBasicType.i16), con.i16);
602 						}
603 						ExtraInstrArgs extra = { addUsers : false, type : getValueType(divisor, ir, context) };
604 						divisor = builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra, divisor).result;
605 					}
606 					else
607 					{
608 						fixIndex(divisor);
609 						if (instrHeader.argSize == IrArgSize.size8) {
610 							ExtraInstrArgs extra = { addUsers : false, type : makeIrType(IrBasicType.i16) };
611 							if (isSigned) divisor = builder.emitInstr!(Amd64Opcode.movsx_btod)(lirBlockIndex, extra, divisor).result;
612 							else divisor = builder.emitInstr!(Amd64Opcode.movzx_btod)(lirBlockIndex, extra, divisor).result;
613 						}
614 					}
615 
616 					IrIndex dividendTop = IrIndex(amd64_reg.dx, argSize);
617 
618 					if (isSigned) {
619 						IrIndex divsxResult = IrIndex(amd64_reg.dx, argSize);
620 						// sign-extend top half of dividend
621 						ExtraInstrArgs extra2 = { argSize : argSize, result : divsxResult };
622 						builder.emitInstr!(Amd64Opcode.divsx)(lirBlockIndex, extra2);
623 					} else {
624 						// zero top half of dividend
625 						makeMov(dividendTop, context.constants.addZeroConstant(makeIrType(IrBasicType.i32)), IrArgSize.size32, lirBlockIndex);
626 					}
627 
628 					// choose result
629 					IrIndex resultReg = dividendTop; // remainder
630 					if (isDivision) {
631 						resultReg = dividendBottom; // dividend
632 					}
633 
634 					// divide
635 					ExtraInstrArgs extra3 = { addUsers : false, argSize : argSize, result : resultReg };
636 					InstrWithResult res;
637 					if (isSigned)
638 						res = builder.emitInstr!(Amd64Opcode.idiv)(lirBlockIndex, extra3, dividendBottom, dividendTop, divisor);
639 					else
640 						res = builder.emitInstr!(Amd64Opcode.div)(lirBlockIndex, extra3, dividendBottom, dividendTop, divisor);
641 
642 					// fix size for size8
643 					resultReg.physRegSize = instrHeader.argSize;
644 
645 					// copy result (quotient) with truncation in case of 8/16 bits
646 					ExtraInstrArgs extra4 = { addUsers : false, argSize : instrHeader.argSize, type : ir.getVirtReg(instrHeader.result(ir)).type };
647 					InstrWithResult movResult = builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra4, resultReg);
648 					recordIndex(instrHeader.result(ir), movResult.result);
649 					break;
650 
651 				case IrOpcode.shl, IrOpcode.lshr, IrOpcode.ashr:
652 					IrIndex rightArg;
653 					if (instrHeader.arg(ir, 1).isSimpleConstant)
654 						rightArg = instrHeader.arg(ir, 1);
655 					else
656 					{
657 						rightArg = IrIndex(amd64_reg.cx, IrArgSize.size8);
658 						makeMov(rightArg, getFixedIndex(instrHeader.arg(ir, 1)), instrHeader.argSize, lirBlockIndex);
659 					}
660 					IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type;
661 					ExtraInstrArgs extra = { addUsers : false, argSize : instrHeader.argSize, type : type };
662 					InstrWithResult res;
663 					switch(instrHeader.op) {
664 						case IrOpcode.shl:
665 							res = builder.emitInstr!(Amd64Opcode.shl)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), rightArg);
666 							break;
667 						case IrOpcode.lshr:
668 							res = builder.emitInstr!(Amd64Opcode.shr)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), rightArg);
669 							break;
670 						case IrOpcode.ashr:
671 							res = builder.emitInstr!(Amd64Opcode.sar)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), rightArg);
672 							break;
673 						default: assert(false);
674 					}
675 					recordIndex(instrHeader.result(ir), res.result);
676 					break;
677 				case IrOpcode.grow_stack:
678 					IrIndex stackPtrReg = IrIndex(lir.getCallConv(context).stackPointer, IrArgSize.size64);
679 					ExtraInstrArgs extra = { addUsers : false, result : stackPtrReg };
680 					builder.emitInstr!(Amd64Opcode.sub)(
681 						lirBlockIndex, extra, stackPtrReg, getFixedIndex(instrHeader.arg(ir, 0)));
682 					break;
683 				case IrOpcode.shrink_stack:
684 					IrIndex stackPtrReg = IrIndex(lir.getCallConv(context).stackPointer, IrArgSize.size64);
685 					ExtraInstrArgs extra = { addUsers : false, result : stackPtrReg };
686 					builder.emitInstr!(Amd64Opcode.add)(
687 						lirBlockIndex, extra, stackPtrReg, getFixedIndex(instrHeader.arg(ir, 0)));
688 					break;
689 				case IrOpcode.push:
690 					IrIndex fixedArg = getFixedLegalIndex(instrHeader.arg(ir, 0), lirBlockIndex);
691 					ExtraInstrArgs extra = { addUsers : false };
692 					builder.emitInstr!(Amd64Opcode.push)(lirBlockIndex, extra, fixedArg);
693 					break;
694 				case IrOpcode.move:
695 					if (instrHeader.result(ir).isVirtReg)
696 						emitLirInstr!(Amd64Opcode.mov);
697 					else
698 						makeMov(instrHeader.result(ir), getFixedIndex(instrHeader.arg(ir, 0)), instrHeader.argSize, lirBlockIndex);
699 					break;
700 				case IrOpcode.copy:
701 					genCopy(getFixedIndex(instrHeader.arg(ir, 0)), getFixedIndex(instrHeader.arg(ir, 1)), lirBlockIndex);
702 					break;
703 				case IrOpcode.load: emitLirInstr!(Amd64Opcode.load); break;
704 				case IrOpcode.store:
705 					IrIndex srcType = ir.getValueType(context, instrHeader.arg(ir, 1));
706 					genStore(getFixedIndex(instrHeader.arg(ir, 0)), 0, instrHeader.arg(ir, 1), 0, srcType, lirBlockIndex, ir);
707 					break;
708 				case IrOpcode.conv:
709 					// Incomplete implementation
710 					IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context);
711 					IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type;
712 					uint typeSizeFrom = context.types.typeSize(typeFrom);
713 					uint typeSizeTo = context.types.typeSize(typeTo);
714 					context.assertf(typeSizeTo <= typeSizeFrom,
715 						"Can't cast from %s bytes to %s bytes", typeSizeFrom, typeSizeTo);
716 					emitLirInstr!(Amd64Opcode.mov);
717 					break;
718 				case IrOpcode.sext:
719 					IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context);
720 					IrArgSize sizeFrom = getTypeArgSize(typeFrom, context);
721 					IrArgSize sizeTo = instrHeader.argSize;
722 					final switch(sizeFrom) with(IrArgSize)
723 					{
724 						case size8:
725 							switch(sizeTo) with(IrArgSize)
726 							{
727 								case size16: emitLirInstr!(Amd64Opcode.movsx_btow); break;
728 								case size32: emitLirInstr!(Amd64Opcode.movsx_btod); break;
729 								case size64: emitLirInstr!(Amd64Opcode.movsx_btoq); break;
730 								default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, sizeTo);
731 							}
732 							break;
733 						case size16:
734 							switch(sizeTo) with(IrArgSize)
735 							{
736 								case size32: emitLirInstr!(Amd64Opcode.movsx_wtod); break;
737 								case size64: emitLirInstr!(Amd64Opcode.movsx_wtoq); break;
738 								default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, sizeTo);
739 							}
740 							break;
741 						case size32:
742 							context.assertf(sizeTo == size64, "%s:%s: Invalid target size %s", funName, instrIndex, sizeTo);
743 							emitLirInstr!(Amd64Opcode.movsx_dtoq);
744 							break;
745 						case size64, size128, size256, size512: context.internal_error("%s:%s: Invalid source size %s", funName, instrIndex, sizeFrom);
746 					}
747 					break;
748 				case IrOpcode.zext:
749 					IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context);
750 					IrArgSize sizeFrom = getTypeArgSize(typeFrom, context);
751 					IrArgSize sizeTo = instrHeader.argSize;
752 					final switch(sizeFrom) with(IrArgSize)
753 					{
754 						case size8:
755 							switch(sizeTo) with(IrArgSize)
756 							{
757 								case size16: emitLirInstr!(Amd64Opcode.movzx_btow); break;
758 								case size32: emitLirInstr!(Amd64Opcode.movzx_btod); break;
759 								case size64: emitLirInstr!(Amd64Opcode.movzx_btoq); break; // TODO use movzx_btod
760 								default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, sizeTo);
761 							}
762 							break;
763 						case size16:
764 							switch(sizeTo) with(IrArgSize)
765 							{
766 								case size32: emitLirInstr!(Amd64Opcode.movzx_wtod); break;
767 								case size64: emitLirInstr!(Amd64Opcode.movzx_wtoq); break; // TODO use movzx_wtod
768 								default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, sizeTo);
769 							}
770 							break;
771 						case size32:
772 							context.assertf(sizeTo == size64, "Invalid target size %s", sizeTo);
773 							emitLirInstr!(Amd64Opcode.mov);
774 							break;
775 						case size64, size128, size256, size512: context.internal_error("%s:%s: Invalid source size %s", funName, instrIndex, sizeFrom);
776 					}
777 					break;
778 				case IrOpcode.trunc:
779 					IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context);
780 					IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type;
781 					uint typeSizeFrom = context.types.typeSize(typeFrom);
782 					uint typeSizeTo = context.types.typeSize(typeTo);
783 					context.assertf(typeSizeTo < typeSizeFrom,
784 						"Can't cast from %s bytes to %s bytes", typeSizeFrom, typeSizeTo);
785 					emitLirInstr!(Amd64Opcode.mov);
786 					break;
787 
788 				case IrOpcode.fpext:
789 					IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context);
790 					IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type;
791 					uint typeSizeFrom = context.types.typeSize(typeFrom);
792 					uint typeSizeTo = context.types.typeSize(typeTo);
793 					context.assertf(typeSizeTo > typeSizeFrom,
794 						"Can't cast from %s bytes to %s bytes", typeSizeFrom, typeSizeTo);
795 					emitLirInstr!(Amd64Opcode.f32_to_f64);
796 					break;
797 
798 				case IrOpcode.fptrunc:
799 					IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context);
800 					IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type;
801 					uint typeSizeFrom = context.types.typeSize(typeFrom);
802 					uint typeSizeTo = context.types.typeSize(typeTo);
803 					context.assertf(typeSizeTo < typeSizeFrom,
804 						"Can't cast from %s bytes to %s bytes", typeSizeFrom, typeSizeTo);
805 					emitLirInstr!(Amd64Opcode.f64_to_f32);
806 					break;
807 
808 				case IrOpcode.uitofp:
809 					IrIndex arg0 = instrHeader.arg(ir, 0);
810 					IrIndex typeFrom = getValueType(arg0, ir, context);
811 					IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type;
812 					uint typeSizeFrom = context.types.typeSize(typeFrom);
813 					uint typeSizeTo = context.types.typeSize(typeTo);
814 
815 					switch(typeSizeTo) {
816 					case 4:
817 						switch(typeSizeFrom) {
818 						case 1:
819 							IrIndex u32 = emit_nonfinal!(Amd64Opcode.movzx_btod)(makeIrType(IrBasicType.i32), arg0);
820 							emit_final!(Amd64Opcode.i32_to_f32)(typeTo, u32);
821 							break switch_instr;
822 						case 2:
823 							IrIndex u32 = emit_nonfinal!(Amd64Opcode.movzx_wtod)(makeIrType(IrBasicType.i32), arg0);
824 							emit_final!(Amd64Opcode.i32_to_f32)(typeTo, u32);
825 							break switch_instr;
826 						case 4:
827 							IrIndex i64 = emit_nonfinal!(Amd64Opcode.mov)(makeIrType(IrBasicType.i64), arg0);
828 							emit_final!(Amd64Opcode.i64_to_f32)(typeTo, i64);
829 							break switch_instr;
830 						case 8: emitLirInstr!(Amd64Opcode.i64_to_f32); break switch_instr; // u64 -> f32
831 						default: context.internal_error("Unexpected source type size %s", typeSizeFrom);
832 						}
833 
834 					case 8:
835 						switch(typeSizeFrom) {
836 						case 1:
837 							IrIndex i32 = emit_nonfinal!(Amd64Opcode.movzx_btod)(makeIrType(IrBasicType.i32), arg0);
838 							emit_final!(Amd64Opcode.i32_to_f64)(typeTo, i32);
839 							break switch_instr;
840 						case 2:
841 							IrIndex i32 = emit_nonfinal!(Amd64Opcode.movzx_wtod)(makeIrType(IrBasicType.i32), arg0);
842 							emit_final!(Amd64Opcode.i32_to_f64)(typeTo, i32);
843 							break switch_instr;
844 						case 4:
845 							IrIndex i64 = emit_nonfinal!(Amd64Opcode.mov)(makeIrType(IrBasicType.i64), arg0);
846 							emit_final!(Amd64Opcode.i64_to_f64)(typeTo, i64);
847 							break switch_instr;
848 						case 8: emitLirInstr!(Amd64Opcode.i64_to_f64); break switch_instr; // u64 -> f64
849 						default: context.internal_error("Unexpected source type size %s", typeSizeFrom);
850 						}
851 					default: context.internal_error("Unexpected target type size %s", typeSizeTo);
852 					}
853 
854 				case IrOpcode.sitofp:
855 					IrIndex arg0 = instrHeader.arg(ir, 0);
856 					IrIndex typeFrom = getValueType(arg0, ir, context);
857 					IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type;
858 					uint typeSizeFrom = context.types.typeSize(typeFrom);
859 					uint typeSizeTo = context.types.typeSize(typeTo);
860 
861 					switch(typeSizeTo) {
862 					case 4:
863 						switch(typeSizeFrom) {
864 						case 1:
865 							IrIndex i32 = emit_nonfinal!(Amd64Opcode.movsx_btod)(makeIrType(IrBasicType.i32), arg0);
866 							emit_final!(Amd64Opcode.i32_to_f32)(typeTo, i32);
867 							break switch_instr;
868 						case 2:
869 							IrIndex i32 = emit_nonfinal!(Amd64Opcode.movsx_wtod)(makeIrType(IrBasicType.i32), arg0);
870 							emit_final!(Amd64Opcode.i32_to_f32)(typeTo, i32);
871 							break switch_instr;
872 						case 4: emitLirInstr!(Amd64Opcode.i32_to_f32); break switch_instr;
873 						case 8: emitLirInstr!(Amd64Opcode.i64_to_f32); break switch_instr;
874 						default: context.internal_error("Unexpected source type size %s", typeSizeFrom);
875 						}
876 
877 					case 8:
878 						switch(typeSizeFrom) {
879 						case 1:
880 							IrIndex i32 = emit_nonfinal!(Amd64Opcode.movsx_btod)(makeIrType(IrBasicType.i32), arg0);
881 							emit_final!(Amd64Opcode.i32_to_f64)(typeTo, i32);
882 							break switch_instr;
883 						case 2:
884 							IrIndex i32 = emit_nonfinal!(Amd64Opcode.movsx_wtod)(makeIrType(IrBasicType.i32), arg0);
885 							emit_final!(Amd64Opcode.i32_to_f64)(typeTo, i32);
886 							break switch_instr;
887 						case 4: emitLirInstr!(Amd64Opcode.i32_to_f64); break switch_instr;
888 						case 8: emitLirInstr!(Amd64Opcode.i64_to_f64); break switch_instr;
889 						default: context.internal_error("Unexpected source type size %s", typeSizeFrom);
890 						}
891 					default: context.internal_error("Unexpected target type size %s", typeSizeTo);
892 					}
893 
894 				case IrOpcode.fptoui:
895 					IrIndex arg0 = instrHeader.arg(ir, 0);
896 					IrIndex typeFrom = getValueType(arg0, ir, context);
897 					IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type;
898 					uint typeSizeFrom = context.types.typeSize(typeFrom);
899 					uint typeSizeTo = context.types.typeSize(typeTo);
900 
901 					switch(typeSizeFrom) {
902 						case 4:
903 						switch(typeSizeTo) {
904 							case 1:
905 							case 2:
906 								IrIndex i32 = emit_nonfinal!(Amd64Opcode.f32_to_i32_trunc)(makeIrType(IrBasicType.i32), arg0);
907 								emit_final!(Amd64Opcode.mov)(typeTo, i32);
908 								break switch_instr;
909 							case 4: emitLirInstr!(Amd64Opcode.f32_to_i64_trunc); break switch_instr; // f32 -> u32
910 							case 8: emitLirInstr!(Amd64Opcode.f32_to_i64_trunc); break switch_instr; // f32 -> u64
911 							default: context.internal_error("Unexpected source type size %s", typeSizeTo);
912 						}
913 
914 						case 8:
915 						switch(typeSizeTo) {
916 							case 1:
917 							case 2:
918 								IrIndex i32 = emit_nonfinal!(Amd64Opcode.f64_to_i32_trunc)(makeIrType(IrBasicType.i32), arg0);
919 								emit_final!(Amd64Opcode.mov)(typeTo, i32);
920 								break switch_instr;
921 							case 4:
922 								IrIndex i64 = emit_nonfinal!(Amd64Opcode.f64_to_i64_trunc)(makeIrType(IrBasicType.i64), arg0);
923 								emit_final!(Amd64Opcode.mov)(typeTo, i64);
924 								break switch_instr;
925 							case 8: emitLirInstr!(Amd64Opcode.f64_to_i64_trunc); break switch_instr; // f64 -> u64
926 							default: context.internal_error("Unexpected source type size %s", typeSizeTo);
927 						}
928 						default: context.internal_error("Unexpected target type size %s", typeSizeFrom);
929 					}
930 
931 				case IrOpcode.fptosi:
932 					IrIndex arg0 = instrHeader.arg(ir, 0);
933 					IrIndex typeFrom = getValueType(arg0, ir, context);
934 					IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type;
935 					uint typeSizeFrom = context.types.typeSize(typeFrom);
936 					uint typeSizeTo = context.types.typeSize(typeTo);
937 
938 					switch(typeSizeFrom) {
939 						case 4:
940 						switch(typeSizeTo) {
941 							case 1:
942 							case 2:
943 								IrIndex i32 = emit_nonfinal!(Amd64Opcode.f32_to_i32_trunc)(makeIrType(IrBasicType.i32), arg0);
944 								emit_final!(Amd64Opcode.mov)(typeTo, i32);
945 								break switch_instr;
946 							case 4: emitLirInstr!(Amd64Opcode.f32_to_i32_trunc); break switch_instr;
947 							case 8: emitLirInstr!(Amd64Opcode.f32_to_i64_trunc); break switch_instr;
948 							default: context.internal_error("Unexpected source type size %s", typeSizeTo);
949 						}
950 
951 						case 8:
952 						switch(typeSizeTo) {
953 							case 1:
954 							case 2:
955 								IrIndex i32 = emit_nonfinal!(Amd64Opcode.f64_to_i32_trunc)(makeIrType(IrBasicType.i32), arg0);
956 								emit_final!(Amd64Opcode.mov)(typeTo, i32);
957 								break switch_instr;
958 							case 4: emitLirInstr!(Amd64Opcode.f64_to_i32_trunc); break switch_instr;
959 							case 8: emitLirInstr!(Amd64Opcode.f64_to_i64_trunc); break switch_instr;
960 							default: context.internal_error("Unexpected source type size %s", typeSizeTo);
961 						}
962 						default: context.internal_error("Unexpected target type size %s", typeSizeFrom);
963 					}
964 
965 				case IrOpcode.set_unary_cond:
966 					IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type;
967 					ExtraInstrArgs extra = { addUsers : false, cond : instrHeader.cond, argSize : instrHeader.argSize, type : type };
968 					InstrWithResult res = builder.emitInstr!(Amd64Opcode.set_unary_cond)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)));
969 					recordIndex(instrHeader.result(ir), res.result);
970 					break;
971 
972 				case IrOpcode.set_binary_cond:
973 					IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type;
974 					ExtraInstrArgs extra = { addUsers : false, cond : instrHeader.cond, argSize : instrHeader.argSize, type : type };
975 					InstrWithResult res = builder.emitInstr!(Amd64Opcode.set_binary_cond)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), getFixedIndex(instrHeader.arg(ir, 1)));
976 					recordIndex(instrHeader.result(ir), res.result);
977 					break;
978 
979 				case IrOpcode.jump:
980 					builder.emitInstr!(Amd64Opcode.jmp)(lirBlockIndex);
981 					break;
982 
983 				case IrOpcode.branch_unary:
984 					ExtraInstrArgs extra = { addUsers : false, cond : instrHeader.cond, argSize : instrHeader.argSize };
985 					IrIndex instruction = builder.emitInstr!(Amd64Opcode.un_branch)(
986 						lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)));
987 					break;
988 
989 				case IrOpcode.branch_binary:
990 					ExtraInstrArgs extra = { addUsers : false, cond : instrHeader.cond, argSize : instrHeader.argSize };
991 					IrIndex instruction = builder.emitInstr!(Amd64Opcode.bin_branch)(
992 						lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), getFixedIndex(instrHeader.arg(ir, 1)));
993 					break;
994 
995 				case IrOpcode.ret:
996 					builder.emitInstr!(Amd64Opcode.ret)(lirBlockIndex);
997 					break;
998 
999 				case IrOpcode.unreachable:
1000 					builder.emitInstr!(Amd64Opcode.ud2)(lirBlockIndex);
1001 					break;
1002 
1003 				default:
1004 					context.internal_error("IrToLir unimplemented IR instr %s", cast(IrOpcode)instrHeader.op);
1005 			}
1006 		}
1007 	}
1008 
1009 	void fixInstrs(IrIndex blockIndex, ref IrBasicBlock lirBlock)
1010 	{
1011 		// all instructions already contain final arguments, add users here
1012 		foreach(IrIndex instrIndex, ref IrInstrHeader instrHeader; lirBlock.instructions(lir))
1013 		{
1014 			foreach(ref IrIndex arg; instrHeader.args(lir))
1015 			{
1016 				builder.addUser(instrIndex, arg);
1017 			}
1018 		}
1019 	}
1020 
1021 	void fixPhis(IrIndex blockIndex, ref IrBasicBlock lirBlock)
1022 	{
1023 		// fix phi args and add users
1024 		foreach(IrIndex phiIndex, ref IrPhi phi; lirBlock.phis(lir))
1025 		{
1026 			foreach(size_t arg_i, ref IrIndex phiArg; phi.args(lir))
1027 			{
1028 				fixIndex(phiArg);
1029 				builder.addUser(phiIndex, phiArg);
1030 			}
1031 		}
1032 	}
1033 
1034 	//dumpFunction(context, lir, "IR -> LIR end"); // uncomment to see generated LIR before fixing
1035 
1036 	foreach (IrIndex blockIndex, ref IrBasicBlock lirBlock; lir.blocks)
1037 	{
1038 		fixInstrs(blockIndex, lirBlock);
1039 		fixPhis(blockIndex, lirBlock);
1040 	}
1041 }