+/* Implementation of `gdbarch_stap_is_single_operand', as defined in
+ gdbarch.h. */
+
+int
+i386_stap_is_single_operand (struct gdbarch *gdbarch, const char *s)
+{
+ return (*s == '$' /* Literal number. */
+ || (isdigit (*s) && s[1] == '(' && s[2] == '%') /* Displacement. */
+ || (*s == '(' && s[1] == '%') /* Register indirection. */
+ || (*s == '%' && isalpha (s[1]))); /* Register access. */
+}
+
+/* Helper function for i386_stap_parse_special_token.
+
+ This function parses operands of the form `-8+3+1(%rbp)', which
+ must be interpreted as `*(-8 + 3 - 1 + (void *) $eax)'.
+
+ Return true if the operand was parsed successfully, false
+ otherwise. */
+
+static bool
+i386_stap_parse_special_token_triplet (struct gdbarch *gdbarch,
+ struct stap_parse_info *p)
+{
+ const char *s = p->arg;
+
+ if (isdigit (*s) || *s == '-' || *s == '+')
+ {
+ bool got_minus[3];
+ int i;
+ long displacements[3];
+ const char *start;
+ char *regname;
+ int len;
+ struct stoken str;
+ char *endp;
+
+ got_minus[0] = false;
+ if (*s == '+')
+ ++s;
+ else if (*s == '-')
+ {
+ ++s;
+ got_minus[0] = true;
+ }
+
+ if (!isdigit ((unsigned char) *s))
+ return false;
+
+ displacements[0] = strtol (s, &endp, 10);
+ s = endp;
+
+ if (*s != '+' && *s != '-')
+ {
+ /* We are not dealing with a triplet. */
+ return false;
+ }
+
+ got_minus[1] = false;
+ if (*s == '+')
+ ++s;
+ else
+ {
+ ++s;
+ got_minus[1] = true;
+ }
+
+ if (!isdigit ((unsigned char) *s))
+ return false;
+
+ displacements[1] = strtol (s, &endp, 10);
+ s = endp;
+
+ if (*s != '+' && *s != '-')
+ {
+ /* We are not dealing with a triplet. */
+ return false;
+ }
+
+ got_minus[2] = false;
+ if (*s == '+')
+ ++s;
+ else
+ {
+ ++s;
+ got_minus[2] = true;
+ }
+
+ if (!isdigit ((unsigned char) *s))
+ return false;
+
+ displacements[2] = strtol (s, &endp, 10);
+ s = endp;
+
+ if (*s != '(' || s[1] != '%')
+ return false;
+
+ s += 2;
+ start = s;
+
+ while (isalnum (*s))
+ ++s;
+
+ if (*s++ != ')')
+ return false;
+
+ len = s - start - 1;
+ regname = (char *) alloca (len + 1);
+
+ strncpy (regname, start, len);
+ regname[len] = '\0';
+
+ if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1)
+ error (_("Invalid register name `%s' on expression `%s'."),
+ regname, p->saved_arg);
+
+ for (i = 0; i < 3; i++)
+ {
+ write_exp_elt_opcode (&p->pstate, OP_LONG);
+ write_exp_elt_type
+ (&p->pstate, builtin_type (gdbarch)->builtin_long);
+ write_exp_elt_longcst (&p->pstate, displacements[i]);
+ write_exp_elt_opcode (&p->pstate, OP_LONG);
+ if (got_minus[i])
+ write_exp_elt_opcode (&p->pstate, UNOP_NEG);
+ }
+
+ write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+ str.ptr = regname;
+ str.length = len;
+ write_exp_string (&p->pstate, str);
+ write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+
+ write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+ write_exp_elt_type (&p->pstate,
+ builtin_type (gdbarch)->builtin_data_ptr);
+ write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+
+ write_exp_elt_opcode (&p->pstate, BINOP_ADD);
+ write_exp_elt_opcode (&p->pstate, BINOP_ADD);
+ write_exp_elt_opcode (&p->pstate, BINOP_ADD);
+
+ write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+ write_exp_elt_type (&p->pstate,
+ lookup_pointer_type (p->arg_type));
+ write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+
+ write_exp_elt_opcode (&p->pstate, UNOP_IND);
+
+ p->arg = s;
+
+ return true;
+ }
+
+ return false;
+}
+
+/* Helper function for i386_stap_parse_special_token.
+
+ This function parses operands of the form `register base +
+ (register index * size) + offset', as represented in
+ `(%rcx,%rax,8)', or `[OFFSET](BASE_REG,INDEX_REG[,SIZE])'.
+
+ Return true if the operand was parsed successfully, false
+ otherwise. */
+
+static bool
+i386_stap_parse_special_token_three_arg_disp (struct gdbarch *gdbarch,
+ struct stap_parse_info *p)
+{
+ const char *s = p->arg;
+
+ if (isdigit (*s) || *s == '(' || *s == '-' || *s == '+')
+ {
+ bool offset_minus = false;
+ long offset = 0;
+ bool size_minus = false;
+ long size = 0;
+ const char *start;
+ char *base;
+ int len_base;
+ char *index;
+ int len_index;
+ struct stoken base_token, index_token;
+
+ if (*s == '+')
+ ++s;
+ else if (*s == '-')
+ {
+ ++s;
+ offset_minus = true;
+ }
+
+ if (offset_minus && !isdigit (*s))
+ return false;
+
+ if (isdigit (*s))
+ {
+ char *endp;
+
+ offset = strtol (s, &endp, 10);
+ s = endp;
+ }
+
+ if (*s != '(' || s[1] != '%')
+ return false;
+
+ s += 2;
+ start = s;
+
+ while (isalnum (*s))
+ ++s;
+
+ if (*s != ',' || s[1] != '%')
+ return false;
+
+ len_base = s - start;
+ base = (char *) alloca (len_base + 1);
+ strncpy (base, start, len_base);
+ base[len_base] = '\0';
+
+ if (user_reg_map_name_to_regnum (gdbarch, base, len_base) == -1)
+ error (_("Invalid register name `%s' on expression `%s'."),
+ base, p->saved_arg);
+
+ s += 2;
+ start = s;
+
+ while (isalnum (*s))
+ ++s;
+
+ len_index = s - start;
+ index = (char *) alloca (len_index + 1);
+ strncpy (index, start, len_index);
+ index[len_index] = '\0';
+
+ if (user_reg_map_name_to_regnum (gdbarch, index, len_index) == -1)
+ error (_("Invalid register name `%s' on expression `%s'."),
+ index, p->saved_arg);
+
+ if (*s != ',' && *s != ')')
+ return false;
+
+ if (*s == ',')
+ {
+ char *endp;
+
+ ++s;
+ if (*s == '+')
+ ++s;
+ else if (*s == '-')
+ {
+ ++s;
+ size_minus = true;
+ }
+
+ size = strtol (s, &endp, 10);
+ s = endp;
+
+ if (*s != ')')
+ return false;
+ }
+
+ ++s;
+
+ if (offset)
+ {
+ write_exp_elt_opcode (&p->pstate, OP_LONG);
+ write_exp_elt_type (&p->pstate,
+ builtin_type (gdbarch)->builtin_long);
+ write_exp_elt_longcst (&p->pstate, offset);
+ write_exp_elt_opcode (&p->pstate, OP_LONG);
+ if (offset_minus)
+ write_exp_elt_opcode (&p->pstate, UNOP_NEG);
+ }
+
+ write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+ base_token.ptr = base;
+ base_token.length = len_base;
+ write_exp_string (&p->pstate, base_token);
+ write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+
+ if (offset)
+ write_exp_elt_opcode (&p->pstate, BINOP_ADD);
+
+ write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+ index_token.ptr = index;
+ index_token.length = len_index;
+ write_exp_string (&p->pstate, index_token);
+ write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+
+ if (size)
+ {
+ write_exp_elt_opcode (&p->pstate, OP_LONG);
+ write_exp_elt_type (&p->pstate,
+ builtin_type (gdbarch)->builtin_long);
+ write_exp_elt_longcst (&p->pstate, size);
+ write_exp_elt_opcode (&p->pstate, OP_LONG);
+ if (size_minus)
+ write_exp_elt_opcode (&p->pstate, UNOP_NEG);
+ write_exp_elt_opcode (&p->pstate, BINOP_MUL);
+ }
+
+ write_exp_elt_opcode (&p->pstate, BINOP_ADD);
+
+ write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+ write_exp_elt_type (&p->pstate,
+ lookup_pointer_type (p->arg_type));
+ write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+
+ write_exp_elt_opcode (&p->pstate, UNOP_IND);
+
+ p->arg = s;
+
+ return true;
+ }
+
+ return false;
+}
+
+/* Implementation of `gdbarch_stap_parse_special_token', as defined in
+ gdbarch.h. */
+
+int
+i386_stap_parse_special_token (struct gdbarch *gdbarch,
+ struct stap_parse_info *p)
+{
+ /* In order to parse special tokens, we use a state-machine that go
+ through every known token and try to get a match. */
+ enum
+ {
+ TRIPLET,
+ THREE_ARG_DISPLACEMENT,
+ DONE
+ };
+ int current_state;
+
+ current_state = TRIPLET;
+
+ /* The special tokens to be parsed here are:
+
+ - `register base + (register index * size) + offset', as represented
+ in `(%rcx,%rax,8)', or `[OFFSET](BASE_REG,INDEX_REG[,SIZE])'.
+
+ - Operands of the form `-8+3+1(%rbp)', which must be interpreted as
+ `*(-8 + 3 - 1 + (void *) $eax)'. */
+
+ while (current_state != DONE)
+ {
+ switch (current_state)
+ {
+ case TRIPLET:
+ if (i386_stap_parse_special_token_triplet (gdbarch, p))
+ return 1;
+ break;
+
+ case THREE_ARG_DISPLACEMENT:
+ if (i386_stap_parse_special_token_three_arg_disp (gdbarch, p))
+ return 1;
+ break;
+ }
+
+ /* Advancing to the next state. */
+ ++current_state;
+ }
+
+ return 0;
+}
+
+/* Implementation of 'gdbarch_stap_adjust_register', as defined in
+ gdbarch.h. */
+
+static std::string
+i386_stap_adjust_register (struct gdbarch *gdbarch, struct stap_parse_info *p,
+ const std::string ®name, int regnum)
+{
+ static const std::unordered_set<std::string> reg_assoc
+ = { "ax", "bx", "cx", "dx",
+ "si", "di", "bp", "sp" };
+
+ /* If we are dealing with a register whose size is less than the size
+ specified by the "[-]N@" prefix, and it is one of the registers that
+ we know has an extended variant available, then use the extended
+ version of the register instead. */
+ if (register_size (gdbarch, regnum) < TYPE_LENGTH (p->arg_type)
+ && reg_assoc.find (regname) != reg_assoc.end ())
+ return "e" + regname;
+
+ /* Otherwise, just use the requested register. */
+ return regname;
+}
+
+\f
+
+/* gdbarch gnu_triplet_regexp method. Both arches are acceptable as GDB always
+ also supplies -m64 or -m32 by gdbarch_gcc_target_options. */
+
+static const char *
+i386_gnu_triplet_regexp (struct gdbarch *gdbarch)
+{
+ return "(x86_64|i.86)";
+}
+
+\f
+
+/* Implement the "in_indirect_branch_thunk" gdbarch function. */
+
+static bool
+i386_in_indirect_branch_thunk (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ return x86_in_indirect_branch_thunk (pc, i386_register_names,
+ I386_EAX_REGNUM, I386_EIP_REGNUM);
+}
+