@ -23,6 +23,7 @@
# include "as.h"
# include "safe-ctype.h"
# include "subsegs.h"
# include "struc-symbol.h"
# include "dwarf2dbg.h"
# include "dw2gencfi.h"
# include "elf/avr.h"
@ -54,6 +55,107 @@ struct avr_opcodes_s avr_opcodes[] =
{ NULL , NULL , NULL , 0 , 0 , 0 }
} ;
/* Stuff for the `__gcc_isr' pseudo instruction.
Purpose of the pseudo instruction is to emit more efficient ISR prologues
and epilogues than GCC currently does . GCC has no explicit ( on RTL level )
modelling of SREG , TMP_REG or ZERO_REG . These regs are used implicitly
during instruction printing . That doesn ' t hurt too much for ordinary
functions , however for small ISRs there might be some overhead .
As implementing http : //gcc.gnu.org/PR20296 would imply an almost complete
rewite of GCC ' s AVR back - end ( which might pop up less optimized code in
other places ) , we provide a pseudo - instruction which is resolved by GAS
into ISR prologue / epilogue as expected by GCC .
Using GAS for this purpose has the additional benefit that it can scan
code emit by inline asm which is opaque to GCC .
The pseudo - instruction is only supposed to handle the starting of
prologue and the ending of epilogues ( without RETI ) which deal with
SREG , TMP_REG and ZERO_REG and one additional , optional general purpose
register .
__gcc_isr consists of 3 different " chunks " :
__gcc_isr 1
Chunk 1 ( ISR_CHUNK_Prologue )
Start the ISR code . Will be replaced by ISR prologue by next Done chunk .
Must be the 1 st chunk in a file or follow a Done chunk from previous
ISR ( which has been patched already ) .
It will finish the current frag and emit a new frag of
type rs_machine_dependent , subtype ISR_CHUNK_Prologue .
__gcc_isr 2
Chunk 2 ( ISR_CHUNK_Epilogue )
Will be replaced by ISR epilogue by next Done chunk . Must follow
chunk 1 ( Prologue ) or chunk 2 ( Epilogue ) . Functions might come
without epilogue or with more than one epilogue , and even code
located statically after the last epilogue might belong to a function .
It will finish the current frag and emit a new frag of
type rs_machine_dependent , subtype ISR_CHUNK_Epilogue .
__gcc_isr 0 , Rx
Chunk 0 ( ISR_CHUNK_Done )
Must follow chunk 1 ( Prologue ) or chunk 2 ( Epilogue ) and finishes
the ISR code . Only GCC can know where a function ' s code ends .
It triggers the patch - up of all rs_machine_dependent frags in the
current frag chain and turns them into ordinary rs_fill code frags .
If Rx is a register > ZERO_REG then GCC also wants to push / pop Rx .
If neither TMP_REG nor ZERO_REG are needed , Rx will be used in
the push / pop sequence avoiding the need for TMP_REG / ZERO_REG .
If Rx < = ZERO_REG then GCC doesn ' t assume anything about Rx .
Assumptions :
o GCC takes care of code that is opaque to GAS like tail calls
or non - local goto .
o Using SEI / CLI does not count as clobbering SREG . This is
because a final RETI will restore the I - flag .
o Using OUT or ST * is supposed not to clobber SREG . Sequences like
IN - SREG + CLI + Atomic - Code + OUT - SREG
will still work as expected because the scan will reveal any
clobber of SREG other than I - flag and emit PUSH / POP of SREG .
*/
enum
{
ISR_CHUNK_Done = 0 ,
ISR_CHUNK_Prologue = 1 ,
ISR_CHUNK_Epilogue = 2
} ;
static struct
{
/* Previous __gcc_isr chunk (one of the enums above)
and it ' s location for diagnostics . */
int prev_chunk ;
unsigned line ;
const char * file ;
/* Replacer for __gcc_isr.n_pushed once we know how many regs are
pushed by the Prologue chunk . */
symbolS * sym_n_pushed ;
/* Set and used during parse from chunk 1 (Prologue) up to chunk 0 (Done).
Set by ` avr_update_gccisr ' and used by ` avr_patch_gccisr_frag ' . */
int need_reg_tmp ;
int need_reg_zero ;
int need_sreg ;
} avr_isr ;
static void avr_gccisr_operands ( struct avr_opcodes_s * , char * * ) ;
static void avr_update_gccisr ( struct avr_opcodes_s * , int , int ) ;
static struct avr_opcodes_s * avr_gccisr_opcode ;
const char comment_chars [ ] = " ; " ;
const char line_comment_chars [ ] = " # " ;
const char line_separator_chars [ ] = " $ " ;
@ -359,9 +461,10 @@ struct avr_opt_s
int no_wrap ; /* -mno-wrap: reject rjmp/rcall with 8K wrap-around. */
int no_link_relax ; /* -mno-link-relax / -mlink-relax: generate (or not)
relocations for linker relaxation . */
int have_gccisr ; /* Whether "__gcc_isr" is a known (pseudo) insn. */
} ;
static struct avr_opt_s avr_opt = { 0 , 0 , 0 , 0 } ;
static struct avr_opt_s avr_opt = { 0 , 0 , 0 , 0 , 0 } ;
const char EXP_CHARS [ ] = " eE " ;
const char FLT_CHARS [ ] = " dD " ;
@ -416,6 +519,33 @@ static struct hash_control *avr_hash;
/* Reloc modifiers hash control (hh8,hi8,lo8,pm_xx). */
static struct hash_control * avr_mod_hash ;
/* Whether some opcode does not change SREG. */
static struct hash_control * avr_no_sreg_hash ;
static const char * const avr_no_sreg [ ] =
{
/* Arithmetic */
" ldi " , " swap " , " mov " , " movw " ,
/* Special instructions. I-Flag will be restored by RETI, and we don't
consider I - Flag as being clobbered when changed . */
" sei " , " cli " , " reti " , " brie " , " brid " ,
" nop " , " wdr " , " sleep " ,
/* Load / Store */
" ld " , " ldd " , " lds " , " pop " , " in " , " lpm " , " elpm " ,
" st " , " std " , " sts " , " push " , " out " ,
/* Jumps and Calls. Calls might call code that changes SREG.
GCC has to filter out ABI calls . The non - ABI transparent calls
must use [ R ] CALL and are filtered out now by not mentioning them . */
" rjmp " , " jmp " , " ijmp " , " ret " ,
/* Skipping. Branches need SREG to be set, hence we regard them
as if they changed SREG and don ' t list them here . */
" sbrc " , " sbrs " , " sbic " , " sbis " , " cpse " ,
/* I/O Manipulation */
" sbi " , " cbi " ,
/* Read-Modify-Write */
" lac " , " las " , " lat " , " xch "
} ;
# define OPTION_MMCU 'm'
enum options
{
@ -424,7 +554,8 @@ enum options
OPTION_NO_WRAP ,
OPTION_ISA_RMW ,
OPTION_LINK_RELAX ,
OPTION_NO_LINK_RELAX
OPTION_NO_LINK_RELAX ,
OPTION_HAVE_GCCISR
} ;
struct option md_longopts [ ] =
@ -436,6 +567,7 @@ struct option md_longopts[] =
{ " mrmw " , no_argument , NULL , OPTION_ISA_RMW } ,
{ " mlink-relax " , no_argument , NULL , OPTION_LINK_RELAX } ,
{ " mno-link-relax " , no_argument , NULL , OPTION_NO_LINK_RELAX } ,
{ " mgcc-isr " , no_argument , NULL , OPTION_HAVE_GCCISR } ,
{ NULL , no_argument , NULL , 0 }
} ;
@ -544,6 +676,7 @@ md_show_usage (FILE *stream)
" -mrmw accept Read-Modify-Write instructions \n "
" -mlink-relax generate relocations for linker relaxation (default) \n "
" -mno-link-relax don't generate relocations for linker relaxation. \n "
" -mgcc-isr accept the __gcc_isr pseudo-instruction. \n "
) ) ;
show_mcu_list ( stream ) ;
}
@ -610,14 +743,38 @@ md_parse_option (int c, const char *arg)
case OPTION_NO_LINK_RELAX :
avr_opt . no_link_relax = 1 ;
return 1 ;
case OPTION_HAVE_GCCISR :
avr_opt . have_gccisr = 1 ;
return 1 ;
}
return 0 ;
}
/* Implement `md_undefined_symbol' */
/* If we are in `__gcc_isr' chunk, pop up `__gcc_isr.n_pushed.<NUM>'
instead of ` __gcc_isr . n_pushed ' . This will be resolved by the Done
chunk in ` avr_patch_gccisr_frag ' to the number of PUSHes produced by
the Prologue chunk . */
symbolS *
md_undefined_symbol ( char * name ATTRIBUTE_UNUSED )
avr _undefined_symbol ( char * name )
{
if ( ISR_CHUNK_Done ! = avr_isr . prev_chunk
& & 0 = = strcmp ( name , " __gcc_isr.n_pushed " ) )
{
if ( ! avr_isr . sym_n_pushed )
{
static unsigned suffix ;
char xname [ 30 ] ;
sprintf ( xname , " %s.%03u " , name , ( + + suffix ) % 1000 ) ;
avr_isr . sym_n_pushed = symbol_new ( xname , undefined_section ,
( valueT ) 0 , & zero_address_frag ) ;
}
return avr_isr . sym_n_pushed ;
}
return NULL ;
}
@ -659,6 +816,17 @@ md_begin (void)
hash_insert ( avr_mod_hash , EXP_MOD_NAME ( i ) , m . ptr ) ;
}
avr_no_sreg_hash = hash_new ( ) ;
for ( i = 0 ; i < ARRAY_SIZE ( avr_no_sreg ) ; + + i )
{
gas_assert ( hash_find ( avr_hash , avr_no_sreg [ i ] ) ) ;
hash_insert ( avr_no_sreg_hash , avr_no_sreg [ i ] , ( char * ) 4 /* dummy */ ) ;
}
avr_gccisr_opcode = ( struct avr_opcodes_s * ) hash_find ( avr_hash , " __gcc_isr " ) ;
gas_assert ( avr_gccisr_opcode ) ;
bfd_set_arch_mach ( stdoutput , TARGET_ARCH , avr_mcu - > mach ) ;
linkrelax = ! avr_opt . no_link_relax ;
}
@ -855,7 +1023,8 @@ static unsigned int
avr_operand ( struct avr_opcodes_s * opcode ,
int where ,
const char * op ,
char * * line )
char * * line ,
int * pregno )
{
expressionS op_expr ;
unsigned int op_mask = 0 ;
@ -904,6 +1073,9 @@ avr_operand (struct avr_opcodes_s *opcode,
}
}
if ( pregno )
* pregno = op_mask ;
if ( avr_mcu - > mach = = bfd_mach_avrtiny )
{
if ( op_mask < 16 | | op_mask > 31 )
@ -1079,6 +1251,16 @@ avr_operand (struct avr_opcodes_s *opcode,
}
break ;
case ' N ' :
{
unsigned int x ;
x = avr_get_constant ( str , 255 ) ;
str = input_line_pointer ;
op_mask = x ;
}
break ;
case ' K ' :
input_line_pointer = str ;
avr_offset_expression ( & op_expr ) ;
@ -1145,6 +1327,8 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
char * str = * line ;
int where = frag - frag_now - > fr_literal ;
static unsigned int prev = 0 ; /* Previous opcode. */
int regno1 = - 2 ;
int regno2 = - 2 ;
/* Opcode have operands. */
if ( * op )
@ -1157,7 +1341,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
/* Parse first operand. */
if ( REGISTER_P ( * op ) )
reg1_present = 1 ;
reg1 = avr_operand ( opcode , where , op , & str ) ;
reg1 = avr_operand ( opcode , where , op , & str , & regno1 ) ;
+ + op ;
/* Parse second operand. */
@ -1170,6 +1354,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
{
reg2 = reg1 ;
reg2_present = 1 ;
regno2 = regno1 ;
}
else
{
@ -1181,7 +1366,7 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
as_bad ( _ ( " `,' required " ) ) ;
str = skip_space ( str ) ;
reg2 = avr_operand ( opcode , where , op , & str ) ;
reg2 = avr_operand ( opcode , where , op , & str , & regno2 ) ;
}
if ( reg1_present & & reg2_present )
@ -1194,6 +1379,9 @@ avr_operands (struct avr_opcodes_s *opcode, char **line)
bin | = reg1 | reg2 ;
}
if ( avr_opt . have_gccisr )
avr_update_gccisr ( opcode , regno1 , regno2 ) ;
/* Detect undefined combinations (like ld r31,Z+). */
if ( ! avr_opt . all_opcodes & & AVR_UNDEF_P ( bin ) )
as_warn ( _ ( " undefined combination of operands " ) ) ;
@ -1698,6 +1886,13 @@ md_assemble (char *str)
return ;
}
if ( opcode = = avr_gccisr_opcode
& & ! avr_opt . have_gccisr )
{
as_bad ( _ ( " pseudo instruction `%s' not supported " ) , op ) ;
return ;
}
/* Special case for opcodes with optional operands (lpm, elpm) -
version with operands exists in avr_opcodes [ ] in the next entry . */
@ -1711,7 +1906,10 @@ md_assemble (char *str)
{
char * t = input_line_pointer ;
avr_operands ( opcode , & str ) ;
if ( opcode = = avr_gccisr_opcode )
avr_gccisr_operands ( opcode , & str ) ;
else
avr_operands ( opcode , & str ) ;
if ( * skip_space ( str ) )
as_bad ( _ ( " garbage at end of line " ) ) ;
input_line_pointer = t ;
@ -2211,3 +2409,385 @@ avr_post_relax_hook (void)
{
avr_create_and_fill_property_section ( ) ;
}
/* Accumulate information about instruction sequence to `avr_isr':
wheter TMP_REG , ZERO_REG and SREG might be touched . Used during parse .
REG1 is either - 1 or a register number used by the instruction as input
or output operand . Similar for REG2 . */
static void
avr_update_gccisr ( struct avr_opcodes_s * opcode , int reg1 , int reg2 )
{
const int tiny_p = avr_mcu - > mach = = bfd_mach_avrtiny ;
const int reg_tmp = tiny_p ? 16 : 0 ;
const int reg_zero = 1 + reg_tmp ;
if ( ISR_CHUNK_Done = = avr_isr . prev_chunk
| | ( avr_isr . need_sreg
& & avr_isr . need_reg_tmp
& & avr_isr . need_reg_zero ) )
{
/* Nothing (more) to do */
return ;
}
/* SREG: Look up instructions that don't clobber SREG. */
if ( ! avr_isr . need_sreg
& & ! hash_find ( avr_no_sreg_hash , opcode - > name ) )
{
avr_isr . need_sreg = 1 ;
}
/* Handle explicit register operands. Record *any* use as clobber.
This is because TMP_REG and ZERO_REG are not global and using
them makes no sense without a previous set . */
avr_isr . need_reg_tmp | = reg1 = = reg_tmp | | reg2 = = reg_tmp ;
avr_isr . need_reg_zero | = reg1 = = reg_zero | | reg2 = = reg_zero ;
/* Handle implicit register operands and some opaque stuff. */
if ( strstr ( opcode - > name , " lpm " )
& & ' ? ' = = * opcode - > constraints )
{
avr_isr . need_reg_tmp = 1 ;
}
if ( strstr ( opcode - > name , " call " )
| | strstr ( opcode - > name , " mul " )
| | 0 = = strcmp ( opcode - > name , " des " )
| | ( 0 = = strcmp ( opcode - > name , " movw " )
& & ( reg1 = = reg_tmp | | reg2 = = reg_tmp ) ) )
{
avr_isr . need_reg_tmp = 1 ;
avr_isr . need_reg_zero = 1 ;
}
}
/* Emit some 1-word instruction to **PWHERE and advance *PWHERE by the number
of octets written . INSN specifies the desired instruction and REG is the
register used by it . This function is only used with restricted subset of
instructions as might be emit by ` __gcc_isr ' . IN / OUT will use SREG
and LDI loads 0. */
static void
avr_emit_insn ( const char * insn , int reg , char * * pwhere )
{
const int sreg = 0x3f ;
unsigned bin = 0 ;
const struct avr_opcodes_s * op
= ( struct avr_opcodes_s * ) hash_find ( avr_hash , insn ) ;
/* We only have to deal with: IN, OUT, PUSH, POP, CLR, LDI 0. All of
these deal with at least one Reg and are 1 - word instructions . */
gas_assert ( op & & 1 = = op - > insn_size ) ;
gas_assert ( reg > = 0 & & reg < = 31 ) ;
if ( strchr ( op - > constraints , ' r ' ) )
{
bin = op - > bin_opcode | ( reg < < 4 ) ;
}
else if ( strchr ( op - > constraints , ' d ' ) )
{
gas_assert ( reg > = 16 ) ;
bin = op - > bin_opcode | ( ( reg & 0xf ) < < 4 ) ;
}
else
abort ( ) ;
if ( strchr ( op - > constraints , ' P ' ) )
{
bin | = ( ( sreg & 0x30 ) < < 5 ) | ( sreg & 0x0f ) ;
}
else if ( 0 = = strcmp ( " r=r " , op - > constraints ) )
{
bin | = ( ( reg & 0x10 ) < < 5 ) | ( reg & 0x0f ) ;
}
else
gas_assert ( 0 = = strcmp ( " r " , op - > constraints )
| | 0 = = strcmp ( " ldi " , op - > name ) ) ;
bfd_putl16 ( ( bfd_vma ) bin , * pwhere ) ;
( * pwhere ) + = 2 * op - > insn_size ;
}
/* Turn rs_machine_dependent frag *FR into an ordinary rs_fill code frag,
using information gathered in ` avr_isr ' . REG is the register number as
supplied by Done chunk " __gcc_isr 0,REG " . */
static void
avr_patch_gccisr_frag ( fragS * fr , int reg )
{
int treg ;
int n_pushed = 0 ;
char * where = fr - > fr_literal ;
const int tiny_p = avr_mcu - > mach = = bfd_mach_avrtiny ;
const int reg_tmp = tiny_p ? 16 : 0 ;
const int reg_zero = 1 + reg_tmp ;
/* Clearing ZERO_REG on non-Tiny needs CLR which clobbers SREG. */
avr_isr . need_sreg | = ! tiny_p & & avr_isr . need_reg_zero ;
/* A working register to PUSH / POP the SREG. We might use the register
as supplied by ISR_CHUNK_Done for that purpose as GCC wants to push
it anyways . If GCC passes ZERO_REG or TMP_REG , it has no clue ( and
no additional regs to safe ) and we use that reg . */
treg
= avr_isr . need_reg_tmp ? reg_tmp
: avr_isr . need_reg_zero ? reg_zero
: avr_isr . need_sreg ? reg
: reg > reg_zero ? reg
: - 1 ;
if ( treg > = 0 )
{
/* Non-empty prologue / epilogue */
if ( ISR_CHUNK_Prologue = = fr - > fr_subtype )
{
avr_emit_insn ( " push " , treg , & where ) ;
n_pushed + + ;
if ( avr_isr . need_sreg )
{
avr_emit_insn ( " in " , treg , & where ) ;
avr_emit_insn ( " push " , treg , & where ) ;
n_pushed + + ;
}
if ( avr_isr . need_reg_zero )
{
if ( reg_zero ! = treg )
{
avr_emit_insn ( " push " , reg_zero , & where ) ;
n_pushed + + ;
}
avr_emit_insn ( tiny_p ? " ldi " : " clr " , reg_zero , & where ) ;
}
if ( reg > reg_zero & & reg ! = treg )
{
avr_emit_insn ( " push " , reg , & where ) ;
n_pushed + + ;
}
}
else if ( ISR_CHUNK_Epilogue = = fr - > fr_subtype )
{
/* Same logic as in Prologue but in reverse order and with counter
parts of either instruction : POP instead of PUSH and OUT instead
of IN . Clearing ZERO_REG has no couter part . */
if ( reg > reg_zero & & reg ! = treg )
avr_emit_insn ( " pop " , reg , & where ) ;
if ( avr_isr . need_reg_zero
& & reg_zero ! = treg )
avr_emit_insn ( " pop " , reg_zero , & where ) ;
if ( avr_isr . need_sreg )
{
avr_emit_insn ( " pop " , treg , & where ) ;
avr_emit_insn ( " out " , treg , & where ) ;
}
avr_emit_insn ( " pop " , treg , & where ) ;
}
else
abort ( ) ;
} /* treg >= 0 */
if ( ISR_CHUNK_Prologue = = fr - > fr_subtype
& & avr_isr . sym_n_pushed )
{
symbolS * sy = avr_isr . sym_n_pushed ;
/* Turn magic `__gcc_isr.n_pushed' into its now known value. */
sy - > sy_value . X_op = O_constant ;
sy - > sy_value . X_add_number = n_pushed ;
S_SET_SEGMENT ( sy , expr_section ) ;
avr_isr . sym_n_pushed = NULL ;
}
/* Turn frag into ordinary code frag of now known size. */
fr - > fr_var = 0 ;
fr - > fr_fix = ( offsetT ) ( where - fr - > fr_literal ) ;
gas_assert ( fr - > fr_fix < = fr - > fr_offset ) ;
fr - > fr_offset = 0 ;
fr - > fr_type = rs_fill ;
fr - > fr_subtype = 0 ;
}
/* Implements `__gcc_isr' pseudo-instruction. For Prologue and Epilogue
chunks , emit a new rs_machine_dependent frag . For Done chunks , traverse
the current segment and patch all rs_machine_dependent frags to become
appropriate rs_fill code frags . If chunks are seen in an odd ordering ,
throw an error instead . */
static void
avr_gccisr_operands ( struct avr_opcodes_s * opcode , char * * line )
{
int bad = 0 ;
int chunk , reg = 0 ;
char * str = * line ;
gas_assert ( avr_opt . have_gccisr ) ;
/* We only use operands "N" and "r" which don't pop new fix-ups. */
/* 1st operand: Which chunk of __gcc_isr: 0...2. */
chunk = avr_operand ( opcode , - 1 , " N " , & str , NULL ) ;
if ( chunk < 0 | | chunk > 2 )
as_bad ( _ ( " %s requires value 0-2 as operand 1 " ) , opcode - > name ) ;
if ( ISR_CHUNK_Done = = chunk )
{
/* 2nd operand: A register to push / pop. */
str = skip_space ( str ) ;
if ( * str = = ' \0 ' | | * str + + ! = ' , ' )
as_bad ( _ ( " `,' required " ) ) ;
else
avr_operand ( opcode , - 1 , " r " , & str , & reg ) ;
}
* line = str ;
/* Chunks must follow in a specific order:
- Prologue : Exactly one
- Epilogue : Any number
- Done : Exactly one . */
bad | = ISR_CHUNK_Prologue = = chunk & & avr_isr . prev_chunk ! = ISR_CHUNK_Done ;
bad | = ISR_CHUNK_Epilogue = = chunk & & avr_isr . prev_chunk = = ISR_CHUNK_Done ;
bad | = ISR_CHUNK_Done = = chunk & & avr_isr . prev_chunk = = ISR_CHUNK_Done ;
if ( bad )
{
if ( avr_isr . file )
as_bad ( _ ( " `%s %d' after `%s %d' from %s:%u " ) , opcode - > name , chunk ,
opcode - > name , avr_isr . prev_chunk , avr_isr . file , avr_isr . line ) ;
else
as_bad ( _ ( " `%s %d' but no chunk open yet " ) , opcode - > name , chunk ) ;
}
if ( ! had_errors ( ) )
{
/* The longest sequence (prologue) might have up to 6 insns (words):
push R0
in R0 , SREG
push R0
push R1
clr R1
push Rx
*/
unsigned int size = 2 * 6 ;
fragS * fr ;
switch ( chunk )
{
case ISR_CHUNK_Prologue :
avr_isr . need_reg_tmp = 0 ;
avr_isr . need_reg_zero = 0 ;
avr_isr . need_sreg = 0 ;
avr_isr . sym_n_pushed = NULL ;
/* FALLTHRU */
case ISR_CHUNK_Epilogue :
/* Emit a new rs_machine_dependent fragment into the fragment chain.
It will be patched and cleaned up once we see the matching
ISR_CHUNK_Done . */
frag_wane ( frag_now ) ;
frag_new ( 0 ) ;
frag_more ( size ) ;
frag_now - > fr_var = 1 ;
frag_now - > fr_offset = size ;
frag_now - > fr_fix = 0 ;
frag_now - > fr_type = rs_machine_dependent ;
frag_now - > fr_subtype = chunk ;
frag_new ( size ) ;
break ;
case ISR_CHUNK_Done :
/* Traverse all frags of the current subseg and turn ones of type
rs_machine_dependent into ordinary code as expected by GCC . */
for ( fr = frchain_now - > frch_root ; fr ; fr = fr - > fr_next )
if ( fr - > fr_type = = rs_machine_dependent )
avr_patch_gccisr_frag ( fr , reg ) ;
break ;
default :
abort ( ) ;
break ;
}
} /* !had_errors */
avr_isr . prev_chunk = chunk ;
avr_isr . file = as_where ( & avr_isr . line ) ;
}
/* Callback used by the function below. Diagnose any dangling stuff from
` __gcc_isr ' , i . e . frags of type rs_machine_dependent . Such frags should
have been resolved during parse by ISR_CHUNK_Done . If such a frag is
seen , report an error and turn it into something harmless . */
static void
avr_check_gccisr_done ( bfd * abfd ATTRIBUTE_UNUSED ,
segT section ,
void * xxx ATTRIBUTE_UNUSED )
{
segment_info_type * info = seg_info ( section ) ;
if ( SEG_NORMAL ( section )
/* BFD may have introduced its own sections without using
subseg_new , so it is possible that seg_info is NULL . */
& & info )
{
fragS * fr ;
frchainS * frch ;
for ( frch = info - > frchainP ; frch ; frch = frch - > frch_next )
for ( fr = frch - > frch_root ; fr ; fr = fr - > fr_next )
if ( fr - > fr_type = = rs_machine_dependent )
{
if ( avr_isr . file )
as_bad_where ( avr_isr . file , avr_isr . line ,
_ ( " dangling `__gcc_isr %d' " ) , avr_isr . prev_chunk ) ;
else if ( ! had_errors ( ) )
as_bad ( _ ( " dangling `__gcc_isr' " ) ) ;
avr_isr . file = NULL ;
/* Avoid Internal errors due to rs_machine_dependent in the
remainder : Turn frag into something harmless . */
fr - > fr_var = 0 ;
fr - > fr_fix = 0 ;
fr - > fr_offset = 0 ;
fr - > fr_type = rs_fill ;
fr - > fr_subtype = 0 ;
}
}
}
/* Implement `md_pre_output_hook' */
/* Run over all relevant sections and diagnose any dangling `__gcc_isr'.
This runs after parsing all inputs but before relaxing and writing . */
void
avr_pre_output_hook ( void )
{
if ( avr_opt . have_gccisr )
bfd_map_over_sections ( stdoutput , avr_check_gccisr_done , NULL ) ;
}