[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:
parent
45d9b20b9e
commit
ae052ea987
123
lonetix/bgp/vm.c
123
lonetix/bgp/vm.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue