2005-09-06 Paul Brook <paul@codesourcery.com>

gas/
	* config/tc-arm.c (arm_it): Add relax field.
	(T16_32_TAB): Add addi, addis, add_pc, add_sp, dec_sp, inc_sp,
	b, bcond, ldr_pc, ldr_pc2, ldr_sp, str_sp, subi, subis.
	(do_t_add_sub, do_t_addr, do_t_branch, do_t_ldst,
	do_t_mov_cmp): Allow relaxation.
	(output_relax_insn): New function.
	(put_thumb32_insn): New function.
	(output_inst): Use new functions.
	(md_assemble): Don't throw error on relaxable instructions.
	(insns): Change "b" entry from TCE(...) to tCE(...).
	(md_estimate_size_before_relax): Return 2.
	(md_convert_frag, relax_immediate, relax_adr, relax_addsub,
	relax_branch, arm_relax_frag): New functions.
	(arm_force_relocation): Return 0 for Thumb-2 immediate operand
	relocations.
	* config/tc-arm.h (md_convert_frag): Remove definition.
	(md_relax_frag): Define.
	(arm_relax_frag): Add prototype.
gas/testsuite/
	* gas/arm/thumb2_relax.d: New test.
	* gas/arm/thumb2_relax.s: New test.
	* gas/arm/thumb32.d: Adjust expected results to include relaxation.
	* gas/arm/thumb32.s: Tweak for better coverage of relaxable
	instructions.  Remove load/store tests.
This commit is contained in:
Paul Brook 2005-09-06 16:59:24 +00:00
parent 9a64e43541
commit 0110f2b896
8 changed files with 1783 additions and 1120 deletions

View File

@ -1,3 +1,24 @@
2005-09-06 Paul Brook <paul@codesourcery.com>
* config/tc-arm.c (arm_it): Add relax field.
(T16_32_TAB): Add addi, addis, add_pc, add_sp, dec_sp, inc_sp,
b, bcond, ldr_pc, ldr_pc2, ldr_sp, str_sp, subi, subis.
(do_t_add_sub, do_t_addr, do_t_branch, do_t_ldst,
do_t_mov_cmp): Allow relaxation.
(output_relax_insn): New function.
(put_thumb32_insn): New function.
(output_inst): Use new functions.
(md_assemble): Don't throw error on relaxable instructions.
(insns): Change "b" entry from TCE(...) to tCE(...).
(md_estimate_size_before_relax): Return 2.
(md_convert_frag, relax_immediate, relax_adr, relax_addsub,
relax_branch, arm_relax_frag): New functions.
(arm_force_relocation): Return 0 for Thumb-2 immediate operand
relocations.
* config/tc-arm.h (md_convert_frag): Remove definition.
(md_relax_frag): Define.
(arm_relax_frag): Add prototype.
2005-09-02 Paul Brook <paul@codesourcery.com>
* config/tc-arm.c (do_rn_rd): Enforce SWP operand constraints.

View File

@ -213,6 +213,9 @@ struct arm_it
int size;
int size_req;
int cond;
/* Set to the opcode if the instruction needs relaxation.
Zero if the instruction is not relaxed. */
unsigned long relax;
struct
{
bfd_reloc_code_real_type type;
@ -5759,17 +5762,24 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
encodings (the latter only in post-V6T2 cores). The index is the
value used in the insns table below. When there is more than one
possible 16-bit encoding for the instruction, this table always
holds variant (1). */
holds variant (1).
Also contains several pseudo-instructions used during relaxation. */
#define T16_32_TAB \
X(adc, 4140, eb400000), \
X(adcs, 4140, eb500000), \
X(add, 1c00, eb000000), \
X(adds, 1c00, eb100000), \
X(addi, 0000, f1000000), \
X(addis, 0000, f1100000), \
X(add_pc,000f, f20f0000), \
X(add_sp,000d, f10d0000), \
X(adr, 000f, f20f0000), \
X(and, 4000, ea000000), \
X(ands, 4000, ea100000), \
X(asr, 1000, fa40f000), \
X(asrs, 1000, fa50f000), \
X(b, e000, f000b000), \
X(bcond, d000, f0008000), \
X(bic, 4380, ea200000), \
X(bics, 4380, ea300000), \
X(cmn, 42c0, eb100f00), \
@ -5777,14 +5787,19 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
X(cpsie, b660, f3af8400), \
X(cpsid, b670, f3af8600), \
X(cpy, 4600, ea4f0000), \
X(dec_sp,80dd, f1bd0d00), \
X(eor, 4040, ea800000), \
X(eors, 4040, ea900000), \
X(inc_sp,00dd, f10d0d00), \
X(ldmia, c800, e8900000), \
X(ldr, 6800, f8500000), \
X(ldrb, 7800, f8100000), \
X(ldrh, 8800, f8300000), \
X(ldrsb, 5600, f9100000), \
X(ldrsh, 5e00, f9300000), \
X(ldr_pc,4800, f85f0000), \
X(ldr_pc2,4800, f85f0000), \
X(ldr_sp,9800, f85d0000), \
X(lsl, 0000, fa00f000), \
X(lsls, 0000, fa10f000), \
X(lsr, 0800, fa20f000), \
@ -5812,8 +5827,11 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
X(str, 6000, f8400000), \
X(strb, 7000, f8000000), \
X(strh, 8000, f8200000), \
X(str_sp,9000, f84d0000), \
X(sub, 1e00, eba00000), \
X(subs, 1e00, ebb00000), \
X(subi, 8000, f1a00000), \
X(subis, 8000, f1b00000), \
X(sxtb, b240, fa4ff080), \
X(sxth, b200, fa0ff080), \
X(tst, 4200, ea100f00), \
@ -5875,17 +5893,61 @@ do_t_add_sub (void)
if (unified_syntax)
{
bfd_boolean flags;
bfd_boolean narrow;
int opcode;
flags = (inst.instruction == T_MNEM_adds
|| inst.instruction == T_MNEM_subs);
if (flags)
narrow = (current_it_mask == 0);
else
narrow = (current_it_mask != 0);
if (!inst.operands[2].isreg)
{
/* ??? Convert large immediates to addw/subw. */
/* ??? 16-bit adds with small immediates. */
/* For an immediate, we always generate a 32-bit opcode;
section relaxation will shrink it later if possible. */
inst.instruction = THUMB_OP32 (inst.instruction);
inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
inst.instruction |= inst.operands[0].reg << 8;
inst.instruction |= inst.operands[1].reg << 16;
inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
opcode = 0;
if (inst.size_req != 4)
{
int add;
add = (inst.instruction == T_MNEM_add
|| inst.instruction == T_MNEM_adds);
/* Attempt to use a narrow opcode, with relaxation if
appropriate. */
if (Rd == REG_SP && Rs == REG_SP && !flags)
opcode = add ? T_MNEM_inc_sp : T_MNEM_dec_sp;
else if (Rd <= 7 && Rs == REG_SP && add && !flags)
opcode = T_MNEM_add_sp;
else if (Rd <= 7 && Rs == REG_PC && add && !flags)
opcode = T_MNEM_add_pc;
else if (Rd <= 7 && Rs <= 7 && narrow)
{
if (flags)
opcode = add ? T_MNEM_addis : T_MNEM_subis;
else
opcode = add ? T_MNEM_addi : T_MNEM_subi;
}
if (opcode)
{
inst.instruction = THUMB_OP16(opcode);
inst.instruction |= (Rd << 4) | Rs;
inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
if (inst.size_req != 2)
inst.relax = opcode;
}
else
constraint (inst.size_req == 2, BAD_HIREG);
}
if (inst.size_req == 4
|| (inst.size_req != 2 && !opcode))
{
/* ??? Convert large immediates to addw/subw. */
inst.instruction = THUMB_OP32 (inst.instruction);
inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
inst.instruction |= inst.operands[0].reg << 8;
inst.instruction |= inst.operands[1].reg << 16;
inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
}
else
{
@ -5893,13 +5955,6 @@ do_t_add_sub (void)
/* See if we can do this with a 16-bit instruction. */
if (!inst.operands[2].shifted && inst.size_req != 4)
{
bfd_boolean narrow;
if (inst.instruction == T_MNEM_adds
|| inst.instruction == T_MNEM_subs)
narrow = (current_it_mask == 0);
else
narrow = (current_it_mask != 0);
if (Rd > 7 || Rs > 7 || Rn > 7)
narrow = FALSE;
@ -5992,10 +6047,16 @@ do_t_add_sub (void)
static void
do_t_adr (void)
{
if (unified_syntax && inst.size_req != 2)
if (unified_syntax && inst.size_req == 0 && inst.operands[0].reg <= 7)
{
/* Always generate a 32-bit opcode;
section relaxation will shrink it later if possible. */
/* Defer to section relaxation. */
inst.relax = inst.instruction;
inst.instruction = THUMB_OP16 (inst.instruction);
inst.instruction |= inst.operands[0].reg << 4;
}
else if (unified_syntax && inst.size_req != 2)
{
/* Generate a 32-bit opcode. */
inst.instruction = THUMB_OP32 (inst.instruction);
inst.instruction |= inst.operands[0].reg << 8;
inst.reloc.type = BFD_RELOC_ARM_T32_ADD_PC12;
@ -6003,6 +6064,7 @@ do_t_adr (void)
}
else
{
/* Generate a 16-bit opcode. */
inst.instruction = THUMB_OP16 (inst.instruction);
inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */
@ -6266,29 +6328,37 @@ do_t_blx (void)
static void
do_t_branch (void)
{
if (unified_syntax && inst.size_req != 2)
int opcode;
if (inst.cond != COND_ALWAYS)
opcode = T_MNEM_bcond;
else
opcode = inst.instruction;
if (unified_syntax && inst.size_req == 4)
{
inst.instruction = THUMB_OP32(opcode);
if (inst.cond == COND_ALWAYS)
{
inst.instruction = 0xf000b000;
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25;
}
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25;
else
{
assert (inst.cond != 0xF);
inst.instruction = (inst.cond << 22) | 0xf0008000;
inst.instruction |= inst.cond << 22;
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH20;
}
}
else
{
inst.instruction = THUMB_OP16(opcode);
if (inst.cond == COND_ALWAYS)
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
else
{
inst.instruction = 0xd000 | (inst.cond << 8);
inst.instruction |= inst.cond << 8;
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
}
/* Allow section relaxation. */
if (unified_syntax && inst.size_req != 2)
inst.relax = opcode;
}
inst.reloc.pc_rel = 1;
@ -6562,21 +6632,67 @@ do_t_ldrexd (void)
static void
do_t_ldst (void)
{
unsigned long opcode;
int Rn;
opcode = inst.instruction;
if (unified_syntax)
{
/* Generation of 16-bit instructions for anything other than
Rd, [Rn, Ri] is deferred to section relaxation time. */
if (inst.operands[1].isreg && inst.operands[1].immisreg
if (inst.operands[1].isreg
&& !inst.operands[1].writeback
&& !inst.operands[1].shifted && !inst.operands[1].postind
&& !inst.operands[1].negative && inst.operands[0].reg <= 7
&& inst.operands[1].reg <= 7 && inst.operands[1].imm <= 7
&& inst.instruction <= 0xffff)
&& opcode <= 0xffff
&& inst.size_req != 4)
{
inst.instruction = THUMB_OP16 (inst.instruction);
goto op16;
/* Insn may have a 16-bit form. */
Rn = inst.operands[1].reg;
if (inst.operands[1].immisreg)
{
inst.instruction = THUMB_OP16 (opcode);
/* [Rn, Ri] */
if (Rn <= 7 && inst.operands[1].imm <= 7)
goto op16;
}
else if ((Rn <= 7 && opcode != T_MNEM_ldrsh
&& opcode != T_MNEM_ldrsb)
|| ((Rn == REG_PC || Rn == REG_SP) && opcode == T_MNEM_ldr)
|| (Rn == REG_SP && opcode == T_MNEM_str))
{
/* [Rn, #const] */
if (Rn > 7)
{
if (Rn == REG_PC)
{
if (inst.reloc.pc_rel)
opcode = T_MNEM_ldr_pc2;
else
opcode = T_MNEM_ldr_pc;
}
else
{
if (opcode == T_MNEM_ldr)
opcode = T_MNEM_ldr_sp;
else
opcode = T_MNEM_str_sp;
}
inst.instruction = inst.operands[0].reg << 8;
}
else
{
inst.instruction = inst.operands[0].reg;
inst.instruction |= inst.operands[1].reg << 3;
}
inst.instruction |= THUMB_OP16 (opcode);
if (inst.size_req == 2)
inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
else
inst.relax = opcode;
return;
}
}
inst.instruction = THUMB_OP32 (inst.instruction);
/* Definitely a 32-bit variant. */
inst.instruction = THUMB_OP32 (opcode);
inst.instruction |= inst.operands[0].reg << 12;
encode_thumb32_addr_mode (1, /*is_t=*/FALSE, /*is_d=*/FALSE);
return;
@ -6708,26 +6824,41 @@ do_t_mov_cmp (void)
{
int r0off = (inst.instruction == T_MNEM_mov
|| inst.instruction == T_MNEM_movs) ? 8 : 16;
unsigned long opcode;
bfd_boolean narrow;
bfd_boolean low_regs;
low_regs = (inst.operands[0].reg <= 7 && inst.operands[1].reg <= 7);
opcode = inst.instruction;
if (current_it_mask)
narrow = inst.instruction != T_MNEM_movs;
narrow = opcode != T_MNEM_movs;
else
narrow = inst.instruction != T_MNEM_movs || low_regs;
narrow = opcode != T_MNEM_movs || low_regs;
if (inst.size_req == 4
|| inst.operands[1].shifted)
narrow = FALSE;
if (!inst.operands[1].isreg)
{
/* For an immediate, we always generate a 32-bit opcode;
section relaxation will shrink it later if possible. */
inst.instruction = THUMB_OP32 (inst.instruction);
inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
inst.instruction |= inst.operands[0].reg << r0off;
inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
/* Immediate operand. */
if (current_it_mask == 0 && opcode == T_MNEM_mov)
narrow = 0;
if (low_regs && narrow)
{
inst.instruction = THUMB_OP16 (opcode);
inst.instruction |= inst.operands[0].reg << 8;
if (inst.size_req == 2)
inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
else
inst.relax = opcode;
}
else
{
inst.instruction = THUMB_OP32 (inst.instruction);
inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
inst.instruction |= inst.operands[0].reg << r0off;
inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
}
else if (!narrow)
{
@ -7501,6 +7632,46 @@ fix_new_arm (fragS * frag,
new_fix->tc_fix_data = thumb_mode;
}
/* Create a frg for an instruction requiring relaxation. */
static void
output_relax_insn (void)
{
char * to;
symbolS *sym;
int offset;
switch (inst.reloc.exp.X_op)
{
case O_symbol:
sym = inst.reloc.exp.X_add_symbol;
offset = inst.reloc.exp.X_add_number;
break;
case O_constant:
sym = NULL;
offset = inst.reloc.exp.X_add_number;
break;
default:
sym = make_expr_symbol (&inst.reloc.exp);
offset = 0;
break;
}
to = frag_var (rs_machine_dependent, INSN_SIZE, THUMB_SIZE,
inst.relax, sym, offset, NULL/*offset, opcode*/);
md_number_to_chars (to, inst.instruction, THUMB_SIZE);
#ifdef OBJ_ELF
dwarf2_emit_insn (INSN_SIZE);
#endif
}
/* Write a 32-bit thumb instruction to buf. */
static void
put_thumb32_insn (char * buf, unsigned long insn)
{
md_number_to_chars (buf, insn >> 16, THUMB_SIZE);
md_number_to_chars (buf + THUMB_SIZE, insn, THUMB_SIZE);
}
static void
output_inst (const char * str)
{
@ -7511,6 +7682,10 @@ output_inst (const char * str)
as_bad ("%s -- `%s'", inst.error, str);
return;
}
if (inst.relax) {
output_relax_insn();
return;
}
if (inst.size == 0)
return;
@ -7519,8 +7694,7 @@ output_inst (const char * str)
if (thumb_mode && (inst.size > THUMB_SIZE))
{
assert (inst.size == (2 * THUMB_SIZE));
md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE);
put_thumb32_insn (to, inst.instruction);
}
else if (inst.size > INSN_SIZE)
{
@ -7819,7 +7993,7 @@ md_assemble (char *str)
if (current_it_mask == 0x10)
current_it_mask = 0;
if (!inst.error)
if (!(inst.error || inst.relax))
{
assert (inst.instruction < 0xe800 || inst.instruction > 0xffff);
inst.size = (inst.instruction > 0xffff ? 4 : 2);
@ -8364,7 +8538,7 @@ static const struct asm_opcode insns[] =
tC3(ldmfd, 8900000, ldmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
TCE(swi, f000000, df00, 1, (EXPi), swi, t_swi),
TCE(b, a000000, e000, 1, (EXPr), branch, t_branch),
tCE(b, a000000, b, 1, (EXPr), branch, t_branch),
TCE(bl, b000000, f000f800, 1, (EXPr), branch, t_branch23),
/* Pseudo ops. */
@ -9600,12 +9774,344 @@ md_chars_to_number (char * buf, int n)
/* MD interface: Sections. */
/* Estimate the size of a frag before relaxing. Assume everything fits in
2 bytes. */
int
md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
md_estimate_size_before_relax (fragS * fragp,
segT segtype ATTRIBUTE_UNUSED)
{
as_fatal (_("md_estimate_size_before_relax\n"));
return 1;
fragp->fr_var = 2;
return 2;
}
/* Convert a machine dependent frag. */
void
md_convert_frag (bfd *abfd, segT asec ATTRIBUTE_UNUSED, fragS *fragp)
{
unsigned long insn;
unsigned long old_op;
char *buf;
expressionS exp;
fixS *fixp;
int reloc_type;
int pc_rel;
int opcode;
buf = fragp->fr_literal + fragp->fr_fix;
old_op = bfd_get_16(abfd, buf);
if (fragp->fr_symbol) {
exp.X_op = O_symbol;
exp.X_add_symbol = fragp->fr_symbol;
} else {
exp.X_op = O_constant;
}
exp.X_add_number = fragp->fr_offset;
opcode = fragp->fr_subtype;
switch (opcode)
{
case T_MNEM_ldr_pc:
case T_MNEM_ldr_pc2:
case T_MNEM_ldr_sp:
case T_MNEM_str_sp:
case T_MNEM_ldr:
case T_MNEM_ldrb:
case T_MNEM_ldrh:
case T_MNEM_str:
case T_MNEM_strb:
case T_MNEM_strh:
if (fragp->fr_var == 4)
{
insn = THUMB_OP32(opcode);
if ((old_op >> 12) == 4 || (old_op >> 12) == 9)
{
insn |= (old_op & 0x700) << 4;
}
else
{
insn |= (old_op & 7) << 12;
insn |= (old_op & 0x38) << 13;
}
insn |= 0x00000c00;
put_thumb32_insn (buf, insn);
reloc_type = BFD_RELOC_ARM_T32_OFFSET_IMM;
}
else
{
reloc_type = BFD_RELOC_ARM_THUMB_OFFSET;
}
pc_rel = (opcode == T_MNEM_ldr_pc2);
break;
case T_MNEM_adr:
if (fragp->fr_var == 4)
{
insn = THUMB_OP32 (opcode);
insn |= (old_op & 0xf0) << 4;
put_thumb32_insn (buf, insn);
reloc_type = BFD_RELOC_ARM_T32_ADD_PC12;
}
else
{
reloc_type = BFD_RELOC_ARM_THUMB_ADD;
exp.X_add_number -= 4;
}
pc_rel = 1;
break;
case T_MNEM_mov:
case T_MNEM_movs:
case T_MNEM_cmp:
case T_MNEM_cmn:
if (fragp->fr_var == 4)
{
int r0off = (opcode == T_MNEM_mov
|| opcode == T_MNEM_movs) ? 0 : 8;
insn = THUMB_OP32 (opcode);
insn = (insn & 0xe1ffffff) | 0x10000000;
insn |= (old_op & 0x700) << r0off;
put_thumb32_insn (buf, insn);
reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
else
{
reloc_type = BFD_RELOC_ARM_THUMB_IMM;
}
pc_rel = 0;
break;
case T_MNEM_b:
if (fragp->fr_var == 4)
{
insn = THUMB_OP32(opcode);
put_thumb32_insn (buf, insn);
reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH25;
}
else
reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH12;
pc_rel = 1;
break;
case T_MNEM_bcond:
if (fragp->fr_var == 4)
{
insn = THUMB_OP32(opcode);
insn |= (old_op & 0xf00) << 14;
put_thumb32_insn (buf, insn);
reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH20;
}
else
reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH9;
pc_rel = 1;
break;
case T_MNEM_add_sp:
case T_MNEM_add_pc:
case T_MNEM_inc_sp:
case T_MNEM_dec_sp:
if (fragp->fr_var == 4)
{
/* ??? Choose between add and addw. */
insn = THUMB_OP32 (opcode);
insn |= (old_op & 0xf0) << 4;
put_thumb32_insn (buf, insn);
reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
else
reloc_type = BFD_RELOC_ARM_THUMB_ADD;
pc_rel = 0;
break;
case T_MNEM_addi:
case T_MNEM_addis:
case T_MNEM_subi:
case T_MNEM_subis:
if (fragp->fr_var == 4)
{
insn = THUMB_OP32 (opcode);
insn |= (old_op & 0xf0) << 4;
insn |= (old_op & 0xf) << 16;
put_thumb32_insn (buf, insn);
reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
else
reloc_type = BFD_RELOC_ARM_THUMB_ADD;
pc_rel = 0;
break;
default:
abort();
}
fixp = fix_new_exp (fragp, fragp->fr_fix, fragp->fr_var, &exp, pc_rel,
reloc_type);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
fragp->fr_fix += fragp->fr_var;
}
/* Return the size of a relaxable immediate operand instruction.
SHIFT and SIZE specify the form of the allowable immediate. */
static int
relax_immediate (fragS *fragp, int size, int shift)
{
offsetT offset;
offsetT mask;
offsetT low;
/* ??? Should be able to do better than this. */
if (fragp->fr_symbol)
return 4;
low = (1 << shift) - 1;
mask = (1 << (shift + size)) - (1 << shift);
offset = fragp->fr_offset;
/* Force misaligned offsets to 32-bit variant. */
if (offset & low)
return -4;
if (offset & ~mask)
return 4;
return 2;
}
/* Return the size of a relaxable adr pseudo-instruction or PC-relative
load. */
static int
relax_adr (fragS *fragp, asection *sec)
{
addressT addr;
offsetT val;
/* Assume worst case for symbols not known to be in the same section. */
if (!S_IS_DEFINED(fragp->fr_symbol)
|| sec != S_GET_SEGMENT (fragp->fr_symbol))
return 4;
val = S_GET_VALUE(fragp->fr_symbol) + fragp->fr_offset;
addr = fragp->fr_address + fragp->fr_fix;
addr = (addr + 4) & ~3;
/* Fix the insn as the 4-byte version if the target address is not
sufficiently aligned. This is prevents an infinite loop when two
instructions have contradictory range/alignment requirements. */
if (val & 3)
return -4;
val -= addr;
if (val < 0 || val > 1020)
return 4;
return 2;
}
/* Return the size of a relaxable add/sub immediate instruction. */
static int
relax_addsub (fragS *fragp, asection *sec)
{
char *buf;
int op;
buf = fragp->fr_literal + fragp->fr_fix;
op = bfd_get_16(sec->owner, buf);
if ((op & 0xf) == ((op >> 4) & 0xf))
return relax_immediate (fragp, 8, 0);
else
return relax_immediate (fragp, 3, 0);
}
/* Return the size of a relaxable branch instruction. BITS is the
size of the offset field in the narrow instruction. */
static int
relax_branch (fragS *fragp, asection *sec, int bits)
{
addressT addr;
offsetT val;
offsetT limit;
/* Assume worst case for symbols not known to be in the same section. */
if (!S_IS_DEFINED(fragp->fr_symbol)
|| sec != S_GET_SEGMENT (fragp->fr_symbol))
return 4;
val = S_GET_VALUE(fragp->fr_symbol) + fragp->fr_offset;
addr = fragp->fr_address + fragp->fr_fix + 4;
val -= addr;
/* Offset is a signed value *2 */
limit = 1 << bits;
if (val >= limit || val < -limit)
return 4;
return 2;
}
/* Relax a machine dependent frag. This returns the amount by which
the current size of the frag should change. */
int
arm_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED)
{
int oldsize;
int newsize;
oldsize = fragp->fr_var;
switch (fragp->fr_subtype)
{
case T_MNEM_ldr_pc2:
newsize = relax_adr(fragp, sec);
break;
case T_MNEM_ldr_pc:
case T_MNEM_ldr_sp:
case T_MNEM_str_sp:
newsize = relax_immediate(fragp, 8, 2);
break;
case T_MNEM_ldr:
case T_MNEM_str:
newsize = relax_immediate(fragp, 5, 2);
break;
case T_MNEM_ldrh:
case T_MNEM_strh:
newsize = relax_immediate(fragp, 5, 1);
break;
case T_MNEM_ldrb:
case T_MNEM_strb:
newsize = relax_immediate(fragp, 5, 0);
break;
case T_MNEM_adr:
newsize = relax_adr(fragp, sec);
break;
case T_MNEM_mov:
case T_MNEM_movs:
case T_MNEM_cmp:
case T_MNEM_cmn:
newsize = relax_immediate(fragp, 8, 0);
break;
case T_MNEM_b:
newsize = relax_branch(fragp, sec, 11);
break;
case T_MNEM_bcond:
newsize = relax_branch(fragp, sec, 8);
break;
case T_MNEM_add_sp:
case T_MNEM_add_pc:
newsize = relax_immediate (fragp, 8, 2);
break;
case T_MNEM_inc_sp:
case T_MNEM_dec_sp:
newsize = relax_immediate (fragp, 7, 2);
break;
case T_MNEM_addi:
case T_MNEM_addis:
case T_MNEM_subi:
case T_MNEM_subis:
newsize = relax_addsub (fragp, sec);
break;
default:
abort();
}
if (newsize < 0)
{
fragp->fr_var = -newsize;
md_convert_frag (sec->owner, sec, fragp);
frag_wane(fragp);
return -(newsize + oldsize);
}
fragp->fr_var = newsize;
return newsize - oldsize;
}
/* Round up a section size to the appropriate boundary. */
@ -11476,7 +11982,10 @@ arm_force_relocation (struct fix * fixp)
/* Resolve these relocations even if the symbol is extern or weak. */
if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE
|| fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM
|| fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
|| fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_IMM12
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12)
return 0;
return generic_force_reloc (fixp);

View File

@ -79,7 +79,9 @@ struct fix;
#define TC_FORCE_RELOCATION(FIX) arm_force_relocation (FIX)
#define md_convert_frag(b, s, f) { as_fatal (_("arm convert_frag\n")); }
#define md_relax_frag(segment, fragp, stretch) \
arm_relax_frag(segment, fragp, stretch)
extern int arm_relax_frag (asection *, struct frag *, long);
#define md_cleanup() arm_cleanup ()

View File

@ -1,3 +1,11 @@
2005-09-06 Paul Brook <paul@codesourcery.com>
* gas/arm/thumb2_relax.d: New test.
* gas/arm/thumb2_relax.s: New test.
* gas/arm/thumb32.d: Adjust expected results to include relaxation.
* gas/arm/thumb32.s: Tweak for better coverage of relaxable
instructions. Remove load/store tests.
2005-09-02 Paul Brook <paul@codesourcery.com>
* gas/arm/arm3-bad.s: New test.

View File

@ -0,0 +1,155 @@
# as: -march=armv6kt2
# objdump: -dr --prefix-addresses --show-raw-insn
.*: +file format .*arm.*
Disassembly of section .text:
0+000 <[^>]+> 7829 ldrb r1, \[r5, #0\]
0+002 <[^>]+> f895 1023 ldrb.w r1, \[r5, #35\]
0+006 <[^>]+> 7fe9 ldrb r1, \[r5, #31\]
0+008 <[^>]+> f895 101f ldrb.w r1, \[r5, #31\]
0+00c <[^>]+> f815 1c1f ldrb.w r1, \[r5, #-31\]
0+010 <[^>]+> f815 1b1f ldrb.w r1, \[r5\], #31
0+014 <[^>]+> f815 1b1f ldrb.w r1, \[r5\], #31
0+018 <[^>]+> f815 1f1f ldrb.w r1, \[r5, #31\]!
0+01c <[^>]+> f815 1d1f ldrb.w r1, \[r5, #-31\]!
0+020 <[^>]+> 5d29 ldrb r1, \[r5, r4\]
0+022 <[^>]+> f819 100c ldrb.w r1, \[r9, ip\]
0+026 <[^>]+> f89f 1014 ldrb.w r1, \[pc, #20\] ; 0+03c <[^>]+>
0+02a <[^>]+> f89f 1010 ldrb.w r1, \[pc, #16\] ; 0+03c <[^>]+>
0+02e <[^>]+> f89f 800c ldrb.w r8, \[pc, #12\] ; 0+03c <[^>]+>
0+032 <[^>]+> f89f 100a ldrb.w r1, \[pc, #10\] ; 0+03e <[^>]+>
0+036 <[^>]+> f81f 1038 ldrb.w r1, \[pc, #-56\] ; 0+000 <[^>]+>
0+03a <[^>]+> 0000 lsls r0, r0, #0
0+03c <[^>]+> bf00 nop
0+03e <[^>]+> f995 1000 ldrsb.w r1, \[r5\]
0+042 <[^>]+> f995 1023 ldrsb.w r1, \[r5, #35\]
0+046 <[^>]+> f995 101f ldrsb.w r1, \[r5, #31\]
0+04a <[^>]+> f995 101f ldrsb.w r1, \[r5, #31\]
0+04e <[^>]+> f915 1c1f ldrsb.w r1, \[r5, #-31\]
0+052 <[^>]+> f915 1b1f ldrsb.w r1, \[r5\], #31
0+056 <[^>]+> f915 1b1f ldrsb.w r1, \[r5\], #31
0+05a <[^>]+> f915 1f1f ldrsb.w r1, \[r5, #31\]!
0+05e <[^>]+> f915 1d1f ldrsb.w r1, \[r5, #-31\]!
0+062 <[^>]+> 5729 ldrsb r1, \[r5, r4\]
0+064 <[^>]+> f919 100c ldrsb.w r1, \[r9, ip\]
0+068 <[^>]+> f99f 1010 ldrsb.w r1, \[pc, #16\] ; 0+07c <[^>]+>
0+06c <[^>]+> f99f 100c ldrsb.w r1, \[pc, #12\] ; 0+07c <[^>]+>
0+070 <[^>]+> f99f 8008 ldrsb.w r8, \[pc, #8\] ; 0+07c <[^>]+>
0+074 <[^>]+> f99f 1006 ldrsb.w r1, \[pc, #6\] ; 0+07e <[^>]+>
0+078 <[^>]+> f91f 103e ldrsb.w r1, \[pc, #-62\] ; 0+03e <[^>]+>
0+07c <[^>]+> bf00 nop
0+07e <[^>]+> 8829 ldrh r1, \[r5, #0\]
0+080 <[^>]+> f8b5 1042 ldrh.w r1, \[r5, #66\]
0+084 <[^>]+> 8fe9 ldrh r1, \[r5, #62\]
0+086 <[^>]+> f8b5 103e ldrh.w r1, \[r5, #62\]
0+08a <[^>]+> f835 1c3e ldrh.w r1, \[r5, #-62\]
0+08e <[^>]+> f835 1b3e ldrh.w r1, \[r5\], #62
0+092 <[^>]+> f835 1b3e ldrh.w r1, \[r5\], #62
0+096 <[^>]+> f835 1f3e ldrh.w r1, \[r5, #62\]!
0+09a <[^>]+> f835 1d3e ldrh.w r1, \[r5, #-62\]!
0+09e <[^>]+> 5b29 ldrh r1, \[r5, r4\]
0+0a0 <[^>]+> f839 100c ldrh.w r1, \[r9, ip\]
0+0a4 <[^>]+> f8bf 1010 ldrh.w r1, \[pc, #16\] ; 0+0b8 <[^>]+>
0+0a8 <[^>]+> f8bf 100c ldrh.w r1, \[pc, #12\] ; 0+0b8 <[^>]+>
0+0ac <[^>]+> f8bf 8008 ldrh.w r8, \[pc, #8\] ; 0+0b8 <[^>]+>
0+0b0 <[^>]+> f8bf 1006 ldrh.w r1, \[pc, #6\] ; 0+0ba <[^>]+>
0+0b4 <[^>]+> f83f 103a ldrh.w r1, \[pc, #-58\] ; 0+07e <[^>]+>
0+0b8 <[^>]+> bf00 nop
0+0ba <[^>]+> f9b5 1000 ldrsh.w r1, \[r5\]
0+0be <[^>]+> f9b5 1042 ldrsh.w r1, \[r5, #66\]
0+0c2 <[^>]+> f9b5 103e ldrsh.w r1, \[r5, #62\]
0+0c6 <[^>]+> f9b5 103e ldrsh.w r1, \[r5, #62\]
0+0ca <[^>]+> f935 1c3e ldrsh.w r1, \[r5, #-62\]
0+0ce <[^>]+> f935 1b3e ldrsh.w r1, \[r5\], #62
0+0d2 <[^>]+> f935 1b3e ldrsh.w r1, \[r5\], #62
0+0d6 <[^>]+> f935 1f3e ldrsh.w r1, \[r5, #62\]!
0+0da <[^>]+> f935 1d3e ldrsh.w r1, \[r5, #-62\]!
0+0de <[^>]+> 5f29 ldrsh r1, \[r5, r4\]
0+0e0 <[^>]+> f939 100c ldrsh.w r1, \[r9, ip\]
0+0e4 <[^>]+> f9bf 1010 ldrsh.w r1, \[pc, #16\] ; 0+0f8 <[^>]+>
0+0e8 <[^>]+> f9bf 100c ldrsh.w r1, \[pc, #12\] ; 0+0f8 <[^>]+>
0+0ec <[^>]+> f9bf 8008 ldrsh.w r8, \[pc, #8\] ; 0+0f8 <[^>]+>
0+0f0 <[^>]+> f9bf 1006 ldrsh.w r1, \[pc, #6\] ; 0+0fa <[^>]+>
0+0f4 <[^>]+> f93f 103e ldrsh.w r1, \[pc, #-62\] ; 0+0ba <[^>]+>
0+0f8 <[^>]+> bf00 nop
0+0fa <[^>]+> 6829 ldr r1, \[r5, #0\]
0+0fc <[^>]+> f8d5 1080 ldr.w r1, \[r5, #128\]
0+100 <[^>]+> 6fe9 ldr r1, \[r5, #124\]
0+102 <[^>]+> f8d5 107c ldr.w r1, \[r5, #124\]
0+106 <[^>]+> f855 1c7c ldr.w r1, \[r5, #-124\]
0+10a <[^>]+> f855 1b7c ldr.w r1, \[r5\], #124
0+10e <[^>]+> f855 1b7c ldr.w r1, \[r5\], #124
0+112 <[^>]+> f855 1f7c ldr.w r1, \[r5, #124\]!
0+116 <[^>]+> f855 1d7c ldr.w r1, \[r5, #-124\]!
0+11a <[^>]+> 5929 ldr r1, \[r5, r4\]
0+11c <[^>]+> f859 100c ldr.w r1, \[r9, ip\]
0+120 <[^>]+> 4904 ldr r1, \[pc, #16\] \(0+134 <[^>]+>\)
0+122 <[^>]+> f8df 1010 ldr.w r1, \[pc, #16\] ; 0+134 <[^>]+>
0+126 <[^>]+> f8df 800c ldr.w r8, \[pc, #12\] ; 0+134 <[^>]+>
0+12a <[^>]+> f8df 100a ldr.w r1, \[pc, #10\] ; 0+136 <[^>]+>
0+12e <[^>]+> f85f 1036 ldr.w r1, \[pc, #-54\] ; 0+0fa <[^>]+>
0+132 <[^>]+> 0000 lsls r0, r0, #0
0+134 <[^>]+> bf00 nop
0+136 <[^>]+> 7029 strb r1, \[r5, #0\]
0+138 <[^>]+> f885 1023 strb.w r1, \[r5, #35\]
0+13c <[^>]+> 77e9 strb r1, \[r5, #31\]
0+13e <[^>]+> f885 101f strb.w r1, \[r5, #31\]
0+142 <[^>]+> f805 1c1f strb.w r1, \[r5, #-31\]
0+146 <[^>]+> f805 1b1f strb.w r1, \[r5\], #31
0+14a <[^>]+> f805 1b1f strb.w r1, \[r5\], #31
0+14e <[^>]+> f805 1f1f strb.w r1, \[r5, #31\]!
0+152 <[^>]+> f805 1d1f strb.w r1, \[r5, #-31\]!
0+156 <[^>]+> 5529 strb r1, \[r5, r4\]
0+158 <[^>]+> f809 100c strb.w r1, \[r9, ip\]
0+15c <[^>]+> f88f 1010 strb.w r1, \[pc, #16\] ; 0+170 <[^>]+>
0+160 <[^>]+> f88f 100c strb.w r1, \[pc, #12\] ; 0+170 <[^>]+>
0+164 <[^>]+> f88f 8008 strb.w r8, \[pc, #8\] ; 0+170 <[^>]+>
0+168 <[^>]+> f88f 1006 strb.w r1, \[pc, #6\] ; 0+172 <[^>]+>
0+16c <[^>]+> f80f 103a strb.w r1, \[pc, #-58\] ; 0+136 <[^>]+>
0+170 <[^>]+> bf00 nop
0+172 <[^>]+> 8029 strh r1, \[r5, #0\]
0+174 <[^>]+> f8a5 1042 strh.w r1, \[r5, #66\]
0+178 <[^>]+> 87e9 strh r1, \[r5, #62\]
0+17a <[^>]+> f8a5 103e strh.w r1, \[r5, #62\]
0+17e <[^>]+> f825 1c3e strh.w r1, \[r5, #-62\]
0+182 <[^>]+> f825 1b3e strh.w r1, \[r5\], #62
0+186 <[^>]+> f825 1b3e strh.w r1, \[r5\], #62
0+18a <[^>]+> f825 1f3e strh.w r1, \[r5, #62\]!
0+18e <[^>]+> f825 1d3e strh.w r1, \[r5, #-62\]!
0+192 <[^>]+> 5329 strh r1, \[r5, r4\]
0+194 <[^>]+> f829 100c strh.w r1, \[r9, ip\]
0+198 <[^>]+> f8af 1010 strh.w r1, \[pc, #16\] ; 0+1ac <[^>]+>
0+19c <[^>]+> f8af 100c strh.w r1, \[pc, #12\] ; 0+1ac <[^>]+>
0+1a0 <[^>]+> f8af 8008 strh.w r8, \[pc, #8\] ; 0+1ac <[^>]+>
0+1a4 <[^>]+> f8af 1006 strh.w r1, \[pc, #6\] ; 0+1ae <[^>]+>
0+1a8 <[^>]+> f82f 103a strh.w r1, \[pc, #-58\] ; 0+172 <[^>]+>
0+1ac <[^>]+> bf00 nop
0+1ae <[^>]+> 6029 str r1, \[r5, #0\]
0+1b0 <[^>]+> f8c5 1080 str.w r1, \[r5, #128\]
0+1b4 <[^>]+> 67e9 str r1, \[r5, #124\]
0+1b6 <[^>]+> f8c5 107c str.w r1, \[r5, #124\]
0+1ba <[^>]+> f845 1c7c str.w r1, \[r5, #-124\]
0+1be <[^>]+> f845 1b7c str.w r1, \[r5\], #124
0+1c2 <[^>]+> f845 1b7c str.w r1, \[r5\], #124
0+1c6 <[^>]+> f845 1f7c str.w r1, \[r5, #124\]!
0+1ca <[^>]+> f845 1d7c str.w r1, \[r5, #-124\]!
0+1ce <[^>]+> 5129 str r1, \[r5, r4\]
0+1d0 <[^>]+> f849 100c str.w r1, \[r9, ip\]
0+1d4 <[^>]+> f8cf 1010 str.w r1, \[pc, #16\] ; 0+1e8 <[^>]+>
0+1d8 <[^>]+> f8cf 100c str.w r1, \[pc, #12\] ; 0+1e8 <[^>]+>
0+1dc <[^>]+> f8cf 8008 str.w r8, \[pc, #8\] ; 0+1e8 <[^>]+>
0+1e0 <[^>]+> f8cf 1006 str.w r1, \[pc, #6\] ; 0+1ea <[^>]+>
0+1e4 <[^>]+> f84f 103a str.w r1, \[pc, #-58\] ; 0+1ae <[^>]+>
0+1e8 <[^>]+> bf00 nop
0+1ea <[^>]+> a104 add r1, pc, #16 \(adr r1,0+1fc <[^>]+>\)
0+1ec <[^>]+> f20f 010c addw r1, pc, #12 ; 0xc
0+1f0 <[^>]+> f20f 0808 addw r8, pc, #8 ; 0x8
0+1f4 <[^>]+> f20f 0106 addw r1, pc, #6 ; 0x6
0+1f8 <[^>]+> f2af 0112 subw r1, pc, #18 ; 0x12
0+1fc <[^>]+> bf00 nop
0+1fe <[^>]+> bf00 nop
0+200 <[^>]+> f20f 0104 addw r1, pc, #4 ; 0x4
0+204 <[^>]+> f20f 0102 addw r1, pc, #2 ; 0x2
0+208 <[^>]+> bf00 nop
0+20a <[^>]+> bf00 nop

View File

@ -0,0 +1,62 @@
.text
.thumb
.syntax unified
thumb2_relax:
.macro ls op w=".w"
1:
\op r1, [r5]
\op r1, [r5, #(far_\op + 4)]
\op r1, [r5, #far_\op]
\op\w r1, [r5, #far_\op]
\op r1, [r5, #-far_\op]
\op r1, [r5], #far_\op
\op r1, [r5], #far_\op
\op r1, [r5, #far_\op]!
\op r1, [r5, #-far_\op]!
\op r1, [r5, r4]
\op r1, [r9, ip]
\op r1, 1f
\op\w r1, 1f
\op r8, 1f
\op r1, 2f
\op r1, 1b
.align 2
1:
nop
2:
.endm
.equ far_ldrb, 0x1f
.equ far_ldrsb, 0x1f
.equ far_ldrh, 0x3e
.equ far_ldrsh, 0x3e
.equ far_ldr, 0x7c
.equ far_strb, 0x1f
.equ far_strh, 0x3e
.equ far_str, 0x7c
ls ldrb
ls ldrsb
ls ldrh
ls ldrsh
ls ldr
ls strb
ls strh
ls str
.purgem ls
1:
adr r1, 1f
adr.w r1, 1f
adr r8, 1f
adr r1, 2f
adr r1, 1b
.align 2
1:
nop
2:
nop
@ Relaxation with conflicting alignment requirements.
adr r1, 1f
adr r1, 2f
1:
nop
2:
nop

File diff suppressed because it is too large Load Diff

View File

@ -42,10 +42,11 @@ encode_thumb32_immediate:
orr r0, r1, #0xa5 << 1
add_sub:
adds r0, r0, #0 @ format 1
@ Should be format 1, Some have equivalent format 2 encodings
adds r0, r0, #0
adds r5, r0, #0
adds r0, r5, #0
adds r0, r0, #5
adds r0, r2, #5
adds r0, #129 @ format 2
adds r0, r0, #129
@ -83,6 +84,10 @@ add_sub:
add.w r9, r0, #0
add.w r0, r9, #0
add.w r0, r0, #129
adds r5, r3, #0x10000
add r0, sp, #1
add r9, sp, #0
add.w sp, sp, #4
add.w r0, r0, r0 @ T32 format 2
adds.w r0, r0, r0
@ -102,7 +107,7 @@ add_sub:
subs r0, r0, #0 @ format 1
subs r5, r0, #0
subs r0, r5, #0
subs r0, r0, #5
subs r0, r2, #5
subs r0, r0, #129
subs r5, #8
@ -118,6 +123,11 @@ add_sub:
subs r8, r0 @ T32 format 2
subs r0, r8
subs r0, #260 @ T32 format 1
subs.w r1, r2, #4
subs r5, r3, #0x10000
sub r1, sp, #4
sub r9, sp, #0
sub.w sp, sp, #4
arit3:
.macro arit3 op ops opw opsw
@ -197,27 +207,27 @@ branches:
@ bl, blx have no short form.
.balign 4
1:
bra beq
bra bne
bra bcs
bra bhs
bra bcc
bra bul
bra blo
bra bmi
bra bpl
bra bvs
bra bvc
bra bhi
bra bls
bra bvc
bra bhi
bra bls
bra bge
bra blt
bra bgt
bra ble
bra b
bra beq.w
bra bne.w
bra bcs.w
bra bhs.w
bra bcc.w
bra bul.w
bra blo.w
bra bmi.w
bra bpl.w
bra bvs.w
bra bvc.w
bra bhi.w
bra bls.w
bra bvc.w
bra bhi.w
bra bls.w
bra bge.w
bra blt.w
bra bgt.w
bra ble.w
bra b.w
bra bl
bra blx
.balign 4
@ -347,30 +357,7 @@ it:
it3 ne eq e e e
ldst:
.macro ls op
\op r1, [r5]
\op r1, [r5, #0x330]
\op r1, [r5, #-0x30]
\op r1, [r5], #0x30
\op r1, [r5], #-0x30
\op r1, [r5, #0x30]!
\op r1, [r5, #-0x30]!
\op r1, [r5, r4]
\op r1, [r9, ip]
\op r1, 1f
\op r1, 1b
.endm
1:
ls ldrb
ls ldrsb
ls ldrh
ls ldrsh
ls ldr
1:
ls strb
ls strh
ls str
pld [r5]
pld [r5, #0x330]
pld [r5, #-0x30]
@ -402,8 +389,6 @@ ldst:
ldrt r1, [r5]
ldrt r1, [r5, #0x30]
.purgem ls
ldxstx:
ldrexb r1, [r4]
ldrexh r1, [r4]
@ -454,8 +439,8 @@ tst_teq_cmp_cmn_mov_mvn:
\opw r0, r0
\ops r9, r0
\opsw r0, r9
\op r0, #129
\op r5, #129
\opw r0, #129
\opw r5, #129
.endm
mt tst tsts tst.w tsts.w