日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

解讀 Intel? Microarchitecture

 盛夏流年閃耀 2014-05-13

以 Intel? sandy bridge 微架構(gòu)為例,了解一下 Intel 近代微架構(gòu)。

    上圖是 Intel? sandy bridge 微架構(gòu)的流水線示意圖,實行了“發(fā)射-執(zhí)行-完成”相分離包括下面的組件(亂序執(zhí)行按序完成):

  1. in-order front-end:程序執(zhí)行順序的前端組件,包括了:
    • L1 ICache 與 ITLB ,指令通過 ITLB 查找 fetch(提?。┻M入到 32K 的 L1 指令 cache。
    • pre-decoder,一個預解碼器,主要用來解析指令的長度,處理 LCPs(length changing prefixes)。
    • instruction queue,經(jīng)過初步解析后存放的指令隊列。
    • decoder,4個解碼器。其中一個為復雜解碼器,能解碼所有 x86/x64 指令。三個簡單解碼器,負責解碼為一個 micro-op。
    • decoded ICache,解碼后的 uops(micro-ops)cache。
    • MSROM(microcode sequencer ROM),一個 microcode sequencer ROM(微代碼裝置 ROM),存放復雜指令的 micro-op 流。
    • BPU(branch prediction unit),分支預測單元。
    • micro-op queue,排列 decoded ICache 的 uops。
  2. out-of-order engine,亂序發(fā)射的引擎組件,包括了:
    • allocater/renamer,資源分配器與重命名器。Renamer 將 x86 架構(gòu)性(architectural)的源/目標操作數(shù)(寄存器)重命令為微架構(gòu)性(microarchitectural)源/目標操作數(shù)(寄存器),解決 uops 間的 false-dependencies(假依賴),并形成 out-of-order 的“data flow”(數(shù)據(jù)流)發(fā)送到 scheduler。 Allocater 分配 uops 需要的 load buffer 以及 store buffer。
    • scheduler,調(diào)度器。等待資源可用(dispatch port 可用,read buffer 或者 store buffer 可用)以及 uop 的操作數(shù)已經(jīng)準備好后,將 uop 綁定到相應 dispatch port 后分派到執(zhí)行單元。scheduler 每個 cycle 最多可以分派 6 個 uops 到執(zhí)行 port。
  3. in-order retirement,按序完成單元。使用 reorder buffer 保存 uops 各個階段的結(jié)果,確保 uops 的執(zhí)行結(jié)果(包括任何可能遇到的異常,中斷)按原始程序的次序完成。
  4. execution unit,執(zhí)行單元,含有 6 個執(zhí)行 port 以及 3 個類型的 stack。因此,schedular 每個 cycle 最多可以調(diào)度并分派 6 個 uops 到執(zhí)行 port。
  5. cache hierarchy,包括下面:
    • L1 DCache:
      • DCU(data cache unit)
      • load buffers
      • store buffers
      • line fill buffers
    • L1 ICache
    • L2 cache
    • LLC(last level cahce)

 

1. in-order front-end

    在按序前端里,包括了下面的組件:

  • L1 ICache 與 ITLB ,通過 ITLB 查找 fetch(提?。┻M入到 32K 的 L1 ICache。
  • legacy decode pipeline,下面的組件被劃分到 legacy decode pipeline 里:
    • pre-decoder,一個預解碼器,主要用來解析指令的長度,處理 LCPs(length changing prefixes)。
    • instruction queue,經(jīng)過初步解析后存放的指令隊列。
    • decoder,4個解碼器。其中一個為復雜解碼器,能解碼所有 x86/x64 指令。三個簡單解碼器,負責解碼為一個 micro-op。
  • decoded ICache,解碼后的 uops(micro-ops)cache。
  • MSROM(microcode sequencer ROM),一個 microcode sequencer ROM(微代碼裝置 ROM),存放復雜指令的 micro-op 流。
  • BPU(branch prediction unit),分支預測單元。
  • micro-op queue,排列 decoded ICache 的 uops。

1.1 ICache 與 ITLB

    在指令提?。╥nstruction fetch)階段,處理器通過 ITLB 查找并從內(nèi)存的 16-byte 邊界上提取指令到 ICache 里。當 ICache hit 時引發(fā) ICache 每個 cycle 傳送 16 bytes 到指令 pre-decoder 組件里。 如果以平均每條指令 4 個字節(jié)來算,那么 ICache 能滿足每個周期 4 個 decoder 的解碼工作??梢哉J為,如果遇到較長指令的話(例如 10 個字節(jié)),ICache 傳送的 16 bytes 是不能滿足 decoder 的。

    在 sandy bridge 微架構(gòu)上,ICacheITLB 的明細信息:

  • size: 32-Kbyte
  • ways: 8
  • ITLB 4K-page entries:128
  • ITLB large-page(2M/1G)entries:8
    也就是說,ICache 共有 32K,8-ways 結(jié)構(gòu)。ITLB 中維護 4K 頁面映射結(jié)果的表項有 128 個,維護 2M 與 1G 頁面映射結(jié)果的表項共有 8 個。 當產(chǎn)生 ITLB miss 時,處理器將在 STLB(second TLB,或者 shared TLB)里繼續(xù)查找。 ITLB miss 而在 STLB hit 時,所需要 7 個 cycles。當然,如果 STLB 也產(chǎn)生 miss 則需要在頁表結(jié)構(gòu)里進行 walk,將需要更多的 cycles。

1.2 pre-decoder

    pre-decoder 接收從 ICache 發(fā)送過來 16 bytes 的指令,它主要執(zhí)行下面的工作:

  • 確定指令的具體長度。
  • 處理指令所有的 prefix。
  • 標記指令的類型屬性。(例如:屬于分支指令

    pre-decoder 在每個 cycle 里,最多可以寫入 6 條指令到 instruction queue 里。也就是說:在每個 cycle 里,pre-decoder 最多可以從 16-bytes 里解析出 6 條指令放入 instruction queue。要達到每 cycle 6 條指令,這說明平均每條指令不能超過 2 個字節(jié)。 如果這 16 字節(jié)里包含多于 6 條指令(例如每條指令為 2 個字節(jié)),則在下一個 cycle 里 pre-decoder 會繼續(xù)按每 cycle 最多解碼 6 條指令進行解碼。

    例如,fetch line(16 字節(jié))里含有 7 條指令,那么首 6 條指令會在一個 cycle 完成解碼寫入 instruciton queue 里,第 7 條指令將在下一個 cycle 里解碼。在下一個 cycle 里,ICache 會繼續(xù)發(fā)送 16 bytes 到 pre-pecoder 里,pre-pecoder 繼續(xù)最多解碼 6 條指令。

    指令的 operand size 以及 address size 會影響著指令長度。我們知道 CS.D 決定了指令的 default operation size(默認的操作寬度)。當 CS.D = 1 時,默認的 operand size 與 address size 為 32 位。CS.D = 0 時,默認的 operand size 與 address size 為 16 位。 但是,default operand-size override prefix 與 default address-size override prefix 可以改變操作數(shù)與地址的寬度,從而改變了指令固定的長度,這兩個 prefix 被稱為 LCP(length changing prefix)。

  • default operand-size override prefix(66H):重新改寫默認的 32 位或者 16 位 operand size 為 16 位或者 32 位。
  • 例如:mov eax, 11223344h。它的指令編碼為 B8 44 33 22 11(指令長度為 5),當插入 66H 字節(jié)時,指令編碼為 66 B8 44 33(指令長度為 4)。
  • default address-size override prefix(67H):重新改寫默認的 32 位或者 16 位 address size 為 16 位或者 32 位。
  • 例如:mov eax, [11223344h],它的指令編碼為 A1 44 33 22 11(指令長度為 5),當插入 67H 字節(jié)時,指令編碼為 67 A1 44 33(指令長度為 4)

    當然,也可能存在超過 1 個 LCP 的情況(同時存在 66H 與 67H)。因此,如果指令含有 LCP 的話,pre-decoder 需要確定最終的指令長度,在解碼 LCP 時需要額外花費 3 個 cycles (注:在前一代架構(gòu)中,解碼 LCP 需要花費 6 個 cycles)。 另外,REX prefix 雖然也能改變指令的長度(MOV reg, [disp32] 或者 MOV reg64, imme64),但 pre-decoder 解碼時并不會花費額外的 cycles。

1.3 instruction queue

    instruction queue 組件在 pre-decoder 與 decoder 之間,經(jīng)過 pre-decoder 后指令的長度已經(jīng)確認,從 ICache 傳送過來的 16-bytes 被解析為 x86 指令寫入 instruction queue(指令隊列)里存放著,instruction queue 最多可以容納 18 條指令。由于 macro-fused(宏融合,兩條指令解碼為 1 個 uop)的存在,instruction queue 每個 cycle 最多傳送 5 條指令(其中包括了兩條可以宏融合的指令)到 decoder 進行解碼。

1.4 decoder

    decoder 負責將 x86/x64 的 CISC 指令解碼為單一功能的 micro-ops(uops,微操作),從 core 微架構(gòu)開始就擁有 4 個 decoder(解碼器)。第 1 個 decoder(decoder 0) 是復雜解碼器,能解碼所有的 x86/x64 指令, 每個 cycle 最多可以解碼為 4 個 uops。其余 3 個為簡單解碼器,將簡單指令解碼為 1 個 uop,每個 cycle 只能解碼 1 個 uop。

    第 1 個復雜解碼器也能解碼簡單指令,因此 4 個 decoder 都能解碼為 1 個 uop,包括 micro-fused(微融合),stack pointer tracking(棧指針跟蹤)以及 macro-fused(宏融合)。但是,只有一個 decoder 能解碼為 4 個 uops。那么,4 個 decoder 每個 cycles 最多可以解碼為 7 個 uops(4 + 1 + 1 + 1)。decoder 解碼后的 uops 被送入到 decoded ICache 以及 micro-op queue。

    MSROM 組件負責提供復雜的 uops 數(shù)據(jù)流,在 sandy bridge 微架構(gòu)里 MSROM 每個 cycle 能提供 4 個 uops,MSROM 用來幫助 decoder 解碼超過 4 個 uops 的指令。因此,當指令超過 4 個 uops 將從 MSROM 里取得。向 MSROM 取 uops 的操作可以由 decoder 或者 decoded ICache 組件發(fā)起。

    通過 macro-fusion(宏融合)技術(shù),decoder 能將兩條 x86 指令解碼為 1 個 uop。4 個 decoder 都可以產(chǎn)生 macro-fused 動作,但在每個 cycle 里 4 個 decoder 只能產(chǎn)生一個 macro-fused。因此,在每個 cycle 里 decoder 最多能解碼 5 條 x86 指令(2 + 1 + 1 + 1),最多能產(chǎn)生 7 個 uops。

1.4.1 micro-fusion(微融合)

    x86/x64 指令允許使用“memory-to-register”類操作數(shù),當指令的操作數(shù)是 memory 與 register 時將解碼為多個 uops。例如:“ADD RAX, [RBX]”指令是一條典型的操作數(shù)為 memory 與 register 的指令,它會被解碼為兩個 uops:一個為 load uop,另一個為 add uop。

    考查下面的一條 store 操作指令:

        mov [rbx + rcx * 8 + 0Ch], rax
            ---------------------  ---
                    |               |
                    |               +--------> store data 操作(使用 port 4)
                    |
                    +------------------------> store address 操作(使用 port 2 或 port 3)

    decoder 0 生成兩個 uops:一個是 store address 操作 uop,可以通過 port 2 或者 3 執(zhí)行。一個是 store data 操作 uop,通過 port 4 執(zhí)行。但在 dispatch 到 execution unit(執(zhí)行單元)時這兩個單一的 uop 被融合為一個復雜的 uop。

    micro-fused(微融合)允許將多個 uops 融合為 1 個復雜的 uop 進行 dispatch 到 execution unit(執(zhí)行單元)。在發(fā)射到執(zhí)行單元時,這個復雜的 uop 與單一的 uop 花費同樣的 cycles。因此,使用 micro-fused 將提高從 decoder 發(fā)射到 execution unit 的吞吐量。但是,在執(zhí)行單元中仍然是執(zhí)行兩個 uops 操作

    從 core 微架構(gòu)開始引入了 micro-fusion 功能,可以將下面幾類操作進行 micro-fused:

  1. store 操作:包括了 sotre 寄存器與立即數(shù)。例如:“mov [rdi], rax”指令與“mov DWORD [rdi], 1
  2. load-and-operation 操作:指令的操作數(shù)是 register 與 memory,執(zhí)行的是“l(fā)oad + op”操作(被解碼為 load uop 與另一個操作 uop)。
    例如:“add rax, [rsi]”,“addps mmx0,[rsi]”,“xor rax, [rsi]”指令等等...
  3. load-and-jump 操作:這是一條分支指令,目標地址從 memory 地址 load 而來。例如:“jmp QWORD [rax]”,“call QWORD [rax]”指令。RET 指令也是屬于 micro-fusion 指令,因為它從 RSP 指向的棧里 load 目標地址(即返回地址)。
  4. memory 與 immediate 之間的 cmp-or-test 操作:CMP 或者 TEST 指令的操作數(shù)是 memory 與 immediate。例如:“cmp DWORD [rsi], 1”,“test DWORD [rsi], 1”指令等。

    在 64-bit 模式下,指令使用 RIP-relative 尋址的 memory 時,在下面的情形下不能產(chǎn)生 micro-fused:

  • 指令的另一個操作數(shù)是 immediate。例如:“mov DWORD [rip + 50h], 400”,“cmp QWORD [rip + 50h], 1”指令等。
  • RIP-relative 尋址出現(xiàn)在分支指令里。例如:“ jmp QWORD [rip + 50h]”指令。

1.4.2 macro-fusion(宏融合)

    macro-fused 將兩條 x86 指令融合為一個 uop,允許進行宏融合的兩條指令需要滿足下面條件:

  1. 第一條指令修改了 eflags/rflags 寄存器(不同微架構(gòu)所支持的指令也不同)。
    • core, nehalem 微架構(gòu)上只支持 CMP 與 TEST 指令。
    • sandy bridge 微架構(gòu)上支持 CMP, TEST, ADD, SUB, AND, INC 以及 DEC 指令。
    如果存在兩個操作數(shù)(operand 1 與 operand 2),這些指令能產(chǎn)生 macro-fused 還需要滿足的條件是:operand 1(目標操作數(shù))是 register,并且 operand 2(源操作數(shù))是 immediate,register,或者非 RIP-relative 尋址的 memory。或者 operand 1 是 memory,而 operand 2 是 register。
    • REG-REG:例如 cmp eax, ecx 指令。
    • REG-IMM:例如 cmp eax, 1 指令。
    • REG-MEM:例如 cmp eax, [esi] 指令(非 RIP-relative 尋址)。
    • MEM-REG:例如 cmp [esi], eax 指令。
    但是,MEM-IMM 操作數(shù)不能產(chǎn)生 macro-fused,例如 cmp DWORD [eax], 1 指令。
  2. 后面的指令是條件分支指令(Jcc 指令)。但是,不同的指令,以及不同的微架構(gòu)所支持的條件不同。
    • TEST 指令所有的條件(所有的 eflags 標志位),包括:OF,CF, ZF, SF 以及 PF 標志位。
    • CMP 指令根據(jù)不同微架構(gòu)支持不同的條件。
      • core 微架構(gòu)僅支持 CFZF 標志位。因此,支持下面的 Jcc 指令:
        • JC/JB/JNAE:CF = 1
        • JNC/JNB/JAE:CF = 0
        • JZ/JE:ZF = 1
        • JNZ/JNE:ZF = 0
        • JBE/JNA:CF = 1 or ZF = 1
        • JA/JNBE:CF = 0 and ZF = 0
      • nehalem 微架構(gòu)增加了對 SF <> OF 與 SF == OF 條件的支持:
        • JL/JNGE:SF <> OF
        • JNL/JGE:SF = OF
        • JLE/JNG:SF <> OF or ZF = 1
        • JNLE/JG:SF = OF and ZF = 0
    • sandy bridge 微架構(gòu)增加了對 ADD, SUB, AND, INC 以及 DEC 指令的支持,它支持的條件如下表所示。
    • 條件

      分支指令

      TEST

      AND

      CMP

      AND

      SUB

      INC

      DEC

      OF = 1

      JO

      Y

      Y

      N

      N

      N

      N

      N

      OF = 0

      JNO

      CF = 1

      JC/JB/JNAE

      Y

      Y

      Y

      Y

      Y

      N

      N

      CF = 0

      JNC/JNB/JAE

      ZF = 1

      JZ/JE

      Y

      Y

      Y

      Y

      Y

      Y

      Y

      ZF = 0

      JNZ/JNE

      CF = 1 or ZF = 1

      JBE/JNA

      Y

      Y

      Y

      Y

      Y

      N

      N

      CF = 0 and ZF = 0

      JNBE/JA

      SF = 1

      JS

      Y

      Y

      N

      N

      N

      N

      N

      SF = 0

      JNS

      PF = 1

      JP/JPE

      PF = 0

      JNP/JPO

      SF <> OF

      JL/JNGE

      Y

      Y

      Y

      Y

      Y

      Y

      Y

      SF = OF

      JGE/JNL

      SF <> OF or ZF = 1

      JLE/JNG

      SF = OF and ZF = 0

      JG/JNLE

      上表中,Y 表示支持宏融合,N 表示不支持宏融合。

    在 core 微架構(gòu)里,不支持 signed 數(shù)的比較產(chǎn)生宏融合(即 JL/JNGE, JGE/JNL, JLE/JNG 以及 JG/JNLE)。這個情況在 nehalem 微架構(gòu)里得到改善,支持 signed 數(shù)的比較結(jié)果產(chǎn)生宏融合。

1.4.3 stack pointer tracker

    PUSH, POP, CALL, LEAVE 以及 RET 指令會隱式地更新 stack pointer 值,在 core 微架構(gòu)之后 decoder 負責維護這個隱式的更新 stack pointer 操作。

    思考一下這條指令 “push rax”,它在以前的微架構(gòu)中會產(chǎn)生多個 uops,大概處理如下面所示:

(1) TEMP = RAX                             ;; ===> renaming ?
(2) RSP = RSP - 8                          ;; ===> 生成 ALU uop
(3) [RSP] = TEMP                           ;; ===> 生成 STA(store address)uop 與 STD(store data)uop

    那么,根據(jù)上面的拆分,decoder 大致可以解碼為 3 個 uops:1 個 SUB uop,1 個 STA(store address) uop 以及 1 個 STD(store data) uop。

    再來看看這兩條指令“pop rax”與“ret”,大概處理如下面所示:

pop rax :
(1) rax = [RSP]                           ;; ===> 生成 LD uop
(2) RSP = RSP + 8                         ;; ===> 生成 ADD uop


ret :
(1) RIP = [RSP]                           ;; ===> 生成 JMP uop
(2) RSP = RSP + 8                         ;; ===> 生成 ADD uop

    pop rax 指令可以解碼為 2 個 uop:1 個 LD(load data) uop 與 1 個 ADD uop。ret 指令可以解碼為 2 個 uop:1 個 JMP uop 與 1 個 ADD uop。

    引進 stack pointer tracker (棧指針跟蹤器)這個功能后,將隱式棧指針更新操作移到 decoder 里實現(xiàn),從而釋放了 execution unit(執(zhí)行單元)資源,增加了發(fā)射與執(zhí)行帶寬。PUSH 指令需要 2 個 uops,而 POP 與 RET 只需要 1 個 uop。

1.5 decoded ICache

    由于 x86 指令的不定長以及指令解碼 uops 數(shù)量的不同,需要使用 decoded ICache 來緩存從 decoder 里解碼出來的 uops。送入 decoder 進行解碼的 16 bytes 可能會解析出少于 4 條 x86 指條或者多于 4 條(按平均每條指令 4 個 bytes 來算),解碼出來的 uops 數(shù)量也會不同,而 out-of-order 執(zhí)行單元每個 cycle 最多允許執(zhí)行 6 個 uops。 造成前端的解碼與后端執(zhí)行的 uops 不匹配,引入 decoded ICache 能很大程度地緩解這些不匹配而帶來的 bandwidth(帶寬)瓶頸。

    decoded ICache 是 8-ways 32-sets 結(jié)構(gòu),如下圖所示:

    每 set 的每個 way 最多能容納 6 個 uops。因此,理想狀態(tài)下整個 decoded ICache 能緩存 6 * 8 * 32 = 1536 個 uops。

    每個 way 裝載的 uops 是由 x86 指令字節(jié)里的 32 bytes 邊界解碼出來的(x86 指令 32 字節(jié)邊界對齊),也就是以 32 bytes 為一個塊,作為裝載單位。

  • 如果 32 bytes 指令塊解碼出來不足 6 個 uops 時,則 way 不會被填滿而留下空位。下一個 32 bytes 指令塊解碼的 uop 會裝入下一個 way 里。
  • 如果 32 bytes 指令塊解碼出來的 uops 超過 6 個時,表明一個 way 不能裝下全部 uops,則余下的 uops 會裝載到下一個 way 里。最多有 3 個連續(xù)的 ways 來容納這些 uops。也就是 32 bytes 的指令塊解碼出來的 uops 最多只能裝入 3 個 ways 里。那么,允許一個 32 bytes 指令塊最多只有 18(6 * 3) 個 uops 可以裝入 decoded ICache 中。

   除了上面,way 的裝填還有一些限制:

  • 如果一條 x86 指令解碼為多個 uops 時,這些 uops 不能跨 way 裝載,只能放入同一個 way 里。
  • 每個 way 里最多只能裝入 2 個分支 uops
  • 當指令從 MSROM 里獲得 uops 時,這些 uops 必須獨占一個 way。
  • 非條件分支 uop 必須是 way 里的最后一個 uop。
  • 如果指令含有 64 位的立即數(shù),這個立即數(shù)必須占用兩個 way。

    當由于這些限制而造成 uops 不能裝入 decoded ICache 時(例如 32 bytes 指令塊解碼出來可能超過 18 個 uops),這些 uops 被直接發(fā)送到 out-of-order engine。

    decoded ICache 是 L1-ICache(instruction cache)和 ITLB 對應的一份 shadow cache,ICache 存放的是 x86 指令字節(jié)碼,而 decoded ICache 存放的是 uops。也就是說:decoded ICache 里緩存的任何一個 uops 都在 ICache 里存在著對應的 x86 指令。那么,刷新 ICache lines 的指令時,也必須刷新 decoded ICache 里指令對應 uops。

    當 ITLB 的某個 entry(或全部)被刷新時,可能造成整個 ICache 被刷新,同時也會使得整個 decoded ICache 被刷新。例如:更新 CR3 寄存器值從而更新了整個頁轉(zhuǎn)換表結(jié)構(gòu)(或者 CR4 寄存器某些頁機制相關(guān)的控制位被更新)。

1.6 BPU (branch prediction unit)

    流水線前端利用 BPU(分支預測單元)盡可能地在確定分支指令的執(zhí)行路徑之前就預測出分支的目標地址。 BPU 能預測下面的分支類型:

  • conditional branches(條件分支):也就是 Jcc 指令族。

            test eax, eax
            jz @taken


            ... ...                                 ;; 分支跳轉(zhuǎn)不成立


    @taken:        
            ... ...                                 ;; 分支跳轉(zhuǎn)成立

    BPU 預測這個分支跳轉(zhuǎn)的目標地址。也就是預測這個跳轉(zhuǎn)是否成立。
  • direct calls/jumps(直接的調(diào)用與跳轉(zhuǎn)),它們的目標地址是基于 RIP 與 offset 值而來。如下面代碼所示:
  •         jmp @target                             ;; 直接跳轉(zhuǎn)


            ... ...


    @target:
            ... ... 
           
            call @fun                               ;; 直接調(diào)用               

  • indirect calls/jumps(間接的調(diào)用與跳轉(zhuǎn)),它們的目標地址從 register 或 memory 里讀取。如下面代碼所示:
  • @fun:           __func
            ... ...


    @target:        __target




            jmp DWORD [@target]                     ;; 間接跳轉(zhuǎn)
            ;;
            ;; 或者:
            ;;      mov eax, __target
            ;;      jmp eax


            ... ...


    __target:
            ... ... 
           
            call DWORD [@fun]                       ;; 間接跳轉(zhuǎn)
            ;;
            ;; 或者:
            ;;      mov eax, __func
            ;;      call eax

  • returns(調(diào)用返回),也就是 RETRET n 指令。使用 16 個 entries 的 RSB(return stack buffer)結(jié)構(gòu)實現(xiàn)。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多