jwasm/invoke.c
2014-06-18 19:16:56 +04:00

1665 lines
74 KiB
C

/****************************************************************************
*
* This code is Public Domain.
*
* ========================================================================
*
* Description: Processing of INVOKE directive.
*
****************************************************************************/
#include <ctype.h>
#include <limits.h>
#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "reswords.h"
#include "expreval.h"
#include "lqueue.h"
#include "equate.h"
#include "assume.h"
#include "segment.h"
#include "listing.h"
#include "myassert.h"
#if DLLIMPORT
#include "mangle.h"
#include "extern.h"
#endif
#include "proc.h"
extern int_64 maxintvalues[];
extern int_64 minintvalues[];
extern enum special_token stackreg[];
#ifdef __I86__
#define NUMQUAL (long)
#else
#define NUMQUAL
#endif
enum reg_used_flags {
R0_USED = 0x01, /* register contents of AX/EAX/RAX is destroyed */
R0_H_CLEARED = 0x02, /* 16bit: high byte of R0 (=AH) has been set to 0 */
R0_X_CLEARED = 0x04, /* 16bit: register R0 (=AX) has been set to 0 */
R2_USED = 0x08, /* contents of DX is destroyed ( via CWD ); cpu < 80386 only */
#if AMD64_SUPPORT
RCX_USED = 0x08, /* win64: register contents of CL/CX/ECX/RCX is destroyed */
RDX_USED = 0x10, /* win64: register contents of DL/DX/EDX/RDX is destroyed */
R8_USED = 0x20, /* win64: register contents of R8B/R8W/R8D/R8 is destroyed */
R9_USED = 0x40, /* win64: register contents of R9B/R9W/R9D/R9 is destroyed */
#define RPAR_START 3 /* Win64: RCX first param start at bit 3 */
#endif
#if OWFC_SUPPORT
ROW_AX_USED = 0x08, /* watc: register contents of AL/AX/EAX is destroyed */
ROW_DX_USED = 0x10, /* watc: register contents of DL/DX/EDX is destroyed */
ROW_BX_USED = 0x20, /* watc: register contents of BL/BX/EBX is destroyed */
ROW_CX_USED = 0x40, /* watc: register contents of CL/CX/ECX is destroyed */
#define ROW_START 3 /* watc: irst param start at bit 3 */
#endif
};
static int size_vararg; /* size of :VARARG arguments */
static int fcscratch; /* exclusively to be used by FASTCALL helper functions */
struct fastcall_conv {
int (* invokestart)( struct dsym const *, int, int, struct asm_tok[], int * );
void (* invokeend) ( struct dsym const *, int, int );
int (* handleparam)( struct dsym const *, int, struct dsym *, bool, struct expr *, char *, uint_8 * );
};
static int ms32_fcstart( struct dsym const *, int, int, struct asm_tok[], int * );
static void ms32_fcend ( struct dsym const *, int, int );
static int ms32_param ( struct dsym const *, int, struct dsym *, bool, struct expr *, char *, uint_8 * );
#if OWFC_SUPPORT
static int watc_fcstart( struct dsym const *, int, int, struct asm_tok[], int * );
static void watc_fcend ( struct dsym const *, int, int );
static int watc_param ( struct dsym const *, int, struct dsym *, bool, struct expr *, char *, uint_8 * );
#endif
#if AMD64_SUPPORT
static int ms64_fcstart( struct dsym const *, int, int, struct asm_tok[], int * );
static void ms64_fcend ( struct dsym const *, int, int );
static int ms64_param ( struct dsym const *, int, struct dsym *, bool, struct expr *, char *, uint_8 * );
#define REGPAR_WIN64 0x0306 /* regs 1, 2, 8 and 9 */
#endif
static const struct fastcall_conv fastcall_tab[] = {
{ ms32_fcstart, ms32_fcend , ms32_param }, /* FCT_MSC */
#if OWFC_SUPPORT
{ watc_fcstart, watc_fcend , watc_param }, /* FCT_WATCOMC */
#endif
#if AMD64_SUPPORT
{ ms64_fcstart, ms64_fcend , ms64_param } /* FCT_WIN64 */
#endif
};
static const enum special_token regax[] = { T_AX, T_EAX,
#if AMD64_SUPPORT
T_RAX
#endif
};
/* 16-bit MS fastcall uses up to 3 registers (AX, DX, BX )
* and additional params are pushed in PASCAL order!
*/
static const enum special_token ms16_regs[] = {
T_AX, T_DX, T_BX
};
static const enum special_token ms32_regs[] = {
T_ECX, T_EDX
};
#if AMD64_SUPPORT
static const enum special_token ms64_regs[] = {
T_CL, T_DL, T_R8B, T_R9B,
T_CX, T_DX, T_R8W, T_R9W,
T_ECX, T_EDX, T_R8D, T_R9D,
T_RCX, T_RDX, T_R8, T_R9
};
#endif
/* segment register names, order must match ASSUME_ enum */
//static const enum special_token segreg_tab[] = {
// T_ES, T_CS, T_SS, T_DS, T_FS, T_GS };
static int ms32_fcstart( struct dsym const *proc, int numparams, int start, struct asm_tok tokenarray[], int *value )
/*******************************************************************************************************************/
{
struct dsym *param;
DebugMsg1(("ms32_fcstart(proc=%s, ofs=%u)\n", proc->sym.name, GetSymOfssize( &proc->sym ) ));
if ( GetSymOfssize( &proc->sym ) == USE16 )
return( 0 );
/* v2.07: count number of register params */
for ( param = proc->e.procinfo->paralist ; param ; param = param->nextparam )
if ( param->sym.state == SYM_TMACRO )
fcscratch++;
return( 1 );
}
static void ms32_fcend( struct dsym const *proc, int numparams, int value )
/*************************************************************************/
{
/* nothing to do */
return;
}
static int ms32_param( struct dsym const *proc, int index, struct dsym *param, bool addr, struct expr *opnd, char *paramvalue, uint_8 *r0used )
/*********************************************************************************************************************************************/
{
enum special_token const *pst;
DebugMsg1(("ms32_param(proc=%s, ofs=%u, index=%u, param=%s) fcscratch=%u\n", proc->sym.name, proc->sym.Ofssize, index, param->sym.name, fcscratch ));
if ( param->sym.state != SYM_TMACRO )
return( 0 );
if ( GetSymOfssize( &proc->sym ) == USE16 ) {
pst = ms16_regs + fcscratch;
fcscratch++;
} else {
fcscratch--;
pst = ms32_regs + fcscratch;
}
if ( addr )
AddLineQueueX( " lea %r, %s", *pst, paramvalue );
else {
enum special_token reg = *pst;
int size;
/* v2.08: adjust register if size of operand won't require the full register */
if ( ( opnd->kind != EXPR_CONST ) &&
( size = SizeFromMemtype( param->sym.mem_type, USE_EMPTY, param->sym.type ) ) < SizeFromRegister( *pst ) ) {
if (( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
AddLineQueueX( " %s %r, %s", ( param->sym.mem_type & MT_SIGNED ) ? "movsx" : "movzx", reg, paramvalue );
} else {
/* this is currently always UNSIGNED */
AddLineQueueX( " mov %r, %s", T_AL + GetRegNo( reg ), paramvalue );
AddLineQueueX( " mov %r, 0", T_AH + GetRegNo( reg ) );
}
} else {
/* v2.08: optimization */
if ( opnd->kind == EXPR_REG && opnd->indirect == 0 && opnd->base_reg ) {
if ( opnd->base_reg->tokval == reg )
return( 1 );
}
AddLineQueueX( " mov %r, %s", reg, paramvalue );
}
}
if ( *pst == T_AX )
*r0used |= R0_USED;
return( 1 );
}
#if AMD64_SUPPORT
static int ms64_fcstart( struct dsym const *proc, int numparams, int start, struct asm_tok tokenarray[], int *value )
/*******************************************************************************************************************/
{
/* v2.04: VARARG didn't work */
if ( proc->e.procinfo->has_vararg ) {
//numparams = ( tokenarray[start].token != T_FINAL ? 1 : 0 );
for ( numparams = 0; tokenarray[start].token != T_FINAL; start++ )
if ( tokenarray[start].token == T_COMMA )
numparams++;
}
DebugMsg1(("ms64_fcstart(%s, numparams=%u) vararg=%u\n", proc->sym.name, numparams, proc->e.procinfo->has_vararg ));
if ( numparams < 4 )
numparams = 4;
else if ( numparams & 1 )
numparams++;
*value = numparams;
if ( ModuleInfo.win64_flags & W64F_AUTOSTACKSP ) {
if ( ( numparams * sizeof( uint_64 ) ) > sym_ReservedStack->value )
sym_ReservedStack->value = numparams * sizeof( uint_64 );
} else
AddLineQueueX( " sub %r, %d", T_RSP, numparams * sizeof( uint_64 ) );
/* since Win64 fastcall doesn't push, it's a better/faster strategy to
* handle the arguments from left to right.
*/
return( 0 );
}
static void ms64_fcend( struct dsym const *proc, int numparams, int value )
/*************************************************************************/
{
/* use <value>, which has been set by ms64_fcstart() */
if ( !( ModuleInfo.win64_flags & W64F_AUTOSTACKSP ) )
AddLineQueueX( " add %r, %d", T_RSP, value * 8 );
return;
}
/* macro to convert register number to param number:
* 1 -> 0 (rCX)
* 2 -> 1 (rDX)
* 8 -> 2 (r8)
* 9 -> 3 (r9)
*/
#define GetParmIndex( x) ( ( (x) >= 8 ) ? (x) - 6 : (x) - 1 )
/*
* parameter for Win64 FASTCALL.
* the first 4 parameters are hold in registers: rcx, rdx, r8, r9 for non-float arguments,
* xmm0, xmm1, xmm2, xmm3 for float arguments. If parameter size is > 8, the address of
* the argument is used instead of the value.
*/
static int ms64_param( struct dsym const *proc, int index, struct dsym *param, bool addr, struct expr *opnd, char *paramvalue, uint_8 *regs_used )
/************************************************************************************************************************************************/
{
uint_32 size;
uint_32 psize;
int reg;
int reg2;
int i;
int base;
bool destroyed = FALSE;
DebugMsg1(("ms64_param(%s, index=%u, param.memtype=%Xh, addr=%u) enter\n", proc->sym.name, index, param->sym.mem_type, addr ));
/* v2.11: default size is 32-bit, not 64-bit */
if ( param->sym.is_vararg ) {
psize = 0;
if ( addr || opnd->instr == T_OFFSET )
psize = 8;
else if ( opnd->kind == EXPR_REG && opnd->indirect == FALSE )
psize = SizeFromRegister( opnd->base_reg->tokval );
else if ( opnd->mem_type != MT_EMPTY )
psize = SizeFromMemtype( opnd->mem_type, USE64, opnd->type );
if ( psize < 4 )
psize = 4;
} else
psize = SizeFromMemtype( param->sym.mem_type, USE64, param->sym.type );
/* check for register overwrites; v2.11: moved out the if( index >= 4 ) block */
if ( opnd->base_reg != NULL ) {
reg = opnd->base_reg->tokval;
if ( GetValueSp( reg ) & OP_R ) {
i = GetRegNo( reg );
if ( REGPAR_WIN64 & ( 1 << i ) ) {
base = GetParmIndex( i );
if ( *regs_used & ( 1 << ( base + RPAR_START ) ) )
destroyed = TRUE;
} else if ( (*regs_used & R0_USED ) && ( ( GetValueSp( reg ) & OP_A ) || reg == T_AH ) ) {
destroyed = TRUE;
}
}
}
if ( opnd->idx_reg != NULL ) {
reg2 = opnd->idx_reg->tokval;
if ( GetValueSp( reg2 ) & OP_R ) {
i = GetRegNo( reg2 );
if ( REGPAR_WIN64 & ( 1 << i ) ) {
base = GetParmIndex( i );
if ( *regs_used & ( 1 << ( base + RPAR_START ) ) )
destroyed = TRUE;
} else if ( (*regs_used & R0_USED ) && ( ( GetValueSp( reg2 ) & OP_A ) || reg2 == T_AH ) ) {
destroyed = TRUE;
}
}
}
if ( destroyed ) {
EmitErr( REGISTER_VALUE_OVERWRITTEN_BY_INVOKE );
*regs_used = 0;
}
if ( index >= 4 ) {
if ( addr || psize > 8 ) {
if ( psize == 4 )
i = T_EAX;
else {
i = T_RAX;
if ( psize < 8 )
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, index+1 );
}
*regs_used |= R0_USED;
AddLineQueueX( " lea %r, %s", i, paramvalue );
AddLineQueueX( " mov [%r+%u], %r", T_RSP, NUMQUAL index*8, i );
DebugMsg(("ms64_param(%s, param=%u): ADDR flags=%X\n", proc->sym.name, index, *regs_used ));
return( 1 );
}
if ( opnd->kind == EXPR_CONST ||
( opnd->kind == EXPR_ADDR && opnd->indirect == FALSE && opnd->mem_type == MT_EMPTY && opnd->instr != T_OFFSET ) ) {
/* v2.06: support 64-bit constants for params > 4 */
if ( psize == 8 &&
( opnd->value64 > LONG_MAX || opnd->value64 < LONG_MIN ) ) {
AddLineQueueX( " mov %r ptr [%r+%u], %r ( %s )", T_DWORD, T_RSP, NUMQUAL index*8, T_LOW32, paramvalue );
AddLineQueueX( " mov %r ptr [%r+%u], %r ( %s )", T_DWORD, T_RSP, NUMQUAL index*8+4, T_HIGH32, paramvalue );
} else {
/* v2.11: no expansion if target type is a pointer and argument is an address part */
if ( param->sym.mem_type == MT_PTR && opnd->kind == EXPR_ADDR && opnd->sym->state != SYM_UNDEFINED ) {
DebugMsg(("ms64_param(%s, param=%u): MT_PTR, type error, psize=%u\n", proc->sym.name, index, psize ));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, index+1 );
}
switch ( psize ) {
case 1: i = T_BYTE; break;
case 2: i = T_WORD; break;
case 4: i = T_DWORD; break;
default: i = T_QWORD; break;
}
AddLineQueueX( " mov %r ptr [%r+%u], %s", i, T_RSP, NUMQUAL index*8, paramvalue );
}
DebugMsg(("ms64_param(%s, param=%u): MT_EMPTY size.p=%u flags=%X\n", proc->sym.name, index, psize, *regs_used ));
} else if ( opnd->kind == EXPR_FLOAT ) {
if ( param->sym.mem_type == MT_REAL8 ) {
AddLineQueueX( " mov %r ptr [%r+%u+0], %r (%s)", T_DWORD, T_RSP, NUMQUAL index*8, T_LOW32, paramvalue );
AddLineQueueX( " mov %r ptr [%r+%u+4], %r (%s)", T_DWORD, T_RSP, NUMQUAL index*8, T_HIGH32, paramvalue );
} else
AddLineQueueX( " mov %r ptr [%r+%u], %s", T_DWORD, T_RSP, NUMQUAL index*8, paramvalue );
} else { /* it's a register or variable */
if ( opnd->kind == EXPR_REG && opnd->indirect == FALSE ) {
size = SizeFromRegister( reg );
if ( size == psize )
i = reg;
else {
if ( size > psize || ( size < psize && param->sym.mem_type == MT_PTR ) ) {
DebugMsg(("ms64_param(%s, param=%u): type error size.p/a=%u/%u flags=%X\n", proc->sym.name, index, psize, size, *regs_used ));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, index+1 );
psize = size;
}
switch ( psize ) {
case 1: i = T_AL; break;
case 2: i = T_AX; break;
case 4: i = T_EAX; break;
default: i = T_RAX; break;
}
*regs_used |= R0_USED;
}
DebugMsg(("ms64_param(%s, param=%u): REG size.p/a=%u/%u flags=%X\n", proc->sym.name, index, psize, size, *regs_used ));
} else {
if ( opnd->mem_type == MT_EMPTY )
size = ( opnd->instr == T_OFFSET ? 8 : 4 );
else
size = SizeFromMemtype( opnd->mem_type, USE64, opnd->type );
DebugMsg(("ms64_param(%s, param=%u): MEM size.p/a=%u/%u flags=%X\n", proc->sym.name, index, psize, size, *regs_used ));
switch ( psize ) {
case 1: i = T_AL; break;
case 2: i = T_AX; break;
case 4: i = T_EAX; break;
default: i = T_RAX; break;
}
*regs_used |= R0_USED;
}
/* v2.11: no expansion if target type is a pointer */
if ( size > psize || ( size < psize && param->sym.mem_type == MT_PTR ) ) {
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, index+1 );
}
if ( size != psize ) {
if ( size == 4 ) {
if ( IS_SIGNED( opnd->mem_type ) )
AddLineQueueX( " movsxd %r, %s", i, paramvalue );
else
AddLineQueueX( " mov %r, %s", i, paramvalue );
} else
AddLineQueueX( " mov%sx %r, %s", IS_SIGNED( opnd->mem_type ) ? "s" : "z", i, paramvalue );
} else if ( opnd->kind != EXPR_REG || opnd->indirect == TRUE )
AddLineQueueX( " mov %r, %s", i, paramvalue );
AddLineQueueX( " mov [%r+%u], %r", T_RSP, NUMQUAL index*8, i );
}
} else if ( param->sym.mem_type == MT_REAL4 ||
param->sym.mem_type == MT_REAL8 ) {
/* v2.04: check if argument is the correct XMM register already */
if ( opnd->kind == EXPR_REG && opnd->indirect == FALSE ) {
if ( GetValueSp( reg ) & OP_XMM ) {
if ( reg == T_XMM0 + index )
DebugMsg(("ms64_param(%s, param=%u): argument optimized\n", proc->sym.name, index ));
else
AddLineQueueX( " movq %r, %s", T_XMM0 + index, paramvalue );
return( 1 );
}
}
if ( opnd->kind == EXPR_FLOAT ) {
*regs_used |= R0_USED;
if ( param->sym.mem_type == MT_REAL4 ) {
AddLineQueueX( " mov %r, %s", T_EAX, paramvalue );
AddLineQueueX( " movd %r, %r", T_XMM0 + index, T_EAX );
} else {
AddLineQueueX( " mov %r, %r ptr %s", T_RAX, T_REAL8, paramvalue );
AddLineQueueX( " movd %r, %r", T_XMM0 + index, T_RAX );
}
} else {
if ( param->sym.mem_type == MT_REAL4 )
AddLineQueueX( " movd %r, %s", T_XMM0 + index, paramvalue );
else
AddLineQueueX( " movq %r, %s", T_XMM0 + index, paramvalue );
}
} else {
if ( addr || psize > 8 ) { /* psize > 8 shouldn't happen! */
if ( psize >= 4 )
AddLineQueueX( " lea %r, %s", ms64_regs[index+2*4+(psize > 4 ? 4 : 0)], paramvalue );
else
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, index+1 );
*regs_used |= ( 1 << ( index + RPAR_START ) );
return( 1 );
}
/* register argument? */
if ( opnd->kind == EXPR_REG && opnd->indirect == FALSE ) {
reg = opnd->base_reg->tokval;
size = SizeFromRegister( reg );
} else if ( opnd->kind == EXPR_CONST || opnd->kind == EXPR_FLOAT ) {
size = psize;
} else if ( opnd->mem_type != MT_EMPTY ) {
size = SizeFromMemtype( opnd->mem_type, USE64, opnd->type );
} else if ( opnd->kind == EXPR_ADDR && opnd->sym->state == SYM_UNDEFINED ) {
DebugMsg1(("ms64_param(%s, param=%u): forward ref=%s, assumed size=%u\n", proc->sym.name, index, opnd->sym->name, psize ));
size = psize;
} else
size = ( opnd->instr == T_OFFSET ? 8 : 4 );
/* v2.11: allow argument extension, so long as the target isn't a pointer */
//if ( size != psize && param->sym.is_vararg == FALSE ) {
if ( size > psize || ( size < psize && param->sym.mem_type == MT_PTR ) ) {
DebugMsg(("ms64_param(%s, param=%u): type error size.p/a=%u/%u flags=%X\n", proc->sym.name, index, psize, size, *regs_used ));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, index+1 );
}
/* v2.11: use parameter size to allow argument extension */
//switch ( size ) {
switch ( psize ) {
case 1: base = 0*4; break;
case 2: base = 1*4; break;
case 4: base = 2*4; break;
default:base = 3*4; break;
}
/* optimization if the register holds the value already */
if ( opnd->kind == EXPR_REG && opnd->indirect == FALSE ) {
if ( GetValueSp( reg ) & OP_R ) {
if ( ms64_regs[index+base] == reg ) {
DebugMsg(("ms64_param(%s, param=%u): argument optimized\n", proc->sym.name, index ));
return( 1 );
}
i = GetRegNo( reg );
if ( REGPAR_WIN64 & ( 1 << i ) ) {
i = GetParmIndex( i );
if ( *regs_used & ( 1 << ( i + RPAR_START ) ) )
EmitErr( REGISTER_VALUE_OVERWRITTEN_BY_INVOKE );
}
}
}
/* v2.11: allow argument extension */
if ( size < psize )
if ( size == 4 ) {
if ( IS_SIGNED( opnd->mem_type ) )
AddLineQueueX( " movsxd %r, %s", ms64_regs[index+base], paramvalue );
else
AddLineQueueX( " mov %r, %s", ms64_regs[index+2*4], paramvalue );
} else
AddLineQueueX( " mov%sx %r, %s", IS_SIGNED( opnd->mem_type ) ? "s" : "z", ms64_regs[index+base], paramvalue );
else
AddLineQueueX( " mov %r, %s", ms64_regs[index+base], paramvalue );
*regs_used |= ( 1 << ( index + RPAR_START ) );
DebugMsg1(("ms64_param(%s, param=%u): size=%u flags=%X\n", proc->sym.name, index, size, *regs_used ));
}
return( 1 );
}
#endif
/* get segment part of an argument
* v2.05: extracted from PushInvokeParam(),
* so it could be used by watc_param() as well.
*/
static short GetSegmentPart( struct expr *opnd, char *buffer, const char *fullparam )
/***********************************************************************************/
{
short reg = T_NULL;
DebugMsg1(("GetSegmentPart(%s) enter [override=%s sym=%s segment=%s]\n",
fullparam, opnd->override ? opnd->override->string_ptr : "NULL",
opnd->sym ? opnd->sym->name : "NULL",
opnd->sym ? opnd->sym->segment ? opnd->sym->segment->name : "NULL" : "NULL" ));
if ( opnd->override != NULL ) {
if ( opnd->override->token == T_REG )
reg = opnd->override->tokval;
else
strcpy( buffer, opnd->override->string_ptr );
} else if ( opnd->sym != NULL && opnd->sym->segment != NULL ) {
struct dsym *dir = GetSegm( opnd->sym );
enum assume_segreg as;
if ( dir->e.seginfo->segtype == SEGTYPE_DATA ||
dir->e.seginfo->segtype == SEGTYPE_BSS )
as = search_assume( (struct asym *)dir, ASSUME_DS, TRUE );
else
as = search_assume( (struct asym *)dir, ASSUME_CS, TRUE );
if ( as != ASSUME_NOTHING ) {
//GetResWName( segreg_tab[as], buffer );
reg = T_ES + as; /* v2.08: T_ES is first seg reg in special.h */
} else {
struct asym *seg;
seg = GetGroup( opnd->sym );
if ( seg == NULL )
seg = &dir->sym;
if ( seg )
strcpy( buffer, seg->name );
else {
strcpy( buffer, "seg " );
strcat( buffer, fullparam );
}
}
} else if ( opnd->sym && opnd->sym->state == SYM_STACK ) {
reg = T_SS;
} else {
strcpy( buffer,"seg " );
strcat( buffer, fullparam );
}
DebugMsg1(("GetSegmentPart: reg%u, buffer=%s\n", reg, reg ? "" : buffer ));
return( reg );
}
#if OWFC_SUPPORT
/* the watcomm fastcall variant is somewhat peculiar:
* 16-bit:
* - for BYTE/WORD arguments, there are 4 registers: AX,DX,BX,CX
* - for DWORD arguments, there are 2 register pairs: DX::AX and CX::BX
* - there is a "usage" flag for each register. Thus the prototype:
* sample proto :WORD, :DWORD, :WORD
* will assign AX to the first param, CX::BX to the second, and DX to
* the third!
*/
static int watc_fcstart( struct dsym const *proc, int numparams, int start, struct asm_tok tokenarray[], int *value )
/*******************************************************************************************************************/
{
DebugMsg1(("watc_fcstart(%s, %u, %u)\n", proc->sym.name, numparams, start ));
return( 1 );
}
static void watc_fcend( struct dsym const *proc, int numparams, int value )
/*************************************************************************/
{
DebugMsg1(("watc_fcend(%s, %u, %u)\n", proc->sym.name, numparams, value ));
if ( proc->e.procinfo->has_vararg ) {
AddLineQueueX( " add %r, %u", stackreg[ModuleInfo.Ofssize], NUMQUAL proc->e.procinfo->parasize + size_vararg );
} else if ( fcscratch < proc->e.procinfo->parasize ) {
AddLineQueueX( " add %r, %u", stackreg[ModuleInfo.Ofssize], NUMQUAL ( proc->e.procinfo->parasize - fcscratch ) );
}
return;
}
/* get the register for parms 0 to 3,
* using the watcom register parm passing conventions ( A D B C )
*/
static int watc_param( struct dsym const *proc, int index, struct dsym *param, bool addr, struct expr *opnd, char *paramvalue, uint_8 *r0used )
/*********************************************************************************************************************************************/
{
int opc;
int qual;
int i;
char regs[64];
char *reg[4];
char *p;
int psize = SizeFromMemtype( param->sym.mem_type, USE_EMPTY, param->sym.type );
DebugMsg1(("watc_param(%s, param=%u [name=%s, state=%u]),addr=%u: psize=%u\n", proc->sym.name, index, param->sym.name, param->sym.state, addr, psize ));
if ( param->sym.state != SYM_TMACRO )
return( 0 );
DebugMsg1(("watc_param(%s): register param=%s\n", proc->sym.name, param->sym.string_ptr ));
fcscratch += CurrWordSize;
/* the "name" might be a register pair */
reg[0] = param->sym.string_ptr;
reg[1] = NULL;
reg[2] = NULL;
reg[3] = NULL;
if ( strchr( reg[0], ':' ) ) {
strcpy( regs, reg[0] );
fcscratch += CurrWordSize;
for ( p = regs, i = 0; i < 4; i++ ) {
reg[i] = p;
p = strchr( p, ':' );
if ( p == NULL )
break;
*p++ = NULLC;
p++;
}
}
if ( addr ) {
if ( opnd->kind == T_REG || opnd->sym->state == SYM_STACK ) {
opc = T_LEA;
qual = T_NULL;
} else {
opc = T_MOV;
qual = T_OFFSET;
}
/* v2.05: filling of segment part added */
i = 0;
if ( reg[1] != NULL ) {
char buffer[128];
short sreg;
if ( sreg = GetSegmentPart( opnd, buffer, paramvalue ) )
AddLineQueueX( "%r %s, %r", T_MOV, reg[0], sreg );
else
AddLineQueueX( "%r %s, %s", T_MOV, reg[0], buffer );
i++;
}
AddLineQueueX( "%r %s, %r %s", opc, reg[i], qual, paramvalue );
return( 1 );
}
for ( i = 3; i >= 0; i-- ) {
if ( reg[i] ) {
if ( opnd->kind == EXPR_CONST ) {
if ( i > 0 )
qual = T_LOWWORD;
else if ( i == 0 && reg[1] != NULL )
qual = T_HIGHWORD;
else
qual = T_NULL;
if ( qual != T_NULL )
AddLineQueueX( "mov %s, %r (%s)", reg[i], qual, paramvalue );
else
AddLineQueueX( "mov %s, %s", reg[i], paramvalue );
} else if ( opnd->kind == EXPR_REG ) {
AddLineQueueX( "mov %s, %s", reg[i], paramvalue );
} else {
if ( i == 0 && reg[1] == NULL )
AddLineQueueX( "mov %s, %s", reg[i], paramvalue );
else {
if ( ModuleInfo.Ofssize )
qual = T_DWORD;
else
qual = T_WORD;
AddLineQueueX( "mov %s, %r %r %s[%u]", reg[i], qual, T_PTR, paramvalue, psize - ( (i+1) * ( 2 << ModuleInfo.Ofssize ) ) );
}
}
}
}
return( 1 );
}
#endif
static void SkipTypecast( char *fullparam, int i, struct asm_tok tokenarray[] )
/*****************************************************************************/
{
int j;
fullparam[0] = NULLC;
for ( j = i; ; j++ ) {
if (( tokenarray[j].token == T_COMMA ) || ( tokenarray[j].token == T_FINAL ) )
break;
if (( tokenarray[j+1].token == T_BINARY_OPERATOR ) && ( tokenarray[j+1].tokval == T_PTR ) )
j = j + 1;
else {
if ( fullparam[0] != NULLC )
strcat( fullparam," " );
strcat( fullparam, tokenarray[j].string_ptr );
}
}
}
/*
* push one parameter of a procedure called with INVOKE onto the stack
* - i : index of the start of the parameter list
* - tokenarray : token array
* - proc : the PROC to call
* - curr : the current parameter
* - reqParam: the index of the parameter which is to be pushed
* - r0flags : flags for register usage across params
*
* psize,asize: size of parameter/argument in bytes.
*/
static int PushInvokeParam( int i, struct asm_tok tokenarray[], struct dsym *proc, struct dsym *curr, int reqParam, uint_8 *r0flags)
/**********************************************************************************************************************************/
{
int currParm;
int psize;
int asize;
int pushsize;
int j;
int fptrsize;
char Ofssize;
bool addr = FALSE; /* ADDR operator found */
struct expr opnd;
char fullparam[MAX_LINE_LEN];
char buffer[MAX_LINE_LEN];
DebugMsg1(("PushInvokeParam(%s, param=%s:%u, i=%u ) enter\n", proc->sym.name, curr ? curr->sym.name : "NULL", reqParam, i ));
for ( currParm = 0; currParm <= reqParam; ) {
if ( tokenarray[i].token == T_FINAL ) { /* this is no real error! */
DebugMsg1(("PushInvokeParam(%s): T_FINAL token, i=%u\n", proc->sym.name, i));
return( ERROR );
}
if ( tokenarray[i].token == T_COMMA ) {
currParm++;
}
i++;
}
/* if curr is NULL this call is just a parameter check */
if ( !curr ) return( NOT_ERROR );
#if 1 /* v2.05 */
psize = curr->sym.total_size;
DebugMsg1(("PushInvokeParam(%s,%u): pmtype=%Xh, psize=%u\n", proc->sym.name, reqParam, curr->sym.mem_type, psize ));
#else
/* set psize (size of parameter) */
if ( curr->is_ptr ) {
psize = 2 << curr->sym.Ofssize;
if ( curr->sym.isfar )
psize += 2;
} else
psize = SizeFromMemtype( curr->sym.mem_type, curr->sym.Ofssize, curr->sym.type );
DebugMsg1(("PushInvokeParam(%s,%u): is_ptr=%u, pmtype=%Xh, psize=%u\n", proc->sym.name, reqParam, curr->is_ptr, curr->sym.mem_type, psize ));
#endif
/* ADDR: the argument's address is to be pushed? */
if ( tokenarray[i].token == T_RES_ID && tokenarray[i].tokval == T_ADDR ) {
addr = TRUE;
i++;
}
/* copy the parameter tokens to fullparam */
for ( j = i; tokenarray[j].token != T_COMMA && tokenarray[j].token != T_FINAL; j++ );
memcpy( fullparam, tokenarray[i].tokpos, tokenarray[j].tokpos - tokenarray[i].tokpos );
fullparam[tokenarray[j].tokpos - tokenarray[i].tokpos] = NULLC;
j = i;
/* v2.11: GetSymOfssize() doesn't work for state SYM_TYPE */
//fptrsize = 2 + ( 2 << GetSymOfssize( &proc->sym ) );
Ofssize = ( proc->sym.state == SYM_TYPE ? proc->sym.seg_ofssize : GetSymOfssize( &proc->sym ) );
fptrsize = 2 + ( 2 << Ofssize );
if ( addr ) {
/* v2.06: don't handle forward refs if -Zne is set */
//if ( EvalOperand( &j, Token_Count, &opnd, 0 ) == ERROR )
if ( EvalOperand( &j, tokenarray, Token_Count, &opnd, ModuleInfo.invoke_exprparm ) == ERROR )
return( ERROR );
/* DWORD (16bit) and FWORD(32bit) are treated like FAR ptrs
* v2.11: argument may be a FAR32 pointer ( psize == 6 ), while
* fptrsize may be just 4!
*/
//if ( psize > fptrsize ) {
if ( psize > fptrsize && fptrsize > 4 ) {
/* QWORD is NOT accepted as a FAR ptr */
DebugMsg1(("PushInvokeParm(%u): error, psize=%u, fptrsize=%u\n", reqParam, psize, fptrsize));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, reqParam+1 );
return( NOT_ERROR );
}
if ( proc->sym.langtype == LANG_FASTCALL )
if ( fastcall_tab[ModuleInfo.fctype].handleparam( proc, reqParam, curr, addr, &opnd, fullparam, r0flags ) )
return( NOT_ERROR );
if ( opnd.kind == EXPR_REG || opnd.indirect ) {
if ( curr->sym.isfar || psize == fptrsize ) {
DebugMsg1(("PushInvokeParam: far ptr, %s isfar=%u, psize=%u, fptrsize=%u\n", curr->sym.name, curr->sym.isfar, psize, fptrsize ));
if ( opnd.sym && opnd.sym->state == SYM_STACK )
GetResWName( T_SS, buffer );
else if ( opnd.override != NULL )
strcpy( buffer, opnd.override->string_ptr );
else
GetResWName( T_DS, buffer );
AddLineQueueX( " push %s", buffer );
}
AddLineQueueX( " lea %r, %s", regax[ModuleInfo.Ofssize], fullparam );
*r0flags |= R0_USED;
AddLineQueueX( " push %r", regax[ModuleInfo.Ofssize] );
} else {
push_address:
/* push segment part of address?
* v2.11: do not assume a far pointer if psize == fptrsize
* ( parameter might be near32 in a 16-bit environment )
*/
//if ( curr->sym.isfar || psize == fptrsize ) {
if ( curr->sym.isfar || psize > ( 2 << curr->sym.Ofssize ) ) {
short sreg;
sreg = GetSegmentPart( &opnd, buffer, fullparam );
if ( sreg ) {
/* v2.11: push segment part as WORD or DWORD depending on target's offset size
* problem: "pushw ds" is not accepted, so just emit a size prefix.
*/
if ( Ofssize != ModuleInfo.Ofssize || ( curr->sym.Ofssize == USE16 && CurrWordSize > 2 ) )
AddLineQueue( " db 66h" );
AddLineQueueX( " push %r", sreg );
} else
AddLineQueueX( " push %s", buffer );
}
/* push offset part of address */
if ( (ModuleInfo.curr_cpu & P_CPU_MASK ) < P_186 ) {
AddLineQueueX( " mov %r, offset %s", T_AX, fullparam );
AddLineQueueX( " push %r", T_AX );
*r0flags |= R0_USED;
} else {
if ( curr->sym.is_vararg && opnd.Ofssize == USE_EMPTY && opnd.sym )
opnd.Ofssize = GetSymOfssize( opnd.sym );
/* v2.04: expand 16-bit offset to 32
* v2.11: also expand if there's an explicit near32 ptr requested in 16-bit
*/
//if ( opnd.Ofssize == USE16 && CurrWordSize > 2 ) {
if ( ( opnd.Ofssize == USE16 && CurrWordSize > 2 ) ||
( curr->sym.Ofssize == USE32 && CurrWordSize == 2 ) ) {
AddLineQueueX( " pushd %r %s", T_OFFSET, fullparam );
} else if ( CurrWordSize > 2 && curr->sym.Ofssize == USE16 &&
( curr->sym.isfar || Ofssize == USE16 ) ) { /* v2.11: added */
AddLineQueueX( " pushw %r %s", T_OFFSET, fullparam );
} else {
AddLineQueueX( " push %r %s", T_OFFSET, fullparam );
/* v2.04: a 32bit offset pushed in 16-bit code */
if ( curr->sym.is_vararg && CurrWordSize == 2 && opnd.Ofssize > USE16 ) {
size_vararg += CurrWordSize;
}
}
}
}
if ( curr->sym.is_vararg ) {
size_vararg += CurrWordSize + ( curr->sym.isfar ? CurrWordSize : 0 );
DebugMsg1(("PushInvokeParm(%u): new value of size_vararg=%u [CurrWordSize=%u]\n", reqParam, size_vararg, CurrWordSize ));
}
} else { /* ! ADDR branch */
/* handle the <reg>::<reg> case here, the evaluator wont handle it */
if ( tokenarray[j].token == T_REG &&
tokenarray[j+1].token == T_DBL_COLON &&
tokenarray[j+2].token == T_REG ) {
int asize2;
/* for pointers, segreg size is assumed to be always 2 */
if ( GetValueSp( tokenarray[j].tokval ) & OP_SR ) {
asize2 = 2;
/* v2.11: if target and current src have different offset sizes,
* the push of the segment register must be 66h-prefixed!
*/
if ( Ofssize != ModuleInfo.Ofssize || ( curr->sym.Ofssize == USE16 && CurrWordSize > 2 ) )
AddLineQueue( " db 66h" );
} else
asize2 = SizeFromRegister( tokenarray[j].tokval );
asize = SizeFromRegister( tokenarray[j+2].tokval );
AddLineQueueX( " push %r", tokenarray[j].tokval );
/* v2.04: changed */
if (( curr->sym.is_vararg ) && (asize + asize2) != CurrWordSize )
size_vararg += asize2;
else
asize += asize2;
strcpy( fullparam, tokenarray[j+2].string_ptr );
opnd.kind = EXPR_REG;
opnd.indirect = FALSE;
opnd.sym = NULL;
opnd.base_reg = &tokenarray[j+2]; /* for error msg 'eax overwritten...' */
} else {
/* v2.06: don't handle forward refs if -Zne is set */
//if ( EvalOperand( &j, Token_Count, &opnd, 0 ) == ERROR ) {
if ( EvalOperand( &j, tokenarray, Token_Count, &opnd, ModuleInfo.invoke_exprparm ) == ERROR ) {
return( ERROR );
}
/* for a simple register, get its size */
if ( opnd.kind == EXPR_REG && opnd.indirect == FALSE ) {
asize = SizeFromRegister( opnd.base_reg->tokval );
//} else if ( opnd.mem_type == MT_EMPTY ) { /* v2.10: a TYPE may return mem_type != MT_EMPTY! */
} else if ( opnd.kind == EXPR_CONST || opnd.mem_type == MT_EMPTY ) {
asize = psize;
/* v2.04: added, to catch 0-size params ( STRUCT without members ) */
if ( psize == 0 ) {
if ( curr->sym.is_vararg == FALSE ) {
DebugMsg1(("PushInvokeParm(%u): error, psize=0\n" ));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, reqParam+1 );
}
/* v2.07: for VARARG, get the member's size if it is a structured var */
if ( opnd.mbr && opnd.mbr->mem_type == MT_TYPE )
asize = SizeFromMemtype( opnd.mbr->mem_type, opnd.Ofssize, opnd.mbr->type );
}
DebugMsg1(("PushInvokeParm(%u): memtype EMPTY, asize=%u psize=%u\n", reqParam, asize, psize ));
} else if ( opnd.mem_type != MT_TYPE ) {
if ( opnd.kind == EXPR_ADDR &&
opnd.indirect == FALSE &&
opnd.sym &&
opnd.instr == EMPTY &&
( opnd.mem_type == MT_NEAR || opnd.mem_type == MT_FAR ) )
goto push_address;
if ( opnd.Ofssize == USE_EMPTY )
opnd.Ofssize = ModuleInfo.Ofssize;
asize = SizeFromMemtype( opnd.mem_type, opnd.Ofssize, opnd.type );
} else {
if ( opnd.sym != NULL )
asize = opnd.sym->type->total_size;
else
asize = opnd.mbr->type->total_size;
}
}
if ( curr->sym.is_vararg == TRUE )
psize = asize;
#ifdef DEBUG_OUT
if ( opnd.sym )
DebugMsg1(("PushInvokeParam(%s, %u): arg name=%s, asize=%u, amtype=%xh psize=%u\n", proc->sym.name, reqParam, opnd.sym->name, asize, opnd.mem_type, psize));
else
DebugMsg1(("PushInvokeParam(%s, %u): arg no name, asize=%u, amtype=%xh psize=%u\n", proc->sym.name, reqParam, asize, opnd.mem_type, psize));
#endif
pushsize = CurrWordSize;
if ( proc->sym.langtype == LANG_FASTCALL )
if ( fastcall_tab[ModuleInfo.fctype].handleparam( proc, reqParam, curr, addr, &opnd, fullparam, r0flags ) )
return( NOT_ERROR );
/* v2.04: this check has been moved behind the fastcall_tab() call */
/* v2.11: if target is a pointer, sizes must match */
//if ( asize > psize ) { /* argument's size too big? */
if ( ( asize > psize ) || ( asize < psize && curr->sym.mem_type == MT_PTR ) ) {
DebugMsg(("PushInvokeParm(%u): argsize error, arg size=%d, parm size=%d\n", reqParam, asize, psize));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, reqParam+1 );
return( NOT_ERROR );
}
if ( ( opnd.kind == EXPR_ADDR && opnd.instr != T_OFFSET ) ||
( opnd.kind == EXPR_REG && opnd.indirect == TRUE ) ) {
/* catch the case when EAX has been used for ADDR,
* and is later used as addressing register!
*
*/
if ( *r0flags &&
(( opnd.base_reg != NULL &&
( opnd.base_reg->tokval == T_EAX
#if AMD64_SUPPORT
|| opnd.base_reg->tokval == T_RAX
#endif
)) ||
( opnd.idx_reg != NULL &&
( opnd.idx_reg->tokval == T_EAX
#if AMD64_SUPPORT
|| opnd.idx_reg->tokval == T_RAX
#endif
)))) {
EmitErr( REGISTER_VALUE_OVERWRITTEN_BY_INVOKE );
*r0flags = 0;
}
if ( curr->sym.is_vararg ) {
size_vararg += ( asize > pushsize ? asize : pushsize );
DebugMsg1(("PushInvokeParm(%u): asize=%u added to size_vararg, now=%u\n",
reqParam, asize > pushsize ? asize : pushsize, size_vararg ));
}
if ( asize > pushsize ) {
short dw = T_WORD;
if (( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
pushsize = 4;
dw = T_DWORD;
}
/* in params like "qword ptr [eax]" the typecast
* has to be removed */
if ( opnd.explicit ) {
SkipTypecast( fullparam, i, tokenarray );
opnd.explicit = FALSE;
}
while ( asize > 0 ) {
if ( asize & 2 ) {
/* ensure the stack remains dword-aligned in 32bit */
if ( ModuleInfo.Ofssize > USE16 ) {
/* v2.05: better push a 0 word? */
//AddLineQueueX( " pushw 0" );
#if AMD64_SUPPORT
AddLineQueueX( " sub %r, 2", stackreg[ModuleInfo.Ofssize] );
#else
AddLineQueueX( " sub %r, 2", T_ESP );
#endif
}
AddLineQueueX( " push word ptr %s+%u", fullparam, NUMQUAL asize-2 );
asize -= 2;
} else {
AddLineQueueX( " push %r ptr %s+%u", dw, fullparam, NUMQUAL asize-pushsize );
asize -= pushsize;
}
}
//return( NOT_ERROR );
} else if ( asize < pushsize ) {
if ( psize > 4 ) {
DebugMsg1(("PushInvokeParm(%u): error, ADDR, psize=%u, is > 4\n",
reqParam, psize ));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, reqParam+1 );
}
/* v2.11: added, use MOVSX/MOVZX if cpu >= 80386 */
if ( asize < 4 && psize > 2 && IS_SIGNED( opnd.mem_type ) && ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
AddLineQueueX( " movsx %r, %s", T_EAX, fullparam );
AddLineQueueX( " push %r", T_EAX );
*r0flags = R0_USED; /* reset R0_H_CLEARED */
} else {
//switch (sym->mem_type) {
switch ( opnd.mem_type ) {
case MT_BYTE:
case MT_SBYTE:
if ( psize == 1 && curr->sym.is_vararg == FALSE ) {
AddLineQueueX( " mov %r, %s", T_AL, fullparam );
AddLineQueueX( " push %r", regax[ModuleInfo.Ofssize] );
} else if ( pushsize == 2 ) { /* 16-bit code? */
if ( opnd.mem_type == MT_BYTE ) {
if ( psize == 4 )
if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) < P_186 ) {
if ( !(*r0flags & R0_X_CLEARED ) )
AddLineQueueX( " xor %r, %r", T_AX, T_AX );
*r0flags |= ( R0_X_CLEARED | R0_H_CLEARED );
AddLineQueueX( " push %r", T_AX );
} else
AddLineQueue( " push 0" );
AddLineQueueX( " mov %r, %s", T_AL, fullparam );
if ( !( *r0flags & R0_H_CLEARED )) {
AddLineQueueX( " mov %r, 0", T_AH );
*r0flags |= R0_H_CLEARED;
}
} else {
AddLineQueueX( " mov %r, %s", T_AL, fullparam );
*r0flags = 0; /* reset AH_CLEARED */
AddLineQueue( " cbw" );
if ( psize == 4 ) {
AddLineQueue( " cwd" );
AddLineQueueX( " push %r", T_DX );
*r0flags |= R2_USED;
}
}
AddLineQueueX( " push %r", T_AX );
} else {
AddLineQueueX( " mov%sx %r, %s", opnd.mem_type == MT_BYTE ? "z" : "s", T_EAX, fullparam );
AddLineQueueX( " push %r", T_EAX );
}
*r0flags |= R0_USED;
break;
case MT_WORD:
case MT_SWORD:
/* pushsize is 4 here, hence it's always 32-bit code!
* v2.04: use the Masm-compatible, non-destructive
* PUSH if psize is 2.
*/
//if ( Options.masm_compat_gencode ) {
/* v2.11: don't push 0 if src operand is signed */
//if ( Options.masm_compat_gencode || psize == 2 ) {
if ( opnd.mem_type == MT_WORD && ( Options.masm_compat_gencode || psize == 2 )) {
/* v2.05: push a 0 word if argument is VARARG
* v2.10: push a 0 word if psize != 2
*/
//if ( curr->sym.is_vararg )
if ( curr->sym.is_vararg || psize != 2 )
AddLineQueueX( " pushw 0" );
else {
#if AMD64_SUPPORT
AddLineQueueX( " sub %r, 2", stackreg[ModuleInfo.Ofssize] );
#else
AddLineQueueX( " sub %r, 2", T_ESP );
#endif
}
AddLineQueueX( " push %s", fullparam );
} else {
AddLineQueueX( " mov%sx %r, %s", opnd.mem_type == MT_WORD ? "z" : "s", T_EAX, fullparam );
AddLineQueueX( " push %r", T_EAX );
*r0flags = R0_USED; /* reset R0_H_CLEARED */
}
break;
default:
AddLineQueueX( " push %s", fullparam );
}
}
} else { /* asize == pushsize */
/* v2.11: changed */
if ( IS_SIGNED( opnd.mem_type ) && psize > asize ) {
if ( psize > 2 && (( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) ) {
AddLineQueueX( " movsx %r, %s", T_EAX, fullparam );
AddLineQueueX( " push %r", T_EAX );
*r0flags = R0_USED; /* reset R0_H_CLEARED */
} else if ( pushsize == 2 && psize > 2 ) {
AddLineQueueX( " mov %r, %s", T_AX, fullparam );
AddLineQueueX( " cwd" );
AddLineQueueX( " push %r", T_DX );
AddLineQueueX( " push %r", T_AX );
*r0flags = R0_USED | R2_USED;
} else
AddLineQueueX( " push %s", fullparam );
} else {
if ( pushsize == 2 && psize > 2 ) {
if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) < P_186 ) {
if ( !(*r0flags & R0_X_CLEARED ) )
AddLineQueueX( " xor %r, %r", T_AX, T_AX );
AddLineQueueX( " push %r", T_AX );
*r0flags |= ( R0_USED | R0_X_CLEARED | R0_H_CLEARED );
} else
AddLineQueueX( " pushw 0" );
}
AddLineQueueX( " push %s", fullparam );
}
}
} else { /* the parameter is a register or constant value! */
//char is_r0 = FALSE;
if ( opnd.kind == EXPR_REG ) {
int reg = opnd.base_reg->tokval;
unsigned optype = GetValueSp( reg );
/* v2.11 */
if ( curr->sym.is_vararg == TRUE && psize < pushsize )
psize = pushsize;
/* v2.06: check if register is valid to be pushed.
* ST(n), MMn, XMMn, YMMn and special registers are NOT valid!
*/
if ( optype & ( OP_STI | OP_MMX | OP_XMM
#if AVXSUPP
| OP_YMM
#endif
| OP_RSPEC ) ) {
return( EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, reqParam+1 ) );
}
if ( ( *r0flags & R0_USED ) && ( reg == T_AH || ( optype & OP_A ) ) ) {
EmitErr( REGISTER_VALUE_OVERWRITTEN_BY_INVOKE );
*r0flags &= ~R0_USED;
} else if ( ( *r0flags & R2_USED ) && ( reg == T_DH || GetRegNo( reg ) == 2 ) ) {
EmitErr( REGISTER_VALUE_OVERWRITTEN_BY_INVOKE );
*r0flags &= ~R2_USED;
}
/* v2.11: use target's "pushsize", not the current one */
//if ( asize != psize || asize < pushsize ) {
if ( asize != psize || asize < ( 2 << Ofssize ) ) {
/* register size doesn't match the needed parameter size.
*/
if ( psize > 4 ) {
DebugMsg1(("PushInvokeParm(%u): error, REG, asize=%u, psize=%u, pushsize=%u\n",
reqParam, asize, psize, pushsize ));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, reqParam+1 );
}
if ( asize <= 2 && ( psize == 4 || pushsize == 4 ) ) {
if (( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 && asize == psize ) {
if ( asize == 2 )
reg = reg - T_AX + T_EAX;
else {
/* v2.11: hibyte registers AH, BH, CH, DH ( no 4-7 ) needs special handling */
if ( reg < T_AH )
reg = reg - T_AL + T_EAX;
else {
AddLineQueueX( " mov %r, %s", T_AL, fullparam );
*r0flags |= R0_USED;
reg = T_EAX;
}
asize = 2; /* done */
}
} else if ( IS_SIGNED( opnd.mem_type ) && pushsize < 4 ) {
/* psize is 4 in this branch */
if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
AddLineQueueX( " movsx %r, %s", T_EAX, fullparam );
*r0flags = R0_USED;
reg = T_EAX;
} else {
*r0flags = R0_USED | R2_USED;
if ( asize == 1 ) {
if ( reg != T_AL )
AddLineQueueX( " mov %r, %s", T_AL, fullparam );
AddLineQueue( " cbw" );
} else if ( reg != T_AX )
AddLineQueueX( " mov %r, %s", T_AX, fullparam );
AddLineQueue( " cwd" );
AddLineQueueX( " push %r", T_DX );
reg = T_AX;
}
asize = 2; /* done */
} else if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_186 ) {
if ( pushsize == 4 ) {
if ( asize == 1 ) {
/* handled below */
} else if ( psize <= 2 ) {
#if AMD64_SUPPORT
AddLineQueueX( " sub %r, 2", stackreg[ModuleInfo.Ofssize] );
#else
AddLineQueueX( " sub %r, 2", T_ESP );
#endif
} else if ( IS_SIGNED( opnd.mem_type ) ) {
AddLineQueueX( " movsx %r, %s", T_EAX, fullparam );
*r0flags = R0_USED;
reg = T_EAX;
} else {
AddLineQueue( " pushw 0" );
}
} else
AddLineQueue( " pushw 0" );
} else {
if ( !(*r0flags & R0_X_CLEARED) ) {
/* v2.11: extra check needed */
if ( reg == T_AH || ( optype & OP_A ) )
EmitErr( REGISTER_VALUE_OVERWRITTEN_BY_INVOKE );
AddLineQueueX( " xor %r, %r", T_AX, T_AX );
}
AddLineQueueX( " push %r", T_AX );
*r0flags = R0_USED | R0_H_CLEARED | R0_X_CLEARED;
}
}
if ( asize == 1 ) {
if ( ( reg >= T_AH && reg <= T_BH ) || psize != 1 ) {
if ( psize != 1 && ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) {
/* v2.10: consider signed type coercion! */
AddLineQueueX( " mov%sx %r, %s", IS_SIGNED( opnd.mem_type ) ? "s" : "z",
regax[ModuleInfo.Ofssize], fullparam );
*r0flags = ( IS_SIGNED( opnd.mem_type ) ? R0_USED : R0_USED | R0_H_CLEARED );
} else {
if ( reg != T_AL ) {
AddLineQueueX( " mov %r, %s", T_AL, fullparam );
*r0flags |= R0_USED;
*r0flags &= ~R0_X_CLEARED;
}
if ( psize != 1 ) /* v2.11: don't modify AH if paramsize is 1 */
if ( IS_SIGNED( opnd.mem_type ) ) {
AddLineQueue( " cbw" );
*r0flags &= ~( R0_H_CLEARED | R0_X_CLEARED );
} else if (!( *r0flags & R0_H_CLEARED )) {
AddLineQueueX( " mov %r, 0", T_AH );
*r0flags |= R0_H_CLEARED;
}
}
reg = regax[ModuleInfo.Ofssize];
} else {
/* convert 8-bit to 16/32-bit register name */
if ( (( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386) &&
( psize == 4 || pushsize == 4 ) ) {
reg = reg - T_AL + T_EAX;
} else
reg = reg - T_AL + T_AX;
}
}
#if 0
if ( is_r0 && ( *r0flags & R0_USED ) ) {
EmitErr( REGISTER_VALUE_OVERWRITTEN_BY_INVOKE );
*r0flags = 0;
}
#endif
}
AddLineQueueX( " push %r", reg );
/* v2.05: don't change psize if > pushsize */
if ( psize < pushsize )
/* v2.04: adjust psize ( for siz_vararg update ) */
psize = pushsize;
} else { /* constant value */
/* v2.06: size check */
if ( psize ) {
if ( opnd.kind == EXPR_FLOAT )
asize = 4;
else if ( opnd.value64 <= 255 && opnd.value64 >= -255 )
asize = 1;
else if ( opnd.value64 <= 65535 && opnd.value64 >= -65535 )
asize = 2;
else if ( opnd.value64 <= maxintvalues[0] && opnd.value64 >= minintvalues[0] )
asize = 4;
else
asize = 8;
if ( psize < asize )
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, reqParam+1 );
}
/* v2.11: don't use CurrWordSize */
//asize = CurrWordSize;
asize = 2 << Ofssize;
if ( psize < asize ) /* ensure that the default argsize (2,4,8) is met */
if ( psize == 0 && curr->sym.is_vararg ) {
/* v2.04: push a dword constant in 16-bit */
if ( asize == 2 &&
( opnd.value > 0xFFFFL || opnd.value < -65535L ) )
psize = 4;
else
psize = asize;
} else
psize = asize;
if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) < P_186 ) {
*r0flags |= R0_USED;
switch ( psize ) {
case 2:
if ( opnd.value != 0 || opnd.kind == EXPR_ADDR ) {
AddLineQueueX( " mov %r, %s", T_AX, fullparam );
} else {
if ( !(*r0flags & R0_X_CLEARED ) ) {
AddLineQueueX( " xor %r, %r", T_AX, T_AX );
}
*r0flags |= R0_H_CLEARED | R0_X_CLEARED;
}
break;
case 4:
if ( opnd.uvalue <= 0xFFFF )
AddLineQueueX( " xor %r, %r", T_AX, T_AX );
else
AddLineQueueX( " mov %r, %r (%s)", T_AX, T_HIGHWORD, fullparam );
AddLineQueueX( " push %r", T_AX );
if ( opnd.uvalue != 0 || opnd.kind == EXPR_ADDR ) {
AddLineQueueX( " mov %r, %r (%s)", T_AX, T_LOWWORD, fullparam );
} else {
*r0flags |= R0_H_CLEARED | R0_X_CLEARED;
}
break;
default:
DebugMsg1(("PushInvokeParm(%u): error, CONST, asize=%u, psize=%u, pushsize=%u\n",
reqParam, asize, psize, pushsize ));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, reqParam+1 );
}
AddLineQueueX( " push %r", T_AX );
} else { /* cpu >= 80186 */
char *instr = "";
char *suffix;
int qual = EMPTY;
//if ( asize != psize ) {
if ( psize != pushsize ) {
switch ( psize ) {
case 2:
instr = "w";
break;
case 6: /* v2.04: added */
/* v2.11: use pushw only for 16-bit target */
if ( Ofssize == USE16 )
suffix = "w";
else if ( Ofssize == USE32 && CurrWordSize == 2 )
suffix = "d";
else
suffix = "";
AddLineQueueX( " push%s (%s) shr 32t", suffix, fullparam );
/* no break */
case 4:
if (( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 )
instr = "d";
else {
AddLineQueueX( " pushw %r (%s)", T_HIGHWORD, fullparam );
instr = "w";
qual = T_LOWWORD;
}
break;
case 8:
#if AMD64_SUPPORT
if (( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_64 )
break;
#endif
/* v2.06: added support for double constants */
if ( opnd.kind == EXPR_CONST || opnd.kind == EXPR_FLOAT ) {
AddLineQueueX( " pushd %r (%s)", T_HIGH32, fullparam );
qual = T_LOW32;
instr = "d";
break;
}
default:
DebugMsg1(("PushInvokeParm(%u): error, CONST, asize=%u, psize=%u, pushsize=%u\n",
reqParam, asize, psize, pushsize ));
EmitErr( INVOKE_ARGUMENT_TYPE_MISMATCH, reqParam+1 );
}
}
if ( qual != EMPTY )
AddLineQueueX( " push%s %r (%s)", instr, qual, fullparam );
else
AddLineQueueX( " push%s %s", instr, fullparam );
}
}
if ( curr->sym.is_vararg ) {
size_vararg += psize;
DebugMsg1(("PushInvokeParm(%u): psize=%u added to size_vararg, now=%u\n", reqParam, psize, size_vararg ));
}
}
}
return( NOT_ERROR );
}
/* generate a call for a prototyped procedure */
ret_code InvokeDirective( int i, struct asm_tok tokenarray[] )
/************************************************************/
{
struct asym *sym;
struct dsym *proc;
char *p;
//char *param;
int numParam;
int value;
int size;
int parmpos;
int namepos;
int porder;
uint_8 r0flags = 0;
//bool uselabel = FALSE;
struct proc_info *info;
struct dsym *curr;
struct expr opnd;
//char buffer[MAX_LINE_LEN];
DebugMsg1(("InvokeDir(%s) enter\n", tokenarray[i].tokpos ));
i++; /* skip INVOKE directive */
namepos = i;
/* if there is more than just an ID item describing the invoke target,
use the expression evaluator to get it
*/
if ( tokenarray[i].token != T_ID || ( tokenarray[i+1].token != T_COMMA && tokenarray[i+1].token != T_FINAL ) ) {
//if ( tokenarray[i+1].token != T_COMMA && tokenarray[i+1].token != T_FINAL ) {
if ( ERROR == EvalOperand( &i, tokenarray, Token_Count, &opnd, 0 ) )
return( ERROR );
DebugMsg1(("InvokeDir: target is expression, kind=%u sym=%s mbr=%s type=%s memtype=%X ofssize=%u\n",
opnd.kind,
opnd.sym ? opnd.sym->name : "NULL",
opnd.mbr ? opnd.mbr->name : "NULL",
opnd.type ? opnd.type->name : "NULL",
opnd.mem_type, opnd.Ofssize ));
#if 1
/* a typecast with PTR? Since v1.95, this has highest priority */
//if (opnd.explicit == TRUE && opnd.type != NULL && opnd.type->state == SYM_TYPE ) {
/* v1.96: removed opnd.explicit!!! */
/* fixme: if opnd.type is set, opnd.type MUST have state SYM_TYPE */
if ( opnd.type != NULL && opnd.type->state == SYM_TYPE ) {
sym = opnd.type;
DebugMsg1(("InvokeDirective: opnd.type=>%s< mem_type=%Xh\n", sym->name, sym->mem_type ));
proc = (struct dsym *)sym;
//if ( opnd.label_tok != NULL ) /* v2.09: uselabel obsolete */
// uselabel = TRUE;
if ( sym->mem_type == MT_PROC ) /* added for v1.95 */
goto isfnproto;
if ( sym->mem_type == MT_PTR ) /* v2.09: mem_type must be MT_PTR */
goto isfnptr;
}
#endif
if ( opnd.kind == EXPR_REG ) {
if ( GetValueSp( opnd.base_reg->tokval ) & OP_RGT8 )
sym = GetStdAssume( GetRegNo( opnd.base_reg->tokval ) );
else
sym = NULL;
} else
sym = ( opnd.mbr ? opnd.mbr : opnd.sym );
} else {
opnd.base_reg = NULL;
sym = SymSearch( tokenarray[i].string_ptr );
i++;
}
if( sym == NULL ) {
/* v2.04: msg changed */
return( EmitErr( INVOKE_REQUIRES_PROTOTYPE ) );
//return( EmitErr( SYMBOL_NOT_DEFINED, name ) );
}
if( sym->isproc ) /* the most simple case: symbol is a PROC */
;
else if ( sym->mem_type == MT_PTR && sym->target_type && sym->target_type->isproc )
sym = sym->target_type;
else if ( sym->mem_type == MT_PTR && sym->target_type && sym->target_type->mem_type == MT_PROC ) {
proc = (struct dsym *)sym->target_type;
goto isfnproto;
} else if ( ( sym->mem_type == MT_TYPE ) && ( sym->type->mem_type == MT_PTR || sym->type->mem_type == MT_PROC ) ) {
/* second case: symbol is a (function?) pointer */
proc = (struct dsym *)sym->type;
if ( proc->sym.mem_type != MT_PROC )
goto isfnptr;
isfnproto:
/* pointer target must be a PROTO typedef */
if ( proc->sym.mem_type != MT_PROC ) {
DebugMsg(("InvokeDir: error proc.name=>%s< .mem_type=%Xh\n", proc->sym.name, proc->sym.mem_type ));
DebugMsg(("InvokeDir: error sym.name=%s\n", sym ? sym->name : "" ));
return( EmitErr( INVOKE_REQUIRES_PROTOTYPE ) );
}
isfnptr:
/* get the pointer target */
sym = proc->sym.target_type;
DebugMsg1(("InvokeDir: proc=%s target_type=>%s<\n", proc->sym.name, sym ? sym->name : "NULL" ));
if ( sym == NULL ) {
return( EmitErr( INVOKE_REQUIRES_PROTOTYPE ) );
}
} else {
DebugMsg(("InvokeDir: error, sym=%s state=%u memtype=%Xh [type=%s memtype=%Xh]\n",
sym->name, sym->state, sym->mem_type,
sym->type ? sym->type->name : "NULL",
sym->type ? sym->type->mem_type : 0));
#ifdef DEBUG_OUT
if ( sym->mem_type == MT_PTR || sym->mem_type == MT_PROC )
DebugMsg(("InvokeDir: error, target_type=%s [memtype=%X pmemtype=%X isproc=%u])\n",
sym->target_type->name,
sym->target_type->mem_type,
sym->target_type->ptr_memtype,
sym->target_type->isproc ));
#endif
return( EmitErr( INVOKE_REQUIRES_PROTOTYPE ) );
}
proc = (struct dsym *)sym;
info = proc->e.procinfo;
#if 0 /* v2.05: can't happen anymore */
/* does FASTCALL variant support INVOKE? */
if ( proc->sym.langtype == LANG_FASTCALL && fastcall_tab[ModuleInfo.fctype].invokestart == NULL ) {
return( EmitError( FASTCALL_VARIANT_NOT_SUPPORTED ) );
}
#endif
/* get the number of parameters */
for ( curr = info->paralist, numParam = 0 ; curr ; curr = curr->nextparam, numParam++ );
DebugMsg1(("InvokeDir: numparams=%u\n", numParam ));
if ( proc->sym.langtype == LANG_FASTCALL ) {
fcscratch = 0;
porder = fastcall_tab[ModuleInfo.fctype].invokestart( proc, numParam, i, tokenarray, &value );
}
curr = info->paralist;
parmpos = i;
if ( !( info->has_vararg ) ) {
/* check if there is a superfluous parameter in the INVOKE call */
if ( PushInvokeParam( i, tokenarray, proc, NULL, numParam, &r0flags ) != ERROR ) {
DebugMsg(("InvokeDir: superfluous argument, i=%u\n", i));
return( EmitErr( TOO_MANY_ARGUMENTS_TO_INVOKE ) );
}
} else {
int j = (Token_Count - i) / 2;
/* for VARARG procs, just push the additional params with
the VARARG descriptor
*/
numParam--;
size_vararg = 0; /* reset the VARARG parameter size count */
while ( curr && curr->sym.is_vararg == FALSE ) curr = curr->nextparam;
DebugMsg1(("InvokeDir: VARARG proc, numparams=%u, actual (max) params=%u, parasize=%u\n", numParam, j, info->parasize));
for ( ; j >= numParam; j-- )
PushInvokeParam( i, tokenarray, proc, curr, j, &r0flags );
/* move to first non-vararg parameter, if any */
for ( curr = info->paralist; curr && curr->sym.is_vararg == TRUE; curr = curr->nextparam );
}
/* the parameters are usually stored in "push" order.
* This if() must match the one in proc.c, ParseParams().
*/
if ( sym->langtype == LANG_STDCALL ||
sym->langtype == LANG_C ||
( sym->langtype == LANG_FASTCALL && porder ) ||
sym->langtype == LANG_SYSCALL ) {
for ( ; curr ; curr = curr->nextparam ) {
numParam--;
if ( PushInvokeParam( i, tokenarray, proc, curr, numParam, &r0flags ) == ERROR ) {
DebugMsg(("InvokeDir: PushInvokeParam(curr=%u, i=%u, numParam=%u) failed\n", curr, i, numParam));
EmitErr( TOO_FEW_ARGUMENTS_TO_INVOKE, sym->name );
}
}
} else {
for ( numParam = 0 ; curr && curr->sym.is_vararg == FALSE; curr = curr->nextparam, numParam++ ) {
if ( PushInvokeParam( i, tokenarray, proc, curr, numParam, &r0flags ) == ERROR ) {
DebugMsg(("InvokeDir: PushInvokeParam(curr=%u, i=%u, numParam=%u) failed\n", curr, i, numParam));
EmitErr( TOO_FEW_ARGUMENTS_TO_INVOKE, sym->name );
}
}
}
#if 1
/* v2.05 added. A warning only, because Masm accepts this. */
if ( opnd.base_reg != NULL &&
Parse_Pass == PASS_1 &&
(r0flags & R0_USED ) &&
opnd.base_reg->bytval == 0 )
EmitWarn( 2, REGISTER_VALUE_OVERWRITTEN_BY_INVOKE );
#endif
p = StringBufferEnd;
strcpy( p, " call " );
p += 6;
/* v2.09: 'uselabel' obsolete */
//if ( uselabel ) {
// DebugMsg1(("InvokeDir: opnd.label_tok is used: %s\n", opnd.label_tok->string_ptr ));
// strcpy( p, opnd.label_tok->string_ptr );
//} else {
#if DLLIMPORT
if ( sym->state == SYM_EXTERNAL && sym->dll ) {
char *iatname = p;
strcpy( p, ModuleInfo.g.imp_prefix );
p += strlen( p );
p += Mangle( sym, p );
namepos++;
if ( sym->iat_used == FALSE ) {
sym->iat_used = TRUE;
sym->dll->cnt++;
if ( sym->langtype != LANG_NONE && sym->langtype != ModuleInfo.langtype )
AddLineQueueX( " externdef %r %s: %r %r", sym->langtype + T_C - 1, iatname, T_PTR, T_PROC );
else
AddLineQueueX( " externdef %s: %r %r", iatname, T_PTR, T_PROC );
}
}
#endif
size = tokenarray[parmpos].tokpos - tokenarray[namepos].tokpos;
memcpy( p, tokenarray[namepos].tokpos, size );
*(p+size) = NULLC;
#if 0 /* v2.09: uselabel obsolete */
}
#endif
AddLineQueue( StringBufferEnd );
if (( sym->langtype == LANG_C || sym->langtype == LANG_SYSCALL ) &&
( info->parasize || ( info->has_vararg && size_vararg ) )) {
if ( info->has_vararg ) {
DebugMsg1(("InvokeDir: size of fix args=%u, var args=%u\n", info->parasize, size_vararg));
AddLineQueueX( " add %r, %u", stackreg[ModuleInfo.Ofssize], NUMQUAL info->parasize + size_vararg );
} else
AddLineQueueX( " add %r, %u", stackreg[ModuleInfo.Ofssize], NUMQUAL info->parasize );
} else if ( sym->langtype == LANG_FASTCALL ) {
fastcall_tab[ModuleInfo.fctype].invokeend( proc, numParam, value );
}
LstWrite( LSTTYPE_DIRECTIVE, GetCurrOffset(), NULL );
RunLineQueue();
return( NOT_ERROR );
}