History

Improved x86-64 XMM register argument passing.

Also made XMM0-7 available for use as temporary registers, since they are not used by the ABI. I'd like to do the same with RSI and RDI but that's trickier since they can be used by gv() as temporary registers and there isn't a way to disable that.

James Lyon committed 13 years ago

Commit cbce6d21 parent 946afd22 files · +119 −76Browse files

Unified · Split

2 files
  1. tccgen.c +1 −1
  2. x86_64-gen.c +118 −75
tccgen.c+1−1
@@ -764,7 +764,7 @@ ST_FUNC int gv(int rc) · 763 unmodified lines
764764 #endif
765765
766766 r = vtop->r & VT_VALMASK;
767− rc2 = RC_INT;
767+ rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT;
768768 if (rc == RC_IRET)
769769 rc2 = RC_LRET;
770770 #ifdef TCC_TARGET_X86_64
x86_64-gen.c+118−75
@@ -23,7 +23,7 @@ · 22 unmodified lines
2323 #ifdef TARGET_DEFS_ONLY
2424
2525 /* number of available registers */
26−#define NB_REGS 18
26+#define NB_REGS 24
2727 #define NB_ASM_REGS 8
2828
2929 /* a register can belong to several classes. The classes must be
@@ -34,13 +34,19 @@ · 4 unmodified lines
3434 #define RC_RAX 0x0004
3535 #define RC_RCX 0x0008
3636 #define RC_RDX 0x0010
37+#define RC_ST0 0x0080 /* only for long double */
3738 #define RC_R8 0x0100
3839 #define RC_R9 0x0200
3940 #define RC_R10 0x0400
4041 #define RC_R11 0x0800
41−#define RC_XMM0 0x0020
42−#define RC_XMM1 0x0040
43−#define RC_ST0 0x0080 /* only for long double */
42+#define RC_XMM0 0x1000
43+#define RC_XMM1 0x2000
44+#define RC_XMM2 0x4000
45+#define RC_XMM3 0x8000
46+#define RC_XMM4 0x10000
47+#define RC_XMM5 0x20000
48+#define RC_XMM6 0x40000
49+#define RC_XMM7 0x80000
4450 #define RC_IRET RC_RAX /* function return: integer register */
4551 #define RC_LRET RC_RDX /* function return: second integer register */
4652 #define RC_FRET RC_XMM0 /* function return: float register */
@@ -61,6 +67,12 @@ enum { · 14 unmodified lines
6167
6268 TREG_XMM0 = 16,
6369 TREG_XMM1 = 17,
70+ TREG_XMM2 = 18,
71+ TREG_XMM3 = 19,
72+ TREG_XMM4 = 20,
73+ TREG_XMM5 = 21,
74+ TREG_XMM6 = 22,
75+ TREG_XMM7 = 23,
6476
6577 TREG_ST0 = 4, // SP slot won't be used
6678
@@ -117,16 +129,22 @@ ST_DATA const int reg_classes[NB_REGS] = { · 50 unmodified lines
117129 0,
118130 0,
119131 0,
120− /*RC_INT |*/ RC_R8,
121− /*RC_INT |*/ RC_R9,
122− /*RC_INT |*/ RC_R10,
123− /*RC_INT |*/ RC_R11,
132+ RC_R8,
133+ RC_R9,
134+ RC_R10,
135+ RC_R11,
124136 0,
125137 0,
126138 0,
127139 0,
128140 /* xmm0 */ RC_FLOAT | RC_XMM0,
129141 /* xmm1 */ RC_FLOAT | RC_XMM1,
142+ /* xmm2 */ RC_FLOAT | RC_XMM2,
143+ /* xmm3 */ RC_FLOAT | RC_XMM3,
144+ /* xmm4 */ RC_FLOAT | RC_XMM4,
145+ /* xmm5 */ RC_FLOAT | RC_XMM5,
146+ /* xmm6 */ RC_FLOAT | RC_XMM6,
147+ /* xmm7 */ RC_FLOAT | RC_XMM7,
130148 };
131149
132150 static unsigned long func_sub_sp_offset;
@@ -141,8 +159,6 @@ void g(int c) · 8 unmodified lines
141159 section_realloc(cur_text_section, ind1);
142160 cur_text_section->data[ind] = c;
143161 ind = ind1;
144− assert((ind < 4) || (cur_text_section->data[ind-4] != ('\362'&0xFF)) || (cur_text_section->data[ind-3] != '\017')
145− || (cur_text_section->data[ind-2] != 'X') || (cur_text_section->data[ind-1] != '\001'));
146162 }
147163
148164 void o(unsigned int c)
@@ -1055,32 +1071,48 @@ void gfunc_call(int nb_args) · 906 unmodified lines
10551071 oad(0xec81, args_size); /* sub $xxx, %rsp */
10561072 }
10571073
1058− for(i = 0; i < nb_args; i++) {
1074+ for(i = 0; i < nb_args;) {
10591075 /* Swap argument to top, it will possibly be changed here,
1060− and might use more temps. All arguments must remain on the
1061− stack, so that get_reg can correctly evict some of them onto
1062− stack. We could use also use a vrott(nb_args) at the end
1063− of this loop, but this seems faster. */
1076+ and might use more temps. At the end of the loop we keep
1077+ in on the stack and swap it back to its original position
1078+ if it is a register. */
10641079 SValue tmp = vtop[0];
10651080 vtop[0] = vtop[-i];
10661081 vtop[-i] = tmp;
1082+
10671083 mode = classify_x86_64_arg(&vtop->type, NULL, &size, &reg_count);
1068− switch (mode) {
1069− case x86_64_mode_memory:
1070− /* allocate the necessary size on stack */
1071− o(0x48);
1072− oad(0xec81, size); /* sub $xxx, %rsp */
1073− /* generate structure store */
1074− r = get_reg(RC_INT);
1075− orex(1, r, 0, 0x89); /* mov %rsp, r */
1076− o(0xe0 + REG_VALUE(r));
1077− vset(&vtop->type, r | VT_LVAL, 0);
1078− vswap();
1079− vstore();
1080− args_size += size;
1084+
1085+ int arg_stored = 1;
1086+ switch (vtop->type.t & VT_BTYPE) {
1087+ case VT_STRUCT:
1088+ if (mode == x86_64_mode_sse) {
1089+ if (sse_reg > 8)
1090+ sse_reg -= reg_count;
1091+ else
1092+ arg_stored = 0;
1093+ } else if (mode == x86_64_mode_integer) {
1094+ if (gen_reg > REGN)
1095+ gen_reg -= reg_count;
1096+ else
1097+ arg_stored = 0;
1098+ }
1099+
1100+ if (arg_stored) {
1101+ /* allocate the necessary size on stack */
1102+ o(0x48);
1103+ oad(0xec81, size); /* sub $xxx, %rsp */
1104+ /* generate structure store */
1105+ r = get_reg(RC_INT);
1106+ orex(1, r, 0, 0x89); /* mov %rsp, r */
1107+ o(0xe0 + REG_VALUE(r));
1108+ vset(&vtop->type, r | VT_LVAL, 0);
1109+ vswap();
1110+ vstore();
1111+ args_size += size;
1112+ }
10811113 break;
10821114
1083− case x86_64_mode_x87:
1115+ case VT_LDOUBLE:
10841116 gv(RC_ST0);
10851117 size = LDOUBLE_SIZE;
10861118 oad(0xec8148, size); /* sub $xxx, %rsp */
@@ -1090,8 +1122,11 @@ void gfunc_call(int nb_args) · 3 unmodified lines
10901122 args_size += size;
10911123 break;
10921124
1093− case x86_64_mode_sse:
1125+ case VT_FLOAT:
1126+ case VT_DOUBLE:
1127+ assert(mode == x86_64_mode_sse);
10941128 if (sse_reg > 8) {
1129+ --sse_reg;
10951130 r = gv(RC_FLOAT);
10961131 o(0x50); /* push $rax */
10971132 /* movq %xmm0, (%rsp) */
@@ -1099,26 +1134,39 @@ void gfunc_call(int nb_args) · 1 unmodified line
10991134 o(0x04 + REG_VALUE(r)*8);
11001135 o(0x24);
11011136 args_size += size;
1137+ } else {
1138+ arg_stored = 0;
11021139 }
1103− sse_reg -= reg_count;
11041140 break;
11051141
1106− case x86_64_mode_integer:
1142+ default:
1143+ assert(mode == x86_64_mode_integer);
11071144 /* simple type */
11081145 /* XXX: implicit cast ? */
11091146 if (gen_reg > REGN) {
1147+ --gen_reg;
11101148 r = gv(RC_INT);
11111149 orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */
11121150 args_size += size;
1151+ } else {
1152+ arg_stored = 0;
11131153 }
1114− gen_reg -= reg_count;
11151154 break;
11161155 }
1117−
1118− /* And swap the argument back to it's original position. */
1156+
1157+ /* And swap the argument back to it's original position. */
11191158 tmp = vtop[0];
1120− vtop[0] = vtop[-i];
1121− vtop[-i] = tmp;
1159+ vtop[0] = vtop[-i];
1160+ vtop[-i] = tmp;
1161+
1162+ if (arg_stored) {
1163+ vrotb(i+1);
1164+ assert(vtop->type.t == tmp.type.t);
1165+ vpop();
1166+ --nb_args;
1167+ } else {
1168+ ++i;
1169+ }
11221170 }
11231171
11241172 /* XXX This should be superfluous. */
@@ -1128,55 +1176,50 @@ void gfunc_call(int nb_args) · 3 unmodified lines
11281176 Note that we cannot set RDX and RCX in this loop because gv()
11291177 may break these temporary registers. Let's use R10 and R11
11301178 instead of them */
1131− gen_reg = nb_reg_args;
1132− sse_reg = nb_sse_args;
1179+ assert(gen_reg <= REGN);
1180+ assert(sse_reg <= 8);
11331181 for(i = 0; i < nb_args; i++) {
11341182 mode = classify_x86_64_arg(&vtop->type, &type, &size, &reg_count);
11351183 /* Alter stack entry type so that gv() knows how to treat it */
11361184 vtop->type = type;
1137− switch (mode) {
1138− default:
1139− break;
1140−
1141− case x86_64_mode_sse:
1142− sse_reg -= reg_count;
1143− if (sse_reg + reg_count <= 8) {
1144− gv(RC_FRET); /* only one float register */
1145− if (sse_reg) { /* avoid redundant movaps %xmm0, %xmm0 */
1146− /* movaps %xmm0, %xmmN */
1147− o(0x280f);
1148− o(0xc0 + (sse_reg << 3));
1149− if (reg_count == 2) {
1150− /* movaps %xmm1, %xmmN */
1151− o(0x280f);
1152− o(0xc1 + ((sse_reg+1) << 3));
1153− }
1154− }
1155− }
1156− break;
1157−
1158− case x86_64_mode_integer:
1185+ if (mode == x86_64_mode_sse) {
1186+ if (reg_count == 2) {
1187+ sse_reg -= 2;
1188+ gv(RC_FRET); /* Use pair load into xmm0 & xmm1 */
1189+ if (sse_reg) { /* avoid redundant movaps %xmm0, %xmm0 */
1190+ /* movaps %xmm0, %xmmN */
1191+ o(0x280f);
1192+ o(0xc0 + (sse_reg << 3));
1193+ /* movaps %xmm1, %xmmN */
1194+ o(0x280f);
1195+ o(0xc1 + ((sse_reg+1) << 3));
1196+ }
1197+ } else {
1198+ assert(reg_count == 1);
1199+ --sse_reg;
1200+ /* Load directly to register */
1201+ gv(RC_XMM0 << sse_reg);
1202+ }
1203+ } else if (mode == x86_64_mode_integer) {
11591204 /* simple type */
11601205 /* XXX: implicit cast ? */
11611206 gen_reg -= reg_count;
1162− if (gen_reg + reg_count <= REGN) {
1163− r = gv((reg_count == 1) ? RC_INT : RC_IRET);
1164− int d = arg_prepare_reg(gen_reg);
1165− orex(1,d,r,0x89); /* mov */
1166− o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
1167− if (reg_count == 2) {
1168− /* Second word of two-word value should always be in rdx
1169− this case is handled via RC_IRET */
1170− assert(vtop->r2 == TREG_RDX);
1171− d = arg_prepare_reg(gen_reg+1);
1172− orex(1,d,vtop->r2,0x89); /* mov */
1173− o(0xc0 + REG_VALUE(vtop->r2) * 8 + REG_VALUE(d));
1174− }
1207+ r = gv(RC_INT);
1208+ int d = arg_prepare_reg(gen_reg);
1209+ orex(1,d,r,0x89); /* mov */
1210+ o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
1211+ if (reg_count == 2) {
1212+ /* Second word of two-word value should always be in rdx
1213+ this case is handled via RC_IRET */
1214+ d = arg_prepare_reg(gen_reg+1);
1215+ orex(1,d,vtop->r2,0x89); /* mov */
1216+ o(0xc0 + REG_VALUE(vtop->r2) * 8 + REG_VALUE(d));
11751217 }
1176− break;
11771218 }
11781219 vtop--;
11791220 }
1221+ assert(gen_reg == 0);
1222+ assert(sse_reg == 0);
11801223
11811224 /* We shouldn't have many operands on the stack anymore, but the
11821225 call address itself is still there, and it might be in %eax

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t