📄 thumb.c
字号:
a computed memory address. The computed address may involve a register which is overwritten by the load. */char *thumb_load_double_from_address (operands) rtx * operands;{ rtx addr; rtx base; rtx offset; rtx arg1; rtx arg2; if (GET_CODE (operands[0]) != REG) fatal ("thumb_load_double_from_address: destination is not a register"); if (GET_CODE (operands[1]) != MEM) fatal ("thumb_load_double_from_address: source is not a computed memory address"); /* Get the memory address. */ addr = XEXP (operands[1], 0); /* Work out how the memory address is computed. */ switch (GET_CODE (addr)) { case REG: operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4)); if (REGNO (operands[0]) == REGNO (addr)) { output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); } else { output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); } break; case CONST: /* Compute <address> + 4 for the high order load. */ operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4)); output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); break; case PLUS: arg1 = XEXP (addr, 0); arg2 = XEXP (addr, 1); if (CONSTANT_P (arg1)) base = arg2, offset = arg1; else base = arg1, offset = arg2; if (GET_CODE (base) != REG) fatal ("thumb_load_double_from_address: base is not a register"); /* Catch the case of <address> = <reg> + <reg> */ if (GET_CODE (offset) == REG) { int reg_offset = REGNO (offset); int reg_base = REGNO (base); int reg_dest = REGNO (operands[0]); /* Add the base and offset registers together into the higher destination register. */ fprintf (asm_out_file, "\tadd\t%s, %s, %s\t\t%s created by thumb_load_double_from_address", reg_names[ reg_dest + 1 ], reg_names[ reg_base ], reg_names[ reg_offset ], ASM_COMMENT_START); /* Load the lower destination register from the address in the higher destination register. */ fprintf (asm_out_file, "\tldr\t%s, [%s, #0]\t\t%s created by thumb_load_double_from_address", reg_names[ reg_dest ], reg_names[ reg_dest + 1], ASM_COMMENT_START); /* Load the higher destination register from its own address plus 4. */ fprintf (asm_out_file, "\tldr\t%s, [%s, #4]\t\t%s created by thumb_load_double_from_address", reg_names[ reg_dest + 1 ], reg_names[ reg_dest + 1 ], ASM_COMMENT_START); } else { /* Compute <address> + 4 for the high order load. */ operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4)); /* If the computed address is held in the low order register then load the high order register first, otherwise always load the low order register first. */ if (REGNO (operands[0]) == REGNO (base)) { output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); } else { output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); } } break; case LABEL_REF: /* With no registers to worry about we can just load the value directly. */ operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4)); output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); break; default: debug_rtx (operands[1]); fatal ("thumb_load_double_from_address: Unhandled address calculation"); break; } return "";}char *output_move_mem_multiple (n, operands) int n; rtx *operands;{ rtx tmp; switch (n) { case 2: if (REGNO (operands[2]) > REGNO (operands[3])) { tmp = operands[2]; operands[2] = operands[3]; operands[3] = tmp; } output_asm_insn ("ldmia\t%1!, {%2, %3}", operands); output_asm_insn ("stmia\t%0!, {%2, %3}", operands); break; case 3: if (REGNO (operands[2]) > REGNO (operands[3])) { tmp = operands[2]; operands[2] = operands[3]; operands[3] = tmp; } if (REGNO (operands[3]) > REGNO (operands[4])) { tmp = operands[3]; operands[3] = operands[4]; operands[4] = tmp; } if (REGNO (operands[2]) > REGNO (operands[3])) { tmp = operands[2]; operands[2] = operands[3]; operands[3] = tmp; } output_asm_insn ("ldmia\t%1!, {%2, %3, %4}", operands); output_asm_insn ("stmia\t%0!, {%2, %3, %4}", operands); break; default: abort (); } return "";} intthumb_epilogue_size (){ return 42; /* The answer to .... */}static char *conds[] ={ "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le"};static char *thumb_condition_code (x, invert) rtx x; int invert;{ int val; switch (GET_CODE (x)) { case EQ: val = 0; break; case NE: val = 1; break; case GEU: val = 2; break; case LTU: val = 3; break; case GTU: val = 8; break; case LEU: val = 9; break; case GE: val = 10; break; case LT: val = 11; break; case GT: val = 12; break; case LE: val = 13; break; default: abort (); } return conds[val ^ invert];}voidthumb_print_operand (f, x, code) FILE *f; rtx x; int code;{ if (code) { switch (code) { case '@': fputs (ASM_COMMENT_START, f); return; case '_': fputs (user_label_prefix, f); return; case 'D': if (x) fputs (thumb_condition_code (x, 1), f); return; case 'd': if (x) fputs (thumb_condition_code (x, 0), f); return; /* An explanation of the 'Q', 'R' and 'H' register operands: In a pair of registers containing a DI or DF value the 'Q' operand returns the register number of the register containing the least signficant part of the value. The 'R' operand returns the register number of the register containing the most significant part of the value. The 'H' operand returns the higher of the two register numbers. On a run where WORDS_BIG_ENDIAN is true the 'H' operand is the same as the 'Q' operand, since the most signficant part of the value is held in the lower number register. The reverse is true on systems where WORDS_BIG_ENDIAN is false. The purpose of these operands is to distinguish between cases where the endian-ness of the values is important (for example when they are added together), and cases where the endian-ness is irrelevant, but the order of register operations is important. For example when loading a value from memory into a register pair, the endian-ness does not matter. Provided that the value from the lower memory address is put into the lower numbered register, and the value from the higher address is put into the higher numbered register, the load will work regardless of whether the value being loaded is big-wordian or little-wordian. The order of the two register loads can matter however, if the address of the memory location is actually held in one of the registers being overwritten by the load. */ case 'Q': if (REGNO (x) > 15) abort (); fputs (reg_names[REGNO (x) + (WORDS_BIG_ENDIAN ? 1 : 0)], f); return; case 'R': if (REGNO (x) > 15) abort (); fputs (reg_names[REGNO (x) + (WORDS_BIG_ENDIAN ? 0 : 1)], f); return; case 'H': if (REGNO (x) > 15) abort (); fputs (reg_names[REGNO (x) + 1], f); return; default: abort (); } } if (GET_CODE (x) == REG) fputs (reg_names[REGNO (x)], f); else if (GET_CODE (x) == MEM) output_address (XEXP (x, 0)); else if (GET_CODE (x) == CONST_INT) { fputc ('#', f); output_addr_const (f, x); } else abort ();}#ifdef AOF_ASSEMBLERint arm_text_section_count = 1;char *aof_text_section (in_readonly) int in_readonly;{ static char buf[100]; if (in_readonly) return ""; sprintf (buf, "\tCODE16\n\tAREA |C$$code%d|, CODE, READONLY", arm_text_section_count++); return buf;}static int arm_data_section_count = 1;char *aof_data_section (){ static char buf[100]; sprintf (buf, "\tAREA |C$$data%d|, DATA", arm_data_section_count++); return buf;}/* The AOF thumb assembler is religiously strict about declarations of imported and exported symbols, so that it is impossible to declare a function as imported near the begining of the file, and then to export it later on. It is, however, possible to delay the decision until all the functions in the file have been compiled. To get around this, we maintain a list of the imports and exports, and delete from it any that are subsequently defined. At the end of compilation we spit the remainder of the list out before the END directive. */struct import{ struct import *next; char *name;};static struct import *imports_list = NULL;voidthumb_aof_add_import (name) char *name;{ struct import *new; for (new = imports_list; new; new = new->next) if (new->name == name) return; new = (struct import *) xmalloc (sizeof (struct import)); new->next = imports_list; imports_list = new; new->name = name;}voidthumb_aof_delete_import (name) char *name;{ struct import **old; for (old = &imports_list; *old; old = & (*old)->next) { if ((*old)->name == name) { *old = (*old)->next; return; } }}voidthumb_aof_dump_imports (f) FILE *f;{ while (imports_list) { fprintf (f, "\tIMPORT\t"); assemble_name (f, imports_list->name); fputc ('\n', f); imports_list = imports_list->next; }}#endif/* Decide whether a type should be returned in memory (true) or in a register (false). This is called by the macro RETURN_IN_MEMORY. */intthumb_return_in_memory (type) tree type;{ if (! AGGREGATE_TYPE_P (type)) { /* All simple types are returned in registers. */ return 0; } else if (int_size_in_bytes (type) > 4) { /* All structures/unions bigger than one word are returned in memory. */ return 1; } else if (TREE_CODE (type) == RECORD_TYPE) { tree field; /* For a struct the APCS says that we must return in a register if every addressable element has an offset of zero. For practical purposes this means that the structure can have at most one non- bit-field element and that this element must be the first one in the structure. */ /* Find the first field, ignoring non FIELD_DECL things which will have been created by C++. */ for (field = TYPE_FIELDS (type); field && TREE_CODE (field) != FIELD_DECL; field = TREE_CHAIN (field)) continue; if (field == NULL) return 0; /* An empty structure. Allowed by an extension to ANSI C. */ /* Now check the remaining fields, if any. */ for (field = TREE_CHAIN (field); field; field = TREE_CHAIN (field)) { if (TREE_CODE (field) != FIELD_DECL) continue; if (! DECL_BIT_FIELD_TYPE (field)) return 1; } return 0; } else if (TREE_CODE (type) == UNION_TYPE) { tree field; /* Unions can be returned in registers if every element is integral, or can be returned in an integer register. */ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) { if (TREE_CODE (field) != FIELD_DECL) continue; if (RETURN_IN_MEMORY (TREE_TYPE (field))) return 1; } return 0; } /* XXX Not sure what should be done for other aggregates, so put them in memory. */ return 1;}voidthumb_override_options (){ if (structure_size_string != NULL) { int size = strtol (structure_size_string, NULL, 0); if (size == 8 || size == 32) arm_structure_size_boundary = size; else warning ("Structure size boundary can only be set to 8 or 32"); } if (flag_pic) { warning ("Position independent code not supported. Ignored"); flag_pic = 0; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -