diff options
-rw-r--r-- | subx/040---tests.cc | 104 | ||||
-rwxr-xr-x | subx/apps/factorial | bin | 167 -> 284 bytes | |||
-rw-r--r-- | subx/apps/factorial.subx | 86 |
3 files changed, 179 insertions, 11 deletions
diff --git a/subx/040---tests.cc b/subx/040---tests.cc new file mode 100644 index 00000000..10356174 --- /dev/null +++ b/subx/040---tests.cc @@ -0,0 +1,104 @@ +//: Beginning of level 3: support for automatically aggregating functions into +//: test suites. +//: +//: (As explained in the transform layer, level 3 runs before level 2. We +//: can't use any of the transforms in previous layers. But we *do* rely on +//: those concepts being present in the input. Particularly labels.) + +:(after "Begin Transforms") +// Begin Level-3 Transforms +Transform.push_back(create_test_function); +// End Level-3 Transforms + +:(scenario run_test) +% Reg[ESP].u = 0x100; +== 0x1 +main: + e8/call run_tests/disp32 # 5 bytes + f4/halt # 1 byte + +test_foo: # offset 7 + 01 d8 # just some unique instruction: add EBX to EAX + c3/return + +# check that code in test_foo ran (implicitly called by run_tests) ++run: inst: 0x00000007 + +:(code) +void create_test_function(program& p) { + if (p.segments.empty()) return; + segment& code = p.segments.at(0); + trace(99, "transform") << "-- create 'run_tests'" << end(); + vector<line> new_insts; + for (int i = 0; i < SIZE(code.lines); ++i) { + line& inst = code.lines.at(i); + for (int j = 0; j < SIZE(inst.words); ++j) { + const word& curr = inst.words.at(j); + if (*curr.data.rbegin() != ':') continue; // not a label + if (!starts_with(curr.data, "test_")) continue; + string fn = drop_last(curr.data); + new_insts.push_back(call(fn)); + } + } + if (new_insts.empty()) return; // no tests found + code.lines.push_back(label("run_tests")); + code.lines.insert(code.lines.end(), new_insts.begin(), new_insts.end()); + code.lines.push_back(ret()); +} + +string to_string(const segment& s) { + ostringstream out; + for (int i = 0; i < SIZE(s.lines); ++i) { + const line& l = s.lines.at(i); + for (int j = 0; j < SIZE(l.words); ++j) { + if (j > 0) out << ' '; + out << to_string(l.words.at(j)); + } + out << '\n'; + } + return out.str(); +} + +string to_string(const word& w) { + ostringstream out; + out << w.data; + for (int i = 0; i < SIZE(w.metadata); ++i) + out << '/' << w.metadata.at(i); + return out.str(); +} + +line label(string s) { + line result; + result.words.push_back(word()); + result.words.back().data = (s+":"); + return result; +} + +line call(string s) { + line result; + result.words.push_back(call()); + result.words.push_back(disp32(s)); + return result; +} + +word call() { + word result; + result.data = "e8"; + result.metadata.push_back("call"); + return result; +} + +word disp32(string s) { + word result; + result.data = s; + result.metadata.push_back("disp32"); + return result; +} + +line ret() { + line result; + result.words.push_back(word()); + result.words.back().data = "c3"; + result.words.back().metadata.push_back("return"); + return result; +} diff --git a/subx/apps/factorial b/subx/apps/factorial index e60e7d21..15fa68ec 100755 --- a/subx/apps/factorial +++ b/subx/apps/factorial Binary files differdiff --git a/subx/apps/factorial.subx b/subx/apps/factorial.subx index 31821dce..d71ea455 100644 --- a/subx/apps/factorial.subx +++ b/subx/apps/factorial.subx @@ -13,17 +13,18 @@ # 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes # main: - # prepare to make a call - 55/push . . . . . . . . # push EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # factorial(5) - 68/push . . . . . . . 5/imm32 # push 5 - e8/call . . . . . . factorial/disp32 - # discard arg - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add 4 to ESP - # clean up after call - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop . . . . . . . . # pop to EBP + e8/call run_tests/disp32 +#? # prepare to make a call +#? 55/push . . . . . . . . # push EBP +#? 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +#? # factorial(5) +#? 68/push . . . . . . . 5/imm32 # push 5 +#? e8/call . . . . . . factorial/disp32 +#? # discard arg +#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add 4 to ESP +#? # clean up after call +#? 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +#? 5d/pop . . . . . . . . # pop to EBP # exit(EAX) 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX @@ -59,4 +60,67 @@ factorial: $factorial:exit: c3/return +test_factorial: + # factorial(5) + # push arg + 68/push . . . . . . . 5/imm32 # push 5 + # call + e8/call . . . . . . factorial/disp32 + # discard arg + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add 4 to ESP + # if EAX == 120 + 3d/compare . . . . . . . 0x78/imm32/120 # compare EAX with 120 + 75/jump-if-unequal . . . . . . $test_factorial:else/disp8 + # print('.') + # push args + 68/push . . . . . . . Test_passed/imm32 + # call + e8/call . . . . . . write_stderr/disp32 + # discard arg + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add 4 to ESP + # return + c3/return + # else: +$test_factorial:else: + # print('F') + # push args + 68/push . . . . . . . Test_failed/imm32 + # call + e8/call . . . . . . write_stderr/disp32 + # discard arg + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add 4 to ESP + # end + c3/return + +## helpers + +write_stderr: # s : (address array byte) -> <void> + # write(2/stderr, (data) s+4, (size) *s) + # fd = 2 (stderr) + bb/copy . . . . . . . 2/imm32 # copy 2 to EBX + # x = s+4 + 8b/copy 1/mod/*+disp8 4/rm32/SIB 4/base/ESP 4/index/none . 1/r32/ECX 4/disp8 . # copy *(ESP+4) to ECX + 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add 4 to ECX + # size = *s + 8b/copy 1/mod/*+disp8 4/rm32/SIB 4/base/ESP 4/index/none . 2/r32/EDX 4/disp8 . # copy *(ESP+4) to EDX + 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX + # call write() + b8/copy . . . . . . . 4/imm32/write # copy 1 to EAX + cd/syscall . . . . . . . 0x80/imm8 # int 80h + # end + c3/return + +== data +Test_passed: + # size + 01 00 00 00 + # data + 2e/dot + +Test_failed: + # size + 01 00 00 00 + # data + 46/F + # vim:ft=subx:nowrap:so=0 |