[lonetix/bgp] Optimize BGP VM code by adding more terminating instructions and factorize their code; remove obsolete ASMTCH instruction, redirecting it to FASMTC

This commit is contained in:
Lorenzo Cogotti 2021-10-18 12:14:22 +02:00
parent 45d9b20b9e
commit ae052ea987
6 changed files with 92 additions and 161 deletions

View File

@ -47,6 +47,8 @@
* by any VM and is always discarded by `Bgp_VmStoreMatch()`.
*/
static Bgpvmmatch discardMatch;
// Used for empty programs
static const Bgpvmbytec emptyProg = BGP_VMOP_END;
Judgement Bgp_InitVm(Bgpvm *vm, size_t heapSiz)
{
@ -64,6 +66,7 @@ Judgement Bgp_InitVm(Bgpvm *vm, size_t heapSiz)
vm->heap = heap;
vm->hMemSiz = siz;
vm->hHighMark = siz;
vm->prog = (Bgpvmbytec *) &emptyProg;
return Bgp_SetErrStat(BGPENOERR);
}
@ -76,8 +79,12 @@ Judgement Bgp_VmEmit(Bgpvm *vm, Bgpvmbytec bytec)
if (vm->progLen + 1 >= vm->progCap) {
// Grow the VM program segment
Bgpvmbytec *oldProg = vm->prog;
if (vm->prog == &emptyProg)
oldProg = NULL;
size_t newSiz = vm->progCap + BGP_VM_GROWPROGN;
Bgpvmbytec *newProg = (Bgpvmbytec *) realloc(vm->prog, newSiz * sizeof(*newProg));
Bgpvmbytec *newProg = (Bgpvmbytec *) realloc(oldProg, newSiz * sizeof(*newProg));
if (!newProg) {
// Flag the VM as bad
vm->setupFailed = TRUE;
@ -157,7 +164,7 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg)
}
// Setup initial VM state
Boolean result = TRUE; // assume PASS unless CFAIL says otherwise
Boolean result = TRUE; // assume pass unless found otherwise
vm->pc = 0;
vm->si = 0;
@ -217,20 +224,29 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg)
break;
EXECUTE(NOT):
Bgp_VmDoNot(vm);
EXPECT(CFAIL, ir, vm);
EXPECT(CPASS, ir, vm);
break;
EXECUTE(CFAIL):
if (Bgp_VmDoCfail(vm)) {
result = FALSE; // immediate terminate on FAIL
if (!Bgp_VmDoBreakPoint(vm, /*breakIf=*/TRUE, /*onBreak=*/FALSE)) {
result = FALSE; // immediate failure
goto terminate;
}
break;
EXECUTE(CPASS):
if (Bgp_VmDoCpass(vm))
goto terminate; // immediate PASS
if (!Bgp_VmDoBreakPoint(vm, /*breakIf=*/TRUE, /*onBreak=*/TRUE))
goto terminate; // immediate pass
break;
EXECUTE(ORFAIL):
if (!Bgp_VmDoBreakPoint(vm, /*breakIf=*/FALSE, /*onBreak=*/FALSE)) {
result = FALSE; // immediate failure
goto terminate;
}
break;
EXECUTE(ORPASS):
if (!Bgp_VmDoBreakPoint(vm, /*breakIf=*/FALSE, /*onBreak=*/TRUE))
goto terminate; // immediate pass
break;
EXECUTE(JZ):
@ -243,9 +259,9 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg)
if (vm->pc > vm->progLen) UNLIKELY
vm->errCode = BGPEVMBADJMP; // jump target out of bounds
} else
} else {
BGP_VMPOP(vm); // no jump, pop the stack and move on
}
break;
EXECUTE(JNZ):
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
@ -257,9 +273,9 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg)
if (vm->pc > vm->progLen) UNLIKELY
vm->errCode = BGPEVMBADJMP; // jump target out of bounds
} else
} else {
BGP_VMPOP(vm); // no jump, pop the stack and move on
}
break;
EXECUTE(CHKT):
Bgp_VmDoChkt(vm, (BgpType) BGP_VMOPARG(ir));
@ -282,9 +298,6 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg)
EXECUTE(ASMTCH):
Bgp_VmDoAsmtch(vm);
break;
EXECUTE(FASMTC):
Bgp_VmDoFasmtc(vm);
break;
EXECUTE(COMTCH):
Bgp_VmDoComtch(vm);
break;
@ -348,86 +361,43 @@ void Bgp_VmStoreMatch(Bgpvm *vm)
vm->nmatches++;
}
Boolean Bgp_VmDoCpass(Bgpvm *vm)
Boolean Bgp_VmDoBreakPoint(Bgpvm *vm,
Boolean breakIf,
Boolean onBreak)
{
/* POPS:
* -1: Last operation Boolean result (as a Sint64, 0 for FALSE)
*
* PUSHES:
* * On PASS result:
* - TRUE
* * On break result:
* - `onBreak`
* * Otherwise:
* - Nothing.
*
* SIDE-EFFECTS:
* - Breaks current BLK on PASS
* - Breaks current BLK if condition is met
* - Updates `curMatch->isPassing` flag accordingly
*/
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
return FALSE; // error, let vm->errCode handle this
return TRUE; // error, let vm->errCode handle this
Boolean shouldTerm = FALSE; // unless proven otherwise
Boolean res = TRUE; // unless we're out of blocks
// If stack top is non-zero we FAIL
if (BGP_VMPEEK(vm, -1)) {
// Leave TRUE on stack and break current BLK, this is a PASS
vm->curMatch->isPassing = TRUE;
if (vm->nblk > 0)
Bgp_VmDoBreak(vm);
else
shouldTerm = TRUE; // no more BLK
} else {
// Pop the stack and move on, no PASS
vm->curMatch->isPassing = FALSE;
BGP_VMPOP(vm);
}
// Current match information has been collected,
// discard anything up to the next relevant operation
vm->curMatch = &discardMatch;
return shouldTerm;
}
Boolean Bgp_VmDoCfail(Bgpvm *vm)
{
/* POPS:
* -1: Last operation Boolean result (as a Sint64, 0 for FALSE)
*
* PUSHES:
* * On FAIL result:
* - FALSE
* * Otherwise:
* - Nothing.
*
* SIDE-EFFECTS:
* - Breaks current BLK on FAIL
* - Updates `curMatch->isPassing` flag accordingly
*/
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
return FALSE; // error, let vm->errCode handle this
Boolean shouldTerm = FALSE; // unless proven otherwise
// If stack top is non-zero we FAIL
Bgpvmval *v = BGP_VMSTKGET(vm, -1);
if (v->val) {
// Push FALSE and break current BLK, this is a FAIL
vm->curMatch->isPassing = FALSE;
if (!!(v->val) == breakIf) {
// Push `onBreak` and break current BLK
vm->curMatch->isPassing = onBreak;
v->val = FALSE;
v->val = onBreak;
if (vm->nblk > 0)
Bgp_VmDoBreak(vm);
else
shouldTerm = TRUE; // no more BLK
res = FALSE; // no more BLK
} else {
// Pop the stack and move on, no FAIL
vm->curMatch->isPassing = TRUE;
// Pop the stack and move on, no break
vm->curMatch->isPassing = !onBreak;
BGP_VMPOP(vm);
}
@ -435,7 +405,7 @@ Boolean Bgp_VmDoCfail(Bgpvm *vm)
// discard anything up to the next relevant operation
vm->curMatch = &discardMatch;
return shouldTerm;
return res;
}
void Bgp_VmDoChkt(Bgpvm *vm, BgpType type)
@ -813,5 +783,6 @@ void Bgp_ResetVm(Bgpvm *vm)
void Bgp_ClearVm(Bgpvm *vm)
{
free(vm->heap);
free(vm->prog);
if (vm->prog != &emptyProg)
free(vm->prog);
}

View File

@ -741,65 +741,6 @@ void *Bgp_VmCompileAsMatch(Bgpvm *vm, const Asn *expression, size_t n)
}
void Bgp_VmDoAsmtch(Bgpvm *vm)
{
/* POPS:
* -1: Asn match array length
* -2: Address to Asn match array
*
* PUSHES:
* TRUE on successful match, FALSE otherwise
*/
Nfacomp nc;
Nfa nfa;
Nfainst *buf, *prog;
if (!BGP_VMCHKSTK(vm, 2))
return;
// Pop arguments from stack
Sint64 n = BGP_VMPOP(vm);
const Asn *match = (const Asn *) BGP_VMPOPA(vm);
if (n <= 0 || match == NULL) {
vm->errCode = BGPEVMBADASMTCH;
return;
}
if (!BGP_VMCHKMSGTYPE(vm, BGP_UPDATE)) {
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
return;
}
// Compile on the fly on temporary memory
const size_t maxsiz = 6 * n * sizeof(*buf);
buf = (Nfainst *) Bgp_VmTempAlloc(vm, maxsiz);
if (!buf)
return;
compinit(&nc, buf);
int err; // compilation status
if ((err = setjmp(nc.oops)) != 0) {
vm->errCode = err;
return;
}
prog = compile(&nc, match, n);
#ifdef DF_DEBUG_ASMTCH
dumpprog(prog);
#endif
BgpvmRet status = execute(vm, prog, LISTSIZ, &nfa);
if (status == BGPEVMASMTCHESIZE)
status = execute(vm, prog, BIGLISTSIZ, &nfa);
Bgp_VmTempFree(vm, maxsiz);
if (status == BGPENOERR)
collect(vm, &nfa);
}
void Bgp_VmDoFasmtc(Bgpvm *vm)
{
/* POPS:
* -1: Precompiled NFA instructions

View File

@ -41,6 +41,8 @@ static const char *OpcString(Bgpvmopc opc)
case BGP_VMOP_NOT: return "NOT";
case BGP_VMOP_CFAIL: return "CFAIL";
case BGP_VMOP_CPASS: return "CPASS";
case BGP_VMOP_ORFAIL: return "ORFAIL";
case BGP_VMOP_ORPASS: return "ORPASS";
case BGP_VMOP_JZ: return "JZ";
case BGP_VMOP_JNZ: return "JNZ";
case BGP_VMOP_CHKT: return "CHKT";
@ -50,7 +52,6 @@ static const char *OpcString(Bgpvmopc opc)
case BGP_VMOP_SUBN: return "SUBN";
case BGP_VMOP_RELT: return "RELT";
case BGP_VMOP_ASMTCH: return "ASMTCH";
case BGP_VMOP_FASMTC: return "FASMTC";
case BGP_VMOP_COMTCH: return "COMTCH";
case BGP_VMOP_ACOMTC: return "ACOMTC";
case BGP_VMOP_END: return "END";
@ -181,7 +182,7 @@ static char *CommentCodeLine(char *line, const char *comment)
void Bgp_VmDumpProgram(Bgpvm *vm, void *streamp, const StmOps *ops)
{
char explainbuf[64];
char explainbuf[64];
char buf[256];
int indent = 0;
@ -234,7 +235,7 @@ void Bgp_VmDumpProgram(Bgpvm *vm, void *streamp, const StmOps *ops)
p = Utoa(opa, p);
if (opc == BGP_VMOP_JZ || opc == BGP_VMOP_JNZ)
opastr = ExplainJump(explainbuf, ip, opa, vm->progLen);
opastr = ExplainJump(explainbuf, ip, opa, vm->progLen);
break;
case BGP_VMOP_TAG:

View File

@ -80,6 +80,8 @@ static void *const bgp_vmOpTab[256] = {
[BGP_VMOP_NOT] = &&EX_NOT,
[BGP_VMOP_CFAIL] = &&EX_CFAIL,
[BGP_VMOP_CPASS] = &&EX_CPASS,
[BGP_VMOP_ORFAIL] = &&EX_ORFAIL,
[BGP_VMOP_ORPASS] = &&EX_ORPASS,
[BGP_VMOP_JZ] = &&EX_JZ,
[BGP_VMOP_JNZ] = &&EX_JNZ,
[BGP_VMOP_CHKT] = &&EX_CHKT,
@ -89,7 +91,6 @@ static void *const bgp_vmOpTab[256] = {
[BGP_VMOP_SUBN] = &&EX_SUBN,
[BGP_VMOP_RELT] = &&EX_RELT,
[BGP_VMOP_ASMTCH] = &&EX_ASMTCH,
[BGP_VMOP_FASMTC] = &&EX_FASMTC,
[BGP_VMOP_COMTCH] = &&EX_COMTCH,
[BGP_VMOP_ACOMTC] = &&EX_ACOMTC,
[BGP_VMOP_END] = &&EX_END

View File

@ -75,26 +75,40 @@ FORCE_INLINE Uint8 BGP_VMOPARG(Bgpvmbytec bytec)
#define BGP_VMOP_TAG U8_C(8)
/// NOT - Boolean negate the topmost stack element
#define BGP_VMOP_NOT U8_C(9)
/// CONDITIONAL FAIL If TRUE - Fail the current matching `BLK` if topmost stack element is non-zero
/// CONDITIONAL FAIL If TRUE - Fail current match `BLK` if topmost stack element is non-zero
#define BGP_VMOP_CFAIL U8_C(10)
/// CONDITIONAL PASS If TRUE - Pass the current matching `BLK` if topmost stack element is non-zero
/// CONDITIONAL PASS If TRUE - Pass current match `BLK` if topmost stack element is non-zero
#define BGP_VMOP_CPASS U8_C(11)
/// FAIL IF FALSE - Fail current match `BLK` if topmost stack element is zero
#define BGP_VMOP_ORFAIL U8_C(12)
/// PASS IF FALSE - Pass current match `BLK` if topmost stack element is zero
#define BGP_VMOP_ORPASS U8_C(13)
FORCE_INLINE Boolean BGP_ISVMOPBREAKING(Bgpvmopc opc)
{
return opc >= BGP_VMOP_CFAIL && opc <= BGP_VMOP_ORPASS;
}
/// Jump if zero - Skip over a positive number of instructions if topmost stack element is 0.
#define BGP_VMOP_JZ U8_C(12)
#define BGP_VMOP_JZ U8_C(14)
/// Jump if non-zero - Skip over a positive number of instructions if topmost stack element is not 0.
#define BGP_VMOP_JNZ U8_C(13)
#define BGP_VMOP_JNZ U8_C(15)
FORCE_INLINE Boolean Bgp_ISVMOPJMP(Bgpvmopc opc)
{
return opc == BGP_VMOP_JZ || opc == BGP_VMOP_JNZ;
}
/// CHECK TYPE - ARG is the `BgpType` to test against
#define BGP_VMOP_CHKT U8_C(14)
#define BGP_VMOP_CHKT U8_C(16)
/// CHECK ATTRIBUTE - ARG is the `BgpAttrCode` to test for existence
#define BGP_VMOP_CHKA U8_C(15)
#define BGP_VMOP_CHKA U8_C(17)
#define BGP_VMOP_EXCT U8_C(16)
#define BGP_VMOP_SUPN U8_C(17)
#define BGP_VMOP_SUBN U8_C(18)
#define BGP_VMOP_EXCT U8_C(18)
#define BGP_VMOP_SUPN U8_C(19)
#define BGP_VMOP_SUBN U8_C(20)
/// RELATED - Tests whether the BGP message contains prefixes related with the provided ones
#define BGP_VMOP_RELT U8_C(19)
#define BGP_VMOP_RELT U8_C(21)
/// Returns `TRUE` if `opc` belongs to an instruction operating on NETwork prefixes.
FORCE_INLINE Boolean BGP_ISVMOPNET(Bgpvmopc opc)
@ -102,17 +116,20 @@ FORCE_INLINE Boolean BGP_ISVMOPNET(Bgpvmopc opc)
return opc >= BGP_VMOP_EXCT && opc <= BGP_VMOP_RELT;
}
/// AS PATH MATCH - Tests BGP message AS PATH against a match expression
#define BGP_VMOP_ASMTCH U8_C(20)
/// FAST AS PATH MATCH - AS PATH test using precompiled AS PATH match expression
#define BGP_VMOP_FASMTC U8_C(21)
/// AS PATH MATCH - Tests BGP message AS PATH against a precompiled match expression
#define BGP_VMOP_ASMTCH U8_C(22)
/// COMMUNITY MATCH - COMMUNITY test using a precompiled COMMUNITY match expression
#define BGP_VMOP_COMTCH U8_C(22)
#define BGP_VMOP_COMTCH U8_C(23)
/// ALL COMMUNITY MATCH - Like COMTCH, but requires all communities to be present inside message
#define BGP_VMOP_ACOMTC U8_C(23)
#define BGP_VMOP_ACOMTC U8_C(24)
/// END - Terminate VM execution with the latest result
#define BGP_VMOP_END U8_C(24)
#define BGP_VMOP_END U8_C(25)
FORCE_INLINE Boolean BGP_ISVMOPENDING(Bgpvmopc opc)
{
return opc == BGP_VMOP_ENDBLK || opc == BGP_VMOP_END;
}
// #define BGP_VMOP_MOVK MOVE K - Move topmost K index to ARG K index
// #define BGP_VMOP_DISCRD DISCARD - discard vm->curMatch if any

View File

@ -201,10 +201,11 @@ FORCE_INLINE void Bgp_VmDoCall(Bgpvm *vm, Uint8 idx)
if (fn) fn(vm);
}
/// Implement `CPASS` (Conditional `PASS` if `TRUE`).
Boolean Bgp_VmDoCpass(Bgpvm *vm);
/// Implement `CFAIL` (Conditional `FAIL` if `TRUE`).
Boolean Bgp_VmDoCfail(Bgpvm *vm);
/**
* \brief Implement `CPASS`, `CFAIL`, `ORPASS`, `ORFAIL`, depending
* on break condition and value.
*/
Boolean Bgp_VmDoBreakPoint(Bgpvm *vm, Boolean breakIf, Boolean onBreak);
/// Implement `TAG` instruction with argument `tag`.
FORCE_INLINE void Bgp_VmDoTag(Bgpvm *vm, Uint8 tag)
@ -242,7 +243,6 @@ void Bgp_VmDoSubn(Bgpvm *vm, Uint8 arg);
void Bgp_VmDoRelt(Bgpvm *vm, Uint8 arg);
void Bgp_VmDoAsmtch(Bgpvm *vm);
void Bgp_VmDoFasmtc(Bgpvm *vm);
void Bgp_VmDoComtch(Bgpvm *vm);
void Bgp_VmDoAcomtc(Bgpvm *vm);