Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of Hash Extern #31

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 15 additions & 155 deletions backends/tc/ebpfCodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ void PNAArchTC::emitHeader(EBPF::CodeBuilder *builder) const {
emitP4TCFilterFields(builder);
// BPF map definitions.
emitInstances(builder);
EBPFHashAlgorithmTypeFactoryPNA::instance()->emitGlobals(builder);
}

// =====================TCIngressPipelinePNA=============================
Expand Down Expand Up @@ -1394,6 +1393,11 @@ bool ConvertToEBPFParserPNA::preorder(const IR::P4ValueSet *pvs) {
return false;
}

void EBPFControlPNA::emit(EBPF::CodeBuilder *builder) {
for (auto h : pna_hashes) h.second->emitVariables(builder);
EBPF::EBPFControl::emit(builder);
}

// =====================ConvertToEBPFControlPNA=============================
bool ConvertToEBPFControlPNA::preorder(const IR::ControlBlock *ctrl) {
control = new EBPFControlPNA(program, ctrl, parserHeaders);
Expand Down Expand Up @@ -1491,8 +1495,8 @@ bool ConvertToEBPFControlPNA::preorder(const IR::ExternBlock *instance) {
cstring name = EBPF::EBPFObject::externalName(di);
cstring typeName = instance->type->getName().name;
if (typeName == "Hash") {
auto hash = new EBPF::EBPFHashPSA(program, di, name);
control->hashes.emplace(name, hash);
auto hash = new EBPFHashPNA(program, di, name);
control->pna_hashes.emplace(name, hash);
} else if (typeName == "Register") {
auto reg = new EBPFRegisterPNA(program, name, di, control->codeGen);
control->pna_registers.emplace(name, reg);
Expand Down Expand Up @@ -2009,7 +2013,7 @@ void ControlBodyTranslatorPNA::processMethod(const P4::ExternMethod *method) {
pna_meter->emitExecute(builder, method, this, nullptr);
return;
} else if (declType->name.name == "Hash") {
auto hash = control->to<EBPF::EBPFControlPSA>()->getHash(name);
auto hash = pnaControl->getHash(name);
hash->processMethod(builder, method->method->name.name, method->expr, this);
return;
} else {
Expand Down Expand Up @@ -2048,7 +2052,7 @@ bool ControlBodyTranslatorPNA::preorder(const IR::AssignmentStatement *a) {
return false;
} else if (ext->originalExternType->name.name == "Hash") {
cstring name = EBPF::EBPFObject::externalName(ext->object);
auto hash = control->to<EBPF::EBPFControlPSA>()->getHash(name);
auto hash = pnaControl->getHash(name);
// Before assigning a value to a left expression we have to calculate a hash.
// Then the hash value is stored in a registerVar variable.
hash->calculateHash(builder, ext->expr, this);
Expand Down Expand Up @@ -2380,160 +2384,16 @@ void DeparserHdrEmitTranslatorPNA::emitField(EBPF::CodeBuilder *builder, cstring

EBPF::EBPFHashAlgorithmPSA *EBPFHashAlgorithmTypeFactoryPNA::create(
int type, const EBPF::EBPFProgram *program, cstring name) {
if (type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::ONES_COMPLEMENT16 ||
type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::TARGET_DEFAULT) {
if (type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::CRC16) {
return new CRCChecksumAlgorithmPNA(program, name, 16);
} else if (type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::CRC32) {
return new CRCChecksumAlgorithmPNA(program, name, 32);
} else if (type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::ONES_COMPLEMENT16 ||
type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::TARGET_DEFAULT) {
return new InternetChecksumAlgorithmPNA(program, name);
}

return nullptr;
}

void CRCChecksumAlgorithmPNA::emitUpdateMethod(EBPF::CodeBuilder *builder, int crcWidth) {
// Note that this update method is optimized for our CRC16 and CRC32, custom
// version may require other method of update. When data_size <= 64 bits,
// applies host byte order for input data, otherwise network byte order is expected.
if (crcWidth == 16) {
// This function calculates CRC16 by definition, it is bit by bit. If input data has more
// than 64 bit, the outer loop process bytes in network byte order - data pointer is
// incremented. For data shorter than or equal 64 bits, bytes are processed in little endian
// byte order - data pointer is decremented by outer loop in this case.
// There is no need for lookup table.
const char *code =
"static __always_inline\n"
"void crc16_update(u16 * reg, const u8 * data, "
"u16 data_size, const u16 poly) {\n"
" if (data_size <= 8)\n"
" data += data_size - 1;\n"
" #pragma clang loop unroll(full)\n"
" for (u16 i = 0; i < data_size; i++) {\n"
" bpf_trace_message(\"CRC16: data byte: %x\\n\", *data);\n"
" *reg ^= *data;\n"
" for (u8 bit = 0; bit < 8; bit++) {\n"
" *reg = (*reg) & 1 ? ((*reg) >> 1) ^ poly : (*reg) >> 1;\n"
" }\n"
" if (data_size <= 8)\n"
" data--;\n"
" else\n"
" data++;\n"
" }\n"
"}";
builder->appendLine(code);
} else if (crcWidth == 32) {
// This function calculates CRC32 using two optimisations: slice-by-8 and Standard
// Implementation. Both algorithms have to properly handle byte order depending on data
// length. There are four cases which must be handled:
// 1. Data size below 8 bytes - calculated using Standard Implementation in little endian
// byte order.
// 2. Data size equal to 8 bytes - calculated using slice-by-8 in little endian byte order.
// 3. Data size more than 8 bytes and multiply of 8 bytes - calculated using slice-by-8 in
// big endian byte order.
// 4. Data size more than 8 bytes and not multiply of 8 bytes - calculated using slice-by-8
// and Standard Implementation both in big endian byte order.
// Lookup table is necessary for both algorithms.
const char *code =
"static __always_inline\n"
"void crc32_update(u32 * reg, const u8 * data, u16 data_size, const u32 poly) {\n"
" u32* current = (u32*) data;\n"
" u32 index = 0;\n"
" u32 lookup_key = 0;\n"
" u32 lookup_value = 0;\n"
" u32 lookup_value1 = 0;\n"
" u32 lookup_value2 = 0;\n"
" u32 lookup_value3 = 0;\n"
" u32 lookup_value4 = 0;\n"
" u32 lookup_value5 = 0;\n"
" u32 lookup_value6 = 0;\n"
" u32 lookup_value7 = 0;\n"
" u32 lookup_value8 = 0;\n"
" u16 tmp = 0;\n"
" if (crc32_table != NULL) {\n"
" for (u16 i = data_size; i >= 8; i -= 8) {\n"
" /* Vars one and two will have swapped byte order if data_size == 8 */\n"
" if (data_size == 8) current = (u32 *)(data + 4);\n"
" bpf_trace_message(\"CRC32: data dword: %x\\n\", *current);\n"
" u32 one = (data_size == 8 ? __builtin_bswap32(*current--) : *current++) ^ "
"*reg;\n"
" bpf_trace_message(\"CRC32: data dword: %x\\n\", *current);\n"
" u32 two = (data_size == 8 ? __builtin_bswap32(*current--) : *current++);\n"
" lookup_key = (one & 0x000000FF);\n"
" lookup_value8 = crc32_table[(u16)(1792 + (u8)lookup_key)];\n"
" lookup_key = (one >> 8) & 0x000000FF;\n"
" lookup_value7 = crc32_table[(u16)(1536 + (u8)lookup_key)];\n"
" lookup_key = (one >> 16) & 0x000000FF;\n"
" lookup_value6 = crc32_table[(u16)(1280 + (u8)lookup_key)];\n"
" lookup_key = one >> 24;\n"
" lookup_value5 = crc32_table[(u16)(1024 + (u8)(lookup_key))];\n"
" lookup_key = (two & 0x000000FF);\n"
" lookup_value4 = crc32_table[(u16)(768 + (u8)lookup_key)];\n"
" lookup_key = (two >> 8) & 0x000000FF;\n"
" lookup_value3 = crc32_table[(u16)(512 + (u8)lookup_key)];\n"
" lookup_key = (two >> 16) & 0x000000FF;\n"
" lookup_value2 = crc32_table[(u16)(256 + (u8)lookup_key)];\n"
" lookup_key = two >> 24;\n"
" lookup_value1 = crc32_table[(u8)(lookup_key)];\n"
" *reg = lookup_value8 ^ lookup_value7 ^ lookup_value6 ^ lookup_value5 ^\n"
" lookup_value4 ^ lookup_value3 ^ lookup_value2 ^ lookup_value1;\n"
" tmp += 8;\n"
" }\n"
" volatile int std_algo_lookup_key = 0;\n"
" if (data_size < 8) {\n"
// Standard Implementation for little endian byte order
" unsigned char *currentChar = (unsigned char *) current;\n"
" currentChar += data_size - 1;\n"
" for (u16 i = tmp; i < data_size; i++) {\n"
" bpf_trace_message(\"CRC32: data byte: %x\\n\", *currentChar);\n"
" std_algo_lookup_key = (u32)(((*reg) & 0xFF) ^ *currentChar--);\n"
" if (std_algo_lookup_key >= 0) {\n"
" lookup_value = "
"crc32_table[(u8)(std_algo_lookup_key & 255)];\n"
" }\n"
" *reg = ((*reg) >> 8) ^ lookup_value;\n"
" }\n"
" } else {\n"
// Standard Implementation for big endian byte order
" /* Consume data not processed by slice-by-8 algorithm above, "
"these data are in network byte order */\n"
" unsigned char *currentChar = (unsigned char *) current;\n"
" for (u16 i = tmp; i < data_size; i++) {\n"
" bpf_trace_message(\"CRC32: data byte: %x\\n\", *currentChar);\n"
" std_algo_lookup_key = (u32)(((*reg) & 0xFF) ^ *currentChar++);\n"
" if (std_algo_lookup_key >= 0) {\n"
" lookup_value = "
"crc32_table[(u8)(std_algo_lookup_key & 255)];\n"
" }\n"
" *reg = ((*reg) >> 8) ^ lookup_value;\n"
" }\n"
" }\n"
" }\n"
"}";
builder->appendLine(code);
}
}

// ===========================CRC16ChecksumAlgorithmPNA===========================

void CRC16ChecksumAlgorithmPNA::emitGlobals(EBPF::CodeBuilder *builder) {
CRCChecksumAlgorithmPNA::emitUpdateMethod(builder, 16);

const char *code =
"static __always_inline "
"u16 crc16_finalize(u16 reg) {\n"
" return reg;\n"
"}";
builder->appendLine(code);
}

// ===========================CRC32ChecksumAlgorithmPNA===========================

void CRC32ChecksumAlgorithmPNA::emitGlobals(EBPF::CodeBuilder *builder) {
CRCChecksumAlgorithmPNA::emitUpdateMethod(builder, 32);

const char *code =
"static __always_inline "
"u32 crc32_finalize(u32 reg) {\n"
" return reg ^ 0xFFFFFFFF;\n"
"}";
builder->appendLine(code);
}

} // namespace P4::TC
50 changes: 7 additions & 43 deletions backends/tc/ebpfCodeGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ using namespace P4::literals;
class ConvertToBackendIR;
class EBPFPnaParser;
class EBPFRegisterPNA;
class EBPFHashPNA;

// Similar to class PSAEbpfGenerator in backends/ebpf/psa/ebpfPsaGen.h

Expand Down Expand Up @@ -279,6 +280,7 @@ class EBPFControlPNA : public EBPF::EBPFControlPSA {
public:
bool addExternDeclaration = false;
std::map<cstring, EBPFRegisterPNA *> pna_registers;
std::map<cstring, EBPFHashPNA *> pna_hashes;

EBPFControlPNA(const EBPF::EBPFProgram *program, const IR::ControlBlock *control,
const IR::Parameter *parserHeaders)
Expand All @@ -289,6 +291,10 @@ class EBPFControlPNA : public EBPF::EBPFControlPSA {
BUG_CHECK(result != nullptr, "No register named %1%", name);
return result;
}
EBPFHashPNA *getHash(cstring name) const {
auto result = ::P4::get(pna_hashes, name);
return result;
}
void emitExternDefinition(EBPF::CodeBuilder *builder) {
if (addExternDeclaration) {
builder->emitIndent();
Expand All @@ -300,6 +306,7 @@ class EBPFControlPNA : public EBPF::EBPFControlPSA {
}
}
void emitTableTypes(EBPF::CodeBuilder *builder) { EBPF::EBPFControl::emitTableTypes(builder); }
void emit(EBPF::CodeBuilder *builder);
};

// Similar to class ConvertToEBPFControlPSA in backends/ebpf/psa/ebpfPsaGen.h
Expand Down Expand Up @@ -415,56 +422,13 @@ class DeparserHdrEmitTranslatorPNA : public EBPF::DeparserPrepareBufferTranslato
unsigned alignment, EBPF::EBPFType *type, bool isMAC);
};

class CRCChecksumAlgorithmPNA : public EBPF::CRCChecksumAlgorithm {
public:
CRCChecksumAlgorithmPNA(const EBPF::EBPFProgram *program, cstring name, int width)
: EBPF::CRCChecksumAlgorithm(program, name, width) {}

static void emitUpdateMethod(EBPF::CodeBuilder *builder, int crcWidth);
};

class CRC16ChecksumAlgorithmPNA : public CRCChecksumAlgorithmPNA {
public:
CRC16ChecksumAlgorithmPNA(const EBPF::EBPFProgram *program, cstring name)
: CRCChecksumAlgorithmPNA(program, name, 16) {
initialValue = "0"_cs;
// We use a 0x8005 polynomial.
// 0xA001 comes from 0x8005 value bits reflection.
polynomial = "0xA001"_cs;
updateMethod = "crc16_update"_cs;
finalizeMethod = "crc16_finalize"_cs;
}

static void emitGlobals(EBPF::CodeBuilder *builder);
};

class CRC32ChecksumAlgorithmPNA : public CRCChecksumAlgorithmPNA {
public:
CRC32ChecksumAlgorithmPNA(const EBPF::EBPFProgram *program, cstring name)
: CRCChecksumAlgorithmPNA(program, name, 32) {
initialValue = "0xffffffff"_cs;
// We use a 0x04C11DB7 polynomial.
// 0xEDB88320 comes from 0x04C11DB7 value bits reflection.
polynomial = "0xEDB88320"_cs;
updateMethod = "crc32_update"_cs;
finalizeMethod = "crc32_finalize"_cs;
}

static void emitGlobals(EBPF::CodeBuilder *builder);
};

class EBPFHashAlgorithmTypeFactoryPNA : public EBPF::EBPFHashAlgorithmTypeFactoryPSA {
public:
static EBPFHashAlgorithmTypeFactoryPNA *instance() {
static EBPFHashAlgorithmTypeFactoryPNA factory;
return &factory;
}

void emitGlobals(EBPF::CodeBuilder *builder) {
CRC16ChecksumAlgorithmPNA::emitGlobals(builder);
CRC32ChecksumAlgorithmPNA::emitGlobals(builder);
}

EBPF::EBPFHashAlgorithmPSA *create(int type, const EBPF::EBPFProgram *program, cstring name);
};

Expand Down
70 changes: 70 additions & 0 deletions backends/tc/tcExterns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,76 @@ void EBPFDigestPNA::emitInitializer(EBPF::CodeBuilder *builder) const {
builder->newline();
}

void EBPFHashPNA::emitVariables(EBPF::CodeBuilder *builder) {
if (engine) {
engine->emitVariables(builder, declaration);
}
}

void EBPFHashPNA::processMethod(EBPF::CodeBuilder *builder, cstring method,
const IR::MethodCallExpression *expr, Visitor *visitor) {
engine->setVisitor(visitor);

if (method == "get_hash") {
BUG_CHECK(expr->arguments->size() == 1 || expr->arguments->size() == 3,
"Expected 1 or 3 arguments: %1%", expr);
engine->emitGet(builder);
} else {
::P4::error(ErrorType::ERR_UNEXPECTED, "Unexpected method call %1%", expr);
}
}

void EBPFHashPNA::calculateHash(EBPF::CodeBuilder *builder, const IR::MethodCallExpression *expr,
Visitor *visitor) {
engine->setVisitor(visitor);
engine->emitClear(builder);
engine->emitAddData(builder, expr->arguments->size() == 3 ? 1 : 0, expr);
builder->newline();
}

void CRCChecksumAlgorithmPNA::emitGet(EBPF::CodeBuilder *builder) {
if (crcWidth == 16) {
builder->appendFormat("%s", registerVar.c_str());
} else {
builder->appendFormat("%s ^ 0xFFFFFFFF", registerVar.c_str());
}
}

void CRCChecksumAlgorithmPNA::emitAddData(EBPF::CodeBuilder *builder, int dataPos,
const IR::MethodCallExpression *expr) {
emitAddData(builder, unpackArguments(expr, dataPos), expr);
}

void CRCChecksumAlgorithmPNA::emitAddData(EBPF::CodeBuilder *builder,
const ArgumentsList &arguments,
const IR::MethodCallExpression *expr) {
cstring kfunc;
if (crcWidth == 16) {
kfunc = expr->arguments->size() == 3 ? "bpf_p4tc_ext_hash_base_crc16"_cs
: "bpf_p4tc_ext_hash_crc16"_cs;
} else {
kfunc = expr->arguments->size() == 3 ? "bpf_p4tc_ext_hash_base_crc32"_cs
: "bpf_p4tc_ext_hash_crc32"_cs;
}
for (auto field : arguments) {
builder->newline();
builder->emitIndent();
builder->appendFormat("%s(&", kfunc);
visitor->visit(field);
builder->append(", sizeof(");
visitor->visit(field);
if (expr->arguments->size() == 3) {
builder->append("), ");
visitor->visit(expr->arguments->at(0));
builder->append(", ");
visitor->visit(expr->arguments->at(2));
builder->appendFormat(", %s);", registerVar);
} else {
builder->appendFormat("), %s);", registerVar);
}
}
}

void EBPFDigestPNA::emitPushElement(EBPF::CodeBuilder *builder, const IR::Expression *elem,
Inspector *codegen) const {
emitInitializer(builder);
Expand Down
Loading
Loading