1local ffi = require('ffi') 2local S = require('syscall') 3 4-- Normalize whitespace and remove empty lines 5local function normalize_code(c) 6 local res = {} 7 for line in string.gmatch(c,'[^\r\n]+') do 8 local op, d, s, t = line:match('(%S+)%s+(%S+)%s+(%S+)%s*([^-]*)') 9 if op then 10 t = t and t:match('^%s*(.-)%s*$') 11 table.insert(res, string.format('%s\t%s %s %s', op, d, s, t)) 12 end 13 end 14 return table.concat(res, '\n') 15end 16 17-- Compile code and check result 18local function compile(t) 19 local bpf = require('bpf') 20 -- require('jit.bc').dump(t.input) 21 local code, err = bpf(t.input) 22 assert.truthy(code) 23 assert.falsy(err) 24 if code then 25 if t.expect then 26 local got = normalize_code(bpf.dump_string(code, 1, true)) 27 -- if normalize_code(t.expect) ~= got then print(bpf.dump_string(code, 1)) end 28 assert.same(normalize_code(t.expect), got) 29 end 30 end 31end 32 33-- Make a mock map variable 34local function makemap(type, max_entries, key_ctype, val_ctype) 35 if not key_ctype then key_ctype = ffi.typeof('uint32_t') end 36 if not val_ctype then val_ctype = ffi.typeof('uint32_t') end 37 if not max_entries then max_entries = 4096 end 38 return { 39 __map = true, 40 max_entries = max_entries, 41 key = ffi.new(ffi.typeof('$ [1]', key_ctype)), 42 val = ffi.new(ffi.typeof('$ [1]', val_ctype)), 43 map_type = S.c.BPF_MAP[type], 44 key_type = key_ctype, 45 val_type = val_ctype, 46 fd = 42, 47 } 48end 49 50describe('codegen', function() 51 -- luacheck: ignore 113 211 212 311 511 52 53 describe('constants', function() 54 it('remove dead constant store', function() 55 compile { 56 input = function () 57 local proto = 5 58 end, 59 expect = [[ 60 MOV R0 #0 61 EXIT R0 #0 62 ]] 63 } 64 end) 65 it('materialize constant', function() 66 compile { 67 input = function () 68 return 5 69 end, 70 expect = [[ 71 MOV R0 #5 72 EXIT R0 #0 73 ]] 74 } 75 end) 76 it('materialize constant longer than i32', function() 77 compile { 78 input = function () 79 return 4294967295 80 end, 81 expect = [[ 82 LDDW R0 #4294967295 83 EXIT R0 #0 84 ]] 85 } 86 end) 87 it('materialize cdata constant', function() 88 compile { 89 input = function () 90 return 5ULL 91 end, 92 expect = [[ 93 LDDW R0 #5 -- composed instruction 94 EXIT R0 #0 95 ]] 96 } 97 end) 98 it('materialize signed cdata constant', function() 99 compile { 100 input = function () 101 return 5LL 102 end, 103 expect = [[ 104 LDDW R0 #5 -- composed instruction 105 EXIT R0 #0 106 ]] 107 } 108 end) 109 it('materialize coercible numeric cdata constant', function() 110 compile { 111 input = function () 112 return 0x00005 113 end, 114 expect = [[ 115 MOV R0 #5 116 EXIT R0 #0 117 ]] 118 } 119 end) 120 it('materialize constant through variable', function() 121 compile { 122 input = function () 123 local proto = 5 124 return proto 125 end, 126 expect = [[ 127 MOV R0 #5 128 EXIT R0 #0 129 ]] 130 } 131 end) 132 it('eliminate constant expressions', function() 133 compile { 134 input = function () 135 return 2 + 3 - 0 136 end, 137 expect = [[ 138 MOV R0 #5 139 EXIT R0 #0 140 ]] 141 } 142 end) 143 it('eliminate constant expressions (if block)', function() 144 compile { 145 input = function () 146 local proto = 5 147 if proto == 5 then 148 proto = 1 149 end 150 return proto 151 end, 152 expect = [[ 153 MOV R0 #1 154 EXIT R0 #0 155 ]] 156 } 157 end) 158 it('eliminate negative constant expressions (if block) NYI', function() 159 -- always negative condition is not fully eliminated 160 compile { 161 input = function () 162 local proto = 5 163 if false then 164 proto = 1 165 end 166 return proto 167 end, 168 expect = [[ 169 MOV R7 #5 170 STXDW [R10-8] R7 171 MOV R7 #0 172 JEQ R7 #0 => 0005 173 LDXDW R0 [R10-8] 174 EXIT R0 #0 175 ]] 176 } 177 end) 178 end) 179 180 describe('variables', function() 181 it('classic packet access (fold constant offset)', function() 182 compile { 183 input = function (skb) 184 return eth.ip.tos -- constant expression will fold 185 end, 186 expect = [[ 187 LDB R0 skb[15] 188 EXIT R0 #0 189 ]] 190 } 191 end) 192 it('classic packet access (load non-constant offset)', function() 193 compile { 194 input = function (skb) 195 return eth.ip.udp.src_port -- need to skip variable-length header 196 end, 197 expect = [[ 198 LDB R0 skb[14] 199 AND R0 #15 200 LSH R0 #2 201 ADD R0 #14 202 STXDW [R10-16] R0 -- NYI: erase dead store 203 LDH R0 skb[R0+0] 204 END R0 R0 205 EXIT R0 #0 206 ]] 207 } 208 end) 209 it('classic packet access (manipulate dissector offset)', function() 210 compile { 211 input = function (skb) 212 local ptr = eth.ip.udp.data + 1 213 return ptr[0] -- dereference dissector pointer 214 end, 215 expect = [[ 216 LDB R0 skb[14] 217 AND R0 #15 218 LSH R0 #2 219 ADD R0 #14 -- NYI: fuse commutative operations in second pass 220 ADD R0 #8 221 ADD R0 #1 222 STXDW [R10-16] R0 223 LDB R0 skb[R0+0] 224 EXIT R0 #0 225 ]] 226 } 227 end) 228 it('classic packet access (multi-byte load)', function() 229 compile { 230 input = function (skb) 231 local ptr = eth.ip.udp.data 232 return ptr(1, 5) -- load 4 bytes 233 end, 234 expect = [[ 235 LDB R0 skb[14] 236 AND R0 #15 237 LSH R0 #2 238 ADD R0 #14 239 ADD R0 #8 240 MOV R7 R0 241 STXDW [R10-16] R0 -- NYI: erase dead store 242 LDW R0 skb[R7+1] 243 END R0 R0 244 EXIT R0 #0 245 ]] 246 } 247 end) 248 it('direct skb field access', function() 249 compile { 250 input = function (skb) 251 return skb.len 252 end, 253 expect = [[ 254 LDXW R7 [R6+0] 255 MOV R0 R7 256 EXIT R0 #0 257 ]] 258 } 259 end) 260 it('direct skb data access (manipulate offset)', function() 261 compile { 262 input = function (skb) 263 local ptr = skb.data + 5 264 return ptr[0] 265 end, 266 expect = [[ 267 LDXW R7 [R6+76] 268 ADD R7 #5 269 LDXB R8 [R7+0] -- NYI: transform LD + ADD to LD + offset addressing 270 MOV R0 R8 271 EXIT R0 #0 272 ]] 273 } 274 end) 275 it('direct skb data access (offset boundary check)', function() 276 compile { 277 input = function (skb) 278 local ptr = skb.data + 5 279 if ptr < skb.data_end then 280 return ptr[0] 281 end 282 end, 283 expect = [[ 284 LDXW R7 [R6+76] 285 ADD R7 #5 286 LDXW R8 [R6+80] 287 JGE R7 R8 => 0008 288 LDXB R8 [R7+0] 289 MOV R0 R8 290 EXIT R0 #0 291 MOV R0 #0 292 EXIT R0 #0 293 ]] 294 } 295 end) 296 it('access stack memory (array, const load, const store)', function() 297 compile { 298 input = function (skb) 299 local mem = ffi.new('uint8_t [16]') 300 mem[0] = 5 301 end, 302 expect = [[ 303 MOV R0 #0 304 STXDW [R10-40] R0 305 STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later 306 STB [R10-48] #5 307 MOV R0 #0 308 EXIT R0 #0 309 ]] 310 } 311 end) 312 it('access stack memory (array, const load, packet store)', function() 313 compile { 314 input = function (skb) 315 local mem = ffi.new('uint8_t [7]') 316 mem[0] = eth.ip.tos 317 end, 318 expect = [[ 319 MOV R0 #0 320 STXDW [R10-40] R0 -- NYI: erase zero-fill on allocation when it's loaded later 321 LDB R0 skb[15] 322 STXB [R10-40] R0 323 MOV R0 #0 324 EXIT R0 #0 325 ]] 326 } 327 end) 328 it('access stack memory (array, packet load, const store)', function() 329 compile { 330 input = function (skb) 331 local mem = ffi.new('uint8_t [1]') 332 mem[eth.ip.tos] = 5 333 end, 334 expect = [[ 335 MOV R0 #0 336 STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later 337 LDB R0 skb[15] 338 MOV R7 R0 339 ADD R7 R10 340 STB [R7-48] #5 341 MOV R0 #0 342 EXIT R0 #0 343 ]] 344 } 345 end) 346 it('access stack memory (array, packet load, packet store)', function() 347 compile { 348 input = function (skb) 349 local mem = ffi.new('uint8_t [7]') 350 local v = eth.ip.tos 351 mem[v] = v 352 end, 353 expect = [[ 354 MOV R0 #0 355 STXDW [R10-40] R0 -- NYI: erase zero-fill on allocation when it's loaded later 356 LDB R0 skb[15] 357 MOV R7 R0 358 ADD R7 R10 359 STXB [R7-40] R0 360 MOV R0 #0 361 EXIT R0 #0 362 ]] 363 } 364 end) 365 it('access stack memory (struct, const/packet store)', function() 366 local kv_t = 'struct { uint64_t a; uint64_t b; }' 367 compile { 368 input = function (skb) 369 local mem = ffi.new(kv_t) 370 mem.a = 5 371 mem.b = eth.ip.tos 372 end, 373 expect = [[ 374 MOV R0 #0 375 STXDW [R10-40] R0 376 STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later 377 MOV R7 #5 378 STXDW [R10-48] R7 379 LDB R0 skb[15] 380 STXDW [R10-40] R0 381 MOV R0 #0 382 EXIT R0 #0 383 ]] 384 } 385 end) 386 it('access stack memory (struct, const/stack store)', function() 387 local kv_t = 'struct { uint64_t a; uint64_t b; }' 388 compile { 389 input = function (skb) 390 local m1 = ffi.new(kv_t) 391 local m2 = ffi.new(kv_t) 392 m1.a = 5 393 m2.b = m1.a 394 end, 395 expect = [[ 396 MOV R0 #0 397 STXDW [R10-48] R0 398 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later 399 MOV R0 #0 400 STXDW [R10-64] R0 401 STXDW [R10-72] R0 -- NYI: erase zero-fill on allocation when it's loaded later 402 MOV R7 #5 403 STXDW [R10-56] R7 404 LDXDW R7 [R10-56] 405 STXDW [R10-64] R7 406 MOV R0 #0 407 EXIT R0 #0 408 ]] 409 } 410 end) 411 it('array map (u32, const key load)', function() 412 local array_map = makemap('array', 256) 413 compile { 414 input = function (skb) 415 return array_map[0] 416 end, 417 expect = [[ 418 LDDW R1 #42 419 STW [R10-28] #0 420 MOV R2 R10 421 ADD R2 #4294967268 422 CALL R0 #1 ; map_lookup_elem 423 JEQ R0 #0 => 0009 424 LDXW R0 [R0+0] 425 EXIT R0 #0 426 ]] 427 } 428 end) 429 it('array map (u32, packet key load)', function() 430 local array_map = makemap('array', 256) 431 compile { 432 input = function (skb) 433 return array_map[eth.ip.tos] 434 end, 435 expect = [[ 436 LDB R0 skb[15] 437 LDDW R1 #42 438 STXW [R10-36] R0 439 MOV R2 R10 440 ADD R2 #4294967260 441 STXDW [R10-24] R0 -- NYI: erase dead store 442 CALL R0 #1 ; map_lookup_elem 443 JEQ R0 #0 => 0011 444 LDXW R0 [R0+0] 445 EXIT R0 #0 446 ]] 447 } 448 end) 449 it('array map (u32, const key store, const value)', function() 450 local array_map = makemap('array', 256) 451 compile { 452 input = function (skb) 453 array_map[0] = 5 454 end, 455 expect = [[ 456 LDDW R1 #42 457 STW [R10-36] #0 458 MOV R2 R10 459 ADD R2 #4294967260 460 MOV R4 #0 461 STW [R10-40] #5 462 MOV R3 R10 463 ADD R3 #4294967256 464 CALL R0 #2 ; map_update_elem 465 MOV R0 #0 466 EXIT R0 #0 467 ]] 468 } 469 end) 470 it('array map (u32, const key store, packet value)', function() 471 local array_map = makemap('array', 256) 472 compile { 473 input = function (skb) 474 array_map[0] = eth.ip.tos 475 end, 476 expect = [[ 477 LDB R0 skb[15] 478 STXDW [R10-24] R0 479 LDDW R1 #42 480 STW [R10-36] #0 481 MOV R2 R10 482 ADD R2 #4294967260 483 MOV R4 #0 484 MOV R3 R10 485 ADD R3 #4294967272 486 CALL R0 #2 ; map_update_elem 487 MOV R0 #0 488 EXIT R0 #0 489 ]] 490 } 491 end) 492 it('array map (u32, const key store, map value)', function() 493 local array_map = makemap('array', 256) 494 compile { 495 input = function (skb) 496 array_map[0] = array_map[1] 497 end, 498 expect = [[ 499 LDDW R1 #42 500 STW [R10-36] #1 501 MOV R2 R10 502 ADD R2 #4294967260 503 CALL R0 #1 ; map_lookup_elem 504 STXDW [R10-24] R0 505 LDDW R1 #42 506 STW [R10-36] #0 507 MOV R2 R10 508 ADD R2 #4294967260 509 MOV R4 #0 510 LDXDW R3 [R10-24] 511 JEQ R3 #0 => 0017 512 LDXW R3 [R3+0] 513 STXW [R10-40] R3 514 MOV R3 R10 515 ADD R3 #4294967256 516 CALL R0 #2 ; map_update_elem 517 MOV R0 #0 518 EXIT R0 #0 519 ]] 520 } 521 end) 522 it('array map (u32, const key replace, const value)', function() 523 local array_map = makemap('array', 256) 524 compile { 525 input = function (skb) 526 local val = array_map[0] 527 if val then 528 val[0] = val[0] + 1 529 else 530 array_map[0] = 5 531 end 532 end, 533 expect = [[ 534 LDDW R1 #42 535 STW [R10-44] #0 536 MOV R2 R10 537 ADD R2 #4294967252 538 CALL R0 #1 ; map_lookup_elem 539 JEQ R0 #0 => 0013 -- if (map_value ~= NULL) 540 LDXW R7 [R0+0] 541 ADD R7 #1 542 STXW [R0+0] R7 543 MOV R7 #0 544 JEQ R7 #0 => 0025 -- skip false branch 545 STXDW [R10-16] R0 546 LDDW R1 #42 547 STW [R10-44] #0 548 MOV R2 R10 549 ADD R2 #4294967252 550 MOV R4 #0 551 STW [R10-48] #5 552 MOV R3 R10 553 ADD R3 #4294967248 554 CALL R0 #2 ; map_update_elem 555 LDXDW R0 [R10-16] 556 MOV R0 #0 557 EXIT R0 #0 558 ]] 559 } 560 end) 561 it('array map (u32, const key replace xadd, const value)', function() 562 local array_map = makemap('array', 256) 563 compile { 564 input = function (skb) 565 local val = array_map[0] 566 if val then 567 xadd(val, 1) 568 else 569 array_map[0] = 5 570 end 571 end, 572 expect = [[ 573 LDDW R1 #42 574 STW [R10-52] #0 575 MOV R2 R10 576 ADD R2 #4294967244 577 CALL R0 #1 ; map_lookup_elem 578 JEQ R0 #0 => 0014 -- if (map_value ~= NULL) 579 MOV R7 #1 580 MOV R8 R0 581 STXDW [R10-16] R0 582 XADDW [R8+0] R7 583 MOV R7 #0 584 JEQ R7 #0 => 0025 -- skip false branch 585 STXDW [R10-16] R0 586 LDDW R1 #42 587 STW [R10-52] #0 588 MOV R2 R10 589 ADD R2 #4294967244 590 MOV R4 #0 591 STW [R10-56] #5 592 MOV R3 R10 593 ADD R3 #4294967240 594 CALL R0 #2 ; map_update_elem 595 MOV R0 #0 596 EXIT R0 #0 597 ]] 598 } 599 end) 600 it('array map (u32, const key replace xadd, const value) inverse nil check', function() 601 local array_map = makemap('array', 256) 602 compile { 603 input = function (skb) 604 local val = array_map[0] 605 if not val then 606 array_map[0] = 5 607 else 608 xadd(val, 1) 609 end 610 end, 611 expect = [[ 612 LDDW R1 #42 613 STW [R10-52] #0 614 MOV R2 R10 615 ADD R2 #4294967244 616 CALL R0 #1 ; map_lookup_elem 617 JNE R0 #0 => 0021 618 STXDW [R10-16] R0 619 LDDW R1 #42 620 STW [R10-52] #0 621 MOV R2 R10 622 ADD R2 #4294967244 623 MOV R4 #0 624 STW [R10-56] #5 625 MOV R3 R10 626 ADD R3 #4294967240 627 CALL R0 #2 ; map_update_elem 628 MOV R7 #0 629 JEQ R7 #0 => 0025 630 MOV R7 #1 631 MOV R8 R0 632 STXDW [R10-16] R0 633 XADDW [R8+0] R7 634 MOV R0 #0 635 EXIT R0 #0 636 ]] 637 } 638 end) 639 it('array map (struct, stack key load)', function() 640 local kv_t = 'struct { uint64_t a; uint64_t b; }' 641 local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) 642 compile { 643 input = function (skb) 644 local key = ffi.new(kv_t) 645 key.a = 2 646 key.b = 3 647 local val = array_map[key] -- Use composite key from stack memory 648 if val then 649 return val.a 650 end 651 end, 652 expect = [[ 653 MOV R0 #0 654 STXDW [R10-48] R0 655 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later 656 MOV R7 #2 657 STXDW [R10-56] R7 658 MOV R7 #3 659 STXDW [R10-48] R7 660 LDDW R1 #42 661 MOV R2 R10 662 ADD R2 #4294967240 663 CALL R0 #1 ; map_lookup_elem 664 JEQ R0 #0 => 0017 665 LDXDW R7 [R0+0] 666 MOV R0 R7 667 EXIT R0 #0 668 MOV R0 #0 669 EXIT R0 #0 670 ]] 671 } 672 end) 673 it('array map (struct, stack key store)', function() 674 local kv_t = 'struct { uint64_t a; uint64_t b; }' 675 local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) 676 compile { 677 input = function (skb) 678 local key = ffi.new(kv_t) 679 key.a = 2 680 key.b = 3 681 array_map[key] = key -- Use composite key from stack memory 682 end, 683 expect = [[ 684 MOV R0 #0 685 STXDW [R10-40] R0 686 STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later 687 MOV R7 #2 688 STXDW [R10-48] R7 689 MOV R7 #3 690 STXDW [R10-40] R7 691 LDDW R1 #42 692 MOV R2 R10 693 ADD R2 #4294967248 694 MOV R4 #0 695 MOV R3 R10 696 ADD R3 #4294967248 697 CALL R0 #2 ; map_update_elem 698 MOV R0 #0 699 EXIT R0 #0 700 ]] 701 } 702 end) 703 it('array map (struct, stack/packet key update, const value)', function() 704 local kv_t = 'struct { uint64_t a; uint64_t b; }' 705 local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) 706 compile { 707 input = function (skb) 708 local key = ffi.new(kv_t) 709 key.a = eth.ip.tos -- Load key part from dissector 710 local val = array_map[key] 711 if val then 712 val.a = 5 713 end 714 end, 715 expect = [[ 716 MOV R0 #0 717 STXDW [R10-48] R0 718 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later 719 LDB R0 skb[15] 720 STXDW [R10-56] R0 721 LDDW R1 #42 722 MOV R2 R10 723 ADD R2 #4294967240 724 CALL R0 #1 ; map_lookup_elem 725 JEQ R0 #0 => 0014 726 MOV R7 #5 727 STXDW [R0+0] R7 728 MOV R0 #0 729 EXIT R0 #0 730 ]] 731 } 732 end) 733 it('array map (struct, stack/packet key update, map value)', function() 734 local kv_t = 'struct { uint64_t a; uint64_t b; }' 735 local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) 736 compile { 737 input = function (skb) 738 local key = ffi.new(kv_t) 739 key.a = eth.ip.tos -- Load key part from dissector 740 local val = array_map[key] 741 if val then 742 val.a = val.b 743 end 744 end, 745 expect = [[ 746 MOV R0 #0 747 STXDW [R10-48] R0 748 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later 749 LDB R0 skb[15] 750 STXDW [R10-56] R0 751 LDDW R1 #42 752 MOV R2 R10 753 ADD R2 #4294967240 754 CALL R0 #1 ; map_lookup_elem 755 JEQ R0 #0 => 0014 756 LDXDW R7 [R0+8] 757 STXDW [R0+0] R7 758 MOV R0 #0 759 EXIT R0 #0 760 ]] 761 } 762 end) 763 it('array map (struct, stack/packet key update, stack value)', function() 764 local kv_t = 'struct { uint64_t a; uint64_t b; }' 765 local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) 766 compile { 767 input = function (skb) 768 local key = ffi.new(kv_t) 769 key.a = eth.ip.tos -- Load key part from dissector 770 local val = array_map[key] 771 if val then 772 val.a = key.b 773 end 774 end, 775 expect = [[ 776 MOV R0 #0 777 STXDW [R10-48] R0 778 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later 779 LDB R0 skb[15] 780 STXDW [R10-56] R0 781 LDDW R1 #42 782 MOV R2 R10 783 ADD R2 #4294967240 784 CALL R0 #1 ; map_lookup_elem 785 JEQ R0 #0 => 0014 786 LDXDW R7 [R10-48] 787 STXDW [R0+0] R7 788 MOV R0 #0 789 EXIT R0 #0 790 ]] 791 } 792 end) 793 it('array map (struct, stack/packet key replace, stack value)', function() 794 local kv_t = 'struct { uint64_t a; uint64_t b; }' 795 local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) 796 compile { 797 input = function (skb) 798 local key = ffi.new(kv_t) 799 key.a = eth.ip.tos -- Load key part from dissector 800 local val = array_map[key] 801 if val then 802 val.a = key.b 803 else 804 array_map[key] = key 805 end 806 end, 807 expect = [[ 808 MOV R0 #0 809 STXDW [R10-48] R0 810 STXDW [R10-56] R0 811 LDB R0 skb[15] 812 STXDW [R10-56] R0 813 LDDW R1 #42 814 MOV R2 R10 815 ADD R2 #4294967240 816 CALL R0 #1 ; map_lookup_elem 817 JEQ R0 #0 => 0016 -- if (map_value ~= NULL) 818 LDXDW R7 [R10-48] 819 STXDW [R0+0] R7 820 MOV R7 #0 821 JEQ R7 #0 => 0026 -- jump over false branch 822 STXDW [R10-24] R0 823 LDDW R1 #42 824 MOV R2 R10 825 ADD R2 #4294967240 826 MOV R4 #0 827 MOV R3 R10 828 ADD R3 #4294967240 829 CALL R0 #2 ; map_update_elem 830 LDXDW R0 [R10-24] 831 MOV R0 #0 832 EXIT R0 #0 833 ]] 834 } 835 end) 836 end) 837 describe('control flow', function() 838 it('condition with constant return', function() 839 compile { 840 input = function (skb) 841 local v = eth.ip.tos 842 if v then 843 return 1 844 else 845 return 0 846 end 847 end, 848 expect = [[ 849 LDB R0 skb[15] 850 JEQ R0 #0 => 0005 851 MOV R0 #1 852 EXIT R0 #0 853 MOV R0 #0 -- 0005 jump target 854 EXIT R0 #0 855 ]] 856 } 857 end) 858 it('condition with cdata constant return', function() 859 local cdata = 2ULL 860 compile { 861 input = function (skb) 862 local v = eth.ip.tos 863 if v then 864 return cdata + 1 865 else 866 return 0 867 end 868 end, 869 expect = [[ 870 LDB R0 skb[15] 871 JEQ R0 #0 => 0006 872 LDDW R0 #3 873 EXIT R0 #0 874 MOV R0 #0 -- 0006 jump target 875 EXIT R0 #0 876 ]] 877 } 878 end) 879 it('condition with constant return (inversed)', function() 880 compile { 881 input = function (skb) 882 local v = eth.ip.tos 883 if not v then 884 return 1 885 else 886 return 0 887 end 888 end, 889 expect = [[ 890 LDB R0 skb[15] 891 JNE R0 #0 => 0005 892 MOV R0 #1 893 EXIT R0 #0 894 MOV R0 #0 -- 0005 jump target 895 EXIT R0 #0 896 ]] 897 } 898 end) 899 it('condition with variable mutation', function() 900 compile { 901 input = function (skb) 902 local v = 0 903 if eth.ip.tos then 904 v = 1 905 end 906 return v 907 end, 908 expect = [[ 909 LDB R0 skb[15] 910 MOV R1 #0 911 STXDW [R10-16] R1 912 JEQ R0 #0 => 0007 913 MOV R7 #1 914 STXDW [R10-16] R7 915 LDXDW R0 [R10-16] 916 EXIT R0 #0 917 ]] 918 } 919 end) 920 it('condition with nil variable mutation', function() 921 compile { 922 input = function (skb) 923 local v -- nil, will be elided 924 if eth.ip.tos then 925 v = 1 926 else 927 v = 0 928 end 929 return v 930 end, 931 expect = [[ 932 LDB R0 skb[15] 933 JEQ R0 #0 => 0007 934 MOV R7 #1 935 STXDW [R10-16] R7 936 MOV R7 #0 937 JEQ R7 #0 => 0009 938 MOV R7 #0 939 STXDW [R10-16] R7 940 LDXDW R0 [R10-16] 941 EXIT R0 #0 942 ]] 943 } 944 end) 945 it('nested condition with variable mutation', function() 946 compile { 947 input = function (skb) 948 local v = 0 949 local tos = eth.ip.tos 950 if tos then 951 if tos > 5 then 952 v = 5 953 else 954 v = 1 955 end 956 end 957 return v 958 end, 959 expect = [[ 960 LDB R0 skb[15] 961 MOV R1 #0 962 STXDW [R10-16] R1 -- materialize v = 0 963 JEQ R0 #0 => 0013 -- if not tos 964 MOV R7 #5 965 JGE R7 R0 => 0011 -- if 5 > tos 966 MOV R7 #5 967 STXDW [R10-16] R7 -- materialize v = 5 968 MOV R7 #0 969 JEQ R7 #0 => 0013 970 MOV R7 #1 -- 0011 jump target 971 STXDW [R10-16] R7 -- materialize v = 1 972 LDXDW R0 [R10-16] 973 EXIT R0 #0 974 ]] 975 } 976 end) 977 it('nested condition with variable shadowing', function() 978 compile { 979 input = function (skb) 980 local v = 0 981 local tos = eth.ip.tos 982 if tos then 983 local v = 0 -- luacheck: ignore 231 984 if tos > 5 then 985 v = 5 -- changing shadowing variable 986 end 987 else 988 v = 1 989 end 990 return v 991 end, 992 expect = [[ 993 LDB R0 skb[15] 994 MOV R1 #0 995 STXDW [R10-16] R1 -- materialize v = 0 996 JEQ R0 #0 => 0011 -- if not tos 997 MOV R7 #5 998 MOV R1 #0 999 STXDW [R10-32] R1 -- materialize shadowing variable 1000 JGE R7 R0 => 0013 -- if 5 > tos 1001 MOV R7 #0 -- erased 'v = 5' dead store 1002 JEQ R7 #0 => 0013 1003 MOV R7 #1 -- 0011 jump target 1004 STXDW [R10-16] R7 -- materialize v = 1 1005 LDXDW R0 [R10-16] -- 0013 jump target 1006 EXIT R0 #0 1007 ]] 1008 } 1009 end) 1010 it('condition materializes shadowing variable at the end of BB', function() 1011 compile { 1012 input = function (skb) 1013 local v = time() 1014 local v1 = 0 -- luacheck: ignore 231 1015 if eth.ip.tos then 1016 v1 = v 1017 end 1018 end, 1019 expect = [[ 1020 CALL R0 #5 ; ktime_get_ns 1021 STXDW [R10-16] R0 1022 LDB R0 skb[15] 1023 MOV R1 #0 1024 STXDW [R10-24] R1 -- materialize v1 = 0 1025 JEQ R0 #0 => 0009 1026 LDXDW R7 [R10-16] 1027 STXDW [R10-24] R7 -- v1 = v0 1028 MOV R0 #0 1029 EXIT R0 #0 1030 ]] 1031 } 1032 end) 1033 1034 end) 1035end) 1036