diff options
Diffstat (limited to 'src/lj_asm_x86.h')
-rw-r--r-- | src/lj_asm_x86.h | 140 |
1 files changed, 104 insertions, 36 deletions
diff --git a/src/lj_asm_x86.h b/src/lj_asm_x86.h index 154ca890..391e2de9 100644 --- a/src/lj_asm_x86.h +++ b/src/lj_asm_x86.h | |||
@@ -369,18 +369,76 @@ static Reg asm_fuseload(ASMState *as, IRRef ref, RegSet allow) | |||
369 | 369 | ||
370 | /* -- Calls --------------------------------------------------------------- */ | 370 | /* -- Calls --------------------------------------------------------------- */ |
371 | 371 | ||
372 | /* Count the required number of stack slots for a call. */ | ||
373 | static int asm_count_call_slots(ASMState *as, const CCallInfo *ci, IRRef *args) | ||
374 | { | ||
375 | uint32_t i, nargs = CCI_NARGS(ci); | ||
376 | int nslots = 0; | ||
377 | #if LJ_64 | ||
378 | if (LJ_ABI_WIN) { | ||
379 | nslots = (int)(nargs*2); /* Only matters for more than four args. */ | ||
380 | } else { | ||
381 | int ngpr = REGARG_NUMGPR, nfpr = REGARG_NUMFPR; | ||
382 | for (i = 0; i < nargs; i++) | ||
383 | if (args[i] && irt_isfp(IR(args[i])->t)) { | ||
384 | if (nfpr > 0) nfpr--; else nslots += 2; | ||
385 | } else { | ||
386 | if (ngpr > 0) ngpr--; else nslots += 2; | ||
387 | } | ||
388 | } | ||
389 | #else | ||
390 | int ngpr = 0; | ||
391 | if ((ci->flags & CCI_CC_MASK) == CCI_CC_FASTCALL) | ||
392 | ngpr = 2; | ||
393 | else if ((ci->flags & CCI_CC_MASK) == CCI_CC_THISCALL) | ||
394 | ngpr = 1; | ||
395 | for (i = 0; i < nargs; i++) | ||
396 | if (args[i] && irt_isfp(IR(args[i])->t)) { | ||
397 | nslots += irt_isnum(IR(args[i])->t) ? 2 : 1; | ||
398 | } else { | ||
399 | if (ngpr > 0) ngpr--; else nslots++; | ||
400 | } | ||
401 | #endif | ||
402 | return nslots; | ||
403 | } | ||
404 | |||
372 | /* Generate a call to a C function. */ | 405 | /* Generate a call to a C function. */ |
373 | static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) | 406 | static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) |
374 | { | 407 | { |
375 | uint32_t n, nargs = CCI_NARGS(ci); | 408 | uint32_t n, nargs = CCI_NARGS(ci); |
376 | int32_t ofs = STACKARG_OFS; | 409 | int32_t ofs = STACKARG_OFS; |
377 | uint32_t gprs = REGARG_GPRS; | ||
378 | #if LJ_64 | 410 | #if LJ_64 |
411 | uint32_t gprs = REGARG_GPRS; | ||
379 | Reg fpr = REGARG_FIRSTFPR; | 412 | Reg fpr = REGARG_FIRSTFPR; |
413 | #if !LJ_ABI_WIN | ||
414 | MCode *patchnfpr = NULL; | ||
415 | #endif | ||
416 | #else | ||
417 | uint32_t gprs = 0; | ||
418 | if ((ci->flags & CCI_CC_MASK) != CCI_CC_CDECL) { | ||
419 | if ((ci->flags & CCI_CC_MASK) == CCI_CC_THISCALL) | ||
420 | gprs = (REGARG_GPRS & 31); | ||
421 | else if ((ci->flags & CCI_CC_MASK) == CCI_CC_FASTCALL) | ||
422 | gprs = REGARG_GPRS; | ||
423 | } | ||
380 | #endif | 424 | #endif |
381 | lua_assert(!(nargs > 2 && (ci->flags&CCI_FASTCALL))); /* Avoid stack adj. */ | ||
382 | if ((void *)ci->func) | 425 | if ((void *)ci->func) |
383 | emit_call(as, ci->func); | 426 | emit_call(as, ci->func); |
427 | #if LJ_64 | ||
428 | if ((ci->flags & CCI_VARARG)) { /* Special handling for vararg calls. */ | ||
429 | #if LJ_ABI_WIN | ||
430 | for (n = 0; n < 4 && n < nargs; n++) { | ||
431 | IRIns *ir = IR(args[n]); | ||
432 | if (irt_isfp(ir->t)) /* Duplicate FPRs in GPRs. */ | ||
433 | emit_rr(as, XO_MOVDto, (irt_isnum(ir->t) ? REX_64 : 0) | (fpr+n), | ||
434 | ((gprs >> (n*5)) & 31)); /* Either MOVD or MOVQ. */ | ||
435 | } | ||
436 | #else | ||
437 | patchnfpr = --as->mcp; /* Indicate number of used FPRs in register al. */ | ||
438 | *--as->mcp = XI_MOVrib | RID_EAX; | ||
439 | #endif | ||
440 | } | ||
441 | #endif | ||
384 | for (n = 0; n < nargs; n++) { /* Setup args. */ | 442 | for (n = 0; n < nargs; n++) { /* Setup args. */ |
385 | IRRef ref = args[n]; | 443 | IRRef ref = args[n]; |
386 | IRIns *ir = IR(ref); | 444 | IRIns *ir = IR(ref); |
@@ -392,15 +450,16 @@ static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) | |||
392 | #elif LJ_64 | 450 | #elif LJ_64 |
393 | /* POSIX/x64 argument registers are used in order of appearance. */ | 451 | /* POSIX/x64 argument registers are used in order of appearance. */ |
394 | if (irt_isfp(ir->t)) { | 452 | if (irt_isfp(ir->t)) { |
395 | r = fpr <= REGARG_LASTFPR ? fpr : 0; fpr++; | 453 | r = fpr <= REGARG_LASTFPR ? fpr++ : 0; |
396 | } else { | 454 | } else { |
397 | r = gprs & 31; gprs >>= 5; | 455 | r = gprs & 31; gprs >>= 5; |
398 | } | 456 | } |
399 | #else | 457 | #else |
400 | if (irt_isfp(ir->t) || !(ci->flags & CCI_FASTCALL)) { | 458 | if (ref && irt_isfp(ir->t)) { |
401 | r = 0; | 459 | r = 0; |
402 | } else { | 460 | } else { |
403 | r = gprs & 31; gprs >>= 5; | 461 | r = gprs & 31; gprs >>= 5; |
462 | if (!ref) continue; | ||
404 | } | 463 | } |
405 | #endif | 464 | #endif |
406 | if (r) { /* Argument is in a register. */ | 465 | if (r) { /* Argument is in a register. */ |
@@ -442,6 +501,9 @@ static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) | |||
442 | ofs += sizeof(intptr_t); | 501 | ofs += sizeof(intptr_t); |
443 | } | 502 | } |
444 | } | 503 | } |
504 | #if LJ_64 && !LJ_ABI_WIN | ||
505 | if (patchnfpr) *patchnfpr = fpr - REGARG_FIRSTFPR; | ||
506 | #endif | ||
445 | } | 507 | } |
446 | 508 | ||
447 | /* Setup result reg/sp for call. Evict scratch regs. */ | 509 | /* Setup result reg/sp for call. Evict scratch regs. */ |
@@ -503,23 +565,50 @@ static void asm_call(ASMState *as, IRIns *ir) | |||
503 | asm_gencall(as, ci, args); | 565 | asm_gencall(as, ci, args); |
504 | } | 566 | } |
505 | 567 | ||
568 | /* Return a constant function pointer or NULL for indirect calls. */ | ||
569 | static void *asm_callx_func(ASMState *as, IRIns *irf, IRRef func) | ||
570 | { | ||
571 | #if LJ_32 | ||
572 | UNUSED(as); | ||
573 | if (irref_isk(func)) | ||
574 | return (void *)irf->i; | ||
575 | #else | ||
576 | if (irref_isk(func)) { | ||
577 | MCode *p; | ||
578 | if (irf->o == IR_KINT64) | ||
579 | p = (MCode *)(void *)ir_k64(irf)->u64; | ||
580 | else | ||
581 | p = (MCode *)(void *)(uintptr_t)(uint32_t)irf->i; | ||
582 | if (p - as->mcp == (int32_t)(p - as->mcp)) | ||
583 | return p; /* Call target is still in +-2GB range. */ | ||
584 | /* Avoid the indirect case of emit_call(). Try to hoist func addr. */ | ||
585 | } | ||
586 | #endif | ||
587 | return NULL; | ||
588 | } | ||
589 | |||
506 | static void asm_callx(ASMState *as, IRIns *ir) | 590 | static void asm_callx(ASMState *as, IRIns *ir) |
507 | { | 591 | { |
508 | IRRef args[CCI_NARGS_MAX]; | 592 | IRRef args[CCI_NARGS_MAX]; |
509 | CCallInfo ci; | 593 | CCallInfo ci; |
594 | IRRef func; | ||
510 | IRIns *irf; | 595 | IRIns *irf; |
511 | ci.flags = asm_callx_flags(as, ir); | 596 | ci.flags = asm_callx_flags(as, ir); |
512 | asm_collectargs(as, ir, &ci, args); | 597 | asm_collectargs(as, ir, &ci, args); |
513 | asm_setupresult(as, ir, &ci); | 598 | asm_setupresult(as, ir, &ci); |
514 | irf = IR(ir->op2); | 599 | #if LJ_32 |
515 | if (LJ_32 && irref_isk(ir->op2)) { /* Call to constant address on x86. */ | 600 | /* Have to readjust stack after non-cdecl calls due to callee cleanup. */ |
516 | ci.func = (ASMFunction)(void *)(uintptr_t)(uint32_t)irf->i; | 601 | if ((ci.flags & CCI_CC_MASK) != CCI_CC_CDECL) |
517 | } else { | 602 | emit_spsub(as, 4 * asm_count_call_slots(as, &ci, args)); |
518 | /* Prefer a non-argument register or RID_RET for indirect calls. */ | 603 | #endif |
519 | RegSet allow = (RSET_GPR & ~RSET_SCRATCH)|RID2RSET(RID_RET); | 604 | func = ir->op2; irf = IR(func); |
520 | Reg r = ra_alloc1(as, ir->op2, allow); | 605 | if (irf->o == IR_CARG) { func = irf->op1; irf = IR(func); } |
606 | ci.func = (ASMFunction)asm_callx_func(as, irf, func); | ||
607 | if (!(void *)ci.func) { | ||
608 | /* Use a (hoistable) non-scratch register for indirect calls. */ | ||
609 | RegSet allow = (RSET_GPR & ~RSET_SCRATCH); | ||
610 | Reg r = ra_alloc1(as, func, allow); | ||
521 | emit_rr(as, XO_GROUP5, XOg_CALL, r); | 611 | emit_rr(as, XO_GROUP5, XOg_CALL, r); |
522 | ci.func = (ASMFunction)(void *)0; | ||
523 | } | 612 | } |
524 | asm_gencall(as, &ci, args); | 613 | asm_gencall(as, &ci, args); |
525 | } | 614 | } |
@@ -2608,35 +2697,14 @@ static void asm_ir(ASMState *as, IRIns *ir) | |||
2608 | static Reg asm_setup_call_slots(ASMState *as, IRIns *ir, const CCallInfo *ci) | 2697 | static Reg asm_setup_call_slots(ASMState *as, IRIns *ir, const CCallInfo *ci) |
2609 | { | 2698 | { |
2610 | IRRef args[CCI_NARGS_MAX]; | 2699 | IRRef args[CCI_NARGS_MAX]; |
2611 | uint32_t nargs = (int)CCI_NARGS(ci); | 2700 | int nslots; |
2612 | int nslots = 0; | ||
2613 | asm_collectargs(as, ir, ci, args); | 2701 | asm_collectargs(as, ir, ci, args); |
2614 | #if LJ_64 | 2702 | nslots = asm_count_call_slots(as, ci, args); |
2615 | if (LJ_ABI_WIN) { | ||
2616 | nslots = (int)(nargs*2); /* Only matters for more than four args. */ | ||
2617 | } else { | ||
2618 | uint32_t i; | ||
2619 | int ngpr = 6, nfpr = 8; | ||
2620 | for (i = 0; i < nargs; i++) | ||
2621 | if (args[i] && irt_isfp(IR(args[i])->t)) { | ||
2622 | if (nfpr > 0) nfpr--; else nslots += 2; | ||
2623 | } else { | ||
2624 | if (ngpr > 0) ngpr--; else nslots += 2; | ||
2625 | } | ||
2626 | } | ||
2627 | if (nslots > as->evenspill) /* Leave room for args in stack slots. */ | 2703 | if (nslots > as->evenspill) /* Leave room for args in stack slots. */ |
2628 | as->evenspill = nslots; | 2704 | as->evenspill = nslots; |
2705 | #if LJ_64 | ||
2629 | return irt_isfp(ir->t) ? REGSP_HINT(RID_FPRET) : REGSP_HINT(RID_RET); | 2706 | return irt_isfp(ir->t) ? REGSP_HINT(RID_FPRET) : REGSP_HINT(RID_RET); |
2630 | #else | 2707 | #else |
2631 | if ((ci->flags & CCI_FASTCALL)) { | ||
2632 | lua_assert(nargs <= 2); | ||
2633 | } else { | ||
2634 | uint32_t i; | ||
2635 | for (i = 0; i < nargs; i++) | ||
2636 | nslots += (args[i] && irt_isnum(IR(args[i])->t)) ? 2 : 1; | ||
2637 | if (nslots > as->evenspill) /* Leave room for args. */ | ||
2638 | as->evenspill = nslots; | ||
2639 | } | ||
2640 | return irt_isfp(ir->t) ? REGSP_INIT : REGSP_HINT(RID_RET); | 2708 | return irt_isfp(ir->t) ? REGSP_INIT : REGSP_HINT(RID_RET); |
2641 | #endif | 2709 | #endif |
2642 | } | 2710 | } |