/**************************************************************************** * * This code is Public Domain. * * ======================================================================== * * Description: Processing of INVOKE directive. * ****************************************************************************/ #include #include #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 , 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 )) != 0 ) 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 :: 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 ); }