nooc

nooc programming language compiler
git clone git://git.nihaljere.xyz/nooc
Log | Files | Refs | LICENSE

x64.c (27224B)


      1 #include <assert.h>
      2 #include <stdbool.h>
      3 #include <stdint.h>
      4 #include <string.h>
      5 
      6 #include "nooc.h"
      7 #include "stack.h"
      8 #include "ir.h"
      9 #include "util.h"
     10 #include "array.h"
     11 #include "target.h"
     12 
     13 enum reg {
     14 	RAX,
     15 	RCX,
     16 	RDX,
     17 	RBX,
     18 	RSP,
     19 	RBP,
     20 	RSI,
     21 	RDI,
     22 	R8,
     23 	R9,
     24 	R10,
     25 	R11,
     26 	R12,
     27 	R13,
     28 	R14,
     29 	R15,
     30 };
     31 
     32 enum rex {
     33 	REX_B = 0x41,
     34 	REX_X = 0x42,
     35 	REX_R = 0x44,
     36 	REX_W = 0x48,
     37 };
     38 
     39 enum mod {
     40 	MOD_INDIRECT,
     41 	MOD_DISP8,
     42 	MOD_DISP32,
     43 	MOD_DIRECT
     44 };
     45 
     46 #define OP_SIZE_OVERRIDE 0x66
     47 
     48 char abi_arg[] = {RAX, RDI, RSI, RDX, R10, R8, R9};
     49 unsigned short used_reg;
     50 
     51 static size_t
     52 add_r64_imm(struct data *const text, const enum reg dest, const uint64_t imm)
     53 {
     54 	uint8_t temp;
     55 	if (text) {
     56 		array_addlit(text, REX_W);
     57 		array_addlit(text, 0x81);
     58 		array_addlit(text, (MOD_DIRECT << 6) | dest);
     59 		array_addlit(text, imm & 0xFF);
     60 		array_addlit(text, (imm >> 8) & 0xFF);
     61 		array_addlit(text, (imm >> 16) & 0xFF);
     62 		array_addlit(text, (imm >> 24) & 0xFF);
     63 	}
     64 
     65 	return 7;
     66 }
     67 
     68 static size_t
     69 mov_r64_imm(struct data *const text, const enum reg dest, const uint64_t imm)
     70 {
     71 	uint8_t temp;
     72 	if (text) {
     73 		array_addlit(text, REX_W | (dest >= 8 ? REX_B : 0));
     74 		array_addlit(text, 0xb8 + (dest & 0x7));
     75 		array_addlit(text, imm & 0xFF);
     76 		array_addlit(text, (imm >> 8) & 0xFF);
     77 		array_addlit(text, (imm >> 16) & 0xFF);
     78 		array_addlit(text, (imm >> 24) & 0xFF);
     79 		array_addlit(text, (imm >> 32) & 0xFF);
     80 		array_addlit(text, (imm >> 40) & 0xFF);
     81 		array_addlit(text, (imm >> 48) & 0xFF);
     82 		array_addlit(text, (imm >> 56) & 0xFF);
     83 	}
     84 
     85 	return 10;
     86 }
     87 
     88 static size_t
     89 mov_r32_imm(struct data *const text, const enum reg dest, const uint32_t imm)
     90 {
     91 	uint8_t temp;
     92 	if (text) {
     93 		array_addlit(text, 0xb8 + (dest & 0x7));
     94 		array_addlit(text, imm & 0xFF);
     95 		array_addlit(text, (imm >> 8) & 0xFF);
     96 		array_addlit(text, (imm >> 16) & 0xFF);
     97 		array_addlit(text, (imm >> 24) & 0xFF);
     98 	}
     99 
    100 	return 5;
    101 }
    102 
    103 static size_t
    104 mov_r16_imm(struct data *const text, const enum reg dest, const uint16_t imm)
    105 {
    106 	uint8_t temp;
    107 	if (text) {
    108 		array_addlit(text, OP_SIZE_OVERRIDE);
    109 		array_addlit(text, 0xb8);
    110 		array_addlit(text, imm & 0xFF);
    111 		array_addlit(text, (imm >> 8) & 0xFF);
    112 	}
    113 
    114 	return 4;
    115 }
    116 
    117 static size_t
    118 mov_r8_imm(struct data *const text, const enum reg dest, const uint8_t imm)
    119 {
    120 	uint8_t temp;
    121 	if (text) {
    122 		array_addlit(text, 0xb0 + (dest & 0x7));
    123 		array_addlit(text, imm);
    124 	}
    125 
    126 	return 2;
    127 }
    128 
    129 static size_t
    130 mov_r64_m64(struct data *const text, const enum reg dest, const uint64_t addr)
    131 {
    132 	uint8_t temp;
    133 	if (text) {
    134 		array_addlit(text, REX_W | (dest >= 8 ? REX_R : 0));
    135 		array_addlit(text, 0x8b);
    136 		array_addlit(text, (MOD_INDIRECT << 6) | ((dest & 7) << 3) | 4);
    137 		array_addlit(text, 0x25);
    138 		array_addlit(text, addr & 0xFF);
    139 		array_addlit(text, (addr >> 8) & 0xFF);
    140 		array_addlit(text, (addr >> 16) & 0xFF);
    141 		array_addlit(text, (addr >> 24) & 0xFF);
    142 	}
    143 
    144 	return 8;
    145 }
    146 
    147 static size_t
    148 mov_r32_m32(struct data *const text, const enum reg dest, const uint32_t addr)
    149 {
    150 	uint8_t temp;
    151 	if (text) {
    152 		if (dest >= 8) array_addlit(text, REX_R);
    153 		array_addlit(text, 0x8b);
    154 		array_addlit(text, (MOD_INDIRECT << 6) | ((dest & 7) << 3) | 4);
    155 		array_addlit(text, 0x25);
    156 		array_addlit(text, addr & 0xFF);
    157 		array_addlit(text, (addr >> 8) & 0xFF);
    158 		array_addlit(text, (addr >> 16) & 0xFF);
    159 		array_addlit(text, (addr >> 24) & 0xFF);
    160 	}
    161 
    162 	return dest >= 8 ? 8 : 7;
    163 }
    164 
    165 static size_t
    166 mov_r16_m16(struct data *const text, const enum reg dest, const uint32_t addr)
    167 {
    168 	uint8_t temp;
    169 	if (text) {
    170 		array_addlit(text, OP_SIZE_OVERRIDE);
    171 		if (dest >= 8) array_addlit(text, REX_R);
    172 		array_addlit(text, 0x8b);
    173 		array_addlit(text, (MOD_INDIRECT << 6) | ((dest & 7) << 3) | 4);
    174 		array_addlit(text, 0x25);
    175 		array_addlit(text, addr & 0xFF);
    176 		array_addlit(text, (addr >> 8) & 0xFF);
    177 		array_addlit(text, (addr >> 16) & 0xFF);
    178 		array_addlit(text, (addr >> 24) & 0xFF);
    179 	}
    180 
    181 	return dest >= 8 ? 9 : 8;
    182 }
    183 
    184 static size_t
    185 mov_r8_m8(struct data *const text, const enum reg dest, const uint32_t addr)
    186 {
    187 	uint8_t temp;
    188 	if (text) {
    189 		if (dest >= 8) array_addlit(text, REX_R);
    190 		array_addlit(text, 0x8a);
    191 		array_addlit(text, (MOD_INDIRECT << 6) | ((dest & 7) << 3) | 4);
    192 		array_addlit(text, 0x25);
    193 		array_addlit(text, addr & 0xFF);
    194 		array_addlit(text, (addr >> 8) & 0xFF);
    195 		array_addlit(text, (addr >> 16) & 0xFF);
    196 		array_addlit(text, (addr >> 24) & 0xFF);
    197 	}
    198 
    199 	return dest >= 8 ? 8 : 7;
    200 }
    201 
    202 static size_t
    203 mov_m64_r64(struct data *const text, const uint64_t addr, const enum reg src)
    204 {
    205 	uint8_t temp;
    206 	if (text) {
    207 		array_addlit(text, REX_W);
    208 		array_addlit(text, 0xA3);
    209 		array_addlit(text, addr & 0xFF);
    210 		array_addlit(text, (addr >> 8) & 0xFF);
    211 		array_addlit(text, (addr >> 16) & 0xFF);
    212 		array_addlit(text, (addr >> 24) & 0xFF);
    213 		array_addlit(text, (addr >> 32) & 0xFF);
    214 		array_addlit(text, (addr >> 40) & 0xFF);
    215 		array_addlit(text, (addr >> 48) & 0xFF);
    216 		array_addlit(text, (addr >> 56) & 0xFF);
    217 	}
    218 
    219 	return 10;
    220 }
    221 
    222 static size_t
    223 mov_m32_r32(struct data *const text, const uint64_t addr, const enum reg src)
    224 {
    225 	uint8_t temp;
    226 	if (text) {
    227 		array_addlit(text, 0xA3);
    228 		array_addlit(text, addr & 0xFF);
    229 		array_addlit(text, (addr >> 8) & 0xFF);
    230 		array_addlit(text, (addr >> 16) & 0xFF);
    231 		array_addlit(text, (addr >> 24) & 0xFF);
    232 		array_addlit(text, (addr >> 32) & 0xFF);
    233 		array_addlit(text, (addr >> 40) & 0xFF);
    234 		array_addlit(text, (addr >> 48) & 0xFF);
    235 		array_addlit(text, (addr >> 56) & 0xFF);
    236 	}
    237 
    238 	return 9;
    239 }
    240 
    241 #define MOVE_FROMREG 0
    242 #define MOVE_TOREG 1
    243 
    244 static size_t
    245 _move_between_reg_and_memaddr_in_reg(struct data *const text, const enum reg reg, const enum reg mem, const uint8_t opsize, const bool dir)
    246 {
    247 	uint8_t temp, rex = opsize == 8 ? REX_W : 0;
    248 	rex |= (reg >= 8 ? REX_R : 0) | (mem >= 8 ? REX_B : 0);
    249 
    250 	if (text) {
    251 		if (opsize == 2)
    252 			array_addlit(text, OP_SIZE_OVERRIDE);
    253 
    254 		if (rex)
    255 			array_addlit(text, rex);
    256 
    257 		array_addlit(text, 0x88 + (opsize != 1) + 2*dir);
    258 
    259 		array_addlit(text, (MOD_INDIRECT << 6) | ((reg & 7) << 3) | (mem & 7));
    260 	}
    261 
    262 	// 8 and 2 have a length of 3, but 4 and 1 have a length of 2
    263 	return !!rex + (opsize == 2) + 2;
    264 }
    265 
    266 static size_t
    267 mov_mr64_r64(struct data *const text, const enum reg dest, const enum reg src)
    268 {
    269 	return _move_between_reg_and_memaddr_in_reg(text, src, dest, 8, MOVE_FROMREG);
    270 }
    271 
    272 static size_t
    273 mov_mr32_r32(struct data *const text, const enum reg dest, const enum reg src)
    274 {
    275 	return _move_between_reg_and_memaddr_in_reg(text, src, dest, 4, MOVE_FROMREG);
    276 }
    277 
    278 static size_t
    279 mov_mr16_r16(struct data *const text, const enum reg dest, const enum reg src)
    280 {
    281 	return _move_between_reg_and_memaddr_in_reg(text, src, dest, 2, MOVE_FROMREG);
    282 }
    283 
    284 static size_t
    285 mov_mr8_r8(struct data *const text, const enum reg dest, const enum reg src)
    286 {
    287 	return _move_between_reg_and_memaddr_in_reg(text, src, dest, 1, MOVE_FROMREG);
    288 }
    289 
    290 static size_t
    291 mov_r64_mr64(struct data *const text, const enum reg dest, const enum reg src)
    292 {
    293 	return _move_between_reg_and_memaddr_in_reg(text, dest, src, 8, MOVE_TOREG);
    294 }
    295 
    296 static size_t
    297 mov_r32_mr32(struct data *const text, const enum reg dest, const enum reg src)
    298 {
    299 	return _move_between_reg_and_memaddr_in_reg(text, dest, src, 4, MOVE_TOREG);
    300 }
    301 
    302 static size_t
    303 mov_r16_mr16(struct data *const text, const enum reg dest, const enum reg src)
    304 {
    305 	return _move_between_reg_and_memaddr_in_reg(text, dest, src, 2, MOVE_TOREG);
    306 }
    307 
    308 static size_t
    309 mov_r8_mr8(struct data *const text, const enum reg dest, const enum reg src)
    310 {
    311 	return _move_between_reg_and_memaddr_in_reg(text, dest, src, 1, MOVE_TOREG);
    312 }
    313 
    314 static size_t
    315 _move_between_reg_and_reg(struct data *const text, const enum reg dest, const enum reg src, const uint8_t opsize)
    316 {
    317 	uint8_t temp, rex = (src >= 8 ? REX_R : 0) | (dest >= 8 ? REX_B : 0) | (opsize == 8 ? REX_W : 0);
    318 	if (text) {
    319 		if (opsize == 2)
    320 			array_addlit(text, OP_SIZE_OVERRIDE);
    321 
    322 		if (rex)
    323 			array_addlit(text, rex);
    324 
    325 		array_addlit(text, 0x88 + (opsize != 1));
    326 		array_addlit(text, (MOD_DIRECT << 6) | ((src & 7) << 3) | (dest & 7));
    327 	}
    328 
    329 	return 2 + !!rex + (opsize == 2);
    330 }
    331 
    332 static size_t
    333 mov_r64_r64(struct data *const text, const enum reg dest, const enum reg src)
    334 {
    335 	return _move_between_reg_and_reg(text, dest, src, 8);
    336 }
    337 
    338 static size_t
    339 mov_r32_r32(struct data *const text, const enum reg dest, const enum reg src)
    340 {
    341 	return _move_between_reg_and_reg(text, dest, src, 4);
    342 }
    343 
    344 static size_t
    345 mov_r16_r16(struct data *const text, const enum reg dest, const enum reg src)
    346 {
    347 	return _move_between_reg_and_reg(text, dest, src, 2);
    348 }
    349 
    350 static size_t
    351 mov_r8_r8(struct data *const text, const enum reg dest, const enum reg src)
    352 {
    353 	return _move_between_reg_and_reg(text, dest, src, 1);
    354 }
    355 
    356 static size_t
    357 _move_between_reg_and_memaddr_in_reg_with_disp(struct data *const text, const enum reg reg, const enum reg mem, const int8_t disp, const uint8_t opsize, const bool dir)
    358 {
    359 	assert((reg & 7) != 4 && (mem & 7) != 4);
    360 	uint8_t temp, rex = opsize == 8 ? REX_W : 0;
    361 	rex |= (reg >= 8 ? REX_R : 0) | (mem >= 8 ? REX_B : 0);
    362 
    363 	if (text) {
    364 		if (opsize == 2)
    365 			array_addlit(text, OP_SIZE_OVERRIDE);
    366 
    367 		if (rex)
    368 			array_addlit(text, rex);
    369 
    370 		array_addlit(text, 0x88 + (opsize != 1) + 2*dir);
    371 
    372 		array_addlit(text, (MOD_DISP8 << 6) | ((reg & 7) << 3) | (mem & 7));
    373 		array_addlit(text, disp);
    374 	}
    375 
    376 	// 8 and 2 have a length of 3, but 4 and 1 have a length of 2
    377 	return !!rex + (opsize == 2) + 3;
    378 }
    379 
    380 static size_t
    381 mov_disp8_m64_r64(struct data *const text, const enum reg dest, const int8_t disp, const enum reg src)
    382 {
    383 	return _move_between_reg_and_memaddr_in_reg_with_disp(text, src, dest, disp, 8, MOVE_FROMREG);
    384 }
    385 
    386 static size_t
    387 mov_disp8_m32_r32(struct data *const text, const enum reg dest, const int8_t disp, const enum reg src)
    388 {
    389 	return _move_between_reg_and_memaddr_in_reg_with_disp(text, src, dest, disp, 4, MOVE_FROMREG);
    390 }
    391 
    392 static size_t
    393 mov_disp8_m16_r16(struct data *const text, const enum reg dest, const int8_t disp, const enum reg src)
    394 {
    395 	return _move_between_reg_and_memaddr_in_reg_with_disp(text, src, dest, disp, 2, MOVE_FROMREG);
    396 }
    397 
    398 static size_t
    399 mov_disp8_m8_r8(struct data *const text, const enum reg dest, const int8_t disp, const enum reg src)
    400 {
    401 	return _move_between_reg_and_memaddr_in_reg_with_disp(text, src, dest, disp, 1, MOVE_FROMREG);
    402 }
    403 
    404 static size_t
    405 mov_disp8_r64_m64(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp)
    406 {
    407 	return _move_between_reg_and_memaddr_in_reg_with_disp(text, dest, src, disp, 8, MOVE_TOREG);
    408 }
    409 
    410 static size_t
    411 mov_disp8_r32_m32(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp)
    412 {
    413 	return _move_between_reg_and_memaddr_in_reg_with_disp(text, dest, src, disp, 4, MOVE_TOREG);
    414 }
    415 
    416 static size_t
    417 mov_disp8_r16_m16(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp)
    418 {
    419 	return _move_between_reg_and_memaddr_in_reg_with_disp(text, dest, src, disp, 2, MOVE_TOREG);
    420 }
    421 
    422 static size_t
    423 mov_disp8_r8_m8(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp)
    424 {
    425 	return _move_between_reg_and_memaddr_in_reg_with_disp(text, dest, src, disp, 1, MOVE_TOREG);
    426 }
    427 
    428 static size_t
    429 _movezx_reg_to_reg(struct data *const text, const uint8_t destsize, const uint8_t srcsize, const enum reg dest, const enum reg src)
    430 {
    431 	assert(srcsize == 1 || srcsize == 2);
    432 	assert(destsize == 1 || destsize == 2 || destsize == 4 || destsize == 8);
    433 	uint8_t temp;
    434 	uint8_t rex = (destsize == 8 ? REX_W : 0) | (dest >= 8 ? REX_R : 0) | (src >= 8 ? REX_B : 0);
    435 	if (text) {
    436 		if (destsize == 2)
    437 			array_addlit(text, OP_SIZE_OVERRIDE);
    438 
    439 		if (rex)
    440 			array_addlit(text, rex);
    441 
    442 		array_addlit(text, 0x0F);
    443 		array_addlit(text, 0xB6 + (srcsize == 2));
    444 		array_addlit(text, (MOD_DIRECT << 6) | (dest << 3) | src);
    445 	}
    446 
    447 	return 3 + !!rex + (destsize == 2);
    448 }
    449 
    450 static size_t
    451 movzx_r64_r8(struct data *const text, const enum reg dest, const enum reg src)
    452 {
    453 	return _movezx_reg_to_reg(text, 8, 1, dest, src);
    454 }
    455 
    456 static size_t
    457 movzx_r32_r8(struct data *const text, const enum reg dest, const enum reg src)
    458 {
    459 	return _movezx_reg_to_reg(text, 4, 1, dest, src);
    460 }
    461 
    462 static size_t
    463 movzx_r16_r8(struct data *const text, const enum reg dest, const enum reg src)
    464 {
    465 	return _movezx_reg_to_reg(text, 2, 1, dest, src);
    466 }
    467 
    468 static size_t
    469 movzx_r64_r16(struct data *const text, const enum reg dest, const enum reg src)
    470 {
    471 	return _movezx_reg_to_reg(text, 8, 2, dest, src);
    472 }
    473 
    474 static size_t
    475 movzx_r32_r16(struct data *const text, const enum reg dest, const enum reg src)
    476 {
    477 	return _movezx_reg_to_reg(text, 4, 2, dest, src);
    478 }
    479 
    480 static size_t
    481 lea_disp8(struct data *const text, const enum reg dest, const enum reg src, const int8_t disp)
    482 {
    483 	uint8_t temp;
    484 	assert(src != 4);
    485 	if (text) {
    486 		array_addlit(text, REX_W);
    487 		array_addlit(text, 0x8d);
    488 		array_addlit(text, (MOD_DISP8 << 6) | (dest << 3) | src);
    489 		array_addlit(text, disp);
    490 	}
    491 
    492 	return 4;
    493 }
    494 
    495 static size_t
    496 add_r64_r64(struct data *const text, const enum reg dest, const enum reg src)
    497 {
    498 	uint8_t temp;
    499 	if (text) {
    500 		array_addlit(text, REX_W);
    501 		array_addlit(text, 0x03);
    502 		array_addlit(text, (MOD_DIRECT << 6) | (dest << 3) | src);
    503 	}
    504 
    505 	return 3;
    506 }
    507 
    508 static size_t
    509 sub_r64_r64(struct data *const text, const enum reg dest, const enum reg src)
    510 {
    511 	uint8_t temp;
    512 	if (text) {
    513 		array_addlit(text, REX_W);
    514 		array_addlit(text, 0x2b);
    515 		array_addlit(text, (MOD_DIRECT << 6) | (dest << 3) | src);
    516 	}
    517 
    518 	return 3;
    519 }
    520 
    521 static size_t
    522 sub_r64_imm(struct data *const text, const enum reg dest, int32_t imm)
    523 {
    524 	uint8_t temp;
    525 	if (text) {
    526 		array_addlit(text, REX_W);
    527 		array_addlit(text, 0x81);
    528 		array_addlit(text, (MOD_DIRECT << 6) | (5 << 3) | dest);
    529 		array_addlit(text, imm & 0xFF);
    530 		array_addlit(text, (imm >> 8) & 0xFF);
    531 		array_addlit(text, (imm >> 16) & 0xFF);
    532 		array_addlit(text, (imm >> 24) & 0xFF);
    533 	}
    534 
    535 	return 7;
    536 }
    537 
    538 static size_t
    539 _cmp_reg_to_reg(struct data *const text, const uint8_t size, const enum reg reg1, const enum reg reg2)
    540 {
    541 	uint8_t temp;
    542 	uint8_t rex = (size == 8 ? REX_W : 0) | (reg1 >= 8 ? REX_R : 0) | (reg2 >= 8 ? REX_B : 0);
    543 	if (text) {
    544 		if (size == 2)
    545 			array_addlit(text, OP_SIZE_OVERRIDE);
    546 
    547 		if (rex)
    548 			array_addlit(text, rex);
    549 
    550 		array_addlit(text, 0x3A + (size != 1));
    551 		array_addlit(text, (MOD_DIRECT << 6) | (reg1 << 3) | reg2);
    552 	}
    553 
    554 	return 2 + !!rex + (size == 2);
    555 }
    556 
    557 static size_t
    558 cmp_r64_r64(struct data *const text, const enum reg reg1, const enum reg reg2)
    559 {
    560 	return _cmp_reg_to_reg(text, 8, reg1, reg2);
    561 }
    562 
    563 static size_t
    564 cmp_r32_r32(struct data *const text, const enum reg reg1, const enum reg reg2)
    565 {
    566 	return _cmp_reg_to_reg(text, 4, reg1, reg2);
    567 }
    568 
    569 static size_t
    570 cmp_r16_r16(struct data *const text, const enum reg reg1, const enum reg reg2)
    571 {
    572 	return _cmp_reg_to_reg(text, 2, reg1, reg2);
    573 }
    574 
    575 static size_t
    576 cmp_r8_r8(struct data *const text, const enum reg reg1, const enum reg reg2)
    577 {
    578 	return _cmp_reg_to_reg(text, 1, reg1, reg2);
    579 }
    580 
    581 static size_t
    582 cmp_r8_imm(struct data *const text, const enum reg reg, const uint8_t imm)
    583 {
    584 	uint8_t temp;
    585 	if (text) {
    586 		if (reg >= 8)
    587 			array_addlit(text, REX_B);
    588 		array_addlit(text, 0x80);
    589 		array_addlit(text, (MOD_DIRECT << 6) | (7 << 3) | (reg & 7));
    590 		array_addlit(text, imm);
    591 	}
    592 
    593 	return 3 + !(reg < 8);
    594 }
    595 
    596 static size_t
    597 jng(struct data *const text, const int64_t offset)
    598 {
    599 	uint8_t temp;
    600 	if (-256 <= offset && offset <= 255) {
    601 		int8_t i = offset;
    602 		if (text) {
    603 			array_addlit(text, 0x7E);
    604 			array_addlit(text, i);
    605 		}
    606 		return 2;
    607 	} else {
    608 		die("unimplemented jng offet!");
    609 	}
    610 
    611 	return 0; // prevents warning
    612 }
    613 
    614 static size_t
    615 jg(struct data *const text, const int64_t offset)
    616 {
    617 	uint8_t temp;
    618 	if (-256 <= offset && offset <= 255) {
    619 		int8_t i = offset;
    620 		if (text) {
    621 			array_addlit(text, 0x7F);
    622 			array_addlit(text, i);
    623 		}
    624 		return 2;
    625 	} else {
    626 		die("unimplemented jg offet!");
    627 	}
    628 
    629 	return 0; // prevents warning
    630 }
    631 
    632 static size_t
    633 jne(struct data *const text, const int64_t offset)
    634 {
    635 	uint8_t temp;
    636 	if (-256 <= offset && offset <= 255) {
    637 		int8_t i = offset;
    638 		if (text) {
    639 			array_addlit(text, 0x75);
    640 			array_addlit(text, i);
    641 		}
    642 		return 2;
    643 	} else {
    644 		die("unimplemented jne offet!");
    645 	}
    646 
    647 	return 0; // prevents warning
    648 }
    649 
    650 static size_t
    651 je(struct data *const text, const int64_t offset)
    652 {
    653 	uint8_t temp;
    654 	if (-128 <= offset && offset <= 127) {
    655 		int8_t i = offset;
    656 		if (text) {
    657 			array_addlit(text, 0x74);
    658 			array_add(text, i);
    659 		}
    660 		return 2;
    661 	} else if (-2147483648 <= offset && offset <= 2147483647) {
    662 		int32_t i = offset;
    663 		if (text) {
    664 			array_addlit(text, 0x0F);
    665 			array_addlit(text, 0x84);
    666 			array_addlit(text, ((uint32_t) i) & 0xFF);
    667 			array_addlit(text, (((uint32_t) i) >> 8) & 0xFF);
    668 			array_addlit(text, (((uint32_t) i) >> 16) & 0xFF);
    669 			array_addlit(text, (((uint32_t) i) >> 24) & 0xFF);
    670 		}
    671 		return 6;
    672 	} else {
    673 		die("unimplemented je offet!");
    674 	}
    675 
    676 	return 0; // prevents warning
    677 }
    678 
    679 static size_t
    680 sete_reg(struct data *const text, const enum reg reg)
    681 {
    682 	uint8_t temp;
    683 	if (text) {
    684 		if (reg >= 8) array_addlit(text, REX_B);
    685 		array_addlit(text, 0x0F);
    686 		array_addlit(text, 0x94);
    687 		array_addlit(text, (MOD_DIRECT << 6) | (reg & 7));
    688 	}
    689 
    690 	return 3 + !(reg < 8);
    691 }
    692 
    693 static size_t
    694 setne_reg(struct data *const text, const enum reg reg)
    695 {
    696 	uint8_t temp;
    697 	if (text) {
    698 		if (reg >= 8) array_addlit(text, REX_B);
    699 		array_addlit(text, 0x0F);
    700 		array_addlit(text, 0x95);
    701 		array_addlit(text, (MOD_DIRECT << 6) | (reg & 7));
    702 	}
    703 
    704 	return 3 + !(reg < 8);
    705 }
    706 
    707 static size_t
    708 jmp(struct data *const text, const int64_t offset)
    709 {
    710 	uint8_t temp;
    711 	if (-2147483648 <= offset && offset <= 2147483647) {
    712 		int32_t i = offset;
    713 		if (text) {
    714 			array_addlit(text, 0xE9);
    715 			array_addlit(text, ((uint32_t) i) & 0xFF);
    716 			array_addlit(text, (((uint32_t) i) >> 8) & 0xFF);
    717 			array_addlit(text, (((uint32_t) i) >> 16) & 0xFF);
    718 			array_addlit(text, (((uint32_t) i) >> 24) & 0xFF);
    719 		}
    720 		return 5;
    721 	} else {
    722 		die("unimplemented jmp offet!");
    723 	}
    724 
    725 	return 0; // prevents warning
    726 }
    727 
    728 static size_t
    729 call(struct data *const text, const int32_t offset)
    730 {
    731 	uint8_t temp;
    732 	if (text) {
    733 		array_addlit(text, 0xE8);
    734 		array_addlit(text, (uint32_t) offset & 0xff);
    735 		array_addlit(text, ((uint32_t) offset >> 8) & 0xff);
    736 		array_addlit(text, ((uint32_t) offset >> 16) & 0xff);
    737 		array_addlit(text, ((uint32_t) offset >> 24) & 0xff);
    738 	}
    739 
    740 	return 5;
    741 }
    742 
    743 static size_t
    744 ret(struct data *const text)
    745 {
    746 	uint8_t temp;
    747 	if (text)
    748 		array_addlit(text, 0xC3);
    749 
    750 	return 1;
    751 }
    752 
    753 static size_t
    754 _pushpop_r64(struct data *const text, const uint8_t ioff, const enum reg reg)
    755 {
    756 	uint8_t temp;
    757 	if (text) {
    758 		if (reg >= 8)
    759 			array_addlit(text, REX_B);
    760 
    761 		array_addlit(text, 0x50 + ioff + (reg & 7));
    762 	}
    763 
    764 	return reg >= 8 ? 2 : 1;
    765 }
    766 
    767 static size_t
    768 push_r64(struct data *const text, const enum reg reg)
    769 {
    770 	return _pushpop_r64(text, 0, reg);
    771 }
    772 
    773 static size_t
    774 pop_r64(struct data *const text, const enum reg reg)
    775 {
    776 	return _pushpop_r64(text, 8, reg);
    777 }
    778 
    779 static size_t emitsyscall(struct data *const text, const uint8_t paramcount);
    780 static size_t emitproc(struct data *const text, const struct iproc *const proc);
    781 
    782 const struct target x64_target = {
    783 	.reserved = (1 << RSP) | (1 << RBP) | (1 << R12) | (1 << R13),
    784 	.emitsyscall = emitsyscall,
    785 	.emitproc = emitproc
    786 };
    787 
    788 #define NEXT ins++; assert(ins <= end);
    789 
    790 static size_t
    791 emitsyscall(struct data *const text, const uint8_t paramcount)
    792 {
    793 	assert(paramcount < 8);
    794 	size_t total = 0;
    795 	uint8_t temp;
    796 	total += push_r64(text, RBP);
    797 	total += mov_r64_r64(text, RBP, RSP);
    798 
    799 	for (size_t i = 0; i < paramcount; i++) {
    800 		total += push_r64(text, abi_arg[i]);
    801 		total += mov_disp8_r64_m64(text, abi_arg[i], RBP, 8*i + 16);
    802 	}
    803 
    804 	if (text) {
    805 		array_addlit(text, 0x0f);
    806 		array_addlit(text, 0x05);
    807 	}
    808 
    809 	total += 2;
    810 
    811 	total += mov_disp8_r64_m64(text, RDI, RBP, 8*paramcount + 16);
    812 	total += mov_mr64_r64(text, RDI, RAX);
    813 
    814 	for (size_t i = paramcount - 1; i < paramcount; i--) {
    815 		total += pop_r64(text, abi_arg[i]);
    816 	}
    817 
    818 	total += pop_r64(text, RBP);
    819 	total += ret(text);
    820 
    821 	return total;
    822 }
    823 
    824 size_t
    825 emitblock(struct data *const text, const struct iproc *const proc, const struct instr *const start, const struct instr *end, uint16_t active, uint16_t curi)
    826 {
    827 	const struct instr *ins = start ? start : proc->data;
    828 	end = end ? end : &proc->data[proc->len];
    829 
    830 	uint64_t dest, src, size, count, label;
    831 	int64_t offset;
    832 	uint64_t localalloc = 0;
    833 
    834 	size_t total = 0;
    835 	if (!start) {
    836 		total += push_r64(text, RBP);
    837 		total += mov_r64_r64(text, RBP, RSP);
    838 	}
    839 
    840 	while (ins < end) {
    841 		curi++;
    842 		for (size_t j = 0; j < proc->temps.len; j++) {
    843 			if ((active & (1 << j)) && proc->temps.data[j].end == curi - 1)
    844 				active &= ~(1 << proc->temps.data[j].reg);
    845 
    846 			if (!(active & (1 << j)) && proc->temps.data[j].start == curi)
    847 				active |= 1 << proc->temps.data[j].reg;
    848 		}
    849 		switch (ins->op) {
    850 		case IR_JUMP:
    851 			assert(ins->valtype == VT_LABEL);
    852 			label = ins->val;
    853 			if (ins < &proc->data[proc->labels.data[label]]) {
    854 				total += jmp(text, emitblock(NULL, proc, ins + 1, &proc->data[proc->labels.data[label]], active, curi));
    855 			} else {
    856 				total += jmp(text, emitblock(NULL, proc, start, &proc->data[proc->labels.data[label]], 0, 0) - total - 5);
    857 			}
    858 			NEXT;
    859 			break;
    860 		case IR_CONDJUMP:
    861 			assert(ins->valtype == VT_LABEL);
    862 			curi++;
    863 			label = ins->val;
    864 			NEXT;
    865 			assert(ins->op == IR_EXTRA);
    866 			assert(ins->valtype == VT_TEMP);
    867 			total += cmp_r8_imm(text, proc->temps.data[ins->val].reg, 0);
    868 			if (ins < &proc->data[proc->labels.data[label]]) {
    869 				total += je(text, emitblock(NULL, proc, ins + 1, &proc->data[proc->labels.data[label]], active, curi));
    870 			} else {
    871 				total += je(text, emitblock(NULL, proc, start, &proc->data[proc->labels.data[label]], 0, 0) - total - 2); // FIXME: 2 = size of short jump
    872 			}
    873 			NEXT;
    874 			break;
    875 		case IR_RETURN:
    876 			assert(ins->valtype == VT_EMPTY);
    877 			total += add_r64_imm(text, RSP, localalloc);
    878 			total += pop_r64(text, RBP);
    879 			total += ret(text);
    880 			NEXT;
    881 			break;
    882 		case IR_STORE:
    883 			assert(ins->valtype == VT_TEMP);
    884 			src = proc->temps.data[ins->val].reg;
    885 			NEXT;
    886 			assert(ins->op == IR_EXTRA);
    887 			assert(ins->valtype == VT_TEMP);
    888 			switch (proc->temps.data[ins->val].size) {
    889 			case 8:
    890 				total += mov_mr64_r64(text, proc->temps.data[ins->val].reg, src);
    891 				break;
    892 			case 4:
    893 				total += mov_mr32_r32(text, proc->temps.data[ins->val].reg, src);
    894 				break;
    895 			case 2:
    896 				total += mov_mr16_r16(text, proc->temps.data[ins->val].reg, src);
    897 				break;
    898 			case 1:
    899 				total += mov_mr8_r8(text, proc->temps.data[ins->val].reg, src);
    900 				break;
    901 			default:
    902 				die("x64: emitblock: IR_STORE: bad size");
    903 			}
    904 			NEXT;
    905 			break;
    906 		case IR_ASSIGN:
    907 			assert(ins->valtype == VT_TEMP);
    908 			dest = proc->temps.data[ins->val].reg;
    909 			size = proc->temps.data[ins->val].size;
    910 			NEXT;
    911 
    912 			switch (ins->op) {
    913 			case IR_NOT:
    914 				assert(ins->valtype == VT_TEMP);
    915 				assert(size == 1);
    916 				total += cmp_r8_imm(text, proc->temps.data[ins->val].reg, 1);
    917 				total += setne_reg(text, dest);
    918 				NEXT;
    919 				break;
    920 			case IR_CEQ:
    921 				assert(ins->valtype == VT_TEMP);
    922 				src = proc->temps.data[ins->val].reg;
    923 				NEXT;
    924 				assert(ins->op == IR_EXTRA);
    925 				assert(ins->valtype == VT_TEMP);
    926 				switch (size) {
    927 				case 8:
    928 					total += cmp_r64_r64(text, src, proc->temps.data[ins->val].reg);
    929 					break;
    930 				case 4:
    931 					total += cmp_r32_r32(text, src, proc->temps.data[ins->val].reg);
    932 					break;
    933 				case 2:
    934 					total += cmp_r16_r16(text, src, proc->temps.data[ins->val].reg);
    935 					break;
    936 				case 1:
    937 					total += cmp_r8_r8(text, src, proc->temps.data[ins->val].reg);
    938 					break;
    939 				default:
    940 					die("x64 emitblock: IR_CEQ: bad size");
    941 				}
    942 				total += sete_reg(text, dest);
    943 				NEXT;
    944 				break;
    945 			case IR_ADD:
    946 				assert(ins->valtype == VT_TEMP);
    947 				total += mov_r64_r64(text, dest, proc->temps.data[ins->val].reg);
    948 				NEXT;
    949 				assert(ins->op == IR_EXTRA);
    950 				assert(ins->valtype == VT_TEMP);
    951 				total += add_r64_r64(text, dest, proc->temps.data[ins->val].reg);
    952 				NEXT;
    953 				break;
    954 			case IR_ZEXT:
    955 				assert(ins->valtype == VT_TEMP);
    956 				if (size == 8) {
    957 					switch (proc->temps.data[ins->val].size) {
    958 					case 1:
    959 						total += movzx_r64_r8(text, dest, proc->temps.data[ins->val].reg);
    960 						break;
    961 					case 2:
    962 						total += movzx_r64_r16(text, dest, proc->temps.data[ins->val].reg);
    963 						break;
    964 					case 4: // upper 32-bits get cleared automatically in x64
    965 						total += mov_r32_r32(text, dest, proc->temps.data[ins->val].reg);
    966 						break;
    967 					default:
    968 						die("x64 emitblock: IR_ZEXT size 8: bad size");
    969 					}
    970 				} else if (size == 4) {
    971 					switch (proc->temps.data[ins->val].size) {
    972 					case 1:
    973 						total += movzx_r32_r8(text, dest, proc->temps.data[ins->val].reg);
    974 						break;
    975 					case 2:
    976 						total += movzx_r32_r16(text, dest, proc->temps.data[ins->val].reg);
    977 						break;
    978 					case 4: // upper 32-bits get cleared automatically in x64
    979 						total += mov_r32_r32(text, dest, proc->temps.data[ins->val].reg);
    980 						break;
    981 					default:
    982 						die("x64 emitblock: IR_ZEXT size 4: bad size");
    983 					}
    984 				} else if (size == 2) {
    985 					switch (proc->temps.data[ins->val].size) {
    986 					case 1:
    987 						total += movzx_r16_r8(text, dest, proc->temps.data[ins->val].reg);
    988 						break;
    989 					default:
    990 						die("x64 emitblock: IR_ZEXT size 2: bad size");
    991 					}
    992 				} else die("x64 emitblock: IR_ZEXT cannot zero extend to 1 byte");
    993 				NEXT;
    994 				break;
    995 			case IR_IMM:
    996 				assert(ins->valtype == VT_IMM);
    997 				total += mov_r64_imm(text, dest, ins->val);
    998 				NEXT;
    999 				break;
   1000 			case IR_IN:
   1001 				total += mov_disp8_r64_m64(text, dest, RBP, 8*ins->val + 16);
   1002 				NEXT;
   1003 				break;
   1004 			case IR_LOAD:
   1005 				assert(ins->valtype == VT_TEMP);
   1006 				switch (size) {
   1007 				case 8:
   1008 					total += mov_r64_mr64(text, dest, proc->temps.data[ins->val].reg);
   1009 					break;
   1010 				case 4:
   1011 					total += mov_r32_mr32(text, dest, proc->temps.data[ins->val].reg);
   1012 					break;
   1013 				case 2:
   1014 					total += mov_r16_mr16(text, dest, proc->temps.data[ins->val].reg);
   1015 					break;
   1016 				case 1:
   1017 					total += mov_r8_mr8(text, dest, proc->temps.data[ins->val].reg);
   1018 					break;
   1019 				default:
   1020 					die("x64 emitblock: IR_LOAD: bad size");
   1021 				}
   1022 				NEXT;
   1023 				break;
   1024 			case IR_ALLOC:
   1025 				assert(ins->valtype == VT_IMM);
   1026 				total += mov_r64_r64(text, dest, RSP);
   1027 				total += sub_r64_imm(text, RSP, 8); // FIXME: hardcoding
   1028 				localalloc += 8;
   1029 				NEXT;
   1030 				break;
   1031 			default:
   1032 				die("x64 emitblock: unhandled assign instruction");
   1033 			}
   1034 			break;
   1035 		case IR_CALL:
   1036 			assert(ins->valtype == VT_FUNC);
   1037 			count = 0;
   1038 			dest = ins->val;
   1039 
   1040 			for (int i = 0; i < 16; i++) {
   1041 				if (active & (1 << i)) {
   1042 					total += push_r64(text, i);
   1043 				}
   1044 			}
   1045 
   1046 			NEXT;
   1047 			while (ins < end && ins->op == IR_CALLARG) {
   1048 				assert(ins->valtype == VT_TEMP);
   1049 				count++;
   1050 				total += push_r64(text, proc->temps.data[ins->val].reg);
   1051 				NEXT;
   1052 			}
   1053 
   1054 			// we assume call is constant width - this should probably change
   1055 			offset = -(proc->addr + total - toplevel.code.data[dest].addr + call(NULL, 0));
   1056 			total += call(text, offset);
   1057 			// FIXME: this won't work with non-64-bit things
   1058 			total += add_r64_imm(text, RSP, 8*count);
   1059 			for (int i = 15; i >= 0; i--) {
   1060 				if (active & (1 << i)) {
   1061 					total += pop_r64(text, i);
   1062 				}
   1063 			}
   1064 			break;
   1065 		case IR_LABEL:
   1066 			assert(ins->valtype == VT_LABEL);
   1067 			NEXT;
   1068 			break;
   1069 		case IR_IMM:
   1070 		case IR_ALLOC:
   1071 			die("x64 emitblock: invalid start of instruction");
   1072 		default:
   1073 			die("x64 emitblock: unknown instruction");
   1074 		}
   1075 	}
   1076 
   1077 	return total;
   1078 }
   1079 
   1080 size_t
   1081 emitproc(struct data *const text, const struct iproc *const proc)
   1082 {
   1083 	return emitblock(text, proc, NULL, NULL, 0, 0);
   1084 }