History

Most x86-64 tests now work; only on error in test1-3.

I've had to introduce the XMM1 register to get the calling convention to work properly, unfortunately this has broken a fair bit of code which assumes that only XMM0 is used.

James Lyon committed 13 years ago

Commit 0e176711 parent b961ba53 files · +129 −67Browse files

Unified · Split

3 files
  1. tccgen.c +47 −22
  2. tests/abitest.c +2 −2
  3. x86_64-gen.c +80 −43
tccgen.c+47−22
@@ -88,7 +88,7 @@ ST_INLN int is_float(int t) · 87 unmodified lines
8888 {
8989 int bt;
9090 bt = t & VT_BTYPE;
91− return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT;
91+ return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT || bt == VT_QFLOAT;
9292 }
9393
9494 /* we use our own 'finite' function to avoid potential problems with
@@ -688,9 +688,7 @@ static void gbound(void) · 593 unmodified lines
688688 ST_FUNC int gv(int rc)
689689 {
690690 int r, bit_pos, bit_size, size, align, i;
691−#ifndef TCC_TARGET_X86_64
692691 int rc2;
693−#endif
694692
695693 /* NOTE: get_reg can modify vstack[] */
696694 if (vtop->type.t & VT_BITFIELD) {
@@ -765,11 +763,14 @@ ST_FUNC int gv(int rc) · 68 unmodified lines
765763 #endif
766764
767765 r = vtop->r & VT_VALMASK;
768−#ifndef TCC_TARGET_X86_64
769766 rc2 = RC_INT;
770767 if (rc == RC_IRET)
771768 rc2 = RC_LRET;
769+#ifdef TCC_TARGET_X86_64
770+ else if (rc == RC_FRET)
771+ rc2 = RC_QRET;
772772 #endif
773+
773774 /* need to reload if:
774775 - constant
775776 - lvalue (need to dereference pointer)
@@ -777,18 +778,25 @@ ST_FUNC int gv(int rc) · 1 unmodified line
777778 if (r >= VT_CONST
778779 || (vtop->r & VT_LVAL)
779780 || !(reg_classes[r] & rc)
780−#ifndef TCC_TARGET_X86_64
781+#ifdef TCC_TARGET_X86_64
782+ || ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2))
783+ || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2))
784+#else
781785 || ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2))
782786 #endif
783787 )
784788 {
785789 r = get_reg(rc);
786−#ifndef TCC_TARGET_X86_64
790+#ifdef TCC_TARGET_X86_64
791+ if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) {
792+#else
787793 if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
794+#endif
788795 int r2;
789796 unsigned long long ll;
790797 /* two register type load : expand to two words
791798 temporarily */
799+#ifndef TCC_TARGET_X86_64
792800 if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
793801 /* load constant */
794802 ll = vtop->c.ull;
@@ -796,23 +804,32 @@ ST_FUNC int gv(int rc) · 1 unmodified line
796804 load(r, vtop);
797805 vtop->r = r; /* save register value */
798806 vpushi(ll >> 32); /* second word */
799− } else if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */
807+ } else
808+#endif
809+ if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */
800810 (vtop->r & VT_LVAL)) {
811+#ifdef TCC_TARGET_X86_64
812+ int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
813+#else
814+ int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
815+#endif
801816 /* We do not want to modifier the long long
802817 pointer here, so the safest (and less
803818 efficient) is to save all the other registers
804819 in the stack. XXX: totally inefficient. */
805820 save_regs(1);
806821 /* load from memory */
822+ vtop->type.t = load_type;
807823 load(r, vtop);
808824 vdup();
809825 vtop[-1].r = r; /* save register value */
810826 /* increment pointer to get second word */
811− vtop->type.t = VT_INT;
827+ vtop->type.t = addr_type;
812828 gaddrof();
813− vpushi(4);
829+ vpushi(load_size);
814830 gen_op('+');
815831 vtop->r |= VT_LVAL;
832+ vtop->type.t = load_type;
816833 } else {
817834 /* move registers */
818835 load(r, vtop);
@@ -827,9 +844,7 @@ ST_FUNC int gv(int rc) · 8 unmodified lines
827844 vpop();
828845 /* write second register */
829846 vtop->r2 = r2;
830− } else
831−#endif
832− if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
847+ } else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
833848 int t1, t;
834849 /* lvalue of scalar type : need to use lvalue type
835850 because of possible cast */
@@ -2479,6 +2494,8 @@ ST_FUNC void vstore(void) · 1643 unmodified lines
24792494 #ifdef TCC_TARGET_X86_64
24802495 if ((ft & VT_BTYPE) == VT_LDOUBLE) {
24812496 rc = RC_ST0;
2497+ } else if ((ft & VT_BTYPE) == VT_QFLOAT) {
2498+ rc = RC_FRET;
24822499 }
24832500 #endif
24842501 }
@@ -2497,29 +2514,29 @@ ST_FUNC void vstore(void) · 12 unmodified lines
24972514 load(t, &sv);
24982515 vtop[-1].r = t | VT_LVAL;
24992516 }
2500− store(r, vtop - 1);
25012517 /* two word case handling : store second register at word + 4 (or +8 for x86-64) */
25022518 #ifdef TCC_TARGET_X86_64
2503− if ((ft & VT_BTYPE) == VT_QLONG) {
2519+ if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) {
2520+ int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
25042521 #else
25052522 if ((ft & VT_BTYPE) == VT_LLONG) {
2523+ int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
25062524 #endif
2525+ vtop[-1].type.t = load_type;
2526+ store(r, vtop - 1);
25072527 vswap();
25082528 /* convert to int to increment easily */
2509−#ifdef TCC_TARGET_X86_64
2510− vtop->type.t = VT_LLONG;
2511− gaddrof();
2512− vpushi(8);
2513−#else
2514− vtop->type.t = VT_INT;
2529+ vtop->type.t = addr_type;
25152530 gaddrof();
2516− vpushi(4);
2517−#endif
2531+ vpushi(load_size);
25182532 gen_op('+');
25192533 vtop->r |= VT_LVAL;
25202534 vswap();
2535+ vtop[-1].type.t = load_type;
25212536 /* XXX: it works because r2 is spilled last ! */
25222537 store(vtop->r2, vtop - 1);
2538+ } else {
2539+ store(r, vtop - 1);
25232540 }
25242541 }
25252542 vswap();
@@ -3897,8 +3914,16 @@ ST_FUNC void unary(void) · 1371 unmodified lines
38973914 /* return in register */
38983915 if (is_float(ret.type.t)) {
38993916 ret.r = reg_fret(ret.type.t);
3917+#ifdef TCC_TARGET_X86_64
3918+ if ((ret.type.t & VT_BTYPE) == VT_QFLOAT)
3919+ ret.r2 = REG_QRET;
3920+#endif
39003921 } else {
3922+#ifdef TCC_TARGET_X86_64
3923+ if ((ret.type.t & VT_BTYPE) == VT_QLONG)
3924+#else
39013925 if ((ret.type.t & VT_BTYPE) == VT_LLONG)
3926+#endif
39023927 ret.r2 = REG_LRET;
39033928 ret.r = REG_IRET;
39043929 }
tests/abitest.c+2−2
@@ -88,8 +88,8 @@ static int ret_2float_test(void) { · 87 unmodified lines
8888 /*
8989 * ret_2double_test:
9090 *
91− * On x86-64, a struct with 2 doubles should be packed into a single
92− * SSE register (this tests VT_QFLOAT).
91+ * On x86-64, a struct with 2 doubles should be passed in two SSE
92+ * registers.
9393 */
9494 typedef struct ret_2double_test_type_s {double x, y;} ret_2double_test_type;
9595 typedef ret_2double_test_type (*ret_2double_test_function_type) (ret_2double_test_type);
x86_64-gen.c+80−43
@@ -23,7 +23,7 @@ · 22 unmodified lines
2323 #ifdef TARGET_DEFS_ONLY
2424
2525 /* number of available registers */
26−#define NB_REGS 5
26+#define NB_REGS 6
2727 #define NB_ASM_REGS 8
2828
2929 /* a register can belong to several classes. The classes must be
@@ -39,10 +39,12 @@ · 9 unmodified lines
3939 #define RC_R10 0x0400
4040 #define RC_R11 0x0800
4141 #define RC_XMM0 0x0020
42−#define RC_ST0 0x0040 /* only for long double */
42+#define RC_XMM1 0x0040
43+#define RC_ST0 0x0080 /* only for long double */
4344 #define RC_IRET RC_RAX /* function return: integer register */
4445 #define RC_LRET RC_RDX /* function return: second integer register */
4546 #define RC_FRET RC_XMM0 /* function return: float register */
47+#define RC_QRET RC_XMM1 /* function return: second float register */
4648
4749 /* pretty names for the registers */
4850 enum {
@@ -50,7 +52,8 @@ enum { · 1 unmodified line
5052 TREG_RCX = 1,
5153 TREG_RDX = 2,
5254 TREG_XMM0 = 3,
53− TREG_ST0 = 4,
55+ TREG_XMM1 = 4,
56+ TREG_ST0 = 5,
5457
5558 TREG_RSI = 6,
5659 TREG_RDI = 7,
@@ -70,6 +73,7 @@ enum { · 13 unmodified lines
7073 #define REG_IRET TREG_RAX /* single word int return register */
7174 #define REG_LRET TREG_RDX /* second word return register (for long long) */
7275 #define REG_FRET TREG_XMM0 /* float return register */
76+#define REG_QRET TREG_XMM1 /* second float return register */
7377
7478 /* defined if function parameters must be evaluated in reverse order */
7579 #define INVERT_FUNC_PARAMS
@@ -108,6 +112,7 @@ ST_DATA const int reg_classes[NB_REGS+7] = { · 32 unmodified lines
108112 /* ecx */ RC_INT | RC_RCX,
109113 /* edx */ RC_INT | RC_RDX,
110114 /* xmm0 */ RC_FLOAT | RC_XMM0,
115+ /* xmm1 */ RC_FLOAT | RC_XMM1,
111116 /* st0 */ RC_ST0,
112117 0,
113118 0,
@@ -375,7 +380,8 @@ void load(int r, SValue *sv) · 261 unmodified lines
375380 if ((ft & VT_BTYPE) == VT_FLOAT) {
376381 b = 0x6e0f66, r = 0; /* movd */
377382 } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
378− b = 0x7e0ff3, r = 0; /* movq */
383+ b = 0x7e0ff3; /* movq */
384+ r -= TREG_XMM0;
379385 } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
380386 b = 0xdb, r = 5; /* fldt */
381387 } else if ((ft & VT_TYPE) == VT_BYTE) {
@@ -387,6 +393,9 @@ void load(int r, SValue *sv) · 5 unmodified lines
387393 } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
388394 b = 0xb70f; /* movzwl */
389395 } else {
396+ assert(((ft & VT_BTYPE) == VT_INT) || ((ft & VT_BTYPE) == VT_LLONG)
397+ || ((ft & VT_BTYPE) == VT_PTR) || ((ft & VT_BTYPE) == VT_ENUM)
398+ || ((ft & VT_BTYPE) == VT_FUNC));
390399 ll = is64_type(ft);
391400 b = 0x8b;
392401 }
@@ -450,18 +459,30 @@ void load(int r, SValue *sv) · 57 unmodified lines
450459 orex(0,r,0,0);
451460 oad(0xb8 + REG_VALUE(r), t ^ 1); /* mov $0, r */
452461 } else if (v != r) {
453− if (r == TREG_XMM0) {
454− assert(v == TREG_ST0);
455− /* gen_cvt_ftof(VT_DOUBLE); */
456− o(0xf0245cdd); /* fstpl -0x10(%rsp) */
457− /* movsd -0x10(%rsp),%xmm0 */
458− o(0x44100ff2);
459− o(0xf024);
462+ if ((r == TREG_XMM0) || (r == TREG_XMM1)) {
463+ if (v == TREG_ST0) {
464+ /* gen_cvt_ftof(VT_DOUBLE); */
465+ o(0xf0245cdd); /* fstpl -0x10(%rsp) */
466+ /* movsd -0x10(%rsp),%xmmN */
467+ o(0x100ff2);
468+ o(0x44 + ((r - TREG_XMM0) << 3)); /* %xmmN */
469+ o(0xf024);
470+ } else {
471+ assert((v == TREG_XMM0) || (v == TREG_XMM1));
472+ if ((ft & VT_BTYPE) == VT_FLOAT) {
473+ o(0x100ff3);
474+ } else {
475+ assert((ft & VT_BTYPE) == VT_DOUBLE);
476+ o(0x100ff2);
477+ }
478+ o(0xc0 + (v - TREG_XMM0) + ((r - TREG_XMM0) << 3));
479+ }
460480 } else if (r == TREG_ST0) {
461− assert(v == TREG_XMM0);
481+ assert((v == TREG_XMM0) || (v == TREG_XMM1));
462482 /* gen_cvt_ftof(VT_LDOUBLE); */
463483 /* movsd %xmm0,-0x10(%rsp) */
464− o(0x44110ff2);
484+ o(0x110ff2);
485+ o(0x44 + ((r - TREG_XMM0) << 3)); /* %xmmN */
465486 o(0xf024);
466487 o(0xf02444dd); /* fldl -0x10(%rsp) */
467488 } else {
@@ -510,7 +531,7 @@ void store(int r, SValue *v) · 42 unmodified lines
510531 o(0x66);
511532 o(pic);
512533 o(0xd60f); /* movq */
513− r = 0;
534+ r -= TREG_XMM0;
514535 } else if (bt == VT_LDOUBLE) {
515536 o(0xc0d9); /* fld %st(0) */
516537 o(pic);
@@ -679,7 +700,7 @@ void gfunc_call(int nb_args) · 162 unmodified lines
679700 struct_size += size;
680701
681702 } else if (is_sse_float(vtop->type.t)) {
682− gv(RC_FLOAT); /* only one float register */
703+ gv(RC_XMM0); /* only one float register */
683704 j = --gen_reg;
684705 if (j >= REGN) {
685706 /* movq %xmm0, j*8(%rsp) */
@@ -861,6 +882,7 @@ static X86_64_Mode classify_x86_64_inner(CType *ty) { · 175 unmodified lines
861882 case VT_LLONG:
862883 case VT_BOOL:
863884 case VT_PTR:
885+ case VT_FUNC:
864886 case VT_ENUM: return x86_64_mode_integer;
865887
866888 case VT_FLOAT:
@@ -881,6 +903,8 @@ static X86_64_Mode classify_x86_64_inner(CType *ty) { · 14 unmodified lines
881903
882904 return mode;
883905 }
906+
907+ assert(0);
884908 }
885909
886910 static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *reg_count) {
@@ -963,6 +987,14 @@ static const uint8_t arg_regs[REGN] = { · 76 unmodified lines
963987 TREG_RDI, TREG_RSI, TREG_RDX, TREG_RCX, TREG_R8, TREG_R9
964988 };
965989
990+static int arg_prepare_reg(int idx) {
991+ if (idx == 2 || idx == 3)
992+ /* idx=2: r10, idx=3: r11 */
993+ return idx + 8;
994+ else
995+ return arg_regs[idx];
996+}
997+
966998 /* Generate function call. The function address is pushed first, then
967999 all the parameters in call order. This functions pops all the
9681000 parameters and the function address. */
@@ -1051,7 +1083,7 @@ void gfunc_call(int nb_args) · 82 unmodified lines
10511083
10521084 case x86_64_mode_sse:
10531085 if (sse_reg > 8) {
1054− gv(RC_FLOAT);
1086+ gv(RC_XMM0);
10551087 o(0x50); /* push $rax */
10561088 /* movq %xmm0, (%rsp) */
10571089 o(0x04d60f66);
@@ -1097,15 +1129,18 @@ void gfunc_call(int nb_args) · 39 unmodified lines
10971129 break;
10981130
10991131 case x86_64_mode_sse:
1100− if (sse_reg > 8) {
1101− sse_reg -= reg_count;
1102− } else {
1103− for (j = 0; j < reg_count; ++j) {
1104− --sse_reg;
1105− gv(RC_FLOAT); /* only one float register */
1132+ sse_reg -= reg_count;
1133+ if (sse_reg + reg_count <= 8) {
1134+ gv(RC_XMM0); /* only one float register */
1135+ if (sse_reg) { /* avoid redundant movaps %xmm0, %xmm0 */
11061136 /* movaps %xmm0, %xmmN */
11071137 o(0x280f);
11081138 o(0xc0 + (sse_reg << 3));
1139+ if (reg_count == 2) {
1140+ /* movaps %xmm1, %xmmN */
1141+ o(0x280f);
1142+ o(0xc1 + ((sse_reg+1) << 3));
1143+ }
11091144 }
11101145 }
11111146 break;
@@ -1113,16 +1148,17 @@ void gfunc_call(int nb_args) · 1 unmodified line
11131148 case x86_64_mode_integer:
11141149 /* simple type */
11151150 /* XXX: implicit cast ? */
1116− if (gen_reg > 8) {
1117− gen_reg -= reg_count;
1118− } else {
1119− for (j = 0; j < reg_count; ++j) {
1120− --gen_reg;
1121− int d = arg_regs[gen_reg];
1122− r = gv(RC_INT);
1123− if (gen_reg == 2 || gen_reg == 3)
1124− /* gen_reg=2: r10, gen_reg=3: r11 */
1125− d = gen_reg + 8;
1151+ gen_reg -= reg_count;
1152+ if (gen_reg + reg_count <= REGN) {
1153+ r = gv((reg_count == 1) ? RC_INT : RC_IRET);
1154+ int d = arg_prepare_reg(gen_reg);
1155+ orex(1,d,r,0x89); /* mov */
1156+ o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
1157+ if (reg_count == 2) {
1158+ /* Second word of two-word value should always be in rdx
1159+ this case is handled via RC_IRET */
1160+ r = TREG_RDX;
1161+ d = arg_prepare_reg(gen_reg+1);
11261162 orex(1,d,r,0x89); /* mov */
11271163 o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
11281164 }
@@ -1263,13 +1299,13 @@ void gfunc_prolog(CType *func_type) · 134 unmodified lines
12631299 case x86_64_mode_sse:
12641300 if (sse_param_index + reg_count <= 8) {
12651301 /* save arguments passed by register */
1302+ loc -= reg_count * 8;
1303+ param_addr = loc;
12661304 for (i = 0; i < reg_count; ++i) {
1267− loc -= 8;
12681305 o(0xd60f66); /* movq */
1269− gen_modrm(sse_param_index, VT_LOCAL, NULL, loc);
1306+ gen_modrm(sse_param_index, VT_LOCAL, NULL, param_addr + i*8);
12701307 ++sse_param_index;
12711308 }
1272− param_addr = loc;
12731309 } else {
12741310 param_addr = addr;
12751311 addr += size;
@@ -1286,11 +1322,12 @@ void gfunc_prolog(CType *func_type) · 10 unmodified lines
12861322 case x86_64_mode_integer: {
12871323 if (reg_param_index + reg_count <= REGN) {
12881324 /* save arguments passed by register */
1325+ loc -= reg_count * 8;
1326+ param_addr = loc;
12891327 for (i = 0; i < reg_count; ++i) {
1290− push_arg_reg(reg_param_index);
1328+ gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, param_addr + i*8);
12911329 ++reg_param_index;
12921330 }
1293− param_addr = loc;
12941331 } else {
12951332 param_addr = addr;
12961333 addr += size;
@@ -1547,12 +1584,12 @@ void gen_opl(int op) · 250 unmodified lines
15471584
15481585 /* generate a floating point operation 'v = t1 op t2' instruction. The
15491586 two operands are guaranted to have the same floating point type */
1550−/* XXX: need to use ST1 too */
1587+/* XXX: need to use ST1 and XMM1 too */
15511588 void gen_opf(int op)
15521589 {
15531590 int a, ft, fc, swapped, r;
15541591 int float_type =
1555− (vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_FLOAT;
1592+ (vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_XMM0; /* to avoid xmm1 handling for now */
15561593
15571594 /* convert constants to memory references */
15581595 if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
@@ -1803,9 +1840,9 @@ void gen_cvt_ftof(int t) · 244 unmodified lines
18031840 ft = vtop->type.t;
18041841 bt = ft & VT_BTYPE;
18051842 tbt = t & VT_BTYPE;
1806−
1843+
18071844 if (bt == VT_FLOAT) {
1808− gv(RC_FLOAT);
1845+ gv(RC_XMM0); /* to avoid rewriting to handle xmm1 for now */
18091846 if (tbt == VT_DOUBLE) {
18101847 o(0xc0140f); /* unpcklps */
18111848 o(0xc05a0f); /* cvtps2pd */
@@ -1817,7 +1854,7 @@ void gen_cvt_ftof(int t) · 5 unmodified lines
18171854 vtop->r = TREG_ST0;
18181855 }
18191856 } else if (bt == VT_DOUBLE) {
1820− gv(RC_FLOAT);
1857+ gv(RC_XMM0); /* to avoid rewriting to handle xmm1 for now */
18211858 if (tbt == VT_FLOAT) {
18221859 o(0xc0140f66); /* unpcklpd */
18231860 o(0xc05a0f66); /* cvtpd2ps */
@@ -1857,7 +1894,7 @@ void gen_cvt_ftoi(int t) · 33 unmodified lines
18571894 bt = VT_DOUBLE;
18581895 }
18591896
1860− gv(RC_FLOAT);
1897+ gv(RC_XMM0);
18611898 if (t != VT_INT)
18621899 size = 8;
18631900 else