+/* Map a cpu string ARG as given with -march= or .machine to the respective
+ enum s390_opcode_cpu_val value. If ALLOW_EXTENSIONS is TRUE, the cpu name
+ can be followed by a list of cpu facility flags each beginning with the
+ character '+'. The active cpu flags are returned through *RET_FLAGS.
+ In case of an error, S390_OPCODE_MAXCPU is returned. */
+
+static unsigned int
+s390_parse_cpu (const char * arg,
+ unsigned int * ret_flags,
+ bfd_boolean allow_extensions)
+{
+ static struct
+ {
+ const char * name;
+ unsigned int name_len;
+ const char * alt_name;
+ unsigned int alt_name_len;
+ unsigned int flags;
+ } cpu_table[S390_OPCODE_MAXCPU] =
+ {
+ { STRING_COMMA_LEN ("g5"), STRING_COMMA_LEN ("arch3"), 0 },
+ { STRING_COMMA_LEN ("g6"), STRING_COMMA_LEN (""), 0 },
+ { STRING_COMMA_LEN ("z900"), STRING_COMMA_LEN ("arch5"), 0 },
+ { STRING_COMMA_LEN ("z990"), STRING_COMMA_LEN ("arch6"), 0 },
+ { STRING_COMMA_LEN ("z9-109"), STRING_COMMA_LEN (""), 0 },
+ { STRING_COMMA_LEN ("z9-ec"), STRING_COMMA_LEN ("arch7"), 0 },
+ { STRING_COMMA_LEN ("z10"), STRING_COMMA_LEN ("arch8"), 0 },
+ { STRING_COMMA_LEN ("z196"), STRING_COMMA_LEN ("arch9"), 0 },
+ { STRING_COMMA_LEN ("zEC12"), STRING_COMMA_LEN ("arch10"),
+ S390_INSTR_FLAG_HTM },
+ { STRING_COMMA_LEN ("z13"), STRING_COMMA_LEN ("arch11"),
+ S390_INSTR_FLAG_HTM | S390_INSTR_FLAG_VX },
+ { STRING_COMMA_LEN ("z14"), STRING_COMMA_LEN ("arch12"),
+ S390_INSTR_FLAG_HTM | S390_INSTR_FLAG_VX },
+ { STRING_COMMA_LEN (""), STRING_COMMA_LEN ("arch13"),
+ S390_INSTR_FLAG_HTM | S390_INSTR_FLAG_VX }
+ };
+ static struct
+ {
+ const char * name;
+ unsigned int mask;
+ bfd_boolean on;
+ } cpu_flags[] =
+ {
+ { "htm", S390_INSTR_FLAG_HTM, TRUE },
+ { "nohtm", S390_INSTR_FLAG_HTM, FALSE },
+ { "vx", S390_INSTR_FLAG_VX, TRUE },
+ { "novx", S390_INSTR_FLAG_VX, FALSE }
+ };
+ unsigned int icpu;
+ char *ilp_bak;
+
+ icpu = S390_OPCODE_MAXCPU;
+ if (strncmp (arg, "all", 3) == 0 && (arg[3] == 0 || arg[3] == '+'))
+ {
+ icpu = S390_OPCODE_MAXCPU - 1;
+ arg += 3;
+ }
+ else
+ {
+ for (icpu = 0; icpu < S390_OPCODE_MAXCPU; icpu++)
+ {
+ unsigned int l, l_alt;
+
+ l = cpu_table[icpu].name_len;
+
+ if (strncmp (arg, cpu_table[icpu].name, l) == 0
+ && (arg[l] == 0 || arg[l] == '+'))
+ {
+ arg += l;
+ break;
+ }
+
+ l_alt = cpu_table[icpu].alt_name_len;
+
+ if (l_alt > 0
+ && strncmp (arg, cpu_table[icpu].alt_name, l_alt) == 0
+ && (arg[l_alt] == 0 || arg[l_alt] == '+'))
+ {
+ arg += l_alt;
+ break;
+ }
+ }
+ }
+
+ if (icpu == S390_OPCODE_MAXCPU)
+ return S390_OPCODE_MAXCPU;
+
+ ilp_bak = input_line_pointer;
+ if (icpu != S390_OPCODE_MAXCPU)
+ {
+ input_line_pointer = (char *) arg;
+ *ret_flags = (cpu_table[icpu].flags & S390_INSTR_FLAG_FACILITY_MASK);
+
+ while (*input_line_pointer == '+' && allow_extensions)
+ {
+ unsigned int iflag;
+ char *sym;
+ char c;
+
+ input_line_pointer++;
+ c = get_symbol_name (&sym);
+ for (iflag = 0; iflag < ARRAY_SIZE (cpu_flags); iflag++)
+ {
+ if (strcmp (sym, cpu_flags[iflag].name) == 0)
+ {
+ if (cpu_flags[iflag].on)
+ *ret_flags |= cpu_flags[iflag].mask;
+ else
+ *ret_flags &= ~cpu_flags[iflag].mask;
+ break;
+ }
+ }
+ if (iflag == ARRAY_SIZE (cpu_flags))
+ as_bad (_("no such machine extension `%s'"), sym - 1);
+ *input_line_pointer = c;
+ if (iflag == ARRAY_SIZE (cpu_flags))
+ break;
+ }
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != 0 && *input_line_pointer != '\n')
+ {
+ as_bad (_("junk at end of machine string, first unrecognized character"
+ " is `%c'"), *input_line_pointer);
+ icpu = S390_OPCODE_MAXCPU;
+ }
+ input_line_pointer = ilp_bak;
+
+ return icpu;
+}
+