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

275 lines
12 KiB
C

/****************************************************************************
*
* Open Watcom Project
*
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
* ========================================================================
*
* This file contains Original Code and/or Modifications of Original
* Code as defined in and that are subject to the Sybase Open Watcom
* Public License version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
* provided with the Original Code and Modifications, and is also
* available at www.sybase.com/developer/opensource.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
* NON-INFRINGEMENT. Please see the License for the specific language
* governing rights and limitations under the License.
*
* ========================================================================
*
* Description: backpatch: short forward jump optimization.
*
****************************************************************************/
#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "fixup.h"
#include "segment.h"
/*
* LABELOPT: short jump label optimization.
* if this is 0, there is just the simple "fixup backpatch",
* which cannot adjust any label offsets between the forward reference
* and the newly defined label, resulting in more passes to be needed.
*/
#define LABELOPT 1
#if 0 /* v1.96: disabled */
#define SkipFixup() \
fixup->nextbp = sym->fixup; \
sym->fixup = fixup
#else
#define SkipFixup()
#endif
static void DoPatch( struct asym *sym, struct fixup *fixup )
/**********************************************************/
{
int_32 disp;
int_32 max_disp;
unsigned size;
struct dsym *seg;
#if LABELOPT
struct asym *sym2;
struct fixup *fixup2;
#endif
/* all relative fixups should occure only at first pass and they signal forward references
* they must be removed after patching or skiped ( next processed as normal fixup )
*/
DebugMsg(("DoPatch(%u, %s): fixup sym=%s type=%u ofs=%" I32_SPEC "Xh loc=%" I32_SPEC "Xh opt=%u def_seg=%s\n",
Parse_Pass + 1, sym->name,
fixup->sym ? fixup->sym->name : "NULL",
fixup->type,
fixup->offset,
fixup->locofs,
fixup->option,
fixup->def_seg ? fixup->def_seg->sym.name : "NULL" ));
seg = GetSegm( sym );
if( seg == NULL || fixup->def_seg != seg ) {
/* if fixup location is in another segment, backpatch is possible, but
* complicated and it's a pretty rare case, so nothing's done.
*/
DebugMsg(("DoPatch: skipped due to seg incompat: %s - %s\n",
fixup->def_seg ? fixup->def_seg->sym.name : "NULL",
seg ? seg->sym.name : "NULL" ));
SkipFixup();
return;
}
if( Parse_Pass == PASS_1 ) {
if( sym->mem_type == MT_FAR && fixup->option == OPTJ_CALL ) {
/* convert near call to push cs + near call,
* (only at first pass) */
DebugMsg(("DoPatch: Phase error! caused by far call optimization\n"));
ModuleInfo.PhaseError = TRUE;
sym->offset++; /* a PUSH CS will be added */
/* todo: insert LABELOPT block here */
OutputByte( 0 ); /* it's pass one, nothing is written */
FreeFixup( fixup );
return;
//} else if( sym->mem_type == MT_NEAR ) {
} else {
/* forward reference, only at first pass */
switch( fixup->type ) {
case FIX_RELOFF32:
case FIX_RELOFF16:
FreeFixup( fixup );
DebugMsg(("DoPatch: FIX_RELOFF32/FIX_RELOFF16, return\n"));
return;
case FIX_OFF8: /* push <forward reference> */
if ( fixup->option == OPTJ_PUSH ) {
size = 1; /* size increases from 2 to 3/5 */
DebugMsg(("DoPatch: FIX_OFF8\n"));
goto patch;
}
}
}
}
size = 0;
switch( fixup->type ) {
case FIX_RELOFF32:
size = 2; /* will be 4 finally */
/* fall through */
case FIX_RELOFF16:
size++; /* will be 2 finally */
/* fall through */
case FIX_RELOFF8:
size++;
/* calculate the displacement */
// disp = fixup->offset + GetCurrOffset() - fixup->location - size;
disp = fixup->offset + fixup->sym->offset - fixup->locofs - size - 1;
max_disp = (1UL << ((size * 8)-1)) - 1;
if( disp > max_disp || disp < (-max_disp-1) ) {
patch:
DebugMsg(("DoPatch(%u): Phase error, disp=%X, fixup=%s(%X), loc=%X!\n", Parse_Pass + 1, disp, fixup->sym->name, fixup->sym->offset, fixup->locofs ));
ModuleInfo.PhaseError = TRUE;
/* ok, the standard case is: there's a forward jump which
* was assumed to be SHORT, but it must be NEAR instead.
*/
switch( size ) {
case 1:
size = 0;
switch( fixup->option ) {
case OPTJ_EXPLICIT:
#if 0 /* don't display the error at the destination line! */
DebugMsg(("DoPatch: jump out of range, disp=%d\n", disp ));
EmitErr( JUMP_OUT_OF_RANGE, disp - max_disp );
#endif
return;
case OPTJ_EXTEND: /* Jxx for 8086 */
size++; /* will be 3/5 finally */
/* fall through */
case OPTJ_JXX: /* Jxx for 386 */
size++;
/* fall through */
default: /* normal JMP (and PUSH) */
// if( CodeInfo->Ofssize ) /* v1.96: don't use CodeInfo here! */
if( seg->e.seginfo->Ofssize )
size += 2; /* NEAR32 instead of NEAR16 */
size++;
#if LABELOPT
/* v2.04: if there's an ORG between src and dst, skip
* the optimization!
*/
if ( Parse_Pass == PASS_1 ) {
for ( fixup2 = seg->e.seginfo->FixupList.head; fixup2; fixup2 = fixup2->nextrlc ) {
if ( fixup2->orgoccured ) {
DebugMsg(("DoPatch: ORG/ALIGN detected, optimization canceled\n" ));
return;
}
/* do this check after the check for ORG! */
if ( fixup2->locofs <= fixup->locofs )
break;
}
}
/* scan the segment's label list and adjust all labels
* which are between the fixup loc and the current sym.
* ( PROCs are NOT contained in this list because they
* use the <next>-field of dsym already!)
*/
for ( sym2 = seg->e.seginfo->label_list; sym2; sym2 = (struct asym *)((struct dsym *)sym2)->next ) {
//if ( sym2 == sym )
// continue;
/* v2.0: location is at least 1 byte too low, so
* use the "<=" operator instead of "<"!
*/
//if ( sym2->offset < fixup->locofs )
if ( sym2->offset <= fixup->locofs )
break;
sym2->offset += size;
DebugMsg(("DoPatch(loc=%" I32_SPEC "X): sym %s, offset changed %" I32_SPEC "X -> %" I32_SPEC "X\n", fixup->locofs, sym2->name, sym2->offset - size, sym2->offset));
}
/* v2.03: also adjust fixup locations located between the
* label reference and the label. This should reduce the
* number of passes to 2 for not too complex sources.
*/
if ( Parse_Pass == PASS_1 ) /* v2.04: added, just to be safe */
for ( fixup2 = seg->e.seginfo->FixupList.head; fixup2; fixup2 = fixup2->nextrlc ) {
if ( fixup2->sym == sym )
continue;
if ( fixup2->locofs <= fixup->locofs )
break;
fixup2->locofs += size;
DebugMsg(("for sym=%s fixup loc %" I32_SPEC "X changed to %" I32_SPEC "X\n", fixup2->sym->name, fixup2->locofs - size, fixup2->locofs ));
}
#else
DebugMsg(("DoPatch: sym %s, offset changed %" I32_SPEC "X -> %" I32_SPEC "X\n", sym->name, sym->offset, sym->offset + size));
sym->offset += size;
#endif
/* it doesn't matter what's actually "written" */
for ( ; size; size-- )
OutputByte( 0xCC );
break;
}
break;
case 2:
case 4:
DebugMsg(("DoPatch: jump out of range, disp=%d\n", disp ));
EmitWarn( 4, JUMP_OUT_OF_RANGE, disp - max_disp );
break;
}
}
#ifdef DEBUG_OUT
else
DebugMsg(("DoPatch, loc=%" I32_SPEC "X: displacement still short: %Xh\n", fixup->locofs, disp ));
#endif
/* v2.04: fixme: is it ok to remove the fixup?
* it might still be needed in a later backpatch.
*/
FreeFixup( fixup );
break;
default:
DebugMsg(("DoPatch: default branch, unhandled fixup type=%u\n", fixup->type ));
SkipFixup();
break;
}
return;
}
ret_code BackPatch( struct asym *sym )
/************************************/
/*
* patching for forward reference labels in Jmp/Call instructions;
* called by LabelCreate(), ProcDef() and data_dir(), that is, whenever
* a (new) label is defined. The new label is the <sym> parameter.
* During the process, the label's offset might be changed!
*
* field sym->fixup is a "descending" list of forward references
* to this symbol. These fixups are only generated during pass 1.
*/
{
struct fixup *fixup;
struct fixup *next;
#ifdef DEBUG_OUT
uint_32 oldofs = sym->offset;
#endif
DebugMsg1(("BackPatch(%s): location=%s:%X, bp_fixup=%p\n", sym->name, sym->segment ? sym->segment->name : "!NULL!", sym->offset, sym->bp_fixup ));
for( fixup = sym->bp_fixup; fixup; fixup = next ) {
next = fixup->nextbp;
DoPatch( sym, fixup );
}
/* fixme: to clear field bp_fixup may cause memory leaks, since not all fixups are freed here.
* usually no problem, because FASTMEM is true ( that is, LclFree() is a NOP ).
* the problem is that these fixups are in 2 queues, one starts in sym.bp_fixup,
* the other start in CurrSeg.FixupList.
*/
sym->bp_fixup = NULL;
#ifdef DEBUG_OUT
if ( oldofs != sym->offset )
DebugMsg1(("BackPatch(%s) exit, new ofs=%X\n", sym->name, sym->offset ));
#endif
return( NOT_ERROR );
}