| @@ -96,9 +96,9 @@ enum { · 95 unmodified lines |
| 96 | 96 | |
| 97 | 97 | /* long double size and alignment, in bytes */ |
| 98 | 98 | #define LDOUBLE_SIZE 16 |
| 99 | | −#define LDOUBLE_ALIGN 8 |
| 99 | +#define LDOUBLE_ALIGN 16 |
| 100 | 100 | /* maximum alignment (for aligned attribute support) */ |
| 101 | | −#define MAX_ALIGN 8 |
| 101 | +#define MAX_ALIGN 16 |
| 102 | 102 | |
| 103 | 103 | /******************************************************/ |
| 104 | 104 | /* ELF defines */ |
| @@ -983,7 +983,7 @@ static X86_64_Mode classify_x86_64_inner(CType *ty) { · 878 unmodified lines |
| 983 | 983 | assert(0); |
| 984 | 984 | } |
| 985 | 985 | |
| 986 | | −static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *reg_count) { |
| 986 | +static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *palign, int *reg_count) { |
| 987 | 987 | X86_64_Mode mode; |
| 988 | 988 | int size, align, ret_t; |
| 989 | 989 | |
| @@ -995,6 +995,7 @@ static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *r · 5 unmodified lines |
| 995 | 995 | } else { |
| 996 | 996 | size = type_size(ty, &align); |
| 997 | 997 | *psize = (size + 7) & ~7; |
| 998 | + *palign = (align + 7) & ~7; |
| 998 | 999 | |
| 999 | 1000 | if (size > 16) { |
| 1000 | 1001 | mode = x86_64_mode_memory; |
| @@ -1042,8 +1043,8 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) { · 41 unmodified lines |
| 1042 | 1043 | enum __va_arg_type { |
| 1043 | 1044 | __va_gen_reg, __va_float_reg, __va_stack |
| 1044 | 1045 | }; |
| 1045 | | − int size, reg_count; |
| 1046 | | − X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, ®_count); |
| 1046 | + int size, align, reg_count; |
| 1047 | + X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, ®_count); |
| 1047 | 1048 | switch (mode) { |
| 1048 | 1049 | default: return __va_stack; |
| 1049 | 1050 | case x86_64_mode_integer: return __va_gen_reg; |
| @@ -1053,9 +1054,9 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) { · 3 unmodified lines |
| 1053 | 1054 | |
| 1054 | 1055 | /* Return 1 if this function returns via an sret pointer, 0 otherwise */ |
| 1055 | 1056 | int gfunc_sret(CType *vt, CType *ret, int *ret_align) { |
| 1056 | | − int size, reg_count; |
| 1057 | + int size, align, reg_count; |
| 1057 | 1058 | *ret_align = 1; // Never have to re-align return values for x86-64 |
| 1058 | | − return (classify_x86_64_arg(vt, ret, &size, ®_count) == x86_64_mode_memory); |
| 1059 | + return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) == x86_64_mode_memory); |
| 1059 | 1060 | } |
| 1060 | 1061 | |
| 1061 | 1062 | #define REGN 6 |
| @@ -1078,77 +1079,187 @@ void gfunc_call(int nb_args) · 16 unmodified lines |
| 1078 | 1079 | { |
| 1079 | 1080 | X86_64_Mode mode; |
| 1080 | 1081 | CType type; |
| 1081 | | − int size, align, r, args_size, i, j, reg_count; |
| 1082 | + int size, align, r, args_size, stack_adjust, run_start, run_end, i, j, reg_count; |
| 1082 | 1083 | int nb_reg_args = 0; |
| 1083 | 1084 | int nb_sse_args = 0; |
| 1084 | 1085 | int sse_reg, gen_reg; |
| 1085 | 1086 | |
| 1086 | | − /* calculate the number of integer/float arguments */ |
| 1087 | | − args_size = 0; |
| 1087 | + /* calculate the number of integer/float register arguments */ |
| 1088 | 1088 | for(i = 0; i < nb_args; i++) { |
| 1089 | | − mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, ®_count); |
| 1090 | | − switch (mode) { |
| 1091 | | − case x86_64_mode_memory: |
| 1092 | | − case x86_64_mode_x87: |
| 1093 | | − args_size += size; |
| 1094 | | − break; |
| 1095 | | − |
| 1096 | | − case x86_64_mode_sse: |
| 1089 | + mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); |
| 1090 | + if (mode == x86_64_mode_sse) |
| 1097 | 1091 | nb_sse_args += reg_count; |
| 1098 | | − if (nb_sse_args > 8) args_size += size; |
| 1099 | | − break; |
| 1100 | | − |
| 1101 | | − case x86_64_mode_integer: |
| 1092 | + else if (mode == x86_64_mode_integer) |
| 1102 | 1093 | nb_reg_args += reg_count; |
| 1103 | | − if (nb_reg_args > REGN) args_size += size; |
| 1104 | | − break; |
| 1105 | | − } |
| 1106 | 1094 | } |
| 1107 | 1095 | |
| 1096 | + /* arguments are collected in runs. Each run is a collection of 8-byte aligned arguments |
| 1097 | + and ended by a 16-byte aligned argument. This is because, from the point of view of |
| 1098 | + the callee, argument alignment is computed from the bottom up. */ |
| 1108 | 1099 | /* for struct arguments, we need to call memcpy and the function |
| 1109 | 1100 | call breaks register passing arguments we are preparing. |
| 1110 | 1101 | So, we process arguments which will be passed by stack first. */ |
| 1111 | 1102 | gen_reg = nb_reg_args; |
| 1112 | 1103 | sse_reg = nb_sse_args; |
| 1113 | | − |
| 1114 | | − /* adjust stack to align SSE boundary */ |
| 1115 | | − if (args_size &= 15) { |
| 1116 | | − /* fetch cpu flag before the following sub will change the value */ |
| 1117 | | − if (vtop >= vstack && (vtop->r & VT_VALMASK) == VT_CMP) |
| 1118 | | − gv(RC_INT); |
| 1119 | | − |
| 1120 | | − args_size = 16 - args_size; |
| 1121 | | − o(0x48); |
| 1122 | | − oad(0xec81, args_size); /* sub $xxx, %rsp */ |
| 1123 | | − } |
| 1124 | | − |
| 1125 | | − for(i = 0; i < nb_args;) { |
| 1126 | | − /* Swap argument to top, it will possibly be changed here, |
| 1127 | | − and might use more temps. At the end of the loop we keep |
| 1128 | | − in on the stack and swap it back to its original position |
| 1129 | | − if it is a register. */ |
| 1130 | | − SValue tmp = vtop[0]; |
| 1131 | | − vtop[0] = vtop[-i]; |
| 1132 | | − vtop[-i] = tmp; |
| 1133 | | − |
| 1134 | | − mode = classify_x86_64_arg(&vtop->type, NULL, &size, ®_count); |
| 1104 | + run_start = 0; |
| 1105 | + args_size = 0; |
| 1106 | + while (run_start != nb_args) { |
| 1107 | + int run_gen_reg = gen_reg, run_sse_reg = sse_reg; |
| 1135 | 1108 | |
| 1136 | | − int arg_stored = 1; |
| 1137 | | − switch (vtop->type.t & VT_BTYPE) { |
| 1138 | | − case VT_STRUCT: |
| 1139 | | − if (mode == x86_64_mode_sse) { |
| 1140 | | − if (sse_reg > 8) |
| 1141 | | − sse_reg -= reg_count; |
| 1109 | + run_end = nb_args; |
| 1110 | + stack_adjust = 0; |
| 1111 | + for(i = run_start; (i < nb_args) && (run_end == nb_args); i++) { |
| 1112 | + mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); |
| 1113 | + switch (mode) { |
| 1114 | + case x86_64_mode_memory: |
| 1115 | + case x86_64_mode_x87: |
| 1116 | + stack_arg: |
| 1117 | + if (align == 16) |
| 1118 | + run_end = i; |
| 1142 | 1119 | else |
| 1120 | + stack_adjust += size; |
| 1121 | + break; |
| 1122 | + |
| 1123 | + case x86_64_mode_sse: |
| 1124 | + sse_reg -= reg_count; |
| 1125 | + if (sse_reg + reg_count > 8) goto stack_arg; |
| 1126 | + break; |
| 1127 | + |
| 1128 | + case x86_64_mode_integer: |
| 1129 | + gen_reg -= reg_count; |
| 1130 | + if (gen_reg + reg_count > REGN) goto stack_arg; |
| 1131 | + break; |
| 1132 | + } |
| 1133 | + } |
| 1134 | + |
| 1135 | + gen_reg = run_gen_reg; |
| 1136 | + sse_reg = run_sse_reg; |
| 1137 | + |
| 1138 | + /* adjust stack to align SSE boundary */ |
| 1139 | + if (stack_adjust &= 15) { |
| 1140 | + /* fetch cpu flag before the following sub will change the value */ |
| 1141 | + if (vtop >= vstack && (vtop->r & VT_VALMASK) == VT_CMP) |
| 1142 | + gv(RC_INT); |
| 1143 | + |
| 1144 | + stack_adjust = 16 - stack_adjust; |
| 1145 | + o(0x48); |
| 1146 | + oad(0xec81, stack_adjust); /* sub $xxx, %rsp */ |
| 1147 | + args_size += stack_adjust; |
| 1148 | + } |
| 1149 | + |
| 1150 | + for(i = run_start; i < run_end;) { |
| 1151 | + /* Swap argument to top, it will possibly be changed here, |
| 1152 | + and might use more temps. At the end of the loop we keep |
| 1153 | + in on the stack and swap it back to its original position |
| 1154 | + if it is a register. */ |
| 1155 | + SValue tmp = vtop[0]; |
| 1156 | + vtop[0] = vtop[-i]; |
| 1157 | + vtop[-i] = tmp; |
| 1158 | + |
| 1159 | + mode = classify_x86_64_arg(&vtop->type, NULL, &size, &align, ®_count); |
| 1160 | + |
| 1161 | + int arg_stored = 1; |
| 1162 | + switch (vtop->type.t & VT_BTYPE) { |
| 1163 | + case VT_STRUCT: |
| 1164 | + if (mode == x86_64_mode_sse) { |
| 1165 | + if (sse_reg > 8) |
| 1166 | + sse_reg -= reg_count; |
| 1167 | + else |
| 1168 | + arg_stored = 0; |
| 1169 | + } else if (mode == x86_64_mode_integer) { |
| 1170 | + if (gen_reg > REGN) |
| 1171 | + gen_reg -= reg_count; |
| 1172 | + else |
| 1173 | + arg_stored = 0; |
| 1174 | + } |
| 1175 | + |
| 1176 | + if (arg_stored) { |
| 1177 | + /* allocate the necessary size on stack */ |
| 1178 | + o(0x48); |
| 1179 | + oad(0xec81, size); /* sub $xxx, %rsp */ |
| 1180 | + /* generate structure store */ |
| 1181 | + r = get_reg(RC_INT); |
| 1182 | + orex(1, r, 0, 0x89); /* mov %rsp, r */ |
| 1183 | + o(0xe0 + REG_VALUE(r)); |
| 1184 | + vset(&vtop->type, r | VT_LVAL, 0); |
| 1185 | + vswap(); |
| 1186 | + vstore(); |
| 1187 | + args_size += size; |
| 1188 | + } |
| 1189 | + break; |
| 1190 | + |
| 1191 | + case VT_LDOUBLE: |
| 1192 | + assert(0); |
| 1193 | + break; |
| 1194 | + |
| 1195 | + case VT_FLOAT: |
| 1196 | + case VT_DOUBLE: |
| 1197 | + assert(mode == x86_64_mode_sse); |
| 1198 | + if (sse_reg > 8) { |
| 1199 | + --sse_reg; |
| 1200 | + r = gv(RC_FLOAT); |
| 1201 | + o(0x50); /* push $rax */ |
| 1202 | + /* movq %xmmN, (%rsp) */ |
| 1203 | + o(0xd60f66); |
| 1204 | + o(0x04 + REG_VALUE(r)*8); |
| 1205 | + o(0x24); |
| 1206 | + args_size += size; |
| 1207 | + } else { |
| 1143 | 1208 | arg_stored = 0; |
| 1144 | | − } else if (mode == x86_64_mode_integer) { |
| 1145 | | − if (gen_reg > REGN) |
| 1146 | | − gen_reg -= reg_count; |
| 1147 | | − else |
| 1209 | + } |
| 1210 | + break; |
| 1211 | + |
| 1212 | + default: |
| 1213 | + assert(mode == x86_64_mode_integer); |
| 1214 | + /* simple type */ |
| 1215 | + /* XXX: implicit cast ? */ |
| 1216 | + if (gen_reg > REGN) { |
| 1217 | + --gen_reg; |
| 1218 | + r = gv(RC_INT); |
| 1219 | + orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */ |
| 1220 | + args_size += size; |
| 1221 | + } else { |
| 1148 | 1222 | arg_stored = 0; |
| 1223 | + } |
| 1224 | + break; |
| 1149 | 1225 | } |
| 1150 | 1226 | |
| 1227 | + /* And swap the argument back to it's original position. */ |
| 1228 | + tmp = vtop[0]; |
| 1229 | + vtop[0] = vtop[-i]; |
| 1230 | + vtop[-i] = tmp; |
| 1231 | + |
| 1151 | 1232 | if (arg_stored) { |
| 1233 | + vrotb(i+1); |
| 1234 | + assert((vtop->type.t == tmp.type.t) && (vtop->r == tmp.r)); |
| 1235 | + vpop(); |
| 1236 | + --nb_args; |
| 1237 | + --run_end; |
| 1238 | + } else { |
| 1239 | + ++i; |
| 1240 | + } |
| 1241 | + } |
| 1242 | + |
| 1243 | + /* handle 16 byte aligned arguments at end of run */ |
| 1244 | + run_start = i = run_end; |
| 1245 | + while (i < nb_args) { |
| 1246 | + /* Rotate argument to top since it will always be popped */ |
| 1247 | + mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); |
| 1248 | + if (align != 16) |
| 1249 | + break; |
| 1250 | + |
| 1251 | + vrotb(i+1); |
| 1252 | + |
| 1253 | + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { |
| 1254 | + gv(RC_ST0); |
| 1255 | + oad(0xec8148, size); /* sub $xxx, %rsp */ |
| 1256 | + o(0x7cdb); /* fstpt 0(%rsp) */ |
| 1257 | + g(0x24); |
| 1258 | + g(0x00); |
| 1259 | + args_size += size; |
| 1260 | + } else { |
| 1261 | + assert(mode == x86_64_mode_memory); |
| 1262 | + |
| 1152 | 1263 | /* allocate the necessary size on stack */ |
| 1153 | 1264 | o(0x48); |
| 1154 | 1265 | oad(0xec81, size); /* sub $xxx, %rsp */ |
| @@ -1161,65 +1272,12 @@ void gfunc_call(int nb_args) · 6 unmodified lines |
| 1161 | 1272 | vstore(); |
| 1162 | 1273 | args_size += size; |
| 1163 | 1274 | } |
| 1164 | | − break; |
| 1165 | 1275 | |
| 1166 | | − case VT_LDOUBLE: |
| 1167 | | − gv(RC_ST0); |
| 1168 | | − size = LDOUBLE_SIZE; |
| 1169 | | − oad(0xec8148, size); /* sub $xxx, %rsp */ |
| 1170 | | − o(0x7cdb); /* fstpt 0(%rsp) */ |
| 1171 | | − g(0x24); |
| 1172 | | − g(0x00); |
| 1173 | | − args_size += size; |
| 1174 | | − break; |
| 1175 | | − |
| 1176 | | − case VT_FLOAT: |
| 1177 | | − case VT_DOUBLE: |
| 1178 | | − assert(mode == x86_64_mode_sse); |
| 1179 | | − if (sse_reg > 8) { |
| 1180 | | − --sse_reg; |
| 1181 | | − r = gv(RC_FLOAT); |
| 1182 | | − o(0x50); /* push $rax */ |
| 1183 | | − /* movq %xmmN, (%rsp) */ |
| 1184 | | − o(0xd60f66); |
| 1185 | | − o(0x04 + REG_VALUE(r)*8); |
| 1186 | | − o(0x24); |
| 1187 | | − args_size += size; |
| 1188 | | − } else { |
| 1189 | | − arg_stored = 0; |
| 1190 | | − } |
| 1191 | | − break; |
| 1192 | | − |
| 1193 | | − default: |
| 1194 | | − assert(mode == x86_64_mode_integer); |
| 1195 | | − /* simple type */ |
| 1196 | | − /* XXX: implicit cast ? */ |
| 1197 | | − if (gen_reg > REGN) { |
| 1198 | | − --gen_reg; |
| 1199 | | − r = gv(RC_INT); |
| 1200 | | − orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */ |
| 1201 | | − args_size += size; |
| 1202 | | − } else { |
| 1203 | | − arg_stored = 0; |
| 1204 | | − } |
| 1205 | | − break; |
| 1206 | | − } |
| 1207 | | − |
| 1208 | | − /* And swap the argument back to it's original position. */ |
| 1209 | | − tmp = vtop[0]; |
| 1210 | | − vtop[0] = vtop[-i]; |
| 1211 | | − vtop[-i] = tmp; |
| 1212 | | − |
| 1213 | | − if (arg_stored) { |
| 1214 | | − vrotb(i+1); |
| 1215 | | − assert(vtop->type.t == tmp.type.t); |
| 1216 | | − vpop(); |
| 1217 | | − --nb_args; |
| 1218 | | − } else { |
| 1219 | | − ++i; |
| 1276 | + vpop(); |
| 1277 | + --nb_args; |
| 1220 | 1278 | } |
| 1221 | 1279 | } |
| 1222 | | − |
| 1280 | + |
| 1223 | 1281 | /* XXX This should be superfluous. */ |
| 1224 | 1282 | save_regs(0); /* save used temporary registers */ |
| 1225 | 1283 | |
| @@ -1230,7 +1288,7 @@ void gfunc_call(int nb_args) · 4 unmodified lines |
| 1230 | 1288 | assert(gen_reg <= REGN); |
| 1231 | 1289 | assert(sse_reg <= 8); |
| 1232 | 1290 | for(i = 0; i < nb_args; i++) { |
| 1233 | | − mode = classify_x86_64_arg(&vtop->type, &type, &size, ®_count); |
| 1291 | + mode = classify_x86_64_arg(&vtop->type, &type, &size, &align, ®_count); |
| 1234 | 1292 | /* Alter stack entry type so that gv() knows how to treat it */ |
| 1235 | 1293 | vtop->type = type; |
| 1236 | 1294 | if (mode == x86_64_mode_sse) { |
| @@ -1324,10 +1382,11 @@ void gfunc_prolog(CType *func_type) · 87 unmodified lines |
| 1324 | 1382 | sym = func_type->ref; |
| 1325 | 1383 | while ((sym = sym->next) != NULL) { |
| 1326 | 1384 | type = &sym->type; |
| 1327 | | − mode = classify_x86_64_arg(type, NULL, &size, ®_count); |
| 1385 | + mode = classify_x86_64_arg(type, NULL, &size, &align, ®_count); |
| 1328 | 1386 | switch (mode) { |
| 1329 | 1387 | default: |
| 1330 | | − seen_stack_size += size; |
| 1388 | + stack_arg: |
| 1389 | + seen_stack_size = ((seen_stack_size + align - 1) & -align) + size; |
| 1331 | 1390 | break; |
| 1332 | 1391 | |
| 1333 | 1392 | case x86_64_mode_integer: |
| @@ -1335,7 +1394,7 @@ void gfunc_prolog(CType *func_type) · 1 unmodified line |
| 1335 | 1394 | seen_reg_num += reg_count; |
| 1336 | 1395 | } else { |
| 1337 | 1396 | seen_reg_num = 8; |
| 1338 | | − seen_stack_size += size; |
| 1397 | + goto stack_arg; |
| 1339 | 1398 | } |
| 1340 | 1399 | break; |
| 1341 | 1400 | |
| @@ -1344,7 +1403,7 @@ void gfunc_prolog(CType *func_type) · 2 unmodified lines |
| 1344 | 1403 | seen_sse_num += reg_count; |
| 1345 | 1404 | } else { |
| 1346 | 1405 | seen_sse_num = 8; |
| 1347 | | − seen_stack_size += size; |
| 1406 | + goto stack_arg; |
| 1348 | 1407 | } |
| 1349 | 1408 | break; |
| 1350 | 1409 | } |
| @@ -1383,7 +1442,7 @@ void gfunc_prolog(CType *func_type) · 32 unmodified lines |
| 1383 | 1442 | /* if the function returns a structure, then add an |
| 1384 | 1443 | implicit pointer parameter */ |
| 1385 | 1444 | func_vt = sym->type; |
| 1386 | | − mode = classify_x86_64_arg(&func_vt, NULL, &size, ®_count); |
| 1445 | + mode = classify_x86_64_arg(&func_vt, NULL, &size, &align, ®_count); |
| 1387 | 1446 | if (mode == x86_64_mode_memory) { |
| 1388 | 1447 | push_arg_reg(reg_param_index); |
| 1389 | 1448 | func_vc = loc; |
| @@ -1392,7 +1451,7 @@ void gfunc_prolog(CType *func_type) · 2 unmodified lines |
| 1392 | 1451 | /* define parameters */ |
| 1393 | 1452 | while ((sym = sym->next) != NULL) { |
| 1394 | 1453 | type = &sym->type; |
| 1395 | | − mode = classify_x86_64_arg(type, NULL, &size, ®_count); |
| 1454 | + mode = classify_x86_64_arg(type, NULL, &size, &align, ®_count); |
| 1396 | 1455 | switch (mode) { |
| 1397 | 1456 | case x86_64_mode_sse: |
| 1398 | 1457 | if (sse_param_index + reg_count <= 8) { |
| @@ -1405,6 +1464,7 @@ void gfunc_prolog(CType *func_type) · 6 unmodified lines |
| 1405 | 1464 | ++sse_param_index; |
| 1406 | 1465 | } |
| 1407 | 1466 | } else { |
| 1467 | + addr = (addr + align - 1) & -align; |
| 1408 | 1468 | param_addr = addr; |
| 1409 | 1469 | addr += size; |
| 1410 | 1470 | sse_param_index += reg_count; |
| @@ -1413,6 +1473,7 @@ void gfunc_prolog(CType *func_type) · 2 unmodified lines |
| 1413 | 1473 | |
| 1414 | 1474 | case x86_64_mode_memory: |
| 1415 | 1475 | case x86_64_mode_x87: |
| 1476 | + addr = (addr + align - 1) & -align; |
| 1416 | 1477 | param_addr = addr; |
| 1417 | 1478 | addr += size; |
| 1418 | 1479 | break; |
| @@ -1427,6 +1488,7 @@ void gfunc_prolog(CType *func_type) · 8 unmodified lines |
| 1427 | 1488 | ++reg_param_index; |
| 1428 | 1489 | } |
| 1429 | 1490 | } else { |
| 1491 | + addr = (addr + align - 1) & -align; |
| 1430 | 1492 | param_addr = addr; |
| 1431 | 1493 | addr += size; |
| 1432 | 1494 | reg_param_index += reg_count; |