From 07b2042891ea8ae0007fdae1f5e34e01f2bf842b Mon Sep 17 00:00:00 2001 From: Doug Evans Date: Fri, 10 Apr 1998 01:37:00 +0000 Subject: [PATCH] * config/tc-dvp.c (VU_LABEL_PREFIX): New macro. (compute_mpgloc): New function. (eval_expr): New arg `cpu'. All callers updated. (non_vu_insn_seen_p): New static global. (RELAX_{MPG,DIRECT,VU,ENCODE,GROWTH,DONE_}): New macros. (struct dvp_fixup): New member `cpu'. (assemble_one_insn): New args init_fixup_count, fixup_offset. All callers updated. (md_assemble): Set non_vu_insn_seen_p as appropriate. (assemble_vif): Set `cpu' field of fixup. Clean up calls to frag_var. Recorded mpgloc is now in bytes. (assemble_vu_insn): Delete, contents moved into ... (assemble_vu): ... here. Don't record fixups until after parsing both upper and lower insns. If branch insn inside mpg, properly compute target address. (dvp_frob_label): Create copies of vu labels inside mpg's. (dvp_relax_frag): Clean up. (md_convert_frag): Ditto. (md_apply_fix3): Signal error if mpg embedded vu code has branch to undefined label (not currently supported). (eval_expr): New arg `cpu'. All callers updated. (insert_operand_final): Convert mpgloc from bytes to dwords. (s_endmpg): Use compute_mpgloc to update $.mpgloc. (s_state): If switching to vu state, initialize $.mpgloc. --- gas/ChangeLog | 26 +++ gas/config/tc-dvp.c | 378 ++++++++++++++++++++++++++++++++------------ 2 files changed, 307 insertions(+), 97 deletions(-) diff --git a/gas/ChangeLog b/gas/ChangeLog index eea25658cda..44c3bd2fdca 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -4,6 +4,32 @@ Thu Apr 9 10:29:42 1998 Doug Evans (print_symbol_value_1): Use it. * expr.h (expr_build_dot): Declare. * expr.c (expr_build_dot): New function. +start-sanitize-sky + * config/tc-dvp.c (VU_LABEL_PREFIX): New macro. + (compute_mpgloc): New function. + (eval_expr): New arg `cpu'. All callers updated. + (non_vu_insn_seen_p): New static global. + (RELAX_{MPG,DIRECT,VU,ENCODE,GROWTH,DONE_}): New macros. + (struct dvp_fixup): New member `cpu'. + (assemble_one_insn): New args init_fixup_count, fixup_offset. + All callers updated. + (md_assemble): Set non_vu_insn_seen_p as appropriate. + (assemble_vif): Set `cpu' field of fixup. + Clean up calls to frag_var. Recorded mpgloc is now in bytes. + (assemble_vu_insn): Delete, contents moved into ... + (assemble_vu): ... here. Don't record fixups until after parsing + both upper and lower insns. If branch insn inside mpg, properly + compute target address. + (dvp_frob_label): Create copies of vu labels inside mpg's. + (dvp_relax_frag): Clean up. + (md_convert_frag): Ditto. + (md_apply_fix3): Signal error if mpg embedded vu code has branch + to undefined label (not currently supported). + (eval_expr): New arg `cpu'. All callers updated. + (insert_operand_final): Convert mpgloc from bytes to dwords. + (s_endmpg): Use compute_mpgloc to update $.mpgloc. + (s_state): If switching to vu state, initialize $.mpgloc. +end-sanitize-sky Wed Apr 8 16:16:11 1998 Doug Evans diff --git a/gas/config/tc-dvp.c b/gas/config/tc-dvp.c index 6f7af591022..4298885b0d7 100644 --- a/gas/config/tc-dvp.c +++ b/gas/config/tc-dvp.c @@ -49,15 +49,20 @@ #define END_LABEL_PREFIX ".L.end." /* Label to use for unique labels. */ #define UNIQUE_LABEL_PREFIX ".L.dvptmp." +/* Prefix for mips version of labels defined in vu code. + Note that symbols that begin with '$' are local symbols + on mips targets, so we can't begin it with '$'. */ +#define VU_LABEL_PREFIX "._." static long parse_float PARAMS ((char **, const char **)); static symbolS * create_label PARAMS ((const char *, const char *)); static symbolS * create_colon_label PARAMS ((int, const char *, const char *)); static char * unique_name PARAMS ((const char *)); +static symbolS * compute_mpgloc PARAMS ((symbolS *, symbolS *, symbolS *)); static int compute_nloop PARAMS ((gif_type, int, int)); static void check_nloop PARAMS ((gif_type, int, int, int, char *, unsigned int)); -static long eval_expr PARAMS ((int, int, const char *, ...)); +static long eval_expr PARAMS ((dvp_cpu, int, int, const char *, ...)); static long parse_dma_addr_autocount (); static void inline_dma_data PARAMS ((int, DVP_INSN *)); static void setup_dma_autocount PARAMS ((const char *, DVP_INSN *, int)); @@ -114,6 +119,10 @@ static int cur_state_index; static void push_asm_state PARAMS ((asm_state)); static void pop_asm_state PARAMS ((int)); static void set_asm_state PARAMS ((asm_state)); + +/* Set to non-zero if any non-vu insn seen. + Used to control type of relocations emitted. */ +static int non_vu_insn_seen_p = 0; /* Current cpu (machine variant) type state. We copy the mips16 way of recording what the current machine type is in @@ -139,7 +148,7 @@ static symbolS *vif_data_start; /* Label at end of insn's data. */ static symbolS *vif_data_end; -/* Special symbol $.mpgloc. */ +/* Special symbol $.mpgloc. The value is in bytes. */ static symbolS *mpgloc_sym; /* Special symbol $.unpackloc. */ static symbolS *unpackloc_sym; @@ -175,6 +184,17 @@ static const dvp_operand *cur_operand; /* Options for the `caller' argument to s_endmpg. */ typedef enum { ENDMPG_USER, ENDMPG_INTERNAL, ENDMPG_MIDDLE } endmpg_caller; + +/* Relaxation support. */ +#define RELAX_MPG 1 +#define RELAX_DIRECT 2 +/* vu insns aren't relaxed, but they use machine dependent frags so we + must handle them during relaxation */ +#define RELAX_VU 3 +#define RELAX_ENCODE(type, growth) (10 + (growth)) +#define RELAX_GROWTH(state) ((state) - 10) +/* Return non-zero if STATE represents a relaxed state. */ +#define RELAX_DONE_P(state) ((state) >= 10) const char *md_shortopts = ""; @@ -220,10 +240,6 @@ DVP options:\n\ "); } -/* Set by md_assemble for use by dvp_fill_insn. */ -static subsegT prev_subseg; -static segT prev_seg; - static void s_dmadata PARAMS ((int)); static void s_enddmadata PARAMS ((int)); static void s_dmapackvif PARAMS ((int)); @@ -280,6 +296,8 @@ md_begin () struct dvp_fixup { + /* the cpu this fixup is associated with */ + dvp_cpu cpu; /* index into `dvp_operands' */ int opindex; /* byte offset from beginning of instruction */ @@ -315,6 +333,7 @@ static const dvp_opcode * assemble_vu_insn PARAMS ((dvp_cpu, static const dvp_opcode * assemble_one_insn PARAMS ((dvp_cpu, const dvp_opcode *, const dvp_operand *, + int, int, char **, DVP_INSN *)); /* Main entry point for assembling an instruction. */ @@ -356,9 +375,13 @@ md_assemble (str) assemble_gif (str); else assemble_vif (str); + non_vu_insn_seen_p = 1; } else if (CUR_ASM_STATE == ASM_DIRECT) - assemble_gif (str); + { + assemble_gif (str); + non_vu_insn_seen_p = 1; + } else if (CUR_ASM_STATE == ASM_VU || CUR_ASM_STATE == ASM_MPG) assemble_vu (str); @@ -398,7 +421,7 @@ assemble_dma (str) opcode = assemble_one_insn (DVP_DMA, dma_opcode_lookup_asm (str), dma_operands, - &str, insn_buf); + 0, 0, &str, insn_buf); if (opcode == NULL) return; if (!output_dma) @@ -499,7 +522,7 @@ assemble_vif (str) opcode = assemble_one_insn (DVP_VIF, vif_opcode_lookup_asm (str), vif_operands, - &str, insn_buf); + 0, 0, &str, insn_buf); if (opcode == NULL) return; @@ -539,6 +562,7 @@ assemble_vif (str) unique_name ("varlen")); vif_data_end = symbol_new (name, now_seg, 0, 0); symbol_table_insert (vif_data_end); + fixups[fixup_count].cpu = DVP_VIF; fixups[fixup_count].exp.X_op = O_symbol; fixups[fixup_count].exp.X_add_symbol = vif_data_end; fixups[fixup_count].exp.X_add_number = 0; @@ -574,8 +598,14 @@ assemble_vif (str) frag_wane (frag_now); frag_new (0); - /* This dance with frag_grow is to ensure the variable part and - fixed part are in the same fragment. */ + /* One could combine the previous two lines with the following. + They're not for clarity: keep separate the actions being + performed. */ + + /* This dance with frag_grow is so we can record frag_now in + insn_frag. frag_var always changes frag_now. We must allocate + the maximal amount of space we need so there's room to move + the insn in the frag during relaxation. */ frag_grow (8); /* Allocate space for the fixed part. */ f = frag_more (4); @@ -583,8 +613,8 @@ assemble_vif (str) frag_var (rs_machine_dependent, 4, /* max chars */ - 0, /* variable part already allocated */ - 1, /* subtype */ + 0, /* variable part is empty at present */ + RELAX_MPG, /* subtype */ NULL, /* no symbol */ 0, /* offset */ f); /* opcode */ @@ -599,13 +629,15 @@ assemble_vif (str) unique_name ("mpg")); insn_frag->fr_symbol = vif_data_start; - /* Get the value of mpgloc. If it wasn't '*' update $.mpgloc. */ + /* Get the value of mpgloc. If it wasn't '*' + then update $.mpgloc. */ { int mpgloc = vif_get_mpgloc (); if (mpgloc != -1) { mpgloc_sym->sy_value.X_op = O_constant; - mpgloc_sym->sy_value.X_add_number = mpgloc; + /* The value is recorded in bytes. */ + mpgloc_sym->sy_value.X_add_number = mpgloc * 8; mpgloc_sym->sy_value.X_unsigned = 1; } } @@ -624,8 +656,14 @@ assemble_vif (str) frag_wane (frag_now); frag_new (0); - /* This dance with frag_grow is to ensure the variable part and - fixed part are in the same fragment. */ + /* One could combine the previous two lines with the following. + They're not for clarity: keep separate the actions being + performed. */ + + /* This dance with frag_grow is so we can record frag_now in + insn_frag. frag_var always changes frag_now. We must allocate + the maximal amount of space we need so there's room to move + the insn in the frag during relaxation. */ frag_grow (16); /* Allocate space for the fixed part. */ f = frag_more (4); @@ -633,8 +671,8 @@ assemble_vif (str) frag_var (rs_machine_dependent, 12, /* max chars */ - 0, /* variable part already allocated */ - 2, /* subtype */ + 0, /* variable part is empty at present */ + RELAX_DIRECT, /* subtype */ NULL, /* no symbol */ 0, /* offset */ f); /* opcode */ @@ -797,7 +835,7 @@ assemble_gif (str) opcode = assemble_one_insn (DVP_GIF, gif_opcode_lookup_asm (str), gif_operands, - &str, insn_buf); + 0, 0, &str, insn_buf); if (opcode == NULL) return; @@ -840,8 +878,13 @@ static void assemble_vu (str) char *str; { + int i; char *f; const dvp_opcode *opcode; + /* The lower instruction has the lower address so insns[0] = lower insn, + insns[1] = upper insn. */ + DVP_INSN insns[2]; + fragS * insn_frag; /* Handle automatic mpg insertion if enabled. */ if (CUR_ASM_STATE == ASM_MPG @@ -854,11 +897,6 @@ assemble_vu (str) record_mach (DVP_VUUP, 0); - /* The lower instruction has the lower address. - Handle this by grabbing 8 bytes now, and then filling each word - as appropriate. */ - f = frag_more (8); - #ifdef VERTICAL_BAR_SEPARATOR char *p = strchr (str, '|'); @@ -869,61 +907,78 @@ assemble_vu (str) } *p = 0; - opcode = assemble_vu_insn (DVP_VUUP, - vu_upper_opcode_lookup_asm (str), vu_operands, - &str, f + 4); + opcode = assemble_one_insn (DVP_VUUP, + vu_upper_opcode_lookup_asm (str), vu_operands, + 0, 4, &str, &insns[1]); *p = '|'; str = p + 1; #else - opcode = assemble_vu_insn (DVP_VUUP, + opcode = assemble_one_insn (DVP_VUUP, vu_upper_opcode_lookup_asm (str), vu_operands, - &str, f + 4); + 0, 4, &str, &insns[1]); #endif /* Don't assemble next one if we couldn't assemble the first. */ if (opcode == NULL) return; - opcode = assemble_vu_insn (DVP_VULO, - vu_lower_opcode_lookup_asm (str), vu_operands, - &str, f); - /* If this was the "loi" pseudo-insn, we need to set the `i' bit. */ - if (opcode != NULL) - { - if (strcmp (opcode->mnemonic, "loi") == 0) - f[7] |= 0x80; - } - - /* Increment the vu insn counter. - If get reach 256 we need to insert an `mpg'. */ - ++vu_count; -} - -static const dvp_opcode * -assemble_vu_insn (cpu, opcode, operand_table, pstr, buf) - dvp_cpu cpu; - const dvp_opcode *opcode; - const dvp_operand *operand_table; - char **pstr; - char *buf; -{ - int i; - DVP_INSN insn; - opcode = assemble_one_insn (cpu, opcode, operand_table, pstr, &insn); + /* Assemble the lower insn. + Pass `fixup_count' for `init_fixup_count' so that we don't clobber + any fixups the upper insn had. */ + opcode = assemble_one_insn (DVP_VULO, + vu_lower_opcode_lookup_asm (str), vu_operands, + fixup_count, 0, &str, &insns[0]); if (opcode == NULL) - return NULL; + return; - /* Write out the instruction. + /* If there were fixups and we're inside mpg, create a machine dependent + fragment so that we can record the current value of $.mpgloc in fr_symbol. Reminder: it is important to fetch enough space in one call to `frag_more'. We use (f - frag_now->fr_literal) to compute where we are and we don't want frag_now to change between calls. */ - md_number_to_chars (buf, insn, 4); + if (fixup_count != 0 + && CUR_ASM_STATE == ASM_MPG) + { + symbolS * cur_mpgloc; + + /* Ensure we get a new frag. */ + frag_wane (frag_now); + frag_new (0); + + /* Compute the current $.mpgloc. */ + cur_mpgloc = compute_mpgloc (mpgloc_sym, vif_data_start, + expr_build_dot ()); + + /* We need to use frag_now afterwards, so we can't just call frag_var. + Instead we use frag_more and save the value of frag_now in + insn_frag. */ + f = frag_more (8); + insn_frag = frag_now; + /* Turn the frag into a machine dependent frag. */ + frag_variant (rs_machine_dependent, + 0, /* max chars */ + 0, /* no variable part */ + RELAX_VU, /* subtype */ + cur_mpgloc, /* $.mpgloc */ + 0, /* offset */ + NULL); /* opcode */ + } + else + { + f = frag_more (8); + insn_frag = frag_now; + } + + /* Write out the instructions. */ + md_number_to_chars (f, insns[0], 4); + md_number_to_chars (f + 4, insns[1], 4); /* Create any fixups. */ for (i = 0; i < fixup_count; ++i) { int op_type, reloc_type; const dvp_operand *operand; + dvp_cpu cpu; /* Create a fixup for this operand. At this point we do not use a bfd_reloc_code_real_type for @@ -932,24 +987,53 @@ assemble_vu_insn (cpu, opcode, operand_table, pstr, buf) operand type, although that is admittedly not a very exciting feature. We pick a BFD reloc type in md_apply_fix. */ + cpu = fixups[i].cpu; op_type = fixups[i].opindex; reloc_type = encode_fixup_reloc_type (cpu, op_type); operand = &vu_operands[op_type]; - fix_new_exp (frag_now, buf - frag_now->fr_literal, 4, + + /* Branch operands inside mpg have to be handled specially. + We want a pc relative relocation in a section different from our own. + See the br-2.s dejagnu testcase for a good example. */ + if (CUR_ASM_STATE == ASM_MPG + && (operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0) + { + symbolS *e1,*e2,*diff_expr; + + /* For "br foo" we want "foo - (. + 8)". */ + e1 = expr_build_binary (O_add, insn_frag->fr_symbol, + expr_build_uconstant (8)); + e2 = make_expr_symbol (&fixups[i].exp); + diff_expr = expr_build_binary (O_subtract, e2, e1); + fixups[i].exp.X_op = O_symbol; + fixups[i].exp.X_add_symbol = diff_expr; + fixups[i].exp.X_add_number = 0; + } + + fix_new_exp (insn_frag, f + fixups[i].offset - insn_frag->fr_literal, 4, &fixups[i].exp, - (operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0, + CUR_ASM_STATE == ASM_MPG /* pcrel */ + ? 0 + : (operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0, (bfd_reloc_code_real_type) reloc_type); } - /* All done. */ - return opcode; + /* If this was the "loi" pseudo-insn, we need to set the `i' bit. */ + if (strcmp (opcode->mnemonic, "loi") == 0) + f[7] |= 0x80; + + /* Increment the vu insn counter. + If get reach 256 we need to insert an `mpg'. */ + ++vu_count; } /* Assemble one instruction at *PSTR. CPU indicates what component we're assembling for. The assembled instruction is stored in INSN_BUF. OPCODE is a pointer to the head of the hash chain. - + INIT_FIXUP_COUNT is the initial value for `fixup_count'. + It exists to allow the fixups for multiple calls to this insn to be + queued up before actually emitting them. *PSTR is updated to point passed the parsed instruction. If the insn is successfully parsed the result is a pointer to the opcode @@ -959,10 +1043,13 @@ assemble_vu_insn (cpu, opcode, operand_table, pstr, buf) the error occured). */ static const dvp_opcode * -assemble_one_insn (cpu, opcode, operand_table, pstr, insn_buf) +assemble_one_insn (cpu, opcode, operand_table, init_fixup_count, fixup_offset, + pstr, insn_buf) dvp_cpu cpu; const dvp_opcode *opcode; const dvp_operand *operand_table; + int init_fixup_count; + int fixup_offset; char **pstr; DVP_INSN *insn_buf; { @@ -987,7 +1074,7 @@ assemble_one_insn (cpu, opcode, operand_table, pstr, insn_buf) dvp_opcode_init_parse (); insn_buf[opcode->opcode_word] = opcode->value; - fixup_count = 0; + fixup_count = init_fixup_count; past_opcode_p = 0; num_suffixes = 0; @@ -1174,6 +1261,7 @@ assemble_one_insn (cpu, opcode, operand_table, pstr, insn_buf) /* We need to generate a fixup for this expression. */ if (fixup_count >= MAX_FIXUPS) as_fatal ("internal error: too many fixups"); + fixups[fixup_count].cpu = cpu; fixups[fixup_count].exp = exp; fixups[fixup_count].opindex = index; /* FIXME: Revisit. Do we really need operand->word? @@ -1181,10 +1269,11 @@ assemble_one_insn (cpu, opcode, operand_table, pstr, insn_buf) twisted. How about defining word 0 as the word with the lowest address and basing operand-shift off that. operand->word could then be deleted. */ + fixups[fixup_count].offset = fixup_offset; if (operand->word != 0) - fixups[fixup_count].offset = operand->word * 4; + fixups[fixup_count].offset += operand->word * 4; else - fixups[fixup_count].offset = (operand->shift / 32) * 4; + fixups[fixup_count].offset += (operand->shift / 32) * 4; ++fixup_count; value = 0; } @@ -1400,18 +1489,41 @@ dvp_after_pass_hook () #endif } -/* Called when a label is defined via tc_frob_label. */ +/* Called via tc_frob_label when a label is defined. */ void dvp_frob_label (sym) symbolS *sym; { + const char * name = S_GET_NAME (sym); + /* All labels in vu code must be specially marked for the disassembler. The disassembler ignores all previous information at each new label (that has a higher address than the last one). */ if (CUR_ASM_STATE == ASM_MPG || CUR_ASM_STATE == ASM_VU) S_SET_OTHER (sym, STO_DVP_VU); + + /* If inside an mpg, move vu space labels to their own section and create + the corresponding ._. version in normal space. */ + + if (CUR_ASM_STATE == ASM_MPG + /* Only do this special processing for user specified symbols. + Not sure how we can distinguish them other than by some prefix. */ + && *name != '.' + /* Check for recursive invocation creating the ._.name. */ + && strncmp (name, VU_LABEL_PREFIX, sizeof (VU_LABEL_PREFIX) - 1) != 0) + { + /* Move this symbol to vu space. */ + symbolS * cur_mpgloc = compute_mpgloc (mpgloc_sym, vif_data_start, + expr_build_dot ()); + S_SET_SEGMENT (sym, expr_section); + sym->sy_value = cur_mpgloc->sy_value; + sym->sy_frag = &zero_address_frag; + + /* Create the ._. symbol in normal space. */ + create_colon_label (STO_DVP_VU, VU_LABEL_PREFIX, name); + } } /* mpg/direct alignment is handled via relaxation */ @@ -1438,7 +1550,9 @@ md_estimate_size_before_relax (fragP, segment) /* Perform the relaxation. All we have to do is figure out how many bytes we need to insert to - get to the recorded symbol (which is at the required alignment). */ + get to the recorded symbol (which is at the required alignment). + This function is also called for machine dependent vu insn frags. + In this case the growth is always 0. */ long dvp_relax_frag (fragP, stretch) @@ -1450,30 +1564,37 @@ dvp_relax_frag (fragP, stretch) /* Symbol marking start of data. */ symbolS * symbolP = fragP->fr_symbol; /* Address of the symbol. */ - long target = S_GET_VALUE (symbolP) + symbolP->sy_frag->fr_address; + long target; long growth; /* subtype >= 10 means "done" */ - if (fragP->fr_subtype >= 10) + if (RELAX_DONE_P (fragP->fr_subtype)) return 0; - /* subtype 1 = mpg */ - if (fragP->fr_subtype == 1) + /* vu insn? */ + if (fragP->fr_subtype == RELAX_VU) + { + fragP->fr_subtype = RELAX_ENCODE (RELAX_VU, 0); + return 0; + } + + target = S_GET_VALUE (symbolP) + symbolP->sy_frag->fr_address; + + if (fragP->fr_subtype == RELAX_MPG) { growth = target - address; if (growth < 0) as_fatal ("internal error: bad mpg alignment handling"); - fragP->fr_subtype = 10 + growth; + fragP->fr_subtype = RELAX_ENCODE (RELAX_MPG, growth); return growth; } - /* subtype 2 = direct */ - if (fragP->fr_subtype == 2) + if (fragP->fr_subtype == RELAX_DIRECT) { growth = target - address; if (growth < 0) as_fatal ("internal error: bad direct alignment handling"); - fragP->fr_subtype = 10 + growth; + fragP->fr_subtype = RELAX_ENCODE (RELAX_DIRECT, growth); return growth; } @@ -1493,7 +1614,7 @@ md_convert_frag (abfd, sec, fragP) segT sec; fragS * fragP; { - int growth = fragP->fr_subtype - 10; + int growth = RELAX_GROWTH (fragP->fr_subtype); fragP->fr_fix += growth; @@ -1555,8 +1676,6 @@ decode_fixup_reloc_type (fixup_reloc, cpuP, operandP) } } -/* Given a fixup reloc type, return a pointer to the operand - /* The location from which a PC relative jump should be calculated, given a PC relative reloc. */ @@ -1733,12 +1852,36 @@ md_apply_fix3 (fixP, valueP, seg) /* Determine a BFD reloc value based on the operand information. We are only prepared to turn a few of the operands into relocs. */ - /* FIXME: This test is a hack. */ if ((operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0) { assert (operand->bits == 11 && operand->shift == 0); - fixP->fx_r_type = BFD_RELOC_MIPS_DVP_11_PCREL; + + /* The fixup isn't recorded as a pc relative branch to some label. + Instead a complicated expression is used to compute the desired + value. Well, that didn't work and we have to emit a reloc. + Things are tricky because the result we want is the difference + of two addresses in a section potentially different from the one + the reloc is in. Ugh. + The solution is to emit two relocs, one that adds the target + address and one that subtracts the source address + 8 (the + linker will perform the byte->dword conversion). + This is rather complicated and rather than risk breaking + existing code we fall back on the old way if the file only + contains vu code. In this case the file is intended to + be fully linked with other vu code and thus we have a normal + situation where the relocation directly corresponds to the + branch insn. */ + + if (non_vu_insn_seen_p) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + "can't handle mpg loaded vu code with branch relocations"); + } + else + { + fixP->fx_r_type = BFD_RELOC_MIPS_DVP_11_PCREL; + } } else if ((operand->flags & DVP_OPERAND_DMA_ADDR) != 0 || (operand->flags & DVP_OPERAND_DMA_NEXT) != 0) @@ -1929,9 +2072,10 @@ scan_symbol (sym) static long #ifdef USE_STDARG -eval_expr (int opindex, int offset, const char *fmt, ...) +eval_expr (dvp_cpu cpu, int opindex, int offset, const char *fmt, ...) #else -eval_expr (opindex, offset, fmt, va_alist) +eval_expr (cpu, opindex, offset, fmt, va_alist) + dvp_cpu cpu; int opindex,offset; const char *fmt; va_dcl @@ -1961,6 +2105,7 @@ eval_expr (opindex, offset, fmt, va_alist) { if (opindex != 0) { + fixups[fixup_count].cpu = cpu; fixups[fixup_count].exp = exp; fixups[fixup_count].opindex = opindex; fixups[fixup_count].offset = offset; @@ -2037,6 +2182,23 @@ unique_name (prefix) ++counter; return result; } + +/* Compute a value for $.mpgloc given a symbol at the start of a chunk + of code, the $.mpgloc value for the start, and a symbol at the end + of the chunk of code. */ + +static symbolS * +compute_mpgloc (startloc, startsym, endsym) + symbolS * startloc; + symbolS * startsym; + symbolS * endsym; +{ + symbolS *s; + + s = expr_build_binary (O_subtract, endsym, startsym); + s = expr_build_binary (O_add, startloc, s); + return s; +} /* Compute a value for nloop. */ @@ -2099,14 +2261,14 @@ setup_dma_autocount (name, insn_buf, inline_p) { /* -1: The count is the number of following quadwords, so skip the one containing the dma tag. */ - count = eval_expr (dma_operand_count, 0, + count = eval_expr (DVP_DMA, dma_operand_count, 0, "((%s%s - %s) >> 4) - 1", END_LABEL_PREFIX, name, name); } else { /* We don't want to subtract 1 here as the begin and end labels properly surround the data we want to compute the length of. */ - count = eval_expr (dma_operand_count, 0, + count = eval_expr (DVP_DMA, dma_operand_count, 0, "(%s%s - %s) >> 4", END_LABEL_PREFIX, name, name); } @@ -2175,7 +2337,7 @@ parse_dma_addr_autocount (opcode, operand, mods, insn_buf, pstr, errmsg) label2 = create_label ("_$", name); endlabel = create_label (END_LABEL_PREFIX, name); - retval = eval_expr (dma_operand_addr, 4, name); + retval = eval_expr (DVP_DMA, dma_operand_addr, 4, name); setup_dma_autocount (name, insn_buf, 0); @@ -2422,6 +2584,7 @@ insert_operand_final (cpu, operand, mods, insn_buf, val, file, line) { offsetT min, max, test; + /* ??? This test belongs more properly in the insert handler. */ if ((operand->flags & DVP_OPERAND_RELATIVE_BRANCH) != 0) { if ((val & 7) != 0) @@ -2433,6 +2596,18 @@ insert_operand_final (cpu, operand, mods, insn_buf, val, file, line) } val >>= 3; } + /* ??? This test belongs more properly in the insert handler. */ + else if ((operand->flags & DVP_OPERAND_VU_ADDRESS) != 0) + { + if ((val & 7) != 0) + { + if (file == (char *) NULL) + as_warn ("misaligned vu address"); + else + as_warn_where (file, line, "misaligned vu address"); + } + val >>= 3; + } if ((operand->flags & DVP_OPERAND_SIGNED) != 0) { @@ -2618,14 +2793,10 @@ s_endmpg (caller) /* Update $.mpgloc. We have to leave the old value alone as it may be used in fixups - already recorded. The new value is the old value plus the number of + already recorded. Since compute_mpgloc allocates a new symbol for the + result we're ok. The new value is the old value plus the number of double words in this chunk. */ - { - symbolS *s; - s = expr_build_binary (O_subtract, vif_data_end, vif_data_start); - s = expr_build_binary (O_divide, s, expr_build_uconstant (8)); - mpgloc_sym = expr_build_binary (O_add, mpgloc_sym, s); - } + mpgloc_sym = compute_mpgloc (mpgloc_sym, vif_data_start, vif_data_end); set_asm_state (ASM_INIT); @@ -2720,7 +2891,7 @@ s_endgif (ignore) when they're in different fragments but the difference is constant. Not sure how much of a slowdown that will introduce though. */ fixup_count = 0; - bytes = eval_expr (gif_operand_nloop, 0, ". - %s - 16", gif_data_name); + bytes = eval_expr (DVP_GIF, gif_operand_nloop, 0, ". - %s - 16", gif_data_name); /* Compute a value for nloop if we can. */ @@ -2787,10 +2958,23 @@ s_state (state) { /* If in MPG state and the user requests to change to VU state, leave the state as MPG. This happens when we see an mpg followed - by a .include that has .vu. */ + by a .include that has .vu. Note that no attempt is made to support + an include depth > 1 for this case. */ if (CUR_ASM_STATE == ASM_MPG && state == ASM_VU) return; + /* If changing to the VU state, we need to set up things for $.mpgloc + calculations. */ + if (state == ASM_VU) + { + /* FIXME: May need to check that we're not clobbering currently + in use versions of these. Also need to worry about which section + the .vu is issued in. On the other hand, ".vu" isn't intended + to be supported everywhere. */ + mpgloc_sym = expr_build_uconstant (0); + vif_data_start = expr_build_dot (); + } + set_asm_state (state); demand_empty_rest_of_line (); -- 2.30.2