GCC后端及汇编发布(13)

6.        genextract 工具

Genextract 从机器描述文件输出 insn-extract.c 。这个文件定义了,从指定的模式中,提取操作数的函数。

 

346  int

347  main (int argc, char **argv)                                                                 in genextract.c

348  {

349    rtx desc;

350    int i;

351    struct extraction *p;

352    struct code_ptr *link;

353    const char *name;

354 

355    progname = "genextract";

356 

357    if (argc <= 1)

358      fatal ("no input file name");

359 

360    if (init_md_reader_args (argc, argv) != SUCCESS_EXIT_CODE)

361      return (FATAL_EXIT_CODE);

362 

363    /* Assign sequential codes to all entries in the machine description

364      i n parallel with the tables in insn-output.c.  */

365 

366    insn_code_number = 0;

367 

368    printf ("/* Generated automatically by the program `genextract'/n/

369          from the machine description file `md'.  *//n/n");

370 

371    printf ("#include /"config.h/"/n");

372    printf ("#include /"system.h/"/n");

373    printf ("#include /"coretypes.h/"/n");

374    printf ("#include /"tm.h/"/n");

375    printf ("#include /"rtl.h/"/n");

376    printf ("#include /"insn-config.h/"/n");

377    printf ("#include /"recog.h/"/n");

378    printf ("#include /"toplev.h/"/n/n");

379 

380    /* This variable exists only so it can be the "location"

381      of any missing operand whose numbers are skipped by a given pattern.  */

382    printf ("static rtx junk ATTRIBUTE_UNUSED;/n");

383 

384    printf ("void/ninsn_extract (rtx insn)/n");

385    printf ("{/n");

386    printf ("  rtx *ro = recog_data.operand;/n");

387    printf ("  rtx **ro_loc = recog_data.operand_loc;/n");

388    printf ("  rtx pat = PATTERN (insn);/n");

389    printf ("  int i ATTRIBUTE_UNUSED;/n/n");

390  #ifdef ENABLE_CHECKING

391    printf ("  memset (ro, 0xab, sizeof (*ro) * MAX_RECOG_OPERANDS);/n");

392    printf ("  memset (ro_loc, 0xab, sizeof (*ro_loc) * MAX_RECOG_OPERANDS);/n");

393  #endif

394    printf ("  switch (INSN_CODE (insn))/n");

395    printf ("    {/n");

396    printf ("    case -1:/n");

397    printf ("      fatal_insn_not_found (insn);/n/n");

398 

399    /* Read the machine description.  */

400 

401    while (1)

402    {

403      int line_no;

404 

405      desc = read_md_rtx (&line_no, &insn_code_number );

406      if (desc == NULL)

407        break ;

408 

409      if (GET_CODE (desc) == DEFINE_INSN)

410      {

411         record_insn_name (insn_code_number , XSTR (desc, 0));

412         gen_insn (desc);

413      }

414 

415      else if (GET_CODE (desc) == DEFINE_PEEPHOLE)

416      {

417         struct code_ptr *link = xmalloc (sizeof (struct code_ptr));

418 

419         link->insn_code = insn_code_number ;

420         link->next = peepholes ;

421         peepholes = link;

422      }

423    }

 

再一次, init_md_reader_args 读入机器描述文件,构建 rtx 对象,并把它们放入链表中;然后函数 read_md_rtx 一个个地读入 rtx 对象。

在上面的 415 行, define_peephole 模式现在不建议使用了,而是期望使用 define_peephole2 模式。这两个模式定义了一个方式来找出窥孔优化的机会。对于 define_peephole ,因为其输出模板给出了结果,因此它的处理非常直接。

至于 define_insn ,在 411 行,该模式的名字,以 insn_code_number 为索引,被记录在 insn_name_ptr 中。这里我们还是使用下面的模式作为例子。

 

467  (define_insn "cmpdi_ccno_1_rex64"                                                            in i386.md

468    [(set (reg 17)

469         (compare (match_operand:DI 0 "nonimmediate_operand" "r,?mr")

470                (match_operand:DI 1 "const0_operand" "n,n")))]

471    "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode)"

472    "@

473     test{q}/t{%0, %0|%0, %0}

474     cmp{q}/t{%1, %0|%0, %1}"

475    [(set_attr "type" "test,icmp")

476     (set_attr "length_immediate" "0,1")

477     (set_attr "mode" "DI")])

 

t34

34 genextract - define_insn 的例子

 

103  static void

104  gen_insn (rtx insn)                                                                              in genextract.c

105  {

106    int i;

107    struct extraction *p;

108    struct code_ptr *link;

109 

110    op_count = 0;

111    dup_count = 0;

112 

113    /* No operands seen so far in this pattern.  */

114    memset (oplocs , 0, sizeof oplocs );

115 

116    /* Walk the insn's pattern, remembering at all times the path

117      down to the walking point.  */

118 

119    if (XVECLEN (insn, 1) == 1)

120      walk_rtx (XVECEXP (insn, 1, 0), "");

121    else

122      for (i = XVECLEN (insn, 1) - 1; i >= 0; i--)

123      {

124        char path[2];

125 

126        path[0] = 'a' + i;

127        path[1] = 0;

128 

129        walk_rtx (XVECEXP (insn, 1, i), path);

130      }

 

walk_rtx 就像我们之前看到的处理其它模式的函数那样,递归来处理 define_insn 模式。

 

182  static void

183  walk_rtx (rtx x, const char *path)

184  {

185    RTX_CODE code;

186    int i;

187    int len;

188    const char *fmt;

189    int depth = strlen (path);

190    char *newpath;

191 

192    if (x == 0)

193      return ;

194 

195    code = GET_CODE (x);

196 

197    switch (code)

198    {

199      case PC:

200      case CC0:

201      case CONST_INT:

202      case SYMBOL_REF:

203        return ;

204 

205      case MATCH_OPERAND:

206      case MATCH_SCRATCH:

207        oplocs [XINT (x, 0)] = xstrdup (path);

208        op_count = MAX (op_count , XINT (x, 0) + 1);

209        break ;

210 

211      case MATCH_DUP:

212        duplocs [dup_count ] = xstrdup (path);

213        dupnums [dup_count ] = XINT (x, 0);

214        dup_count ++;

215        break ;

216 

217      case MATCH_PAR_DUP:

218      case MATCH_OP_DUP:

219        duplocs [dup_count ] = xstrdup (path);

220        dupnums [dup_count ] = XINT (x, 0);

221        dup_count ++;

222 

223        newpath = xmalloc (depth + 2);

224        strcpy (newpath, path);

225        newpath[depth + 1] = 0;

226 

227        for (i = XVECLEN (x, 1) - 1; i >= 0; i--)

228        {

229          newpath[depth] = (code == MATCH_OP_DUP ? '0' : 'a') + i;

230          walk_rtx (XVECEXP (x, 1, i), newpath);

231        }

232        free (newpath);

233        return ;

234 

235      case MATCH_OPERATOR:

236        oplocs [XINT (x, 0)] = xstrdup (path);

237        op_count = MAX (op_count , XINT (x, 0) + 1);

238 

239        newpath = xmalloc (depth + 2);

240        strcpy (newpath, path);

241        newpath[depth + 1] = 0;

242 

243        for (i = XVECLEN (x, 2) - 1; i >= 0; i--)

244        {

245          newpath[depth] = '0' + i;

246          walk_rtx (XVECEXP (x, 2, i), newpath);

247        }

248        free (newpath);

249        return ;

250 

251      case MATCH_PARALLEL:

252        oplocs [XINT (x, 0)] = xstrdup (path);

253        op_count = MAX (op_count , XINT (x, 0) + 1);

254 

255        newpath = xmalloc (depth + 2);

256        strcpy (newpath, path);

257        newpath[depth + 1] = 0;

258 

259        for (i = XVECLEN (x, 2) - 1; i >= 0; i--)

260        {

261          newpath[depth] = 'a' + i;

262          walk_rtx (XVECEXP (x, 2, i), newpath);

263        }

264        free (newpath);

265        return ;

266 

267      case ADDRESS:

268        walk_rtx (XEXP (x, 0), path);

269        return ;

270 

271      default :

272        break ;

273    }

274 

275    newpath = xmalloc (depth + 2);

276    strcpy (newpath, path);

277    newpath[depth + 1] = 0;

278 

279    fmt = GET_RTX_FORMAT (code);

280    len = GET_RTX_LENGTH (code);

281    for (i = 0; i < len; i++)

282    {

283      if (fmt[i] == 'e' || fmt[i] == 'u')

284      {

285        newpath[depth] = '0' + i;

286        walk_rtx (XEXP (x, i), newpath);

287      }

288      else if (fmt[i] == 'E')

289      {

290        int j;

291        for (j = XVECLEN (x, i) - 1; j >= 0; j--)

292        {

293          newpath[depth] = 'a' + j;

294          walk_rtx (XVECEXP (x, i, j), newpath);

295        }

296      }

297    }

298    free (newpath);

299  }

 

注意到 rtl 编码 SET 的模板具有格式“ ee ”, REG 则具有格式“ i00 ”, COMPARE 的格式也是“ ee ”。对于我们的例子, walk_rtx 相当简单。当它返回时, oplocs 指向‘ 0 ’及‘ 1 ’, op_count 2 。回到 gen_insn extraction code_ptr 具有如下的定义:

 

43    struct extraction                                                                                   in genextract.c

44    {

45      int op_count;

46      char *oplocs[MAX_RECOG_OPERANDS];

47      int dup_count;

48      char *duplocs[MAX_DUP_OPERANDS];

49      int dupnums[MAX_DUP_OPERANDS];

50      struct code_ptr *insns;

51      struct extraction *next;

52    };

 

56    struct code_ptr

57    {

58      int insn_code;

59      struct code_ptr *next;

60    };

 

其中, 45 行的 op_count 记录指令中较大的指令号; 47 行的 dup_count 则是在指令中出现的 MATCH_DUP 的次数,从 0 开始计数。 Duplocs dupnums 对应于每个 MATCH_DUP ,其中 duplocs 记录了 MATCH_DUP 的操作数的行号, dupnums 记录了 MATCH_DUP 的操作数个数。宏 MAX_DUP_OPERANDS MAX_RECOG_OPERANDS 是由工具 genconfig 根据机器描述产生的配置的一部分。

对于具有相似形式的模式,它们将共享同一个 extraction ,而在 50 行的 insns 域将保存它们的指令编码号的记录,所有的 extraction 将通过 51 行的 next 域链接在一起。

 

gen_insn (continued)

 

132    link = xmalloc (sizeof (struct code_ptr));

133    link->insn_code = insn_code_number ;

134 

135    /* See if we find something that already had this extraction method.  */

136 

137    for (p = extractions ; p; p = p->next)

138    {

139      if (p->op_count != op_count || p->dup_count != dup_count )

140        continue ;

141 

142      for (i = 0; i < op_count ; i++)

143        if (p->oplocs[i] != oplocs [i]

144           && ! (p->oplocs[i] != 0 && oplocs [i] != 0

145                   && ! strcmp (p->oplocs[i], oplocs [i])))

146          break ;

147 

148      if (i != op_count )

149        continue ;

150 

151      for (i = 0; i < dup_count ; i++)

152        if (p->dupnums[i] != dupnums [i]

153           || strcmp (p->duplocs[i], duplocs [i]))

154          break ;

155 

156      if (i != dup_count )

157        continue ;

158 

159      /* This extraction is the same as ours. Just link us in.  */

160      link->next = p->insns;

161      p->insns = link;

162      return ;

163    }

164 

165    /* Otherwise, make a new extraction method.  */

166 

167    p = xmalloc (sizeof (struct extraction));

168    p->op_count = op_count ;

169    p->dup_count = dup_count ;

170    p->next = extractions ;

171    extractions = p;

172    p->insns = link;

173    link->next = 0;

174 

175    for (i = 0; i < op_count ; i++)

176      p->oplocs[i] = oplocs [i];

177 

178    for (i = 0; i < dup_count ; i++)

179      p->dupnums[i] = dupnums [i], p->duplocs[i] = duplocs [i];

180  }

 

gen_insn 的余下部分 它把 walk_rtx 的结果链接入 extractions 链表。回到 main ,它将处理这个数据。

 

main (continued)

 

425    /* Write out code to handle peepholes and the insn_codes that it should

426      be called for.  */

427    if (peepholes )

428    {

429      for (link = peepholes ; link; link = link->next)

430        printf ("    case %d:/n", link->insn_code);

431 

432      /* The vector in the insn says how many operands it has.

433         And all it contains are operands. In fact, the vector was

434        created just for the sake of this function. We need to set the

435         location of the operands for sake of simplifications after

436         extraction, like eliminating subregs.  */

437      printf ("      for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)/n");

438      printf ("          ro[i] = *(ro_loc[i] = &XVECEXP (pat, 0, i));/n");

439      printf ("      break;/n/n");

440    }

441 

442    /* Write out all the ways to extract insn operands.  */

443    for (p = extractions ; p; p = p->next)

444    {

445      for (link = p->insns; link; link = link->next)

446      {

447         i = link->insn_code;

448         name = get_insn_name (i);

449         if (name)

450           printf ("    case %d:  /* %s *//n", i, name);

451         else

452           printf ("    case %d:/n", i);

453      }

454 

455      for (i = 0; i < p->op_count; i++)

456      {

457         if (p->oplocs[i] == 0)

458         {

459           printf ("      ro[%d] = const0_rtx;/n", i);

460           printf ("      ro_loc[%d] = &junk;/n", i);

461          }

462         else

463         {

464           printf ("      ro[%d] = *(ro_loc[%d] = &", i, i);

465           print_path (p->oplocs[i]);

466           printf (");/n");

467         }

468      }

469 

470      for (i = 0; i < p->dup_count; i++)

471      {

472         printf ("      recog_data.dup_loc[%d] = &", i);

473         print_path (p->duplocs[i]);

474         printf (";/n");

475         printf ("      recog_data.dup_num[%d] = %d;/n", i, p->dupnums[i]);

476      }

477 

478      printf ("      break;/n/n");

479    }

480 

481    /* This should never be reached. Note that we would also reach this abort

482      if we tried to extract something whose INSN_CODE was a DEFINE_EXPAND or

483       DEFINE_SPLIT, but that is correct.  */

484    printf ("    default:/n      abort ();/n");

485 

486    printf ("    }/n}/n");

487 

488    fflush (stdout);

489    return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);

490  }

 

剩下的代码相当简单,但在实际的输出代码中,我们的例子出现在 i386 32 位系统中,它在 read_md_rtx 中被丢弃,因为 1002 行的 maybe_eval_c_test 显示其条件可以证明总是 false 。对于真实的输出,看起来将是如下:

 

1     /* Generated automatically by the program `genextract'

2     from the machine description file `md'.  */

3    

4     #include "config.h"

5     #include "system.h"

6     #include "coretypes.h"

7     #include "tm.h"

8     #include "rtl.h"

9     #include "insn-config.h"

10    #include "recog.h"

11    #include "toplev.h"

12   

13    static rtx junk ATTRIBUTE_UNUSED;

14    void

15    insn_extract (rtx insn)

16    {

17      rtx *ro = recog_data.operand;

18      rtx **ro_loc = recog_data.operand_loc;

19      rtx pat = PATTERN (insn);

20      int i ATTRIBUTE_UNUSED;

21   

22      memset (ro, 0xab, sizeof (*ro) * MAX_RECOG_OPERANDS);

23      memset (ro_loc, 0xab, sizeof (*ro_loc) * MAX_RECOG_OPERANDS);

24      switch (INSN_CODE (insn))

25      {

26        case -1:

27           fatal_insn_not_found (insn);

28   

29        case 819:  /* monitor */

30          ro[0] = *(ro_loc[0] = &XVECEXP (pat, 0, 0));

31          ro[1] = *(ro_loc[1] = &XVECEXP (pat, 0, 1));

32          ro[2] = *(ro_loc[2] = &XVECEXP (pat, 0, 2));

33          break;

34       

35        default:

36          gcc_unreachable ();

37      }

38    }

7.        genpreds 工具

Genpreds 从机器描述文件输出文件 tm-preds.h 。这个文件声明了由 define_insn 等使用的断言函数。注意到断言函数被定义在文件 `arch`.c 中。对于不同的架构,该工具输出匹配的 tm-preds.h

 

1     int

2     main (void)                                                                                               in genpreds.c

3     {

4       puts ("/* Generated automatically by the program `genpreds'.  *//n");

5       puts ("#ifndef GCC_TM_PREDS_H");

6       puts ("#define GCC_TM_PREDS_H/n");

7    

8       output_predicate_decls ();

9    

10      puts ("#endif /* GCC_TM_PREDS_H */");

11   

12      if (ferror (stdout) || fflush (stdout) || fclose (stdout))

13         return FATAL_EXIT_CODE;

14   

15      return SUCCESS_EXIT_CODE;

16    }

 

一个简单的 main 函数。

 

33    static void

34    output_predicate_decls (void)                                                                     in genpreds.c

35    {

36    #ifdef PREDICATE_CODES

37      static const struct {

38        const char *const name;

39        const RTX_CODE codes[NUM_RTX_CODE];

40      } predicate[] = {

41        PREDICATE_CODES

42      };

43      size_t i;

44   

45      puts ("#ifdef RTX_CODE/n");

46      for (i = 0; i < ARRAY_SIZE (predicate); i++)

47        printf ("extern int %s (rtx, enum machine_mode);/n",

48               predicate[i].name);

49      puts ("/n#endif /* RTX_CODE *//n");

50    #endif

51    }

 

PREDICATE_CODES 被定义在目标系统中。对于 i386 系统 它是如下。

 

2916 #define PREDICATE_CODES                                               /                    in i386.h

2917   {"x86_64_immediate_operand", {CONST_INT, SUBREG, REG,                /

2918                             SYMBOL_REF, LABEL_REF, CONST}},         /

2919   {"x86_64_nonmemory_operand", {CONST_INT, SUBREG, REG,                     /

2920                             SYMBOL_REF, LABEL_REF, CONST}},         /

2921   {"x86_64_movabs_operand", {CONST_INT, SUBREG, REG,                    /

2922                             SYMBOL_REF, LABEL_REF, CONST}},         /

2923   {"x86_64_szext_nonmemory_operand", {CONST_INT, SUBREG, REG,            /

2924                                  SYMBOL_REF, LABEL_REF, CONST}},       /

2925   {"x86_64_general_operand", {CONST_INT, SUBREG, REG, MEM,           /

2926                            SYMBOL_REF, LABEL_REF, CONST}},             /

2927   {"x86_64_szext_general_operand", {CONST_INT, SUBREG, REG, MEM,  /

2928                                SYMBOL_REF, LABEL_REF, CONST}},    /

2929   {"x86_64_zext_immediate_operand", {CONST_INT, CONST_DOUBLE, CONST,     /

2930                                    SYMBOL_REF, LABEL_REF}},          /

2931   {"shiftdi_operand", {SUBREG, REG, MEM}},                           /

2932   {"const_int_1_31_operand", {CONST_INT}},                            /

2933   {"symbolic_operand", {SYMBOL_REF, LABEL_REF, CONST}},                    /

2934   {"aligned_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,     /

2935                      LABEL_REF, SUBREG, REG, MEM}},                     /

2936   {"pic_symbolic_operand", {CONST}},                               /

2937   {"call_insn_operand", {REG, SUBREG, MEM, SYMBOL_REF}},             /

2938   {"sibcall_insn_operand", {REG, SUBREG, SYMBOL_REF}},                   /

2939   {"constant_call_address_operand", {SYMBOL_REF, CONST}},         /

2940   {"const0_operand", {CONST_INT, CONST_DOUBLE}},                   /

2941   {"const1_operand", {CONST_INT}},                                 /

2942   {"const248_operand", {CONST_INT}},                              /

2943   {"const_0_to_3_operand", {CONST_INT}},                        /

2944   {"const_0_to_7_operand", {CONST_INT}},                        /

2945   {"const_0_to_15_operand", {CONST_INT}},                      /

2946   {"const_0_to_255_operand", {CONST_INT}},                           /

2947   {"incdec_operand", {CONST_INT}},                                 /

2948   {"mmx_reg_operand", {REG}},                                         /

2949   {"reg_no_sp_operand", {SUBREG, REG}},                               /

2950   {"general_no_elim_operand", {CONST_INT, CONST_DOUBLE, CONST,         /

2951                      SYMBOL_REF, LABEL_REF, SUBREG, REG, MEM}},  /

2952   {"nonmemory_no_elim_operand", {CONST_INT, REG, SUBREG}},         /

2953   {"index_register_operand", {SUBREG, REG}},                          /

2954   {"flags_reg_operand", {REG}},                                         /

2955   {"q_regs_operand", {SUBREG, REG}},                              /

2956   {"non_q_regs_operand", {SUBREG, REG}},                       /

2957   {"fcmov_comparison_operator", {EQ, NE, LTU, GTU, LEU, GEU, UNORDERED, /

2958                             ORDERED, LT, UNLT, GT, UNGT, LE, UNLE,       /

2959                             GE, UNGE, LTGT, UNEQ}},            /

2960   {"sse_comparison_operator", {EQ, LT, LE, UNORDERED, NE, UNGE, UNGT,       /

2961                             ORDERED, UNEQ, UNLT, UNLE, LTGT, GE, GT      /

2962                             }},                                /

2963   {"ix86_comparison_operator", {EQ, NE, LE, LT, GE, GT, LEU, LTU, GEU,     /

2964                             GTU, UNORDERED, ORDERED, UNLE, UNLT,       /

2965                             UNGE, UNGT, LTGT, UNEQ }},          /

2966   {"ix86_carry_flag_operator", {LTU, LT, UNLT, GT, UNGT, LE, UNLE,     /

2967                             GE, UNGE, LTGT, UNEQ}},            /

2968   {"cmp_fp_expander_operand", {CONST_DOUBLE, SUBREG, REG, MEM}},   /

2969   {"ext_register_operand", {SUBREG, REG}},                      /

2970   {"binary_fp_operator", {PLUS, MINUS, MULT, DIV}},                    /

2971   {"mult_operator", {MULT}},                                      /

2972   {"div_operator", {DIV}},                                    /

2973   {"arith_or_logical_operator", {PLUS, MULT, AND, IOR, XOR, SMIN, SMAX, /

2974                             UMIN, UMAX, COMPARE, MINUS, DIV, MOD,   /

2975                             UDIV, UMOD, ASHIFT, ROTATE, ASHIFTRT,       /

2976                             LSHIFTRT, ROTATERT}},                /

2977   {"promotable_binary_operator", {PLUS, MULT, AND, IOR, XOR, ASHIFT}},  /

2978   {"memory_displacement_operand", {MEM}},                            /

2979   {"cmpsi_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,       /

2980                    LABEL_REF, SUBREG, REG, MEM, AND}},         /

2981   {"long_memory_operand", {MEM}},                                 /

2982   {"tls_symbolic_operand", {SYMBOL_REF}},                            /

2983   {"global_dynamic_symbolic_operand", {SYMBOL_REF}},                /

2984   {"local_dynamic_symbolic_operand", {SYMBOL_REF}},                  /

2985   {"initial_exec_symbolic_operand", {SYMBOL_REF}},               /

2986   {"local_exec_symbolic_operand", {SYMBOL_REF}},                /

2987   {"any_fp_register_operand", {REG}},                                /

2988   {"register_and_not_any_fp_reg_operand", {REG}},                    /

2989   {"fp_register_operand", {REG}},                                /

2990   {"register_and_not_fp_reg_operand", {REG}},                           /

2991   {"zero_extended_scalar_load_operand", {MEM}},                      /

2992   {"vector_move_operand", {CONST_VECTOR, SUBREG, REG, MEM}},          /

2993   {"no_seg_address_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF, /

2994                            LABEL_REF, SUBREG, REG, MEM, PLUS, MULT}}, /

2995   {"compare_operator", {COMPARE}},

 

以上的函数声明将被输出。在 4.3 版本及之后,这些定义也将从一个新的机器描述文件—— predicate.md 中产生。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值