qbe

Internal scc patchset buffer for QBE
Log | Files | Refs | README | LICENSE

emit.c (14667B)


      1 #include "all.h"
      2 
      3 
      4 #define CMP(X) \
      5 	X(Ciule,      "be") \
      6 	X(Ciult,      "b")  \
      7 	X(Cisle,      "le") \
      8 	X(Cislt,      "l")  \
      9 	X(Cisgt,      "g")  \
     10 	X(Cisge,      "ge") \
     11 	X(Ciugt,      "a")  \
     12 	X(Ciuge,      "ae") \
     13 	X(Cieq,       "z")  \
     14 	X(Cine,       "nz") \
     15 	X(NCmpI+Cfle, "be") \
     16 	X(NCmpI+Cflt, "b")  \
     17 	X(NCmpI+Cfgt, "a")  \
     18 	X(NCmpI+Cfge, "ae") \
     19 	X(NCmpI+Cfeq, "z")  \
     20 	X(NCmpI+Cfne, "nz") \
     21 	X(NCmpI+Cfo,  "np") \
     22 	X(NCmpI+Cfuo, "p")
     23 
     24 enum {
     25 	SLong = 0,
     26 	SWord = 1,
     27 	SShort = 2,
     28 	SByte = 3,
     29 
     30 	Ki = -1, /* matches Kw and Kl */
     31 	Ka = -2, /* matches all classes */
     32 };
     33 
     34 /* Instruction format strings:
     35  *
     36  * if the format string starts with -, the instruction
     37  * is assumed to be 3-address and is put in 2-address
     38  * mode using an extra mov if necessary
     39  *
     40  * if the format string starts with +, the same as the
     41  * above applies, but commutativity is also assumed
     42  *
     43  * %k  is used to set the class of the instruction,
     44  *     it'll expand to "l", "q", "ss", "sd", depending
     45  *     on the instruction class
     46  * %0  designates the first argument
     47  * %1  designates the second argument
     48  * %=  designates the result
     49  *
     50  * if %k is not used, a prefix to 0, 1, or = must be
     51  * added, it can be:
     52  *   M - memory reference
     53  *   L - long  (64 bits)
     54  *   W - word  (32 bits)
     55  *   H - short (16 bits)
     56  *   B - byte  (8 bits)
     57  *   S - single precision float
     58  *   D - double precision float
     59  */
     60 static struct {
     61 	short op;
     62 	short cls;
     63 	char *asm;
     64 } omap[] = {
     65 	{ Oadd,    Ka, "+add%k %1, %=" },
     66 	{ Osub,    Ka, "-sub%k %1, %=" },
     67 	{ Oand,    Ki, "+and%k %1, %=" },
     68 	{ Oor,     Ki, "+or%k %1, %=" },
     69 	{ Oxor,    Ki, "+xor%k %1, %=" },
     70 	{ Osar,    Ki, "-sar%k %B1, %=" },
     71 	{ Oshr,    Ki, "-shr%k %B1, %=" },
     72 	{ Oshl,    Ki, "-shl%k %B1, %=" },
     73 	{ Omul,    Ki, "+imul%k %1, %=" },
     74 	{ Omul,    Ks, "+mulss %1, %=" },
     75 	{ Omul,    Kd, "+mulsd %1, %=" },
     76 	{ Odiv,    Ka, "-div%k %1, %=" },
     77 	{ Ostorel, Ka, "movq %L0, %M1" },
     78 	{ Ostorew, Ka, "movl %W0, %M1" },
     79 	{ Ostoreh, Ka, "movw %H0, %M1" },
     80 	{ Ostoreb, Ka, "movb %B0, %M1" },
     81 	{ Ostores, Ka, "movss %S0, %M1" },
     82 	{ Ostored, Ka, "movsd %D0, %M1" },
     83 	{ Oload,   Ka, "mov%k %M0, %=" },
     84 	{ Oloadsw, Kl, "movslq %M0, %L=" },
     85 	{ Oloadsw, Kw, "movl %M0, %W=" },
     86 	{ Oloaduw, Ki, "movl %M0, %W=" },
     87 	{ Oloadsh, Ki, "movsw%k %M0, %=" },
     88 	{ Oloaduh, Ki, "movzw%k %M0, %=" },
     89 	{ Oloadsb, Ki, "movsb%k %M0, %=" },
     90 	{ Oloadub, Ki, "movzb%k %M0, %=" },
     91 	{ Oextsw,  Kl, "movslq %W0, %L=" },
     92 	{ Oextuw,  Kl, "movl %W0, %W=" },
     93 	{ Oextsh,  Ki, "movsw%k %H0, %=" },
     94 	{ Oextuh,  Ki, "movzw%k %H0, %=" },
     95 	{ Oextsb,  Ki, "movsb%k %B0, %=" },
     96 	{ Oextub,  Ki, "movzb%k %B0, %=" },
     97 
     98 	{ Oexts,   Kd, "cvtss2sd %0, %=" },
     99 	{ Otruncd, Ks, "cvtsd2ss %0, %=" },
    100 	{ Ostosi,  Ki, "cvttss2si%k %0, %=" },
    101 	{ Odtosi,  Ki, "cvttsd2si%k %0, %=" },
    102 	{ Oswtof,  Ka, "cvtsi2%k %W0, %=" },
    103 	{ Osltof,  Ka, "cvtsi2%k %L0, %=" },
    104 	{ Ocast,   Ki, "movq %D0, %L=" },
    105 	{ Ocast,   Ka, "movq %L0, %D=" },
    106 
    107 	{ Oaddr,   Ki, "lea%k %M0, %=" },
    108 	{ Oswap,   Ki, "xchg%k %0, %1" },
    109 	{ Osign,   Kl, "cqto" },
    110 	{ Osign,   Kw, "cltd" },
    111 	{ Oxdiv,   Ki, "div%k %0" },
    112 	{ Oxidiv,  Ki, "idiv%k %0" },
    113 	{ Oxcmp,   Ks, "ucomiss %S0, %S1" },
    114 	{ Oxcmp,   Kd, "ucomisd %D0, %D1" },
    115 	{ Oxcmp,   Ki, "cmp%k %0, %1" },
    116 	{ Oxtest,  Ki, "test%k %0, %1" },
    117 #define X(c, s) \
    118 	{ Oflag+c, Ki, "set" s " %B=\n\tmovzb%k %B=, %=" },
    119 	CMP(X)
    120 #undef X
    121 	{ NOp, 0, 0 }
    122 };
    123 
    124 static char *rname[][4] = {
    125 	[RAX] = {"rax", "eax", "ax", "al"},
    126 	[RBX] = {"rbx", "ebx", "bx", "bl"},
    127 	[RCX] = {"rcx", "ecx", "cx", "cl"},
    128 	[RDX] = {"rdx", "edx", "dx", "dl"},
    129 	[RSI] = {"rsi", "esi", "si", "sil"},
    130 	[RDI] = {"rdi", "edi", "di", "dil"},
    131 	[RBP] = {"rbp", "ebp", "bp", "bpl"},
    132 	[RSP] = {"rsp", "esp", "sp", "spl"},
    133 	[R8 ] = {"r8" , "r8d", "r8w", "r8b"},
    134 	[R9 ] = {"r9" , "r9d", "r9w", "r9b"},
    135 	[R10] = {"r10", "r10d", "r10w", "r10b"},
    136 	[R11] = {"r11", "r11d", "r11w", "r11b"},
    137 	[R12] = {"r12", "r12d", "r12w", "r12b"},
    138 	[R13] = {"r13", "r13d", "r13w", "r13b"},
    139 	[R14] = {"r14", "r14d", "r14w", "r14b"},
    140 	[R15] = {"r15", "r15d", "r15w", "r15b"},
    141 };
    142 
    143 
    144 static int
    145 slot(Ref r, Fn *fn)
    146 {
    147 	int s;
    148 
    149 	s = rsval(r);
    150 	assert(s <= fn->slot);
    151 	/* specific to NAlign == 3 */
    152 	if (s < 0)
    153 		return -4 * s;
    154 	else if (fn->vararg)
    155 		return -176 + -4 * (fn->slot - s);
    156 	else
    157 		return -4 * (fn->slot - s);
    158 }
    159 
    160 static void
    161 emitcon(Con *con, FILE *f)
    162 {
    163 	char *p, *l;
    164 
    165 	switch (con->type) {
    166 	case CAddr:
    167 		l = str(con->sym.id);
    168 		p = l[0] == '"' ? "" : T.assym;
    169 		if (con->sym.type == SThr) {
    170 			if (T.apple)
    171 				fprintf(f, "%s%s@TLVP", p, l);
    172 			else
    173 				fprintf(f, "%%fs:%s%s@tpoff", p, l);
    174 		} else
    175 			fprintf(f, "%s%s", p, l);
    176 		if (con->bits.i)
    177 			fprintf(f, "%+"PRId64, con->bits.i);
    178 		break;
    179 	case CBits:
    180 		fprintf(f, "%"PRId64, con->bits.i);
    181 		break;
    182 	default:
    183 		die("unreachable");
    184 	}
    185 }
    186 
    187 static char *
    188 regtoa(int reg, int sz)
    189 {
    190 	static char buf[6];
    191 
    192 	assert(reg <= XMM15);
    193 	if (reg >= XMM0) {
    194 		sprintf(buf, "xmm%d", reg-XMM0);
    195 		return buf;
    196 	} else
    197 		return rname[reg][sz];
    198 }
    199 
    200 static Ref
    201 getarg(char c, Ins *i)
    202 {
    203 	switch (c) {
    204 	case '0':
    205 		return i->arg[0];
    206 	case '1':
    207 		return i->arg[1];
    208 	case '=':
    209 		return i->to;
    210 	default:
    211 		die("invalid arg letter %c", c);
    212 	}
    213 }
    214 
    215 static void emitins(Ins, Fn *, FILE *);
    216 
    217 static void
    218 emitcopy(Ref r1, Ref r2, int k, Fn *fn, FILE *f)
    219 {
    220 	Ins icp;
    221 
    222 	icp.op = Ocopy;
    223 	icp.arg[0] = r2;
    224 	icp.to = r1;
    225 	icp.cls = k;
    226 	emitins(icp, fn, f);
    227 }
    228 
    229 static void
    230 emitf(char *s, Ins *i, Fn *fn, FILE *f)
    231 {
    232 	static char clstoa[][3] = {"l", "q", "ss", "sd"};
    233 	char c;
    234 	int sz;
    235 	Ref ref;
    236 	Mem *m;
    237 	Con off;
    238 
    239 	switch (*s) {
    240 	case '+':
    241 		if (req(i->arg[1], i->to)) {
    242 			ref = i->arg[0];
    243 			i->arg[0] = i->arg[1];
    244 			i->arg[1] = ref;
    245 		}
    246 		/* fall through */
    247 	case '-':
    248 		assert((!req(i->arg[1], i->to) || req(i->arg[0], i->to)) &&
    249 			"cannot convert to 2-address");
    250 		emitcopy(i->to, i->arg[0], i->cls, fn, f);
    251 		s++;
    252 		break;
    253 	}
    254 
    255 	fputc('\t', f);
    256 Next:
    257 	while ((c = *s++) != '%')
    258 		if (!c) {
    259 			fputc('\n', f);
    260 			return;
    261 		} else
    262 			fputc(c, f);
    263 	switch ((c = *s++)) {
    264 	case '%':
    265 		fputc('%', f);
    266 		break;
    267 	case 'k':
    268 		fputs(clstoa[i->cls], f);
    269 		break;
    270 	case '0':
    271 	case '1':
    272 	case '=':
    273 		sz = KWIDE(i->cls) ? SLong : SWord;
    274 		s--;
    275 		goto Ref;
    276 	case 'D':
    277 	case 'S':
    278 		sz = SLong; /* does not matter for floats */
    279 	Ref:
    280 		c = *s++;
    281 		ref = getarg(c, i);
    282 		switch (rtype(ref)) {
    283 		case RTmp:
    284 			assert(isreg(ref));
    285 			fprintf(f, "%%%s", regtoa(ref.val, sz));
    286 			break;
    287 		case RSlot:
    288 			fprintf(f, "%d(%%rbp)", slot(ref, fn));
    289 			break;
    290 		case RMem:
    291 		Mem:
    292 			m = &fn->mem[ref.val];
    293 			if (rtype(m->base) == RSlot) {
    294 				off.type = CBits;
    295 				off.bits.i = slot(m->base, fn);
    296 				addcon(&m->offset, &off);
    297 				m->base = TMP(RBP);
    298 			}
    299 			if (m->offset.type != CUndef)
    300 				emitcon(&m->offset, f);
    301 			fputc('(', f);
    302 			if (!req(m->base, R))
    303 				fprintf(f, "%%%s", regtoa(m->base.val, SLong));
    304 			else if (m->offset.type == CAddr)
    305 				fprintf(f, "%%rip");
    306 			if (!req(m->index, R))
    307 				fprintf(f, ", %%%s, %d",
    308 					regtoa(m->index.val, SLong),
    309 					m->scale
    310 				);
    311 			fputc(')', f);
    312 			break;
    313 		case RCon:
    314 			fputc('$', f);
    315 			emitcon(&fn->con[ref.val], f);
    316 			break;
    317 		default:
    318 			die("unreachable");
    319 		}
    320 		break;
    321 	case 'L':
    322 		sz = SLong;
    323 		goto Ref;
    324 	case 'W':
    325 		sz = SWord;
    326 		goto Ref;
    327 	case 'H':
    328 		sz = SShort;
    329 		goto Ref;
    330 	case 'B':
    331 		sz = SByte;
    332 		goto Ref;
    333 	case 'M':
    334 		c = *s++;
    335 		ref = getarg(c, i);
    336 		switch (rtype(ref)) {
    337 		case RMem:
    338 			goto Mem;
    339 		case RSlot:
    340 			fprintf(f, "%d(%%rbp)", slot(ref, fn));
    341 			break;
    342 		case RCon:
    343 			off = fn->con[ref.val];
    344 			emitcon(&off, f);
    345 			if (off.type == CAddr)
    346 			if (off.sym.type != SThr || T.apple)
    347 				fprintf(f, "(%%rip)");
    348 			break;
    349 		case RTmp:
    350 			assert(isreg(ref));
    351 			fprintf(f, "(%%%s)", regtoa(ref.val, SLong));
    352 			break;
    353 		default:
    354 			die("unreachable");
    355 		}
    356 		break;
    357 	default:
    358 		die("invalid format specifier %%%c", c);
    359 	}
    360 	goto Next;
    361 }
    362 
    363 static void *negmask[4] = {
    364 	[Ks] = (uint32_t[4]){ 0x80000000 },
    365 	[Kd] = (uint64_t[2]){ 0x8000000000000000 },
    366 };
    367 
    368 static void
    369 emitins(Ins i, Fn *fn, FILE *f)
    370 {
    371 	Ref r;
    372 	int64_t val;
    373 	int o, t0;
    374 	Ins ineg;
    375 	Con *con;
    376 	char *sym;
    377 
    378 	switch (i.op) {
    379 	default:
    380 	Table:
    381 		/* most instructions are just pulled out of
    382 		 * the table omap[], some special cases are
    383 		 * detailed below */
    384 		for (o=0;; o++) {
    385 			/* this linear search should really be a binary
    386 			 * search */
    387 			if (omap[o].op == NOp)
    388 				die("no match for %s(%c)",
    389 					optab[i.op].name, "wlsd"[i.cls]);
    390 			if (omap[o].op == i.op)
    391 			if (omap[o].cls == i.cls
    392 			|| (omap[o].cls == Ki && KBASE(i.cls) == 0)
    393 			|| (omap[o].cls == Ka))
    394 				break;
    395 		}
    396 		emitf(omap[o].asm, &i, fn, f);
    397 		break;
    398 	case Onop:
    399 		/* just do nothing for nops, they are inserted
    400 		 * by some passes */
    401 		break;
    402 	case Omul:
    403 		/* here, we try to use the 3-addresss form
    404 		 * of multiplication when possible */
    405 		if (rtype(i.arg[1]) == RCon) {
    406 			r = i.arg[0];
    407 			i.arg[0] = i.arg[1];
    408 			i.arg[1] = r;
    409 		}
    410 		if (KBASE(i.cls) == 0 /* only available for ints */
    411 		&& rtype(i.arg[0]) == RCon
    412 		&& rtype(i.arg[1]) == RTmp) {
    413 			emitf("imul%k %0, %1, %=", &i, fn, f);
    414 			break;
    415 		}
    416 		goto Table;
    417 	case Osub:
    418 		/* we have to use the negation trick to handle
    419 		 * some 3-address subtractions */
    420 		if (req(i.to, i.arg[1]) && !req(i.arg[0], i.to)) {
    421 			ineg = (Ins){Oneg, i.cls, i.to, {i.to}};
    422 			emitins(ineg, fn, f);
    423 			emitf("add%k %0, %=", &i, fn, f);
    424 			break;
    425 		}
    426 		goto Table;
    427 	case Oneg:
    428 		if (!req(i.to, i.arg[0]))
    429 			emitf("mov%k %0, %=", &i, fn, f);
    430 		if (KBASE(i.cls) == 0)
    431 			emitf("neg%k %=", &i, fn, f);
    432 		else
    433 			fprintf(f,
    434 				"\txorp%c %sfp%d(%%rip), %%%s\n",
    435 				"xxsd"[i.cls],
    436 				T.asloc,
    437 				stashbits(negmask[i.cls], 16),
    438 				regtoa(i.to.val, SLong)
    439 			);
    440 		break;
    441 	case Odiv:
    442 		/* use xmm15 to adjust the instruction when the
    443 		 * conversion to 2-address in emitf() would fail */
    444 		if (req(i.to, i.arg[1])) {
    445 			i.arg[1] = TMP(XMM0+15);
    446 			emitf("mov%k %=, %1", &i, fn, f);
    447 			emitf("mov%k %0, %=", &i, fn, f);
    448 			i.arg[0] = i.to;
    449 		}
    450 		goto Table;
    451 	case Ocopy:
    452 		/* copies are used for many things; see my note
    453 		 * to understand how to load big constants:
    454 		 * https://c9x.me/notes/2015-09-19.html */
    455 		assert(rtype(i.to) != RMem);
    456 		if (req(i.to, R) || req(i.arg[0], R))
    457 			break;
    458 		if (req(i.to, i.arg[0]))
    459 			break;
    460 		t0 = rtype(i.arg[0]);
    461 		if (i.cls == Kl
    462 		&& t0 == RCon
    463 		&& fn->con[i.arg[0].val].type == CBits) {
    464 			val = fn->con[i.arg[0].val].bits.i;
    465 			if (isreg(i.to))
    466 			if (val >= 0 && val <= UINT32_MAX) {
    467 				emitf("movl %W0, %W=", &i, fn, f);
    468 				break;
    469 			}
    470 			if (rtype(i.to) == RSlot)
    471 			if (val < INT32_MIN || val > INT32_MAX) {
    472 				emitf("movl %0, %=", &i, fn, f);
    473 				emitf("movl %0>>32, 4+%=", &i, fn, f);
    474 				break;
    475 			}
    476 		}
    477 		if (isreg(i.to)
    478 		&& t0 == RCon
    479 		&& fn->con[i.arg[0].val].type == CAddr) {
    480 			emitf("lea%k %M0, %=", &i, fn, f);
    481 			break;
    482 		}
    483 		if (rtype(i.to) == RSlot
    484 		&& (t0 == RSlot || t0 == RMem)) {
    485 			i.cls = KWIDE(i.cls) ? Kd : Ks;
    486 			i.arg[1] = TMP(XMM0+15);
    487 			emitf("mov%k %0, %1", &i, fn, f);
    488 			emitf("mov%k %1, %=", &i, fn, f);
    489 			break;
    490 		}
    491 		/* conveniently, the assembler knows if it
    492 		 * should use movabsq when reading movq */
    493 		emitf("mov%k %0, %=", &i, fn, f);
    494 		break;
    495 	case Oaddr:
    496 		if (!T.apple
    497 		&& rtype(i.arg[0]) == RCon
    498 		&& fn->con[i.arg[0].val].sym.type == SThr) {
    499 			/* derive the symbol address from the TCB
    500 			 * address at offset 0 of %fs */
    501 			assert(isreg(i.to));
    502 			con = &fn->con[i.arg[0].val];
    503 			sym = str(con->sym.id);
    504 			emitf("movq %%fs:0, %L=", &i, fn, f);
    505 			fprintf(f, "\tleaq %s%s@tpoff",
    506 				sym[0] == '"' ? "" : T.assym, sym);
    507 			if (con->bits.i)
    508 				fprintf(f, "%+"PRId64, con->bits.i);
    509 			fprintf(f, "(%%%s), %%%s\n",
    510 				regtoa(i.to.val, SLong),
    511 				regtoa(i.to.val, SLong));
    512 			break;
    513 		}
    514 		goto Table;
    515 	case Ocall:
    516 		/* calls simply have a weird syntax in AT&T
    517 		 * assembly... */
    518 		switch (rtype(i.arg[0])) {
    519 		case RCon:
    520 			fprintf(f, "\tcallq ");
    521 			emitcon(&fn->con[i.arg[0].val], f);
    522 			fprintf(f, "\n");
    523 			break;
    524 		case RTmp:
    525 			emitf("callq *%L0", &i, fn, f);
    526 			break;
    527 		default:
    528 			die("invalid call argument");
    529 		}
    530 		break;
    531 	case Osalloc:
    532 		/* there is no good reason why this is here
    533 		 * maybe we should split Osalloc in 2 different
    534 		 * instructions depending on the result
    535 		 */
    536 		emitf("subq %L0, %%rsp", &i, fn, f);
    537 		if (!req(i.to, R))
    538 			emitcopy(i.to, TMP(RSP), Kl, fn, f);
    539 		break;
    540 	case Oswap:
    541 		if (KBASE(i.cls) == 0)
    542 			goto Table;
    543 		/* for floats, there is no swap instruction
    544 		 * so we use xmm15 as a temporary
    545 		 */
    546 		emitcopy(TMP(XMM0+15), i.arg[0], i.cls, fn, f);
    547 		emitcopy(i.arg[0], i.arg[1], i.cls, fn, f);
    548 		emitcopy(i.arg[1], TMP(XMM0+15), i.cls, fn, f);
    549 		break;
    550 	case Odbgloc:
    551 		emitdbgloc(i.arg[0].val, f);
    552 		break;
    553 	}
    554 }
    555 
    556 static uint64_t
    557 framesz(Fn *fn)
    558 {
    559 	uint64_t i, o, f;
    560 
    561 	/* specific to NAlign == 3 */
    562 	for (i=0, o=0; i<NCLR; i++)
    563 		o ^= 1 & (fn->reg >> amd64_sysv_rclob[i]);
    564 	f = fn->slot;
    565 	f = (f + 3) & -4;
    566 	return 4*f + 8*o + 176*fn->vararg;
    567 }
    568 
    569 void
    570 amd64_emitfn(Fn *fn, FILE *f)
    571 {
    572 	static char *ctoa[] = {
    573 	#define X(c, s) [c] = s,
    574 		CMP(X)
    575 	#undef X
    576 	};
    577 	static int id0;
    578 	Blk *b, *s;
    579 	Ins *i, itmp;
    580 	int *r, c, o, n, lbl;
    581 	uint64_t fs;
    582 
    583 	emitfnlnk(fn->name, &fn->lnk, f);
    584 	fputs("\tpushq %rbp\n\tmovq %rsp, %rbp\n", f);
    585 	fs = framesz(fn);
    586 	if (fs)
    587 		fprintf(f, "\tsubq $%"PRIu64", %%rsp\n", fs);
    588 	if (fn->vararg) {
    589 		o = -176;
    590 		for (r=amd64_sysv_rsave; r<&amd64_sysv_rsave[6]; r++, o+=8)
    591 			fprintf(f, "\tmovq %%%s, %d(%%rbp)\n", rname[*r][0], o);
    592 		for (n=0; n<8; ++n, o+=16)
    593 			fprintf(f, "\tmovaps %%xmm%d, %d(%%rbp)\n", n, o);
    594 	}
    595 	for (r=amd64_sysv_rclob; r<&amd64_sysv_rclob[NCLR]; r++)
    596 		if (fn->reg & BIT(*r)) {
    597 			itmp.arg[0] = TMP(*r);
    598 			emitf("pushq %L0", &itmp, fn, f);
    599 			fs += 8;
    600 		}
    601 
    602 	for (lbl=0, b=fn->start; b; b=b->link) {
    603 		if (lbl || b->npred > 1)
    604 			fprintf(f, "%sbb%d:\n", T.asloc, id0+b->id);
    605 		for (i=b->ins; i!=&b->ins[b->nins]; i++)
    606 			emitins(*i, fn, f);
    607 		lbl = 1;
    608 		switch (b->jmp.type) {
    609 		case Jhlt:
    610 			fprintf(f, "\tud2\n");
    611 			break;
    612 		case Jret0:
    613 			if (fn->dynalloc)
    614 				fprintf(f,
    615 					"\tmovq %%rbp, %%rsp\n"
    616 					"\tsubq $%"PRIu64", %%rsp\n",
    617 					fs
    618 				);
    619 			for (r=&amd64_sysv_rclob[NCLR]; r>amd64_sysv_rclob;)
    620 				if (fn->reg & BIT(*--r)) {
    621 					itmp.arg[0] = TMP(*r);
    622 					emitf("popq %L0", &itmp, fn, f);
    623 				}
    624 			fprintf(f,
    625 				"\tleave\n"
    626 				"\tret\n"
    627 			);
    628 			break;
    629 		case Jjmp:
    630 		Jmp:
    631 			if (b->s1 != b->link)
    632 				fprintf(f, "\tjmp %sbb%d\n",
    633 					T.asloc, id0+b->s1->id);
    634 			else
    635 				lbl = 0;
    636 			break;
    637 		default:
    638 			c = b->jmp.type - Jjf;
    639 			if (0 <= c && c <= NCmp) {
    640 				if (b->link == b->s2) {
    641 					s = b->s1;
    642 					b->s1 = b->s2;
    643 					b->s2 = s;
    644 				} else
    645 					c = cmpneg(c);
    646 				fprintf(f, "\tj%s %sbb%d\n", ctoa[c],
    647 					T.asloc, id0+b->s2->id);
    648 				goto Jmp;
    649 			}
    650 			die("unhandled jump %d", b->jmp.type);
    651 		}
    652 	}
    653 	id0 += fn->nblk;
    654 	if (!T.apple)
    655 		elf_emitfnfin(fn->name, f);
    656 }