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.lir_amd64;
7 
8 import std.bitmanip : bitfields;
9 import std.stdio;
10 import std.format;
11 
12 import vox.all;
13 import vox.be.amd64asm;
14 
15 // usage reg_names[physRegClass][physRegSize][physRegIndex]
16 immutable string[][][] reg_names = [[
17 		["al", "cl", "dl", "bl", "spl","bpl","sil","dil","r8b","r9b","r10b","r11b","r12b","r13b","r14b","r15b"],
18 		["ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8w","r9w","r10w","r11w","r12w","r13w","r14w","r15w"],
19 		["eax","ecx","edx","ebx","esp","ebp","esi","edi","r8d","r9d","r10d","r11d","r12d","r13d","r14d","r15d"],
20 		["rax","rcx","rdx","rbx","rsp","rbp","rsi","rdi","r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" ],
21 	],
22 	[
23 		["xmm0b","xmm1b","xmm2b","xmm3b","xmm4b","xmm5b","xmm6b","xmm7b","xmm8b","xmm9b","xmm10b","xmm11b","xmm12b","xmm13b","xmm14b","xmm15b"], // 8
24 		["xmm0w","xmm1w","xmm2w","xmm3w","xmm4w","xmm5w","xmm6w","xmm7w","xmm8w","xmm9w","xmm10w","xmm11w","xmm12w","xmm13w","xmm14w","xmm15w"], // 16
25 		["xmm0d","xmm1d","xmm2d","xmm3d","xmm4d","xmm5d","xmm6d","xmm7d","xmm8d","xmm9d","xmm10d","xmm11d","xmm12d","xmm13d","xmm14d","xmm15d"], // 32
26 		["xmm0q","xmm1q","xmm2q","xmm3q","xmm4q","xmm5q","xmm6q","xmm7q","xmm8q","xmm9q","xmm10q","xmm11q","xmm12q","xmm13q","xmm14q","xmm15q"], // 64
27 		["xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7","xmm8","xmm9","xmm10","xmm11","xmm12","xmm13","xmm14","xmm15"], // 128
28 		["ymm0","ymm1","ymm2","ymm3","ymm4","ymm5","ymm6","ymm7","ymm8","ymm9","ymm10","ymm11","ymm12","ymm13","ymm14","ymm15"], // 256
29 		["zmm0","zmm1","zmm2","zmm3","zmm4","zmm5","zmm6","zmm7","zmm8","zmm9","zmm10","zmm11","zmm12","zmm13","zmm14","zmm15"], // 512
30 	],
31 ];
32 
33 /// Creates physicalRegister index
34 IrIndex amd64Reg(uint index, uint regSize)
35 {
36 	IrIndex result;
37 	result.storageUintIndex = regSize << 12 | (index & ((1 << 12) - 1));
38 	result.kind = IrValueKind.physicalRegister;
39 	return result;
40 }
41 
42 uint typeToRegSize(IrIndex type, CompilationContext* context) {
43 	uint typeSize = context.types.typeSize(type);
44 	switch (typeSize) {
45 		case 1: return ArgType.BYTE;
46 		case 2: return ArgType.WORD;
47 		case 4: return ArgType.DWORD;
48 		case 8: return ArgType.QWORD;
49 		default:
50 			context.internal_error("Type %s of size %s cannot be stored in a register",
51 				IrTypeDump(type, *context), typeSize);
52 	}
53 }
54 
55 struct PhysReg {
56 	this(ubyte _regClass, ubyte _regIndex) {
57 		regClass = _regClass;
58 		regIndex = _regIndex;
59 	}
60 
61 	mixin(bitfields!(
62 		uint, "regIndex", 5,
63 		uint, "regClass", 3,
64 	));
65 
66 	void toString(scope void delegate(const(char)[]) sink) {
67 		sink.formattedWrite("%s %s %s", regClass, regIndex, regClass*(1<<5) + regIndex);
68 	}
69 }
70 
71 enum NUM_REG_CLASSES = 2;
72 enum NUM_REGS_PER_CLASS = 32;
73 enum NUM_TOTAL_REGS = NUM_REG_CLASSES * NUM_REGS_PER_CLASS;
74 
75 enum AMD64_REG_CLASS : ubyte {
76 	GPR = 0,
77 	XMM = 1,
78 }
79 
80 // Register set of registers of all classes
81 struct FullRegSet {
82 	ClassRegSet[NUM_REG_CLASSES] classes;
83 
84 	ubyte length() {
85 		ubyte result;
86 		foreach(i, cl; classes)
87 			result += classes[i].length;
88 		return result;
89 	}
90 
91 	FullRegSet opUnary(string op)()
92 		if (op == "~")
93 	{
94 		FullRegSet result = this;
95 		result.classes = ~result.classes[];
96 		return result;
97 	}
98 
99 	FullRegSet opBinary(string op)(FullRegSet rhs)
100 		if (op == "|" || op == "^" || op == "&")
101 	{
102 		FullRegSet result;
103 		foreach(i, cl; classes)
104 			result.classes[i] = mixin("classes[i] "~op~" rhs.classes[i]");
105 		return result;
106 	}
107 
108 	FullRegSet opBinary(string op)(PhysReg rhs)
109 		if (op == "|" || op == "^" || op == "&")
110 	{
111 		FullRegSet result = this;
112 		mixin("result.classes[rhs.regClass].bits "~op~"= 1 << rhs.regIndex;");
113 		return result;
114 	}
115 
116 	void opOpAssign(string op)(FullRegSet rhs)
117 		if (op == "|" || op == "^" || op == "&")
118 	{
119 		foreach(i, cl; classes)
120 			classes[i] = mixin("classes[i] "~op~" rhs.classes[i]");
121 	}
122 
123 	void opOpAssign(string op)(PhysReg rhs)
124 		if (op == "|" || op == "^" || op == "&")
125 	{
126 		mixin("classes[rhs.regClass].bits "~op~"= 1 << rhs.regIndex;");
127 	}
128 
129 	void disable(PhysReg rhs)
130 	{
131 		classes[rhs.regClass].bits &= ~(1 << rhs.regIndex);
132 	}
133 
134 	FullRegSet lowest(int n) {
135 		FullRegSet result;
136 		foreach(i, cl; classes)
137 			result.classes[i] = classes[i].lowest(n);
138 		return result;
139 	}
140 
141 	int opApply(scope int delegate(PhysReg) dg)
142 	{
143 		foreach(ubyte regClass; 0..NUM_REG_CLASSES) {
144 			uint[1] bits = classes[regClass].bits;
145 			foreach(regIndex; bitsSet(bits[]))
146 				if (int res = dg(PhysReg(regClass, cast(ubyte)regIndex))) return res;
147 		}
148 		return 0;
149 	}
150 
151 	int opApply(scope int delegate(uint index, PhysReg) dg)
152 	{
153 		uint index;
154 		foreach(ubyte regClass; 0..NUM_REG_CLASSES) {
155 			uint[1] bits = classes[regClass].bits;
156 			foreach(regIndex; bitsSet(bits[])) {
157 				if (int res = dg(index, PhysReg(regClass, cast(ubyte)regIndex))) return res;
158 				++index;
159 			}
160 		}
161 		return 0;
162 	}
163 }
164 
165 /// Register set of a single register class
166 struct ClassRegSet {
167 	// Assume at most 32 registers per register class
168 	uint bits;
169 
170 	ubyte length() { return cast(ubyte)popcnt(bits); }
171 
172 	ClassRegSet opBinary(string op)(ClassRegSet rhs)
173 		if (op == "|" || op == "^" || op == "&")
174 	{
175 		return ClassRegSet(mixin("bits "~op~" rhs.bits"));
176 	}
177 
178 	// returns set with lowest n registers
179 	ClassRegSet lowest(int n) {
180 		uint slotBits = bits;
181 		uint result;
182 		while (slotBits != 0 && n) {
183 			// Extract lowest set isolated bit
184 			// 111000 -> 001000; 0 -> 0
185 			uint lowestSetBit = slotBits & -slotBits;
186 			result |= lowestSetBit;
187 
188 			// Disable lowest set isolated bit
189 			// 111000 -> 110000
190 			slotBits ^= lowestSetBit;
191 			--n;
192 		}
193 		return ClassRegSet(result);
194 	}
195 
196 	int opApply(scope int delegate(ubyte) dg)
197 	{
198 		uint[1] bitsCopy = bits;
199 		foreach(regIndex; bitsSet(bitsCopy[]))
200 			if (int res = dg(cast(ubyte)regIndex)) return res;
201 		return 0;
202 	}
203 }
204 
205 struct MachineInfo
206 {
207 	// total number of registers
208 	ubyte numRegisters;
209 	// index is register class. Each entry specifies number of registers in this class
210 	ubyte[NUM_REG_CLASSES] numRegsPerClass;
211 	// first index is register class
212 	// second index is register size
213 	// third index is register index
214 	// Sizes of name arrays must match corresponding regsPerClass
215 	immutable string[][][] reg_names;
216 	//PhysicalRegister[] registers;
217 	InstrInfo[] instrInfo;
218 
219 	string regName(IrIndex reg) {
220 		return reg_names[reg.physRegClass][reg.physRegSize][reg.physRegIndex];
221 	}
222 }
223 
224 __gshared MachineInfo mach_info_amd64 = MachineInfo(
225 	32,
226 	[16, 16],
227 	reg_names,
228 	amd64InstrInfos.dup
229 );
230 
231 enum amd64_reg : PhysReg {
232 	ax    = PhysReg(0,  0),
233 	cx    = PhysReg(0,  1),
234 	dx    = PhysReg(0,  2),
235 	bx    = PhysReg(0,  3),
236 	sp    = PhysReg(0,  4),
237 	bp    = PhysReg(0,  5),
238 	si    = PhysReg(0,  6),
239 	di    = PhysReg(0,  7),
240 	r8    = PhysReg(0,  8),
241 	r9    = PhysReg(0,  9),
242 	r10   = PhysReg(0, 10),
243 	r11   = PhysReg(0, 11),
244 	r12   = PhysReg(0, 12),
245 	r13   = PhysReg(0, 13),
246 	r14   = PhysReg(0, 14),
247 	r15   = PhysReg(0, 15),
248 
249 	xmm0  = PhysReg(1,  0),
250 	xmm1  = PhysReg(1,  1),
251 	xmm2  = PhysReg(1,  2),
252 	xmm3  = PhysReg(1,  3),
253 	xmm4  = PhysReg(1,  4),
254 	xmm5  = PhysReg(1,  5),
255 	xmm6  = PhysReg(1,  6),
256 	xmm7  = PhysReg(1,  7),
257 	xmm8  = PhysReg(1,  8),
258 	xmm9  = PhysReg(1,  9),
259 	xmm10 = PhysReg(1, 10),
260 	xmm11 = PhysReg(1, 11),
261 	xmm12 = PhysReg(1, 12),
262 	xmm13 = PhysReg(1, 13),
263 	xmm14 = PhysReg(1, 14),
264 	xmm15 = PhysReg(1, 15),
265 }
266 
267 struct CallConv
268 {
269 	PhysReg[] gprParamRegs;
270 	PhysReg[] sseParamRegs;
271 	FullRegSet volatileRegs;
272 	FullRegSet calleeSaved;
273 	// Size of the register preserved by the callee
274 	IrArgSize[] calleeSavedSizePerClass;
275 	/// Included into calleeSaved
276 	/// Can be used as frame pointer when
277 	/// frame pointer is enabled for the function, or
278 	/// can be used as allocatable register if not (in which case it is considered as callee saved)
279 	PhysReg framePointer;
280 	PhysReg stackPointer;
281 
282 	ubyte minStackAlignmentPower;
283 
284 	uint flags;
285 
286 	bool hasShadowSpace() { return cast(bool)(flags & CallConvFlags.hasShadowSpace); }
287 	bool hasRedZone() { return cast(bool)(flags & CallConvFlags.hasRedZone); }
288 	bool hasReverseStackOrder() { return cast(bool)(flags & CallConvFlags.hasReverseStackOrder); }
289 }
290 
291 enum CallConvention : ubyte {
292 	win64,
293 	sysv64,
294 	sysv64_syscall,
295 }
296 
297 enum CallConvFlags : uint {
298 	hasShadowSpace = 1 << 0,
299 	hasRedZone     = 1 << 1,
300 	/// By default parameters that are passed via stack are passed in left-to-right order
301 	/// This means that leftmost parameter has the smallest memory address, and rightmost parameter has biggest address
302 	/// If set, parameters are passed right-to-left on the stack
303 	hasReverseStackOrder = 1 << 2,
304 }
305 
306 __gshared CallConv*[] callConventions = [
307 	&win64_call_conv,
308 	&sysv64_call_conv,
309 	&sysv64_syscall_call_conv,
310 ];
311 
312 __gshared CallConv win64_call_conv = CallConv
313 (
314 	// parameters in registers
315 	[amd64_reg.cx, amd64_reg.dx, amd64_reg.r8, amd64_reg.r9],
316 	[amd64_reg.xmm0, amd64_reg.xmm1, amd64_reg.xmm2, amd64_reg.xmm3],
317 
318 	// volatile regs, zero cost allocation
319 	//   ax cx dx r8 r9 r10 r11
320 	//   xmm0 xmm1 xmm2 xmm3 xmm4 xmm5
321 	FullRegSet([
322 		//            1111 11
323 		//            5432 1098 7654 3210
324 		ClassRegSet(0b0000_1111_0000_0111),
325 		ClassRegSet(0b0000_0000_0011_1111)]),
326 
327 	// callee saved regs, need to save/restore to use
328 	//   bp bx si di r12 r13 r14 r15
329 	FullRegSet([
330 		//            1111 11
331 		//            5432 1098 7654 3210
332 		ClassRegSet(0b1111_0000_1110_1000),
333 		ClassRegSet(0b1111_1111_1100_0000)]),
334 
335 	[IrArgSize.size64, IrArgSize.size128],
336 
337 	amd64_reg.bp, // frame pointer
338 	amd64_reg.sp, // stack pointer
339 
340 	4,
341 
342 	CallConvFlags.hasShadowSpace,
343 );
344 
345 __gshared CallConv sysv64_call_conv = CallConv
346 (
347 	// parameters in registers
348 	[amd64_reg.di, amd64_reg.si, amd64_reg.dx, amd64_reg.cx, amd64_reg.r8, amd64_reg.r9],
349 	[amd64_reg.xmm0, amd64_reg.xmm1, amd64_reg.xmm2, amd64_reg.xmm3, amd64_reg.xmm4, amd64_reg.xmm5, amd64_reg.xmm6, amd64_reg.xmm7],
350 
351 	// volatile regs, zero cost allocation
352 	//   ax cx dx si di r8 r9 r10 r11
353 	//   xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7
354 	FullRegSet([
355 		//            1111 11
356 		//            5432 1098 7654 3210
357 		ClassRegSet(0b0000_1111_1100_0111),
358 		ClassRegSet(0b1111_1111_1111_1111)]),
359 
360 	// callee saved regs, need to save/restore to use
361 	//   bp bx r12 r13 r14 r15
362 	FullRegSet([
363 		//            1111 11
364 		//            5432 1098 7654 3210
365 		ClassRegSet(0b1111_0000_0010_1000),
366 		ClassRegSet(0b0000_0000_0000_0000)]),
367 
368 	[IrArgSize.size64, IrArgSize.size128],
369 
370 	amd64_reg.bp, // frame pointer
371 	amd64_reg.sp, // stack pointer
372 
373 	4,
374 	CallConvFlags.hasRedZone,
375 );
376 
377 __gshared CallConv sysv64_syscall_call_conv = CallConv
378 (
379 	// parameters in registers
380 	[amd64_reg.di, amd64_reg.si, amd64_reg.dx, amd64_reg.r10, amd64_reg.r8, amd64_reg.r9],
381 	[amd64_reg.xmm0, amd64_reg.xmm1, amd64_reg.xmm2, amd64_reg.xmm3, amd64_reg.xmm4, amd64_reg.xmm5, amd64_reg.xmm6, amd64_reg.xmm7],
382 
383 	// volatile regs, zero cost allocation
384 	// cx and r11 are clobbered by syscall
385 	//   ax cx dx si di r8 r9 r10 r11
386 	//   xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7
387 	FullRegSet([
388 		//            1111 11
389 		//            5432 1098 7654 3210
390 		ClassRegSet(0b0000_1111_1100_0111),
391 		ClassRegSet(0b1111_1111_1111_1111)]),
392 
393 	// callee saved regs, need to save/restore to use
394 	//   bp bx r12 r13 r14 r15
395 	FullRegSet([
396 		//            1111 11
397 		//            5432 1098 7654 3210
398 		ClassRegSet(0b1111_0000_0010_1000),
399 		ClassRegSet(0b0000_0000_0000_0000)]),
400 
401 	[IrArgSize.size64, IrArgSize.size128],
402 
403 	amd64_reg.bp, // frame pointer
404 	amd64_reg.sp, // stack pointer
405 
406 	4,
407 	0,
408 );
409 
410 immutable InstrInfo[] amd64InstrInfos = gatherInstrInfos!Amd64Opcode;
411 
412 private alias _ii = InstrInfo;
413 ///
414 enum Amd64Opcode : ushort {
415 	@_ii() invalid,
416 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst|IFLG.isCommutative) add, // arg0 = arg0 + arg1
417 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst) sub, // arg0 = arg0 - arg1
418 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst) mul,
419 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst|IFLG.isCommutative) imul,
420 	@_ii(3,IFLG.hasResult) div, // (dx, ax) = div (dx, ax) / v2
421 	@_ii(3,IFLG.hasResult) idiv, // (dx, ax) = div (dx, ax) / v2
422 	@_ii(0,IFLG.hasResult) divsx, // CWD/CDQ/CQO
423 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst|IFLG.isCommutative) and,
424 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst|IFLG.isCommutative) or,
425 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst|IFLG.isCommutative) xor,
426 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst) shl,
427 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst) shr,
428 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst) sar,
429 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst) lea,
430 
431 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst|IFLG.isCommutative) fadd, // arg0 = arg0 + arg1
432 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst) fsub, // arg0 = arg0 - arg1
433 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst|IFLG.isCommutative) fmul, // arg0 = arg0 * arg1
434 	@_ii(2,IFLG.hasResult|IFLG.isResultInDst) fdiv, // arg0 = arg0 / arg1
435 
436 	@_ii(1,IFLG.hasResult|IFLG.isMov) mov, // mov rr/ri
437 	@_ii(1,IFLG.hasResult|IFLG.isLoad) load, // mov rm
438 	@_ii(2,IFLG.isStore) store, // mov mr/mi
439 
440 	@_ii(1,IFLG.hasResult) movzx_btow,
441 	@_ii(1,IFLG.hasResult) movzx_btod,
442 	@_ii(1,IFLG.hasResult) movzx_btoq,
443 	@_ii(1,IFLG.hasResult) movzx_wtod,
444 	@_ii(1,IFLG.hasResult) movzx_wtoq,
445 
446 	@_ii(1,IFLG.hasResult) movsx_btow,
447 	@_ii(1,IFLG.hasResult) movsx_btod,
448 	@_ii(1,IFLG.hasResult) movsx_btoq,
449 	@_ii(1,IFLG.hasResult) movsx_wtod,
450 	@_ii(1,IFLG.hasResult) movsx_wtoq,
451 	@_ii(1,IFLG.hasResult) movsx_dtoq,
452 
453 	@_ii(1,IFLG.hasResult) f32_to_f64,
454 	@_ii(1,IFLG.hasResult) f64_to_f32,
455 
456 	@_ii(1,IFLG.hasResult) i32_to_f32,
457 	@_ii(1,IFLG.hasResult) i64_to_f32,
458 	@_ii(1,IFLG.hasResult) i32_to_f64,
459 	@_ii(1,IFLG.hasResult) i64_to_f64,
460 	@_ii(1,IFLG.hasResult) f32_to_i32_trunc,
461 	@_ii(1,IFLG.hasResult) f32_to_i64_trunc,
462 	@_ii(1,IFLG.hasResult) f64_to_i32_trunc,
463 	@_ii(1,IFLG.hasResult) f64_to_i64_trunc,
464 
465 	@_ii(1,IFLG.hasResult) f32_to_i32_round,
466 	@_ii(1,IFLG.hasResult) f32_to_i64_round,
467 	@_ii(1,IFLG.hasResult) f64_to_i32_round,
468 	@_ii(1,IFLG.hasResult) f64_to_i64_round,
469 
470 	@_ii(2) xchg, // xchg mr/mr
471 
472 	@_ii(1,IFLG.hasResult|IFLG.isResultInDst) not,
473 	@_ii(1,IFLG.hasResult|IFLG.isResultInDst) neg,
474 
475 	@_ii(1,IFLG.hasResult|IFLG.isResultInDst) fneg,
476 
477 	@_ii(2) cmp,
478 	@_ii(1) test,
479 
480 	// machine specific branches
481 	@_ii(0,IFLG.isJump | IFLG.isBlockExit) jmp,
482 	@_ii(1,IFLG.isBlockExit) jcc,
483 	// high-level branches
484 	@_ii(2,IFLG.hasCondition | IFLG.isBranch | IFLG.isBlockExit) bin_branch,
485 	@_ii(1,IFLG.hasCondition | IFLG.isBranch | IFLG.isBlockExit) un_branch,
486 	@_ii(1,IFLG.hasResult | IFLG.hasCondition) set_unary_cond,
487 	@_ii(2,IFLG.hasResult | IFLG.hasCondition) set_binary_cond,
488 
489 	@_ii(1,IFLG.hasCondition) setcc,
490 
491 	@_ii(1,IFLG.hasVariadicArgs | IFLG.hasVariadicResult | IFLG.isCall) call,
492 	@_ii(1,IFLG.hasVariadicArgs | IFLG.hasVariadicResult | IFLG.isCall) syscall,
493 	@_ii(0,IFLG.isBlockExit) ret,
494 
495 	@_ii(0,IFLG.hasResult) pop,
496 	@_ii(1) push,
497 
498 	@_ii(3) rep_stos,
499 
500 	@_ii(0,IFLG.isBlockExit) ud2,
501 }
502 
503 Condition[] IrBinCondToAmd64Condition = [
504 	Condition.E,  // eq
505 	Condition.NE, // ne
506 	Condition.A,  // ugt
507 	Condition.AE, // uge
508 	Condition.B,  // ult
509 	Condition.BE, // ule
510 	Condition.G,  // sgt
511 	Condition.GE, // sge
512 	Condition.L,  // slt
513 	Condition.LE, // sle
514 	Condition.A,  // fgt
515 	Condition.AE, // fge
516 	Condition.B,  // flt
517 	Condition.BE, // fle
518 ];
519 
520 Condition[] IrUnCondToAmd64Condition = [
521 	Condition.Z,  // zero
522 	Condition.NZ, // not_zero
523 ];
524 
525 void dumpAmd64Instr(ref InstrPrintInfo p)
526 {
527 	switch(p.instrHeader.op)
528 	{
529 		case Amd64Opcode.bin_branch:
530 			dumpBinBranch(p);
531 			break;
532 		case Amd64Opcode.un_branch:
533 			dumpUnBranch(p);
534 			break;
535 		case Amd64Opcode.jmp: dumpJmp(p); break;
536 		case Amd64Opcode.jcc:
537 			p.sink.putf("    j%s %s",
538 				cast(Condition)p.instrHeader.cond,
539 				IrIndexDump(p.instrHeader.arg(p.ir, 0), p));
540 			break;
541 		default:
542 			dumpOptionalResult(p);
543 			p.sink.putf("%s", cast(Amd64Opcode)p.instrHeader.op);
544 			dumpArgs(p);
545 			break;
546 	}
547 }
548 
549 void dumpLirAmd64Index(scope void delegate(const(char)[]) sink, ref CompilationContext context, IrIndex i)
550 {
551 	if (!i.isDefined) {
552 		sink("<null>");
553 		return;
554 	}
555 
556 	final switch(i.kind) with(IrValueKind) {
557 		case none: sink.formattedWrite("0x%X", i.asUint); break;
558 		case array: sink.formattedWrite("arr%s", i.storageUintIndex); break;
559 		case instruction: sink.formattedWrite("i%s", i.storageUintIndex); break;
560 		case basicBlock: sink.formattedWrite("@%s", i.storageUintIndex); break;
561 		case constant:
562 			final switch(i.constantKind) with(IrConstantKind) {
563 				case smallZx: sink.formattedWrite("%s", i.constantIndex); break;
564 				case smallSx: sink.formattedWrite("%s", (cast(int)i.constantIndex << 8) >> 8); break;
565 				case big: sink.formattedWrite("%s", context.constants.get(i).i64); break;
566 			}
567 			break;
568 		case constantAggregate: sink.formattedWrite("cagg%s", i.storageUintIndex); break;
569 		case constantZero:
570 			if (i.typeKind == IrTypeKind.basic)
571 				sink("0");
572 			else
573 				sink("zeroinit");
574 			break;
575 		case global: sink.formattedWrite("g%s", i.storageUintIndex); break;
576 		case phi: sink.formattedWrite("phi%s", i.storageUintIndex); break;
577 		case stackSlot: sink.formattedWrite("s%s", i.storageUintIndex); break;
578 		case virtualRegister: sink.formattedWrite("v%s", i.storageUintIndex); break;
579 		case physicalRegister: sink(reg_names[i.physRegClass][i.physRegSize][i.physRegIndex]); break;
580 		case type: dumpIrType(sink, context, i); break;
581 		case variable: assert(false);
582 		case func: sink.formattedWrite("f%s", i.storageUintIndex); break;
583 	}
584 }