日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

如何将自定义代码生成TVM

發(fā)布時(shí)間:2023/11/28 生活经验 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何将自定义代码生成TVM 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

如何將自定義代碼生成TVM
如何將自定義代碼生成TVM
本文參考鏈接:
https://tvm.apache.org/docs/dev/how_to/relay_bring_your_own_codegen.html
https://blog.csdn.net/weixin_42164269/article/details/104291635
簡(jiǎn)介
深度學(xué)習(xí)針對(duì)的硬件設(shè)備的數(shù)量不斷增加,用戶需要在各種設(shè)備上實(shí)現(xiàn)高性能所需的知識(shí)。硬件后端提供者要么提供像MKLDNN或cuDNN類的庫,包含許多常用的深度學(xué)習(xí)運(yùn)算符,要么提供諸如TensorRT這樣的框架,用戶以某種方式描述模型實(shí)現(xiàn)高性能。但是,用戶嘗試在新的庫或設(shè)備上工作時(shí),必須學(xué)習(xí)新的編程接口。結(jié)果,對(duì)統(tǒng)一編程接口的需求變得越來越重要。
1)讓所有用戶和硬件后端提供者站在同一頁面上
2)提供一種可行的解決方案,允許專用硬件或庫僅支持具有極高性能的廣泛使用的運(yùn)算符,但不支持的運(yùn)算符回退到CPU / GPU等常規(guī)設(shè)備。
本文主要內(nèi)容如下:
目錄
簡(jiǎn)介

  1. 生成C代碼。
  2. 生成任何其它圖形表示。
    實(shí)現(xiàn)一個(gè)C代碼生成器
    實(shí)現(xiàn)【CodegenC】
    運(yùn)算符代碼生成
    輸入變量的代碼生成
    代碼發(fā)送
    實(shí)現(xiàn)【CSourceCodegen 】
    實(shí)現(xiàn)【GenCFunc 】
    實(shí)現(xiàn)【CreateCSourceModule 】
    注冊(cè)代碼生成
    實(shí)現(xiàn)一個(gè)代碼生成表示
    實(shí)現(xiàn)【ExampleJsonCodeGen 】
    實(shí)現(xiàn)自定義runtime
    實(shí)現(xiàn)構(gòu)造函數(shù)
    實(shí)現(xiàn)【GetFunction 】
    實(shí)現(xiàn)運(yùn)行
    實(shí)現(xiàn)【SaveToBinary】和【LoadFromBinary 】
    總結(jié)
    在本開發(fā)人員指南中,演示了作為硬件后端提供者,如何輕松實(shí)現(xiàn)自定義代碼生成,注冊(cè)為Relay后端編譯器,支持硬件設(shè)備/庫。本文根據(jù)需要的不同圖形表示形式,涵蓋兩種類型的代碼生成器:
  3. 要生成C代碼。
    如果硬件已經(jīng)具有經(jīng)過優(yōu)化的C/C ++庫,如對(duì)CPU擁有Intel CBLAS / MKL,GPU擁有NVIDIA CUBLAS,這就是所需要的。幸運(yùn)的是,C源代碼模塊與TVM runtime模塊完全兼容,生成的代碼可以由具有適當(dāng)編譯標(biāo)志的任何C / C ++編譯器進(jìn)行編譯,唯一的任務(wù)就是實(shí)現(xiàn)一個(gè)為子圖生成C代碼的代碼生成器和一個(gè)C源模塊,集成到TVM runtime模塊中。在下一節(jié)中,將演示如何為硬件實(shí)現(xiàn)C代碼生成器。
  4. 生成任何其它圖形表示。
    硬件可能需要其它形式的圖形表示形式,如JSON。在這種情況下,不僅需要實(shí)現(xiàn)代碼生成,還需要實(shí)現(xiàn)自定義的TVM runtime模塊,使TVM runtime知道應(yīng)如何執(zhí)行圖形表示。如果已經(jīng)為硬件配備了完整的圖形執(zhí)行引擎,如用于GPU的TensorRT,可以考慮采用這種解決方案。
    在完成代碼生成和runtime后,可以讓客戶使用自定義標(biāo)簽注釋模型使用。
    實(shí)現(xiàn)一個(gè)C代碼生成器
    在這一部分中,演示如何實(shí)現(xiàn)使用預(yù)實(shí)現(xiàn)的運(yùn)算符函數(shù),生成C代碼的代碼生成器。簡(jiǎn)化起見,示例代碼生成器不依賴于第三方庫。相反,在C中手動(dòng)實(shí)現(xiàn)了兩個(gè)宏:
    #define CSOURCE_BINARY_OP_1D(p_ID_, p_OP_, p_DIM1_)
    extern “C” void p_ID_(float* a, float* b, float* out) {
    for (int64_t i = 0; i < p_DIM1_; ++i) {
    out[i] = a[i] p_OP_ b[i];
    }
    }

#define CSOURCE_BINARY_OP_2D(p_ID_, p_OP_, p_DIM1_, p_DIM2_)
extern “C” void p_ID_(float* a, float* b, float* out) {
for (int64_t i = 0; i < p_DIM1_; ++i) {
for (int64_t j = 0; j < p_DIM2_; ++j) {
int64_t k = i * p_DIM2_ + j;
out[k] = a[k] p_OP_ b[k];
}
}
}
使用這兩個(gè)宏,可以為一維和二維張量,生成二進(jìn)制運(yùn)算符。如給定一個(gè)子圖如下。假設(shè)所有輸入都是二維張量,形狀為(10,10)。
c_compiler_input0
|
add <-- c_compiler_input1
|
subtract <-- c_compiler_input2
|
multiply <-- c_compiler_input3
|
out
目標(biāo)是生成以下可編譯代碼執(zhí)行子圖:
#include <tvm/runtime/c_runtime_api.h>
#include <tvm/runtime/packed_func.h>
#include <dlpack/dlpack.h>
#include
#include
#include

#define GCC_BINARY_OP_1D(p_ID_, p_OP_, p_DIM1_)
extern “C” void p_ID_(float* a, float* b, float* out) {
for (int64_t i = 0; i < p_DIM1_; ++i) {
out[i] = a[i] p_OP_ b[i];
}
}

#define GCC_BINARY_OP_2D(p_ID_, p_OP_, p_DIM1_, p_DIM2_)
extern “C” void p_ID_(float* a, float* b, float* out) {
for (int64_t i = 0; i < p_DIM1_; ++i) {
for (int64_t j = 0; j < p_DIM2_; ++j) {
int64_t k = i * p_DIM2_ + j;
out[k] = a[k] p_OP_ b[k];
}
}
}

// Note 1
GCC_BINARY_OP_2D(gcc_0_0, *, 10, 10);
GCC_BINARY_OP_2D(gcc_0_1, -, 10, 10);
GCC_BINARY_OP_2D(gcc_0_2, +, 10, 10);

// Note 2
extern “C” void gcc_0_(float* gcc_input0, float* gcc_input1,
float* gcc_input2, float* gcc_input3, float* out) {
float* buf_0 = (float*)malloc(4 * 100);
float* buf_1 = (float*)malloc(4 * 100);
gcc_0_2(gcc_input0, gcc_input1, buf_0);
gcc_0_1(buf_0, gcc_input2, buf_1);
gcc_0_0(buf_1, gcc_input3, out);
free(buf_0);
free(buf_1);
}

// Note 3
extern “C” int gcc_0_wrapper(DLTensor* arg0, DLTensor* arg1, DLTensor* arg2,
DLTensor* arg3, DLTensor* out) {
gcc_0_(static_cast<float*>(arg0->data), static_cast<float*>(arg1->data),
static_cast<float*>(arg2->data), static_cast<float*>(arg3->data),
static_cast<float*>(out->data));
return 0;
}
TVM_DLL_EXPORT_TYPED_FUNC(gcc_0, gcc_0_wrapper);
在這里,突出顯示上面代碼中標(biāo)記的注釋:
Note1,子圖中三個(gè)節(jié)點(diǎn)的函數(shù)實(shí)現(xiàn)。
Note2,一個(gè)函數(shù),通過分配中間緩沖區(qū),調(diào)用相應(yīng)函數(shù)執(zhí)行子圖。
Note3,TVM runtime兼容的包裝函數(shù)。接受一個(gè)輸入張量和一個(gè)輸出張量的列表(最后一個(gè)參數(shù)),轉(zhuǎn)換為正確的數(shù)據(jù)類型,調(diào)用Note2中描述的子圖函數(shù)。此外,【TVM_DLL_EXPORT_TYPED_FUNC】是一個(gè)TVM宏,生成另一個(gè)函數(shù)【gcc_0】,【gcc_0】具有統(tǒng)一的函數(shù)參數(shù),通過把所有的參數(shù)張量打包成【TVMArgs】。結(jié)果,TVM runtime可以直接調(diào)用gcc_0執(zhí)行子圖,無需付出額外的努力。使用上面生成的代碼,TVM可以與圖的其余部分一起編譯,導(dǎo)出單個(gè)庫以進(jìn)行部署。
在本節(jié)的其余部分,將逐步實(shí)現(xiàn)一個(gè)codegen生成上述代碼。自定義代碼源必須位于src/relay/backend/contrib//。在示例中,將代碼源命名為“ codegen_c”,將放在“此處https://github.com/apache/incubator-tvm/blob/master/src/relay/backend/contrib/codegen_c/codegen.cc下`_。可以隨時(shí)檢查文件,獲取完整的實(shí)現(xiàn)。
在此文件中實(shí)現(xiàn)兩個(gè)類,這是相互關(guān)系:
subgraph subgraph
TVM backend -----------------------------> CSourceCodegen -------------> CodegenC
^ | ^ |
| | | |
---------------------------------------- ------------------------
generated C source runtime module generated C code
當(dāng)TVM后端在Relay中找到一個(gè)函數(shù)(子圖)時(shí),使用已注冊(cè)的編譯器標(biāo)記進(jìn)行注釋(【ccompiler】在此示例中),TVM后端將調(diào)用【CSourceCodegen】,轉(zhuǎn)換該子圖。【CSourceCodegen】的成員函數(shù)【CreateCSourceModule】將
1)為子圖生成C代碼,
2)將生成的C代碼包裝到C源runtime模塊中,供TVM后端編譯和部署。
特別地,C代碼生成對(duì)于【CodegenC】類是透明的,因?yàn)樘峁┝嗽S多有用的實(shí)用程序,簡(jiǎn)化代碼生成的實(shí)現(xiàn)。以下各節(jié)將以自底向上的順序?qū)崿F(xiàn)這兩個(gè)類。
實(shí)現(xiàn)【CodegenC】
在src/relay/backend/contrib/codegen_c/codegen.cc中,先在【tvm.relay.contrib】名稱空間下,創(chuàng)建一個(gè)代碼生成類骨架:
#include <tvm/relay/expr_functor.h>
#include <tvm/relay/transform.h>
#include <tvm/relay/type.h>
#include <tvm/runtime/module.h>
#include <tvm/runtime/object.h>

#include
#include

#include “codegen_c.h”

namespace tvm {
namespace relay {
namespace contrib {

class CodegenC : public ExprVisitor, public CodegenCBase {
public:
explicit CodegenC(const std::string& id) { this->ext_func_id_ = id; }

void VisitExpr_(const VarNode* node) { ; }
void VisitExpr_(const CallNode* call) final { ; }
std::string JIT() { ; }

private:
/*! \brief The function id that represents a C source function. /
std::string ext_func_id_ = “”;
/
! \brief The index of a wrapped C function. /
int func_idx = 0;
/
! \brief The index of allocated buffers. /
int buf_idx_ = 0;
/
! \brief The arguments of a C compiler compatible function. /
std::vectorstd::string ext_func_args_;
/
! \brief The statements of a C compiler compatible function. /
std::vectorstd::string ext_func_body;
/
! \brief The declaration statements of a C compiler compatible function. /
std::vectorstd::string func_decl_;
/
! \brief The declaration statements of buffers. /
std::vectorstd::string buf_decl_;
/
! \brief The name and index pairs for output. /
std::vector<std::pair<std::string, int>> out_;
}
【CodegenC】類繼承兩個(gè)類:【ExprVisitor】提供遍歷子圖,收集所需的信息并生成子圖的功能的能力,如【gcc_0_】; 【CodegenCBase】提供了生成包裝函數(shù)的功能和用法,如gcc_0上面的示例。可以看出,只需要在此codegen類中,實(shí)現(xiàn)三個(gè)函數(shù)即可工作。
運(yùn)算符代碼生成
先實(shí)現(xiàn)【VisitExpr_(const CallNode
call)】。遍歷子圖時(shí),此函數(shù)訪問所有調(diào)用節(jié)點(diǎn)。每個(gè)調(diào)用節(jié)點(diǎn)都包含一個(gè)要卸載到硬件上的運(yùn)算符。結(jié)果,需要按照拓?fù)漤樞蚴褂谜_的運(yùn)算符,生成相應(yīng)的C代碼。按以下步驟逐步實(shí)現(xiàn)此功能。

  1. 生成函數(shù)聲明
    結(jié)果示例:【GCC_BINARY_OP_2D(gcc_0_0, , 10, 10);】
    如上所示,要生成函數(shù)聲明,需要
    1)函數(shù)名稱(例如gcc_0_0)
    2)運(yùn)算符的類型(如

    3)輸入張量形狀(如(10, 10))。
    幸運(yùn)的是,可以從【CallNode】位置輕松獲取此信息:

std::ostringstream macro_stream;
std::ostringstream decl_stream;
std::ostringstream buf_stream;

// Generate a unique function name you like.
std::string func_name = ext_func_id_ + “_” + std::to_string(func_idx++);

// Make function declaration string.
macro_stream << “CSOURCE_BINARY_OP_” << call->args.size() << “D(” << func_name << ", ";

// Check the operator type.
if (IsOp(call, “add”)) {
macro_stream << “+”;
} else if (IsOp(call, “subtract”)) {
macro_stream << “-”;
} else if (IsOp(call, “multiply”)) {
macro_stream << “*”;
} else {
LOG(FATAL) << “Unrecognized op”;
}

// Extract the input tensor shape.
auto in_shape = GetShape(call->args[0]->checked_type());
for (size_t i = 0; i < in_shape.size(); ++i) {
macro_stream << ", " << in_shape[i];
}
macro_stream << “);”;
func_decl_.push_back(macro_stream.str());
可以看出,將生成的代碼放到類成員變量【func_decl_】。在完成遍歷整個(gè)子圖后,已經(jīng)收集了所有必需的函數(shù)聲明,唯一需要做的就是讓由GCC進(jìn)行編譯。【VisitExpr_(const CallNode* call)】的實(shí)現(xiàn),也遵循此概念。
2. 生成函數(shù)調(diào)用
結(jié)果示例:【gcc_0_0(buf_1, gcc_input3, out);】
生成函數(shù)聲明后,需要生成具有正確輸入和輸出的函數(shù)調(diào)用。要知道在調(diào)用此函數(shù)時(shí),應(yīng)放置哪些輸入或緩沖區(qū),必須訪問參數(shù):
bool first = true;
decl_stream << func_name << “(”;
for (size_t i = 0; i < call->args.size(); ++i) {
VisitExpr(call->args[i]); // Note 1
for (auto out : out_) {
if (!first) {
decl_stream << ", ";
}
first = false;
decl_stream << out.first;
}
}
// Note 2
同樣,要突出顯示以上代碼中的注釋:
Note1:【VisitExpr(call->args[i])】是遞歸調(diào)用,訪問當(dāng)前函數(shù)的參數(shù)。參數(shù)可以是另一個(gè)節(jié)點(diǎn)的輸出或輸入張量。在示例實(shí)現(xiàn)中,確保每個(gè)節(jié)點(diǎn)在離開訪問器前,都更新一個(gè)類變量【out_】。這是一個(gè)例子:
arg_node arg_node <- Visit arg (Note 1) arg_node
| | |
curr_node <- Process curr_node curr_node <- Put “buf_0” as an input buffer

(a) out_ = {} (b) out_ = {} ? out_ = {(“buf_0”, 20)}
可以在上圖中看到,在訪問參數(shù)節(jié)點(diǎn)前,類變量【out_】為空,填充了【arg_node】輸出緩沖區(qū)的名稱和大小。結(jié)果,當(dāng)完成訪問參數(shù)節(jié)點(diǎn)時(shí),可以通過查看【out_】,應(yīng)該放置適當(dāng)?shù)妮斎刖彌_區(qū)。將在本節(jié)末尾和下一節(jié)中找到更新【out_】的方式。
注意2:可能會(huì)注意到,在此步驟中沒有關(guān)閉函數(shù)調(diào)用字符串。當(dāng)前的函數(shù)調(diào)用字符串,如下所示:【gcc_0_0(buf_1, gcc_input3】。這是因?yàn)闆]有將最后一個(gè)參數(shù)(即輸出)放入此調(diào)用。函數(shù)調(diào)用的輸出可以是分配的臨時(shí)緩沖區(qū),也可以是子圖輸出張量。簡(jiǎn)化起見,在此示例中,為每個(gè)調(diào)用節(jié)點(diǎn)分配一個(gè)輸出緩沖區(qū)(下一步),將結(jié)果從最后一個(gè)緩沖區(qū)復(fù)制到輸出張量。
3.生成輸出緩沖區(qū)
結(jié)果示例: 【float* buf_0 = (float*)malloc(4 * 100);】
如上一步所述,除了子圖輸入和輸出張量外,可能還需要緩沖區(qū)來保留中間結(jié)果。為了生成緩沖區(qū),提取形狀信息以確定緩沖區(qū)的類型和大小:
// This example only supports single output.
auto type_node = call->checked_type().as();
CHECK(type_node != nullptr && runtime::TypeMatch(type_node->dtype, kDLFloat, 32))
<< “Only support single output tensor with float type”;

// Generate a unique buffer name.
std::string out = “buf_” + std::to_string(buf_idx_++);

// Extract the shape to be the buffer size.
auto out_shape = GetShape(call->checked_type());
int out_size = 1;
for (size_t i = 0; i < out_shape.size(); ++i) {
out_size *= out_shape[i];
}

// Make the buffer allocation and push to the buffer declarations.
buf_stream << "float* " << out << " = (float*)std::malloc(4 * " << out_size << “);”;
buf_decl_.push_back(buf_stream.str());
分配輸出緩沖區(qū)后,現(xiàn)在可以關(guān)閉函數(shù)調(diào)用字符串,將生成的函數(shù)調(diào)用,放到類變量【ext_func_body】。
decl_stream << ", " << out << “);”;
ext_func_body.push_back(decl_stream.str());
4. 更新輸出緩沖區(qū)
為了接受當(dāng)前調(diào)用節(jié)點(diǎn)的輸出,作為輸入的下一個(gè)節(jié)點(diǎn),知道應(yīng)使用的緩沖區(qū),需要在離開此訪問函數(shù)前,更新類變量【out_】。
out_.clear();
out_.push_back({out, out_size});
恭喜!已經(jīng)完成了本文中最困難的功能。在接下來的兩節(jié)中,只需要組成此函數(shù)中的一些次要缺失部分。
輸入變量的代碼生成
回想一下,通過訪問調(diào)用節(jié)點(diǎn)的參數(shù),收集輸入緩沖區(qū)的信息(上一節(jié)的第二步),處理了參數(shù)是另一個(gè)調(diào)用節(jié)點(diǎn)的情況(第四步)。在本節(jié)中,以【VarNode】示例,演示如何處理其它節(jié)點(diǎn)。
【VarNode】表示模型中的輸入張量。擁有的唯一的,但重要的信息是名稱提示(如data,weight等)。在訪問【VarNode】時(shí),只需更新類變量【out_】傳遞名稱提示,以便后代調(diào)用節(jié)點(diǎn)可以生成正確的函數(shù)調(diào)用。
void VisitExpr_(const VarNode* node) {
ext_func_args_.push_back(node->name_hint());
out_.clear();
out_.push_back({node->name_hint(), 0});
}
在此示例中,假設(shè)要卸載的子圖僅具有調(diào)用節(jié)點(diǎn)和變量節(jié)點(diǎn)。如果子圖包含其它類型的節(jié)點(diǎn),如TupleNode,需要訪問并繞過輸出緩沖區(qū)信息。
代碼發(fā)送
該【codegen】類的最后一部分是一個(gè)【JIT】函數(shù),該函數(shù)為子圖發(fā)送C函數(shù),將剛生成的C代碼用作函數(shù)體。除了前面幾節(jié)中生成的子圖函數(shù)外,需要一個(gè)包裝器函數(shù),該函數(shù)具有統(tǒng)一的參數(shù),TVM runtime可以調(diào)用和傳遞數(shù)據(jù)。幸運(yùn)的是,繼承的基類已經(jīng)提供了實(shí)現(xiàn)【JitImpl】來生成函數(shù)。例如,可以調(diào)用【JitImpl】如下:
JitImpl(“gcc_0” /* Subgraph symbol (ID) /,
{“gcc_input0”, “gcc_input1”, “gcc_input2”, “gcc_input3”} /
Input arguments /,
{“float buf_0 = (float)malloc(4 * 20)”, …} /
Buffer allocations /,
{“gcc_0_2(gcc_input0, gcc_input1, buf_0);”} /
Function body /,
{“out”} /
Output */);
上面的調(diào)用將生成三個(gè)函數(shù)(一個(gè)來自TVM包裝器宏):

  1. 子圖函數(shù)【gcc_0_】(在函數(shù)名的末尾,還有一個(gè)下劃線),包含生成的所有C代碼以執(zhí)行子圖。
  2. 裝飾函數(shù)【gcc_0__wrapper_】帶有【DLTensor】參數(shù)列表,該參數(shù)列表將數(shù)據(jù)轉(zhuǎn)換為正確的類型,調(diào)用【gcc_0_】。
  3. TVM runtime兼容函數(shù)【gcc_0】具有TVM統(tǒng)一函數(shù)參數(shù),可解壓縮TVM打包的張量,調(diào)用【gcc_0__wrapper_】。

因此,【JIT】實(shí)現(xiàn)過程中唯一需要做的,就是將生成的所有子圖函數(shù)代碼傳遞給【JitImpl】:
std::string JIT() {
// Write function macros
for (auto decl : func_decl_) {
code_stream_ << decl << “\n”;
}
return JitImpl(ext_func_id_, ext_func_args_, buf_decl_, ext_func_body, out_);
}
傳遞的所有的變量(【ext_func_id】等)都是類變量,在遍歷子圖時(shí)會(huì)被填充。
實(shí)現(xiàn)【CSourceCodegen 】
同樣,創(chuàng)建一個(gè)類框架,實(shí)現(xiàn)所需的功能。注意,繼承【CSourceModuleCodegenBase】
class CSourceCodegen : public CSourceModuleCodegenBase {
public:
// Pass a subgraph function, and generate the C code.
void GenCFunc(const Function& func) { ; }

// Use GenCFunc to generate the C code and wrap it as a C source module.
runtime::Module CreateCSourceModule(const NodeRef& ref) override { ; }

private:
std::ostringstream code_stream_;
};
實(shí)現(xiàn)【GenCFunc 】
【GenCFunc】只需使用【CodegenC】,只是實(shí)現(xiàn)遍歷Relay函數(shù)(子圖),獲得生成的C代碼即可。內(nèi)置函數(shù)【GetExtSymbol】在Relay 函數(shù)中,檢索唯一的符號(hào)名稱(例如gcc_0),用作C函數(shù)名稱,因?yàn)樵摲?hào)將用于DSOruntime查找。

void GenCFunc(const Function& func) {
CHECK(func.defined()) << “Input error: expect a Relay function.”;

// Record the external symbol for runtime lookup.
auto sid = GetExtSymbol(func);

CodeGenC builder(sid);
builder.VisitExpr(func->body);
code_stream_ << builder.JIT();
}
實(shí)現(xiàn)【CreateCSourceModule 】
該函數(shù)為外部庫創(chuàng)建一個(gè)runtime模塊。在此示例中,創(chuàng)建了一個(gè)【CSourceModule】,可以直接編譯,與TVM生成的DSOModule鏈接在一起。實(shí)現(xiàn)【CodegenC】后,實(shí)現(xiàn)此功能相對(duì)簡(jiǎn)單:
runtime::Module CreateCSourceModule(const NodeRef& ref) override {
// Create headers
code_stream_ << “#include \n”;
code_stream_ << “#include \n”;
code_stream_ << “#include \n”;
code_stream_ << “#include <stdio.h>\n”;
code_stream_ << “#include \n”;
code_stream_ << “#include <tvm/runtime/c_runtime_api.h>\n”;
code_stream_ << “#include <dlpack/dlpack.h>\n”;

// Append some common macro for operator definition.
const char* operator_macro = R"op_macro(
#define CSOURCE_BINARY_OP_1D(p_ID_, p_OP_, p_DIM1_)
extern “C” void p_ID_(float* a, float* b, float* out) {
for (int64_t i = 0; i < p_DIM1_; ++i) {
out[i] = a[i] p_OP_ b[i];
}
}

#define CSOURCE_BINARY_OP_2D(p_ID_, p_OP_, p_DIM1_, p_DIM2_)
extern “C” void p_ID_(float* a, float* b, float* out) {
for (int64_t i = 0; i < p_DIM1_; ++i) {
for (int64_t j = 0; j < p_DIM2_; ++j) {
int64_t k = i * p_DIM2_ + j;
out[k] = a[k] p_OP_ b[k];
}
}
}
)op_macro";

code_stream_ << operator_macro << “\n\n”;

// Generate C code for the subgraph.
if (ref->IsInstance()) {
GenCFunc(Downcast(ref));
} else if (ref->IsInstancerelay::ModuleNode()) {
relay::Module mod = Downcastrelay::Module(ref);
for (const auto& it : mod->functions) {
GenCFunc(Downcast(it.second));
}
} else {
LOG(FATAL) << “The input ref is expected to be a Relay function or module”
<< “\n”;
}

// Create a CSourceModule
const auto* pf = runtime::Registry::Get(“module.csource_module_create”);
CHECK(pf != nullptr) << “Cannot find csource module to create the external runtime module”;
return (pf)(code_stream_.str(), “cc”);
}
注冊(cè)代碼生成
最后一步是將代碼生成器注冊(cè)到TVM后端。先實(shí)現(xiàn)一個(gè)簡(jiǎn)單的函數(shù),調(diào)用代碼生成器,生成一個(gè)runtime模塊。
runtime::Module CCompiler(const NodeRef& ref) {
CSourceCodegen csource;
return csource.CreateCSourceModule(ref);
}
最后,將此功能注冊(cè)到TVM后端:
TVM_REGISTER_GLOBAL(“relay.ext.ccompiler”).set_body_typed(CCompiler);
其中【ccompiler】是一個(gè)自定義標(biāo)簽,用于TVM知道這是在用【ccompiler】注釋子圖時(shí),應(yīng)使用生成和卸載子圖的代碼生成器。
最后,一個(gè)好的做法是設(shè)置CMake配置標(biāo)志,僅為客戶提供編譯器。先創(chuàng)建一個(gè)cmake文件【cmake/modules/contrib/CODEGENC.cmake】:
if(USE_CODEGENC)
file(GLOB CSOURCE_RELAY_CONTRIB_SRC src/relay/backend/contrib/codegen_c/codegen.cc)
list(APPEND COMPILER_SRCS ${CSOURCE_RELAY_CONTRIB_SRC})
endif(USE_CODEGENC)
這樣,用戶可以在配置TVM時(shí),使用【config.cmake】以下命令,配置是否包括編譯器:
set(USE_CODEGENC ON)
為表示實(shí)現(xiàn)一個(gè)代碼生成
盡管已經(jīng)演示了如何實(shí)現(xiàn)C代碼生成,但是硬件可能需要其它的圖形表示形式,如JSON。在這種情況下,可以修改【CodegenC】類,已經(jīng)實(shí)現(xiàn)了自定義圖形表示,實(shí)現(xiàn)定制的runtime模塊,使TVM runtime知道,如何執(zhí)行該圖形表示。
為了簡(jiǎn)化,在本文中定義了一個(gè)名為“ ExampleJSON”的圖表示。ExampleJSON不是真正的JSON,僅僅是沒有控制流的圖的簡(jiǎn)單表示。例如,假設(shè)有一個(gè)名為【subgraph_0】的子圖:
input0
|
add <-- input1
|
subtract <-- input2
|
multiply <-- input3
|
out
然后,該子圖的【ExampleJON】如下所示:
subgraph_0
input 0 10 10
input 1 10 10
input 2 10 10
input 3 10 10
add 4 inputs: 0 1 shape: 10 10
sub 5 inputs: 4 2 shape: 10 10
add 6 inputs: 5 3 shape: 10 10
【input】關(guān)鍵字聲明輸入張量的ID和形狀; 其它語句則以語法描述計(jì)算:
【 inputs: [input ID] shape: [shape]】
在本節(jié)中,目標(biāo)是實(shí)現(xiàn)以下定制的TVM runtime模塊,執(zhí)行【ExampleJSON】圖。
runtime::Module ExampleJsonCompiler(const NodeRef& ref) {
ExampleJsonCodeGen codegen(ref);
std::string code = codegen.gen(); // Note 1
const auto
pf = runtime::Registry::Get(“module.examplejson_module_create”); // Note 2
CHECK(pf != nullptr) << “Cannot find ExampleJson module to create the external runtime module”;
return (*pf)(code);
}
TVM_REGISTER_GLOBAL(“relay.ext.examplejsoncompiler”).set_body_typed(ExampleJsonCompiler);
Note1:將實(shí)現(xiàn)自定義代碼生成,通過子圖生成ExampleJSON代碼字符串。
Note2:此行獲得指向用于創(chuàng)建定制runtime模塊的函數(shù)的指針。可以看到采用了剛剛生成的ExampleJSON格式的子圖代碼,初始化了runtime模塊。
在以下各節(jié)中,將介紹
1)如何實(shí)現(xiàn)【ExampleJsonCodeGen】
2)如何實(shí)現(xiàn)和注冊(cè)【examplejson_module_create】。
實(shí)現(xiàn)【ExampleJsonCodeGen 】
類似于C代碼生成器,從【ExprVisitor】派生了【ExampleJsonCodeGen】,利用訪問者模式,進(jìn)行子圖遍歷的方法。另一方面,不需要繼承【CodegenCBase】,因?yàn)椴恍枰猅VM C ++裝飾器。codegen類的實(shí)現(xiàn)如下:
#include <tvm/relay/expr_functor.h>
#include <tvm/relay/transform.h>
#include <tvm/relay/type.h>
#include <tvm/runtime/module.h>
#include <tvm/runtime/object.h>

#include
#include

namespace tvm {
namespace relay {
namespace contrib {

class ExampleJsonCodeGen : public ExprVisitor {
public:
explicit ExampleJsonCodeGen();

// Note 1
void VisitExpr_(const VarNode* node) { /* Skip in this example. */ }
void VisitExpr_(const CallNode* call) final { /* Skip in this example. */ }// Note 2
std::string gen(NodeRef& ref) {this->code = "";if (ref->IsInstance<FunctionNode>()) {this->visit(Downcast<Function>(ref));} else if (ref->IsInstance<relay::ModuleNode>()) {relay::Module mod = Downcast<relay::Module>(ref);for (const auto& it : mod->functions) {this->visit(Downcast<Function>(it.second));}} else {LOG(FATAL) << "The input ref is expected to be a Relay function or module";}return this->code;
}

private:
/*! \brief The function id that represents a C source function. */
std::string code;
}
Note1:再次實(shí)現(xiàn)相應(yīng)的訪問者函數(shù),生成ExampleJSON代碼,將存儲(chǔ)到類變量【code】中(在本示例中,跳過了訪問器函數(shù)的實(shí)現(xiàn),因?yàn)楦拍钆cC代碼基本相同)。完成圖訪問后,應(yīng)該在【code】中有一個(gè)ExampleJSON圖。
Note2:定義了一個(gè)內(nèi)部API gen,獲取子圖生成ExampleJSON代碼。該API可以采用喜歡的任意名稱。
下一步是實(shí)施自定義的runtime,輸出ExampleJsonCodeGen。
實(shí)現(xiàn)自定義runtime
在本節(jié)中,將逐步實(shí)現(xiàn)自定義的TVM runtime,將注冊(cè)到TVM runtime模塊。自定義的runtime應(yīng)位于src/runtime/contrib//。在示例中,將runtime命名為“ example_ext_runtime”,將放在“ here <src / runtime / contrib / example_ext_runtime / example_ext_runtime.cc>” _下。隨時(shí)檢查此文件,獲取完整的實(shí)現(xiàn)。
再次,先定義一個(gè)自定義的runtime類,如下所示。該類必須從TVM派生【ModuleNode】,以便與其它TVM runtime模塊兼容。
#include <dmlc/logging.h>
#include <tvm/runtime/c_runtime_api.h>
#include <tvm/runtime/memory.h>
#include <tvm/runtime/module.h>
#include <tvm/runtime/ndarray.h>
#include <tvm/runtime/object.h>
#include <tvm/runtime/packed_func.h>
#include <tvm/runtime/registry.h>

#include
#include
#include
#include
#include
#include

namespace tvm {
namespace runtime {
class ExampleJsonModule : public ModuleNode {
public:
explicit ExampleJsonModule(std::string graph_json);

PackedFunc GetFunction(const std::string& name,
const ObjectPtr& sptr_to_self) final;

const char* type_key() const { return “examplejson”; }

void SaveToBinary(dmlc::Stream* stream) final;

static Module LoadFromBinary(void* strm);

static Module Create(const std::string& path);

std::string GetSource(const std::string& format = “”);

void Run(int id, const std::vector& inputs, int output);

void ParseJson(const std::string& json);

private:
/* \brief The json string that represents a computational graph. /
std::string graph_json_;
/
\brief The subgraph that being processed. /
std::string curr_subgraph_;
/
! \brief A simple graph from subgraph id to node entries. /
std::map<std::string, std::vector > graph_;
/
\brief A simple pool to contain the tensor for each node in the graph. /
std::vector data_entry_;
/
\brief A mapping from node id to op name. */
std::vectorstd::string op_id_;
};
特別的,必須在【ExampleJsonModule】中,實(shí)現(xiàn)一些【ModuleNode】派生的函數(shù):
構(gòu)造函數(shù):此類的構(gòu)造函數(shù)應(yīng)接受一個(gè)子圖(以表示形式),以所需的任何方式,進(jìn)行處理和存儲(chǔ)。保存的子圖可由以下兩個(gè)函數(shù)使用。
【GetFunction】:這是此類中最重要的函數(shù)。當(dāng)TVM runtime要使用編譯器標(biāo)記執(zhí)行子圖時(shí),TVM runtime會(huì)從自定義runtime模塊調(diào)用此函數(shù)。提供函數(shù)名稱以及runtime參數(shù),【GetFunction】應(yīng)返回打包的函數(shù)實(shí)現(xiàn),供TVM runtime執(zhí)行。
【SaveToBinary】和【LoadFromBinary】:【SaveToBinary】將runtime模塊序列化為二進(jìn)制格式,供以后部署。用戶使用【export_libraryAPI 】時(shí),TVM將調(diào)用此函數(shù)。另一方面,由于現(xiàn)在使用自定義圖表示形式,因此必須確保【LoadFromBinary】能夠通過采用【SaveToBinary】生成的序列化二進(jìn)制文件,構(gòu)造相同的runtime模塊。
【GetSource】(可選):如果想查看生成的【ExampleJSON】代碼,可以實(shí)現(xiàn)此函數(shù)轉(zhuǎn)儲(chǔ);否則,可以跳過實(shí)施。
其它功能和類變量將與上述必備功能的實(shí)現(xiàn)一起引入。
實(shí)現(xiàn)構(gòu)造函數(shù)
explicit ExampleJsonModule(std::string graph_json) {
this->graph_json_ = graph_json;
ParseJson(this->graph_json_);
}
然后,實(shí)現(xiàn)【ParseJson】解析ExampleJSON格式的子圖,在內(nèi)存中構(gòu)造一個(gè)圖,供以后使用。由于在此示例中不支持帶有分支的子圖,因此僅使用數(shù)組,按順序存儲(chǔ)子圖中的每個(gè)節(jié)點(diǎn)。
void ParseJson(const std::string& json) {
std::string line;
std::string curr_subgraph;
std::stringstream ss(json);

while (std::getline(ss, line, ‘\n’)) {
std::stringstream ss2(line);
std::string token;
int id = 0;

ss2 >> token;
if (token.find("subgraph_") != std::string::npos) {curr_subgraph = token;continue;
}ss2 >> id;
if (op_id_.size() <= static_cast<size_t>(id)) {op_id_.resize(id + 1);data_entry_.resize(id + 1);
}int64_t total_elements = 1;
std::vector<int64_t> shape;
if (token == "input") {int64_t size = 0;while (ss2 >> size) {total_elements *= size;shape.push_back(size);}
} else {op_id_[id] = token; // Note 1bool shape_data = false;NodeEntry entry;while (ss2 >> token) {if (token == "shape:") {shape_data = true;} else if (shape_data) {total_elements *= std::stoll(token);shape.push_back(std::stoll(token));} else if (token != "inputs:") {entry.inputs.push_back(std::stoi(token));}}entry.id = id;entry.output = id;graph_[curr_subgraph].push_back(entry); // Note 2
}
DLContext ctx;
ctx.device_type = static_cast<DLDeviceType>(1);
ctx.device_id = 0;
data_entry_[id] = NDArray::Empty(shape, DLDataType{kDLFloat, 32, 1}, ctx); // Note 3

}
}
Note1:使用類變量【op_id_】將子圖節(jié)點(diǎn)ID,映射到運(yùn)算符名稱(如【add】),可以在runtime調(diào)用相應(yīng)的運(yùn)算符函數(shù)。
Note2:使用類變量【graph_】,將子圖名稱映射到節(jié)點(diǎn)數(shù)組。【GetFunction】將在runtime通過子圖ID查詢圖節(jié)點(diǎn)。
Note3:使用類變量【data_entry_】,將子圖節(jié)點(diǎn)ID映射到張量數(shù)據(jù)占位符。將在runtime將輸入和輸出放入相應(yīng)的數(shù)據(jù)條目。
實(shí)現(xiàn)【GetFunction 】
構(gòu)造后,應(yīng)該準(zhǔn)備好上述類變量。然后,實(shí)現(xiàn)【GetFunction】為TVM runtime,提供可執(zhí)行的子圖函數(shù):
PackedFunc GetFunction(const std::string& name,
const ObjectPtr& sptr_to_self) final {
if (this->graph_.find(name) != this->graph_.end()) {
this->curr_subgraph_ = name;
return PackedFunc([sptr_to_self, this](TVMArgs args, TVMRetValue* rv) {

  // Copy input tensors to corresponding data entries.for (auto i = 0; i < args.size(); ++i) {CHECK(args[i].type_code() == kNDArrayContainer || args[i].type_code() == kArrayHandle)<< "Expect NDArray or DLTensor as inputs\n";if (args[i].type_code() == kArrayHandle) {DLTensor* arg = args[i];this->data_entry_[i].CopyFrom(arg);} else {NDArray arg = args[i];this->data_entry_[i].CopyFrom(arg);}}// Execute the subgraph.for (const auto& it : this->graph_[this->curr_subgraph_]) {this->Run(it.id, it.inputs, it.output);}CHECK_GT(graph_.count(this->curr_subgraph_), 0U);// Copy the output from a data entry back to TVM runtime argument.auto out_idx = graph_[this->curr_subgraph_].back().output;if (args[args.size() - 1].type_code() == kArrayHandle) {DLTensor* arg = args[args.size() - 1];this->data_entry_[out_idx].CopyTo(arg);} else {NDArray arg = args[args.size() - 1];this->data_entry_[out_idx].CopyTo(arg);}*rv = data_entry_.back();
});

} else {
LOG(FATAL) << "Unknown subgraph: " << name << “\n”;
return PackedFunc();
}
}
可以看出,【GetFunction】由三個(gè)主要部分組成。第一部分將數(shù)據(jù)從TVM runtime參數(shù)復(fù)制到在構(gòu)造函數(shù)中分配的相應(yīng)數(shù)據(jù)條目。第二部分使用【Run】函數(shù)(將在以后實(shí)現(xiàn))執(zhí)行子圖,將結(jié)果保存到另一個(gè)數(shù)據(jù)條目中。第三部分將結(jié)果從輸出數(shù)據(jù)條目,復(fù)制回相應(yīng)的TVM runtime參數(shù)進(jìn)行輸出。
實(shí)現(xiàn)運(yùn)行
現(xiàn)在讓實(shí)現(xiàn)【Run】函數(shù)。此函數(shù)接受:
1)一個(gè)子圖ID;
2)輸入數(shù)據(jù)條目索引的列表
3)輸出數(shù)據(jù)條目索引。
void Run(int id, const std::vector& inputs, int output) {
// Make a list data entry indexs.
std::vector args(inputs.begin(), inputs.end());
args.push_back(output);

// Initialize data holders.
std::vector values(args.size());
std::vector type_codes(args.size());

// Initialize a TVM arg setter with TVMValue and its type code.
TVMArgsSetter setter(values.data(), type_codes.data());

// Set each argument to its corresponding data entry.
if (op_id_[id] == “add” || op_id_[id] == “sub” || op_id_[id] == “mul”) {
for (size_t i = 0; i < args.size(); i++) {
setter(i, data_entry_[args[i]]);
}
}

// Invoke the corresponding operator function.
if (op_id_[id] == “add”) {
Add(values.data(), type_codes.data(), args.size());
} else if (op_id_[id] == “sub”) {
Sub(values.data(), type_codes.data(), args.size());
} else if (op_id_[id] == “mul”) {
Mul(values.data(), type_codes.data(), args.size());
} else {
LOG(FATAL) << "Unknown op: " << op_id_[id] << “\n”;
}
}
【Run】函數(shù)主要有兩個(gè)部分。第一部分分配一個(gè)【TVMValue】列表,映射相應(yīng)的數(shù)據(jù)條目塊。這將成為運(yùn)算符函數(shù)的參數(shù)。第二部分將調(diào)用運(yùn)算符函數(shù)。雖然使用與前面的例子相同的C函數(shù),可以用自定義引擎更換Add,Sub及Mul。只需要確保引擎將結(jié)果存儲(chǔ)到最后一個(gè)參數(shù),就可以將傳輸回TVM runtime。
通過實(shí)現(xiàn)上述功能,自定義的代碼生成和runtime現(xiàn)在可以執(zhí)行子圖。最后一步是注冊(cè)API(【examplejson_module_create】)以創(chuàng)建此模塊:
TVM_REGISTER_GLOBAL(“module.examplejson_module_create”)
.set_body_typed([](std::string code){
auto n = make_object(code);
return runtime::Module(n);
});
實(shí)現(xiàn)【SaveToBinary】和【LoadFromBinary 】
到目前為止,已經(jīng)實(shí)現(xiàn)了自定義runtime的主要功能,可以用作其它TVM runtime。但是,當(dāng)用戶要將已構(gòu)建的runtime保存到磁盤以進(jìn)行部署時(shí),TVM不知道如何保存。這就是要實(shí)現(xiàn)【SaveToBinary】和【LoadFromBinary】的原因,告訴TVM如何保留和恢復(fù)自定義的runtime。
先實(shí)現(xiàn)【SaveToBinary】,允許用戶將該模塊保存在磁盤中的功能。
void SaveToBinary(dmlc::Stream* stream) final {
stream->Write(this->graph_json_);
}
可以發(fā)現(xiàn)此函數(shù)非常簡(jiǎn)單。回想一下,在構(gòu)造函數(shù)中使用的唯一參數(shù)是一個(gè)子圖表示,只需要一個(gè)子圖表示,即可構(gòu)造/恢復(fù)此定制的runtime模塊。結(jié)果,【SaveToBinary】只需將子圖寫入輸出DMLC流。當(dāng)用戶使用【export_library】API導(dǎo)出模塊時(shí),自定義模塊將是子圖的ExampleJSON流。
同樣,【LoadFromBinary】讀取子圖流,重新構(gòu)建自定義的runtime模塊:
static Module LoadFromBinary(void* strm) {
dmlc::Stream* stream = static_castdmlc::Stream*(strm);
std::string graph_json;
stream->Read(&graph_json);
auto n = tvm::runtime::make_object(graph_json);
return Module(n);
}
需要注冊(cè)此函數(shù),啟用相應(yīng)的Python API:
TVM_REGISTER_GLOBAL(“module.loadbinary_examplejson”)
.set_body_typed(ExampleJsonModule::LoadFromBinary);
上面的注冊(cè)當(dāng)用戶調(diào)用【tvm.runtime.load(lib_path)】API,導(dǎo)出的庫具有ExampleJSON流時(shí),【LoadFromBinary】將被調(diào)用,創(chuàng)建相同的自定義runtime模塊。
另外,如果想直接從ExampleJSON文件支持模塊創(chuàng)建,可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的函數(shù),注冊(cè)Python API,如下所示:
static Module Create(const std::string& path) {
std::ifstream filep;
filep.open(path, std::ios::in);
std::string graph_json;
std::string line;
while (std::getline(filep, line)) {
graph_json += line;
graph_json += “\n”;
}
filep.close();
auto n = tvm::runtime::make_object(graph_json);
return Module(n);
}

TVM_REGISTER_GLOBAL(“module.loadfile_examplejson”)
.set_body([](TVMArgs args, TVMRetValue* rv) {
*rv = ExampleJsonModule::Create(args[0]);
});
用戶可以手動(dòng)編寫/修改ExampleJSON文件,使用Python API 【tvm.runtime.load(“mysubgraph.examplejson”, “examplejson”)】構(gòu)造自定義模塊。

總結(jié)
總之,這是一份清單供參考:
派生自【ExprVisitor】和【CodegenCBase】的代碼生成類和(僅對(duì)于C代碼生成),具有以下函數(shù)。
【VisitExpr_(const CallNode* call)】 收集調(diào)用節(jié)點(diǎn)信息。
收集子圖信息所需的其它訪問器函數(shù)。
【JIT 】生成子圖代碼。
注冊(cè)代碼生成器。
創(chuàng)建【CSourceModule】的函數(shù)(用于C代碼生成)。
從【ModuleNode】派生的runtime模塊類,具有下面的函數(shù)(用于圖形表示)。
構(gòu)造函數(shù)。
【GetFunction】生成TVM runtime兼容的【PackedFunc】。
【Run 】執(zhí)行子圖。
注冊(cè)runtime創(chuàng)建API。
【SaveToBinary】和【LoadFromBinary】序列化/反序列化自定義的runtime模塊。
注冊(cè)【LoadFromBinary】API以支持【tvm.runtime.load(your_module_lib_path)】。
(可選)【Create】從表示中的子圖文件,支持定制的runtime模塊構(gòu)造。
一個(gè)用于對(duì)用戶Relay程序進(jìn)行注釋的注釋器,利用編譯器和runtime(TBA)。

參考鏈接:
https://tvm.apache.org/docs/dev/how_to/relay_bring_your_own_codegen.html
https://blog.csdn.net/weixin_42164269/article/details/104291635

總結(jié)

以上是生活随笔為你收集整理的如何将自定义代码生成TVM的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

黄色亚洲片 | 99精品免费久久久久久日本 | 天天天插| 免费福利片2019潦草影视午夜 | 日韩免费视频一区二区 | 久久久免费精品国产一区二区 | 婷婷中文在线 | 亚洲精品国产精品国自产 | 探花视频免费观看 | 久久天| 成年人免费在线观看网站 | 亚洲一区尤物 | 国产剧情久久 | 国产在线观看不卡 | 日韩com| 超碰97国产精品人人cao | 久久久免费看片 | 在线蜜桃视频 | 天天做日日做天天爽视频免费 | 日韩av在线一区二区 | 91精品高清| 日韩欧美国产精品 | 精品一区二区影视 | 国产精品福利一区 | 精精国产xxxx视频在线播放 | 91成人区 | 国产一区二区高清不卡 | 91九色国产在线 | 久久久久久国产精品亚洲78 | 精品国产一区二区三区四 | 91日韩在线 | 日韩欧美综合视频 | 日本精品视频在线观看 | 黄p网站在线观看 | www.大网伊人 | 欧美日韩在线视频一区二区 | 国产免费国产 | 久久成人国产精品免费软件 | 亚洲国产999| 欧美日韩视频在线播放 | 欧美成年人在线观看 | 亚洲一区精品人人爽人人躁 | h网站免费在线观看 | 九色在线视频 | 日韩视频免费观看高清 | 婷婷精品在线视频 | 久草视频99 | 激情五月看片 | 美女网站在线播放 | 毛片随便看 | 国产精品美女999 | 91在线免费观看国产 | 欧美最猛性xxxxx(亚洲精品) | 中文字幕在线观看一区二区三区 | 国产不卡免费视频 | 久久电影网站中文字幕 | 蜜桃av综合网 | 国产又粗又猛又爽又黄的视频先 | 91精彩在线视频 | 午夜精品久久久久久久99水蜜桃 | 久久成人毛片 | 国产高清视频在线播放一区 | 成人黄色在线 | 成人看片| 美女视频黄免费网站 | 久久精品亚洲综合专区 | 免费视频99| 亚洲人精品午夜 | 美女福利视频 | 一区二区三区中文字幕在线观看 | 黄色一级免费 | 色干干| 日本中文字幕观看 | 成人一级片视频 | 欧美视频一区二 | 成人在线视 | 国产一区在线免费观看视频 | 美女又爽又黄 | 人人干人人爽 | 麻豆影视在线观看 | 99久久久久久久久久 | 日韩天天干 | 91色在线观看视频 | 免费亚洲一区二区 | 国产高清视频在线免费观看 | 综合网伊人 | 五月天国产精品 | 日日爱av| 亚洲第一av在线 | 成人一区二区在线观看 | www国产亚洲 | 天天草天天 | 福利电影一区二区 | 91av在线视频免费观看 | 久久久资源| 午夜精品一区二区三区在线视频 | 中文字幕亚洲欧美日韩2019 | av黄网站| 欧美久久久久久久久久久 | 欧美日本不卡高清 | 国产精品一区二区av影院萌芽 | 成人影音在线 | 中文字幕在线视频一区二区 | av在线一级 | 久久精品一区二区三区中文字幕 | 在线视频欧美精品 | 美女视频黄色免费 | 国产精品久久久久影视 | 国产第一页在线观看 | 麻豆传媒在线视频 | 久草.com | 久久久久国产a免费观看rela | 久久精品日产第一区二区三区乱码 | 深爱激情五月综合 | 国产一区二区影院 | 制服丝袜在线91 | h久久| 黄色成人毛片 | 午夜av网站 | 亚洲精区二区三区四区麻豆 | 成人黄色片免费 | 日韩网站免费观看 | 99免费精品 | 国产美女视频网站 | 亚洲精品国产精品乱码不99热 | 欧美另类tv | 国产亚洲亚洲 | 国产精品美女www爽爽爽视频 | 日本福利视频在线 | 日韩久久久 | 欧美少妇18p| 久久99精品一区二区三区三区 | 亚州av一区 | 日批视频在线播放 | 日本黄色免费电影网站 | 97超碰免费在线观看 | 欧美一区二区三区激情视频 | 中文字幕有码在线观看 | 欧美成人xxxxx | 成人a免费 | 成人黄在线 | 中文字幕在线观看2018 | 久草国产在线观看 | 日韩在线免费高清视频 | av在线播放快速免费阴 | 国产精品久久久久久久久大全 | 国产精品永久久久久久久久久 | 久久久久久久久国产 | 日韩高清国产精品 | 精品国产99国产精品 | 在线国产视频一区 | 狠狠的操狠狠的干 | 中文字幕在线观看的网站 | 国产日韩欧美在线影视 | 国模视频一区二区三区 | 国产精品成人av在线 | 日日干日日操 | 婷婷综合久久 | 激情电影在线观看 | 久草网站在线观看 | 狠狠婷婷 | 亚洲国产美女久久久久 | 日韩美女黄色片 | 日韩激情网 | 在线 成人 | 91欧美国产 | 日韩精品三区四区 | 99高清视频有精品视频 | 欧美日韩亚洲精品在线 | 欧美精品二区 | 色综合久久综合中文综合网 | 亚洲精品乱码久久久久久按摩 | 一区二区精品久久 | 99热999 | 99国产在线视频 | 免费看成人片 | 99久久9| 成人久久综合 | av在线中文 | 在线观看91精品国产网站 | 中文字幕国产视频 | 亚洲成人资源在线观看 | 亚洲一级片在线观看 | 国产精品毛片一区二区在线 | 久久久精品一区二区 | 粉嫩一区二区三区粉嫩91 | 人人干人人搞 | 欧美精品久久久久久久久老牛影院 | 91在线精品秘密一区二区 | 最新高清无码专区 | 182午夜在线观看 | 91视频这里只有精品 | 在线观看免费成人 | 中文字幕在线高清 | 在线免费观看视频一区 | 国内精品久久久久影院日本资源 | 久久久天堂| 五月婷婷中文网 | 久久久精品视频网站 | 欧美日韩中文国产一区发布 | 国产你懂的在线 | 婷婷六月丁香激情 | 日韩一区二区在线免费观看 | 国产精品爽爽久久久久久蜜臀 | 成人av电影网址 | 日本中文字幕网址 | 亚洲国产片色 | 男女精品久久 | 91av官网 | 99免费看片| 不卡的av电影在线观看 | 精品国产一区二区三区在线观看 | 成人av电影免费在线观看 | 国产精品不卡一区 | 欧美日韩在线精品一区二区 | 麻豆91视频| 免费看的国产视频网站 | av不卡中文 | a午夜电影| 97天天综合网 | 日韩午夜电影 | 亚洲精品高清视频 | 国产精品一区二区三区免费视频 | 成人在线观看av | 色综合色综合久久综合频道88 | 免费又黄又爽的视频 | 在线观看 国产 | 综合色在线 | 中文字幕免费高清在线观看 | 久久午夜视频 | 日韩av片无码一区二区不卡电影 | 91精品国产高清自在线观看 | 伊人伊成久久人综合网站 | 黄色视屏免费在线观看 | 九九热视频在线 | 亚洲精品高清在线观看 | 中文字幕乱码亚洲精品一区 | 极品国产91在线网站 | 毛片基地黄久久久久久天堂 | av在线免费观看网站 | 国产成人在线看 | 成人免费网站在线观看 | 日韩一区二区三区在线观看 | 色婷婷影视 | 亚洲精品www| 人人干人人爽 | 久久成年人 | 国产免费视频一区二区裸体 | 正在播放 国产精品 | 国产色道| 丁香视频在线观看 | 国产分类视频 | 激情久久久 | 亚洲综合色激情五月 | 亚洲国产影院 | 亚洲精品永久免费视频 | 日韩理论片在线观看 | 操操爽 | 久久久久97国产 | 国产另类xxxxhd高清 | 欧美日性视频 | 久久久久在线观看 | 日韩电影一区二区三区在线观看 | 成人小视频在线 | 国产成人av在线影院 | 日韩在线中文字幕 | 韩国三级一区 | 午夜精品视频免费在线观看 | 91大神免费视频 | 国产裸体视频网站 | 天天操天天射天天舔 | 亚洲欧美国产日韩在线观看 | 在线视频日韩一区 | 欧美99热| 国产在线专区 | 一区二区欧美日韩 | 久久美女精品 | 国产成人亚洲精品自产在线 | 久久在线免费 | 久久露脸国产精品 | 日韩久久影院 | 亚洲一区二区精品3399 | 精品欧美一区二区三区久久久 | 欧美日韩中文字幕视频 | 国产又粗又猛又爽又黄的视频免费 | 91精品一区国产高清在线gif | 亚洲日韩欧美视频 | 亚洲少妇久久 | 国产日产欧美在线观看 | 天天做日日爱夜夜爽 | 免费情趣视频 | 久久免费视频在线观看 | 亚洲永久av | 在线天堂中文www视软件 | 久久精品国产v日韩v亚洲 | 国产免费久久精品 | 97免费在线观看视频 | 国产韩国精品一区二区三区 | 精品国产123 | 久久影院精品 | 丁香婷婷自拍 | 久草| 五月综合激情 | 日韩精品久久久久久中文字幕8 | 狠狠狠色丁香综合久久天下网 | 超碰在线97国产 | 日韩网站在线 | 69欧美视频 | 日本三级久久久 | 国产美女精品视频 | 精品日韩av| 99精品热视频只有精品10 | 2020天天干夜夜爽 | 国产黄色美女 | 婷婷色亚洲| 日韩精品一区二区三区水蜜桃 | 国产成人a亚洲精品v | 99在线高清视频在线播放 | 狠狠色狠狠色综合日日小说 | 国产一区二区三区视频在线 | 亚洲一区二区视频 | 国产精品不卡一区 | 手机av电影在线观看 | 国产成人av在线影院 | 91九色自拍 | 久青草国产在线 | 久久久首页 | 黄色成人av在线 | 国产精品精品久久久久久 | 亚洲无在线 | 欧美精品亚洲二区 | 91视频啪 | 久久人人爽视频 | 天天摸夜夜操 | 在线观看成年人 | 日韩在线第一 | 国产情侣一区 | 久久精品中文视频 | 日韩v在线 | 色综合天天综合网国产成人网 | 中中文字幕av在线 | 亚洲国产美女精品久久久久∴ | 人人躁 | 国产亚洲日 | 日韩欧美在线观看一区二区三区 | 亚洲午夜久久久久久久久久久 | 九九久久久久久久久激情 | 伊人狠狠干 | 日韩黄色大片在线观看 | 久久久天堂| 日韩专区中文字幕 | 97色在线视频| 欧美日韩视频网站 | 日韩福利在线观看 | 亚洲精品一区二区精华 | 高清av免费一区中文字幕 | 人人插人人草 | 免费在线观看一区 | 国产精品99蜜臀久久不卡二区 | 亚洲高清91 | 久久久人人人 | 不卡日韩av | 高清av在线免费观看 | 81国产精品久久久久久久久久 | av资源中文字幕 | 国产黄在线| 国产成人福利在线观看 | 黄色在线免费观看网址 | 午夜狠狠操 | 日韩欧美高清免费 | 亚洲欧美激情插 | 国产精品久久久区三区天天噜 | 久久激情日本aⅴ | 亚洲黄色高清 | 久操97| 欧美国产三区 | 欧美激情精品久久久 | 久久国产二区 | 日韩videos| 久久久久久国产精品美女 | www.888av| 久久视频二区 | 欧美精品v国产精品v日韩精品 | 天天射天天操天天 | 色综合久久久网 | 一区二区中文字幕在线播放 | 亚洲最新av在线网址 | 免费观看福利视频 | 美女免费av | 国产69精品久久99不卡的观看体验 | 成人久久影院 | 日韩免费在线看 | 女人18精品一区二区三区 | 久久这里只精品 | 人人狠 | 婷婷久久综合九色综合 | 99热这里只有精品久久 | 黄色字幕网 | 97视频久久久 | 中文在线免费一区三区 | 国产69精品久久久久9999apgf | 国产成人一二三 | 日日干夜夜草 | 2019中文字幕第一页 | 天堂在线一区二区 | 久艹在线免费观看 | 美女视频黄是免费的 | 日日摸日日添夜夜爽97 | 久久99国产精品久久99 | 激情视频久久 | 久艹视频在线免费观看 | 国产成人在线精品 | 91麻豆精品| 中文字幕影片免费在线观看 | 国产欧美精品一区aⅴ影院 99视频国产精品免费观看 | 97免费中文视频在线观看 | 国产日韩一区在线 | 在线免费av观看 | 开心婷婷色 | 97操操操| 欧美亚洲成人xxx | 国产一级精品视频 | 久久精品影片 | 一区二区精品在线 | 亚洲国产精品久久久久婷婷884 | a色网站| 国产三级久久久 | 久草视频观看 | 日韩av成人在线观看 | 热re99久久精品国产66热 | 久久久久国产精品一区二区 | 国产中文字幕在线观看 | 中文字幕超清在线免费 | 欧美日韩精品二区第二页 | 国产精品久久久视频 | 国产小视频精品 | 97国产精品亚洲精品 | 91精品久久久久久久久 | 综合久久久| 中文字幕网址 | 日韩在线中文字幕 | 免费午夜视频在线观看 | 亚洲精品欧美视频 | 午夜性生活 | 99久久久国产精品免费99 | 国产精品门事件 | 四虎在线观看精品视频 | 国产成人免费精品 | 欧美国产日韩一区二区三区 | av三级在线免费观看 | 久久精品高清视频 | 婷婷六月久久 | 久久久亚洲麻豆日韩精品一区三区 | 成人精品久久久 | 日韩久久久 | 在线精品在线 | 亚洲精品88欧美一区二区 | 波多野结衣电影久久 | 在线看国产日韩 | 久久草在线视频国产 | 在线观看完整版 | 精品一区精品二区高清 | 亚洲一区美女视频在线观看免费 | av免费看在线| 国产精品美女久久久 | 97超碰人人在线 | 国产小视频国产精品 | 国产精品普通话 | 午夜三级福利 | 欧美亚洲xxx | 在线观看va | 6080yy午夜一二三区久久 | 国产精品免费在线 | 一级黄色在线视频 | 国产色就色 | 中文字幕乱在线伦视频中文字幕乱码在线 | 亚洲精品久久在线 | 在线a亚洲视频播放在线观看 | 日韩av免费在线电影 | 国产精品亚洲综合久久 | av资源网在线播放 | 77国产精品 | www免费| 91精品1区2区 | 中文字幕视频观看 | 日韩91精品 | 一区二区不卡在线观看 | 久草网在线视频 | 337p西西人体大胆瓣开下部 | 欧洲一区二区在线观看 | 色婷婷综合久久久久 | 97日日碰人人模人人澡分享吧 | 欧美久久精品 | 激情视频国产 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 麻豆视频免费网站 | 三级性生活视频 | 综合婷婷久久 | 日日夜夜免费精品视频 | 国产综合视频在线观看 | 久久国产日韩 | 国产精品成人在线观看 | 国产午夜精品av一区二区 | 国产精品免费久久久久久久久久中文 | 国产高清中文字幕 | 国产免费二区 | 青草视频在线看 | 人人超碰人人 | 久草在线视频中文 | 成人av影视 | 在线免费中文字幕 | 黄色成人av | 国产亚洲免费的视频看 | 成人免费视频免费观看 | 永久免费精品视频网站 | 高清不卡毛片 | 久艹视频在线免费观看 | 免费在线观看黄网站 | 国产亚洲精品女人久久久久久 | 色综合五月 | 六月丁香色婷婷 | 伊人天堂网 | 久久婷婷色综合 | 激情欧美一区二区免费视频 | 99r国产精品 | 伊人天堂久久 | 亚洲无毛专区 | 久久久久美女 | 91免费日韩 | 天天爽人人爽夜夜爽 | 午夜在线观看一区 | 免费视频91 | 久久久久久久影院 | 亚洲国产午夜精品 | 欧美福利片在线观看 | 在线你懂 | 毛片网免费 | 久久精品99国产国产 | 激情丁香久久 | 超碰在线色 | 国产一级免费在线 | 日本久久精品 | 久久亚洲区 | 欧美坐爱视频 | 99精彩视频在线观看免费 | 国产在线1区 | av丁香 | 久久精选视频 | 日韩在线观看一区二区三区 | 伊人看片| 91插插视频 | 97碰碰精品嫩模在线播放 | 色www. | 一级免费黄色 | 992tv在线成人免费观看 | 亚洲成 人精品 | 久久专区| 国产一级视频 | www.com.日本一级 | 国产免费视频一区二区裸体 | 欧美男同网站 | 欧美精品久久久久久久亚洲调教 | 波多野结衣一区二区三区中文字幕 | 亚洲视屏在线播放 | 日韩免费看 | 在线视频国产区 | 亚洲国产精品va在线看 | 夜夜澡人模人人添人人看 | 久久丁香 | 久久视频6 | ww亚洲ww亚在线观看 | 91大神精品视频在线观看 | 日韩黄色一级电影 | 国产中文字幕在线播放 | 久久国产女人 | 亚洲一区视频在线播放 | 91精品国产麻豆国产自产影视 | 免费高清在线观看成人 | 亚洲精品美女免费 | 天堂av色婷婷一区二区三区 | 新版资源中文在线观看 | 婷婷丁香国产 | 正在播放 久久 | 在线看免费 | 中文字幕免费在线看 | 天天玩天天干 | 国产伦理久久 | 久久一精品 | 久久艹影院 | 国产成人亚洲在线观看 | 日精品在线观看 | 99久久精品电影 | 久久久久久久99精品免费观看 | 91高清一区 | 亚洲免费a | 免费看的国产视频网站 | 久久亚洲私人国产精品 | 激情网婷婷 | 最近中文字幕久久 | 激情偷乱人伦小说视频在线观看 | 99精品国产99久久久久久97 | 国产精品久久久久永久免费 | 九七人人干 | 国产精品久久久久久久免费 | 国产成人一区二区三区在线观看 | 亚洲欧洲日韩在线观看 | 亚洲精品久久久蜜臀下载官网 | 高清久久久久久 | 黄色三级网站在线观看 | 一区二区三区高清在线观看 | 中文字幕a∨在线乱码免费看 | 国产一区在线精品 | 99热国产在线观看 | 久热爱| av高清网站在线观看 | 久久热首页| 在线观看91| 日韩精品一区电影 | 欧美va电影 | 欧美性色综合网站 | 精品伊人久久久 | 2021国产视频| 麻豆视频免费播放 | 嫩嫩影院理论片 | 午夜美女福利直播 | 波多野结衣在线中文字幕 | 97在线观看免费观看高清 | 亚洲精品2区 | 91人网站| 久久99精品国产麻豆宅宅 | 亚洲精品福利视频 | 亚州欧美视频 | 中文字幕在线观看日本 | 天天操夜夜叫 | 亚洲国产精品久久久久久 | 99视频久久 | 国产视频在线观看一区 | 欧美日韩免费视频 | 国产一区二区三区在线免费观看 | 国产精品露脸在线 | 超碰免费av| 久久精品欧美 | 亚洲精品在线免费看 | 黄色大全免费观看 | 制服丝袜一区二区 | 色婷婷婷 | 97超碰福利久久精品 | 国产高清视频在线免费观看 | 丁香婷婷综合色啪 | 婷婷色狠狠 | 免费久久视频 | 色婷婷激情电影 | 国产精品欧美一区二区三区不卡 | 2023亚洲精品国偷拍自产在线 | 天天操天天干天天操天天干 | 五月婷婷视频在线观看 | 精品国产一区二区三区久久久蜜月 | 国产一区精品在线 | 一本一道久久a久久精品 | 久久久久久国产一区二区三区 | 在线日韩中文字幕 | 狠狠网| 爱爱一区 | 视频一区二区精品 | 国产福利精品一区二区 | 国产美女视频免费观看的网站 | 亚洲最大成人网4388xx | 欧美天堂视频在线 | 中文免费 | 中字幕视频在线永久在线观看免费 | 中文在线字幕观看电影 | 在线播放日韩av | 国产精品9区 | 日韩激情av在线 | 在线午夜电影神马影院 | 免费看一级黄色大全 | 人人干人人搞 | 免费在线色视频 | 日本黄色大片免费 | 国内精品久久久久影院男同志 | 91精品国自产在线偷拍蜜桃 | 免费精品久久久 | 国内精品久久久 | 免费看在线看www777 | 色婷婷九月 | 91av影视| 怡红院久久 | 国产成人一区二区三区电影 | 久久久亚洲电影 | 在线免费中文字幕 | 亚洲理论影院 | 九九色综合 | 天天干天天上 | 99热亚洲精品 | 国产综合婷婷 | 国内精品久久久久久久久久久 | 黄色一级网 | 成人在线观看你懂的 | 缴情综合网五月天 | www.午夜色.com| 久久99精品久久久久久三级 | 国产一级片网站 | 日韩视频二区 | 九色视频网 | 精品亚洲va在线va天堂资源站 | 五月天综合色 | 亚洲精品高清在线观看 | 日韩精品一区二区三区高清免费 | 99视频免费 | 色狠狠婷婷 | 免费h精品视频在线播放 | 日韩av影视 | 欧美,日韩 | 人成在线免费视频 | 精品在线视频播放 | 日韩av成人在线观看 | 亚洲永久精品国产 | 久久精品国产v日韩v亚洲 | 日b视频在线观看网址 | 婷婷视频| 丁香六月婷 | 久久tv| 黄色视屏免费在线观看 | 天天色天天操综合网 | 国产99久久九九精品免费 | 99久精品视频 | 国产精品999久久久 久产久精国产品 | 在线你懂的视频 | 久久免费毛片视频 | 成年人在线观看网站 | 二区在线播放 | 亚洲精品中文字幕视频 | 国产一区欧美在线 | 国产精品成人免费 | 五月天婷亚洲天综合网鲁鲁鲁 | 日韩视频一区二区三区 | 国产91在线看 | 在线视频一二三 | 国产精品99久久久精品 | 一区二区三区久久 | 在线日韩中文 | 韩国av一区二区 | 日韩精品中文字幕在线不卡尤物 | 在线视频在线观看 | 黄色一区三区 | 亚洲一区二区三区四区在线视频 | av高清网站在线观看 | 成人免费视频免费观看 | 97超在线视频 | 久久黄色片子 | 区一区二区三区中文字幕 | 欧美 国产 视频 | 色94色欧美 | 欧美一级大片在线观看 | www成人精品 | 在线观看www.| 人人插人人 | 日韩av电影免费观看 | 日韩午夜小视频 | 中文超碰字幕 | 久久国产视频网 | 精品国产美女 | 日韩久久久久久久 | 一区二区视频在线免费观看 | 国产在线视频资源 | 国产一区二区在线观看免费 | 人人爽人人爽人人爽学生一级 | 高清免费在线视频 | 天天干,天天插 | 五月天电影免费在线观看一区 | 黄色www在线观看 | 国产黄色看片 | 国产精品岛国久久久久久久久红粉 | 天天爽人人爽 | 五月激情片 | 亚洲精品国产精品国自产在线 | 久草资源免费 | 欧美日韩国产二区三区 | 狠狠操狠狠操 | 一区二区视频播放 | 成年人视频在线免费 | av软件在线观看 | 99精品欧美一区二区 | 国产色视频一区二区三区qq号 | 五月天久久综合 | 丁香九月婷婷 | 在线导航福利 | 国产精品videoxxxx | 91在线视频 | 99精品免费 | 最新色站 | 亚洲精品日韩在线观看 | 在线视频 区 | 色综合久久中文综合久久牛 | 99久久久成人国产精品 | 久久人人射 | 99精品视频免费看 | 日批视频在线播放 | 少妇性bbb搡bbb爽爽爽欧美 | 国产剧情在线一区 | 日韩一区二区三区免费电影 | 天天天天天天天天操 | 欧美日韩视频在线观看一区二区 | 欧美日韩高清一区二区三区 | 久久天天操 | 五月综合激情网 | 国产69久久 | 国色天香在线 | 精品视频成人 | 亚洲乱亚洲乱亚洲 | 国产一区二区精品久久91 | 亚洲国产精品推荐 | 婷婷丁香视频 | 97在线视频观看 | 久久色视频| 天天天天综合 | 国产精品一区二区三区免费视频 | 亚洲日本成人网 | 久久国产视频网 | 中文资源在线播放 | 日韩欧美在线影院 | 久久美女高清视频 | 色婷久久 | 国产成人一区二区精品非洲 | 月下香电影 | 日本精品视频免费 | 午夜视频欧美 | 久久国产欧美日韩 | 超碰999| 亚洲国产中文字幕 | 国产成人精品一区二区三区在线 | 久久国产电影院 | 超碰成人网| 人人干人人草 | 国产亚洲片 | 欧美激情视频一二三区 | 久久好看免费视频 | 久久精品电影 | 中文字幕乱码亚洲精品一区 | 国产日本在线播放 | 久章草在线观看 | 久久久亚洲麻豆日韩精品一区三区 | 国产成人精品综合 | 国产精品国产亚洲精品看不卡15 | 亚洲三区在线 | 亚洲精品久久激情国产片 | 中文字幕精品三级久久久 | 国产福利91精品一区 | 国产高清在线免费 | www日日夜夜| 五月天狠狠操 | 亚洲一区二区三区在线看 | 97激情影院 | 国内精自线一二区永久 | 2019天天干天天色 | 玖草在线观看 | www.五月婷婷 | 成人avav| 丁香六月婷婷开心 | 超碰在线人人艹 | 成人亚洲免费 | 99色网站| 亚洲国产精品视频在线观看 | 欧美一级视频一区 | a黄色影院 | 亚洲毛片一区二区三区 | 免费成人av在线看 | 精品国产一区二区三区日日嗨 | 亚洲1区在线 | 国产精品免费不卡 | 国产精品女教师 | 国产999精品久久久久久绿帽 | a在线一区| 成人av亚洲 | 黄色三级免费片 | 在线观看深夜福利 | 97av免费视频 | 午夜精品一区二区三区可下载 | 免费看毛片网站 | av色综合网 | 精品国产电影一区二区 | 欧美一区二区三区在线观看 | 五月天亚洲综合小说网 | 深夜福利视频一区二区 | 久久精品专区 | 日韩视频一区二区在线观看 | 狠狠干婷婷色 | 毛片二区 | 91精品夜夜 | 日韩精品网址 | 日韩久久精品一区二区三区下载 | 国产免费久久久久 | 国产色综合 | 欧美一区二区三区激情视频 | 91色国产| 久久久精品影视 | 综合网婷婷 | 精品国产区在线 | 日韩网站在线播放 | 欧美伦理一区 | 欧美日韩免费视频 | av直接看 | 国产在线观看你懂得 | 久久久999精品视频 国产美女免费观看 | 免费成人在线观看视频 | adn—256中文在线观看 | 亚洲无吗天堂 | 天天射狠狠干 | 波多野结衣一区二区三区中文字幕 | 成人黄色电影在线播放 | 天天干天天玩天天操 | 久久www免费人成看片高清 | 欧美 日韩 国产 中文字幕 | 欧美性生活一级片 | 日日干夜夜骑 | 国产高清在线一区 | 久久色亚洲 | 亚洲精品在线免费播放 | 成人免费观看大片 | 精品国产免费人成在线观看 | 99热官网 | 日本公妇在线观看 | 69视频网站 | 色狠狠干| 久久久久影视 | 综合网在线视频 | 91麻豆精品国产91久久久使用方法 | 制服丝袜亚洲 | 国产黄色免费在线观看 | 免费情缘| 欧美性色综合 | 精品久久一区二区三区 | 91香蕉国产 | 国产高潮久久 | 日日夜夜中文字幕 | 日本中文字幕久久 | 欧美日韩3p | 色播激情五月 | 欧美日韩不卡一区 | 久久精品在线 | 国产在线观看午夜 | 国产精品久久电影观看 | 成人av在线网 | 国产精品一区二区吃奶在线观看 | 久草在线官网 | 成年人视频在线免费 | 国产精品久久久久永久免费观看 | 日韩欧美在线视频一区二区 | 涩涩色亚洲一区 | 国产精品亚洲成人 | 免费在线观看的av网站 | 91污污视频在线观看 | 99精品热视频 | 天天躁天天躁天天躁婷 | 97影视| 日韩有色 | 中文字幕频道 | 婷婷视频在线 | www最近高清中文国语在线观看 | 久久久久高清毛片一级 | 碰超在线97人人 | 国产精品 日本 | 亚洲视频在线观看 | 亚洲综合射| 亚洲精品乱码久久久久久写真 | 五月在线视频 | 国产老熟| 婷婷六月天综合 | 久久久久久麻豆 | 久草.com| 婷婷丁香九月 | 国产精品www| 欧美一级片 | 久久五月天婷婷 | 国产裸体视频bbbbb | 91视频免费国产 | 五月婷婷综合在线观看 | 国产在线观看91 | 超碰日韩在线 | 欧美国产在线看 | 人人澡超碰碰 | 97在线观看免费观看高清 | 伊人亚洲综合网 | 久久成年人 | 四虎在线免费观看 | 国产精品免费一区二区三区在线观看 | 在线播放一区二区三区 | 18网站在线观看 | 亚洲天天草 | 亚洲永久精品国产 | 中文字幕成人在线 | 最新中文字幕在线资源 | 2019天天干夜夜操 | 国内精品久久久久影院优 | 亚洲精选99 | 免费av看片 | 国产精品视频久久久 | 97在线观视频免费观看 |