📄 v850.c
字号:
cc_status.value1 = 0; break; case CC_SET_ZN: /* Insn sets the Z,N flags of CC to recog_data.operand[0]. V,C is in an unusable state. */ CC_STATUS_INIT; cc_status.flags |= CC_OVERFLOW_UNUSABLE | CC_NO_CARRY; cc_status.value1 = recog_data.operand[0]; break; case CC_SET_ZNV: /* Insn sets the Z,N,V flags of CC to recog_data.operand[0]. C is in an unusable state. */ CC_STATUS_INIT; cc_status.flags |= CC_NO_CARRY; cc_status.value1 = recog_data.operand[0]; break; case CC_COMPARE: /* The insn is a compare instruction. */ CC_STATUS_INIT; cc_status.value1 = SET_SRC (body); break; case CC_CLOBBER: /* Insn doesn't leave CC in a usable state. */ CC_STATUS_INIT; break; }}/* Retrieve the data area that has been chosen for the given decl. */v850_data_areav850_get_data_area (decl) tree decl;{ if (lookup_attribute ("sda", DECL_ATTRIBUTES (decl)) != NULL_TREE) return DATA_AREA_SDA; if (lookup_attribute ("tda", DECL_ATTRIBUTES (decl)) != NULL_TREE) return DATA_AREA_TDA; if (lookup_attribute ("zda", DECL_ATTRIBUTES (decl)) != NULL_TREE) return DATA_AREA_ZDA; return DATA_AREA_NORMAL;}/* Store the indicated data area in the decl's attributes. */static voidv850_set_data_area (decl, data_area) tree decl; v850_data_area data_area;{ tree name; switch (data_area) { case DATA_AREA_SDA: name = get_identifier ("sda"); break; case DATA_AREA_TDA: name = get_identifier ("tda"); break; case DATA_AREA_ZDA: name = get_identifier ("zda"); break; default: return; } DECL_ATTRIBUTES (decl) = tree_cons (name, NULL, DECL_ATTRIBUTES (decl));}const struct attribute_spec v850_attribute_table[] ={ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ { "interrupt_handler", 0, 0, true, false, false, v850_handle_interrupt_attribute }, { "interrupt", 0, 0, true, false, false, v850_handle_interrupt_attribute }, { "sda", 0, 0, true, false, false, v850_handle_data_area_attribute }, { "tda", 0, 0, true, false, false, v850_handle_data_area_attribute }, { "zda", 0, 0, true, false, false, v850_handle_data_area_attribute }, { NULL, 0, 0, false, false, false, NULL }};/* Handle an "interrupt" attribute; arguments as in struct attribute_spec.handler. */static treev850_handle_interrupt_attribute (node, name, args, flags, no_add_attrs) tree *node; tree name; tree args ATTRIBUTE_UNUSED; int flags ATTRIBUTE_UNUSED; bool *no_add_attrs;{ if (TREE_CODE (*node) != FUNCTION_DECL) { warning ("`%s' attribute only applies to functions", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE;}/* Handle a "sda", "tda" or "zda" attribute; arguments as in struct attribute_spec.handler. */static treev850_handle_data_area_attribute (node, name, args, flags, no_add_attrs) tree *node; tree name; tree args ATTRIBUTE_UNUSED; int flags ATTRIBUTE_UNUSED; bool *no_add_attrs;{ v850_data_area data_area; v850_data_area area; tree decl = *node; /* Implement data area attribute. */ if (is_attribute_p ("sda", name)) data_area = DATA_AREA_SDA; else if (is_attribute_p ("tda", name)) data_area = DATA_AREA_TDA; else if (is_attribute_p ("zda", name)) data_area = DATA_AREA_ZDA; else abort (); switch (TREE_CODE (decl)) { case VAR_DECL: if (current_function_decl != NULL_TREE) { error_with_decl (decl, "\a data area attribute cannot be specified for local variables"); *no_add_attrs = true; } /* Drop through. */ case FUNCTION_DECL: area = v850_get_data_area (decl); if (area != DATA_AREA_NORMAL && data_area != area) { error_with_decl (decl, "\data area of '%s' conflicts with previous declaration"); *no_add_attrs = true; } break; default: break; } return NULL_TREE;}/* Return nonzero if FUNC is an interrupt function as specified by the "interrupt" attribute. */intv850_interrupt_function_p (func) tree func;{ tree a; int ret = 0; if (v850_interrupt_cache_p) return v850_interrupt_p; if (TREE_CODE (func) != FUNCTION_DECL) return 0; a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func)); if (a != NULL_TREE) ret = 1; else { a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func)); ret = a != NULL_TREE; } /* Its not safe to trust global variables until after function inlining has been done. */ if (reload_completed | reload_in_progress) v850_interrupt_p = ret; return ret;}static voidv850_encode_data_area (decl) tree decl;{ const char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0); int len = strlen (str); char * newstr; /* Map explict sections into the appropriate attribute */ if (v850_get_data_area (decl) == DATA_AREA_NORMAL) { if (DECL_SECTION_NAME (decl)) { const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); if (streq (name, ".zdata") || streq (name, ".zbss")) v850_set_data_area (decl, DATA_AREA_ZDA); else if (streq (name, ".sdata") || streq (name, ".sbss")) v850_set_data_area (decl, DATA_AREA_SDA); else if (streq (name, ".tdata")) v850_set_data_area (decl, DATA_AREA_TDA); } /* If no attribute, support -m{zda,sda,tda}=n */ else { int size = int_size_in_bytes (TREE_TYPE (decl)); if (size <= 0) ; else if (size <= small_memory [(int) SMALL_MEMORY_TDA].max) v850_set_data_area (decl, DATA_AREA_TDA); else if (size <= small_memory [(int) SMALL_MEMORY_SDA].max) v850_set_data_area (decl, DATA_AREA_SDA); else if (size <= small_memory [(int) SMALL_MEMORY_ZDA].max) v850_set_data_area (decl, DATA_AREA_ZDA); } if (v850_get_data_area (decl) == DATA_AREA_NORMAL) return; } newstr = alloca (len + 2); strcpy (newstr + 1, str); switch (v850_get_data_area (decl)) { case DATA_AREA_ZDA: *newstr = ZDA_NAME_FLAG_CHAR; break; case DATA_AREA_TDA: *newstr = TDA_NAME_FLAG_CHAR; break; case DATA_AREA_SDA: *newstr = SDA_NAME_FLAG_CHAR; break; default: abort (); } XSTR (XEXP (DECL_RTL (decl), 0), 0) = ggc_alloc_string (newstr, len + 2);}static voidv850_encode_section_info (decl, first) tree decl; int first;{ if (first && TREE_CODE (decl) == VAR_DECL && (TREE_STATIC (decl) || DECL_EXTERNAL (decl))) v850_encode_data_area (decl);}static const char *v850_strip_name_encoding (str) const char *str;{ return str + (ENCODED_NAME_P (str) || *str == '*');}/* Return true if the given RTX is a register which can be restored by a function epilogue. */intregister_is_ok_for_epilogue (op, mode) rtx op; enum machine_mode ATTRIBUTE_UNUSED mode;{ /* The save/restore routines can only cope with registers 20 - 31. */ return ((GET_CODE (op) == REG) && (((REGNO (op) >= 20) && REGNO (op) <= 31)));}/* Return nonzero if the given RTX is suitable for collapsing into jump to a function epilogue. */intpattern_is_ok_for_epilogue (op, mode) rtx op; enum machine_mode ATTRIBUTE_UNUSED mode;{ int count = XVECLEN (op, 0); int i; /* If there are no registers to restore then the function epilogue is not suitable. */ if (count <= 2) return 0; /* The pattern matching has already established that we are performing a function epilogue and that we are popping at least one register. We must now check the remaining entries in the vector to make sure that they are also register pops. There is no good reason why there should ever be anything else in this vector, but being paranoid always helps... The test below performs the C equivalent of this machine description pattern match: (set (match_operand:SI n "register_is_ok_for_epilogue" "r") (mem:SI (plus:SI (reg:SI 3) (match_operand:SI n "immediate_operand" "i")))) */ for (i = 3; i < count; i++) { rtx vector_element = XVECEXP (op, 0, i); rtx dest; rtx src; rtx plus; if (GET_CODE (vector_element) != SET) return 0; dest = SET_DEST (vector_element); src = SET_SRC (vector_element); if (GET_CODE (dest) != REG || GET_MODE (dest) != SImode || ! register_is_ok_for_epilogue (dest, SImode) || GET_CODE (src) != MEM || GET_MODE (src) != SImode) return 0; plus = XEXP (src, 0); if (GET_CODE (plus) != PLUS || GET_CODE (XEXP (plus, 0)) != REG || GET_MODE (XEXP (plus, 0)) != SImode || REGNO (XEXP (plus, 0)) != STACK_POINTER_REGNUM || GET_CODE (XEXP (plus, 1)) != CONST_INT) return 0; } return 1;}/* Construct a JR instruction to a routine that will perform the equivalent of the RTL passed in as an argument. This RTL is a function epilogue that pops registers off the stack and possibly releases some extra stack space as well. The code has already verified that the RTL matches these requirements. */char *construct_restore_jr (op) rtx op;{ int count = XVECLEN (op, 0); int stack_bytes; unsigned long int mask; unsigned long int first; unsigned long int last; int i; static char buff [100]; /* XXX */ if (count <= 2) { error ("bogus JR construction: %d\n", count); return NULL; } /* Work out how many bytes to pop off the stack before retrieving registers. */ if (GET_CODE (XVECEXP (op, 0, 1)) != SET) abort (); if (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) != PLUS) abort (); if (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)) != CONST_INT) abort (); stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)); /* Each pop will remove 4 bytes from the stack... */ stack_bytes -= (count - 2) * 4; /* Make sure that the amount we are popping either 0 or 16 bytes. */ if (stack_bytes != 0 && stack_bytes != 16) { error ("bad amount of stack space removal: %d", stack_bytes); return NULL; } /* Now compute the bit mask of registers to push. */ mask = 0; for (i = 2; i < count; i++) { rtx vector_element = XVECEXP (op, 0, i); if (GET_CODE (vector_element) != SET) abort (); if (GET_CODE (SET_DEST (vector_element)) != REG) abort (); if (! register_is_ok_for_epilogue (SET_DEST (vector_element), SImode)) abort (); mask |= 1 << REGNO (SET_DEST (vector_element)); } /* Scan for the first register to pop. */ for (first = 0; first < 32; first++) { if (mask & (1 << first)) break; } if (first >= 32) abort (); /* Discover the last register to pop. */ if (mask & (1 << LINK_POINTER_REGNUM)) { if (stack_bytes != 16) abort (); last = LINK_POINTER_REGNUM; } else { if (stack_bytes != 0) abort (); if ((mask & (1 << 29)) == 0) abort (); last = 29; } /* Note, it is possible to have gaps in the register mask. We ignore this here, and generate a JR anyway. We will be popping more registers than is strictly necessary, but it does save code space. */ if (TARGET_LONG_CALLS) { char name[40]; if (first == last) sprintf (name, "__return_%s", reg_names [first]); else sprintf (name, "__return_%s_%s", reg_names [first], reg_names [last]); sprintf (buff, "movhi hi(%s), r0, r6\n\tmovea lo(%s), r6, r6\n\tjmp r6", name, name); } else { if (first == last) sprintf (buff, "jr __return_%s", reg_names [first]); else sprintf (buff, "jr __return_%s_%s", reg_names [first], reg_names [last]); } return buff;}/* Return nonzero if the given RTX is suitable for collapsing into a jump to a function prologue. */intpattern_is_ok_for_prologue (op, mode) rtx op; enum machine_mode ATTRIBUTE_UNUSED mode;{ int count = XVECLEN (op, 0); int i; rtx vector_element; /* If there are no registers to save then the function prologue is not suitable. */ if (count <= 2) return 0; /* The pattern matching has already established that we are adjusting the stack and pushing at least one register. We must now check that the remaining entries in the vector to make sure that they are also register pushes, except for the last entry which should be a CLOBBER of r10. The test below performs the C equivalent of this machine description pattern match: (set (mem:SI (plus:SI (reg:SI 3) (match_operand:SI 2 "immediate_operand" "i"))) (match_operand:SI 3 "register_is_ok_for_epilogue" "r")) */ for (i = 2; i < count - 1; i++) { rtx dest; rtx src; rtx plus; vector_element = XVECEXP (op, 0, i); if (GET_CODE (vector_element) != SET) return 0; dest = SET_DEST (vector_element); src = SET_SRC (vector_element); if (GET_CODE (dest) != MEM || GET_MODE (dest) != SImode || GET_CODE (src) != REG || GET_MODE (src) != SImode || ! register_is_ok_for_epilogue (src, SImode)) return 0; plus = XEXP (dest, 0); if ( GET_CODE (plus) != PLUS || GET_CODE (XEXP (plus, 0)) != REG || GET_MODE (XEXP (plus, 0)) != SImode || REGNO (XEXP (plus, 0)) != STACK_POINTER_REGNUM || GET_CODE (XE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -